Types¶
lm15.types — Core vocabulary for foundation model interaction.
The fundamental unit is the Part: an atomic, typed block of content. Parts compose into Messages (attributed to a speaker). Messages compose into Requests (sent to a model). Models produce Responses (containing a Message).
Streams reveal Responses incrementally through Deltas — typed fragments of streamable response parts. Not every Part is streamable; use StreamablePart / NonStreamablePart to make that boundary explicit.
Design principles:
-
Parts, Deltas, and Events are proper discriminated unions. Each variant is an independent frozen dataclass. Fields that don't belong to a variant don't exist on it — accessing them raises AttributeError. Check .type or use isinstance() before accessing variant-specific fields.
-
One representation per concept. A Delta is always a typed Delta object, never a dict. Tool call arguments are called "input" everywhere — in memory, in deltas, in serialization.
-
Frozen + slotted dataclasses throughout. Dataclass attributes are shallowly immutable after construction: fields cannot be rebound, while caller-provided JSON containers remain ordinary mutable Python containers.
-
Universal structure, provider-specific values. The shape of a Request is universal. Provider-specific configuration flows through Config.extensions — clearly separated from universal knobs. Provider-specific response metadata lives in Response.provider_data.
-
Runtime validation is deliberately narrow. Constructors enforce the invariants that make objects meaningful (required identities, one media source, non-negative token counts, JSON-serializable extension fields) while LMs remain responsible for normalizing provider quirks before constructing these types.
ContinuationState
dataclass
¶
Opaque provider-owned state needed to continue/replay a transcript.
Continuation state is not visible model content. It travels with the Message or Part it describes so provider adapters can reconstruct future provider requests without relying on detached response metadata.
TextPart
dataclass
¶
A block of text content.
ImagePart
dataclass
¶
Bases: _MediaMixin
An image, addressed by exactly one of data/url/file_id/path.
AudioPart
dataclass
¶
Bases: _MediaMixin
Audio content, addressed by exactly one of data/url/file_id/path.
VideoPart
dataclass
¶
Bases: _MediaMixin
Video content, addressed by exactly one of data/url/file_id/path.
DocumentPart
dataclass
¶
Bases: _MediaMixin
A document (PDF, etc.), addressed by exactly one of data/url/file_id/path.
BinaryPart
dataclass
¶
Bases: _MediaMixin
Arbitrary binary content, addressed by exactly one media source.
ToolCallPart
dataclass
¶
The model requests an external computation.
ToolResultPart
dataclass
¶
The result of an external computation, sent back to the model.
ThinkingPart
dataclass
¶
Model reasoning trace.
Hidden thinking (Anthropic redacted_thinking, an OpenAI reasoning
item with no summary) is a ThinkingPart with empty text and its
replay state in continuation. There is no flag and no placeholder
text (MAP-7 rule 11, ratified 2026-09-06).
RefusalPart
dataclass
¶
Model explicitly refused to respond.
Refusals require non-empty text because they are final semantic content;
empty TextPart/ThinkingPart values remain allowed for streaming
reassembly and provider redaction edge cases.
CitationPart
dataclass
¶
A reference to source material.
DataPart
dataclass
¶
Structured data as content (changes/2026-09-17-judgments.md, D2).
In a user/system message: structured input (a provider that reads
JSON state takes value as such; a text-only wire gets it as JSON
text). In an assistant message: the answer to a json_schema
request that declares judgments (MAP-14) — value is the model's
JSON object, probabilities a distribution per judgment over its
declared keys when one was measured, method how (JudgmentMethod).
value is an opaque payload: any JSON value, verbatim (INV-002).
Message
dataclass
¶
A contribution to a conversation, attributed to a speaker.
A message is a sequence of typed Parts. Roles:
user— end-user inputassistant— model outputtool— tool execution resultsdeveloper— high-authority instructions from the application developer. Can appear mid-conversation to inject new instructions without invalidating the KV-cache prefix. On OpenAI this maps to the nativedeveloperrole; on other providers the LM converts it to a user message with a clear prefix.
text: str | None
property
¶
Text only when the message contains text and nothing else.
developer(content: PromptContent) -> 'Message'
staticmethod
¶
Create a developer message.
Developer messages carry instructions with higher authority than
user messages (equivalent to OpenAI's developer role). They
can appear anywhere in the conversation — including mid-conversation
— which is useful for injecting new instructions without
invalidating the KV-cache prefix built from earlier turns.
On providers that don't natively support a developer role
(Anthropic, Gemini), the LM converts these to user messages
with a clear [developer] prefix so the model still sees the
instruction boundary.
tool(results: 'str | ToolResultPart | Sequence[ToolResultPart] | dict[str, ToolResultContent]', output: 'ToolResultContent | None' = None, *, is_error: bool = False) -> 'Message'
staticmethod
¶
Create a tool message. Three spellings:
Message.tool(call_id, output, is_error=False)— one result.Message.tool({call_id: output, ...})— answer several calls at once (cannot express is_error; use the other forms).Message.tool(part_or_parts)— explicit ToolResultPart(s), e.g. from :func:lm15.tool_result.
parts_of(cls: type[_P]) -> list[_P]
¶
Return all parts that are instances of cls.
first(cls: type[_P]) -> _P | None
¶
Return the first part that is an instance of cls, if any.
TextDelta
dataclass
¶
A text fragment arriving during streaming.
logprobs carries the token logprobs for exactly the tokens in this
fragment, when the request asked for them (Config.logprobs) and the
provider streams them per chunk (verified live for OpenAI Responses,
2026-09-01). Materialization concatenates fragment logprobs in arrival
order into Response.logprobs. logprobs_complete=False means
local text editing left some retained text without its original score
(for example, a stop inside a token). Scores always describe original,
whole provider tokens. True does not promise the provider supplied any.
ThinkingDelta
dataclass
¶
A reasoning/thinking fragment arriving during streaming.
AudioDelta
dataclass
¶
An audio fragment arriving during streaming.
Media deltas are partial stream chunks, not final media parts. They may carry unaligned base64 data, a URL/file id update, or metadata only; final media validation happens when the stream is assembled into an AudioPart.
ImageDelta
dataclass
¶
An image fragment, addressed by exactly one of data/url/file_id.
ToolCallDelta
dataclass
¶
A tool-call input fragment, optionally carrying call identity.
CitationDelta
dataclass
¶
A citation fragment arriving during streaming.
ContinuationDelta
dataclass
¶
Opaque provider continuation state arriving during streaming.
part_index=None attaches the state to the assistant message; otherwise it attaches to the completed part with that index.
ErrorDetail
dataclass
¶
Structured error information. A dataclass, not a dict.
StreamStartEvent
dataclass
¶
The response stream has started.
StreamDeltaEvent
dataclass
¶
A typed content delta arrived.
StreamEndEvent
dataclass
¶
The response stream completed.
StreamErrorEvent
dataclass
¶
The stream failed.
FunctionTool
dataclass
¶
Serializable function tool specification sent to the model.
BuiltinTool
dataclass
¶
A provider-native tool (web search, code execution, etc.).
Reasoning
dataclass
¶
How much hidden thinking the model does before it answers (MAP-7).
effort is the one dial and is required. Vocabulary: off,
minimal, low, medium, high, xhigh, max. Every
provider has this dial with these words; each model accepts a subset
and rejects the rest loudly. Adapters send the word verbatim where
the provider has levels; budget-only model classes (Anthropic 4.5 and
earlier, Gemini 2.5) express it through one documented grading table.
Words with no native level on a provider RAISE rather than downgrade.
There is no "adaptive" value: leaving config.reasoning unset is
how you let the model decide, on every provider. effort="off"
reaches the wire as the provider's disable or fails loudly where the
provider cannot disable (MAP-5).
thinking_budget is a token cap on providers that count thinking
separately (Anthropic manual class, Gemini); it RAISES where the wire
has no budget. On budget-only classes the budget is the spelling on
the wire and effort is the intent. total_budget was removed
2026-09-02: Config.max_tokens is the ceiling.
summary is visibility: None = provider default, "auto" =
show the thinking where a knob exists (satisfied silently where the
provider always shows it), "concise"/"detailed" = OpenAI's
detail levels (RAISE elsewhere).
CacheConfig
dataclass
¶
Universal prompt cache configuration (MAP-6, docs/mapping-rules.md).
Every provider caches in up to three tiers: automatic (nothing to
send, best-effort), breakpoint (a mark on a block, guaranteed above a
per-model minimum), resource (a stored, named, billed object). The
fields name INTENTS; each adapter maps an intent to the best tier it
has, and the result is always visible in Usage.cache_read_tokens.
- mode="auto": the cheapest safe instruction (a mark on the stable beginning where marks exist; nothing elsewhere).
- mode="off": send nothing; where a provider has a real off switch for cache WRITES, send it.
- prefix="stable": mark the end of system + tools. prefix="history": mark the end of the last message (a growing chat). Providers without marks fall back to the automatic tier: that fallback spends nothing and its outcome is observable, the two conditions that permit it.
- prefix_until_index: the same intent with a precise position (the
last block of that message). Mutually exclusive with
prefix. - retention: "long" asks for the longer lifetime at extra cost; providers without one RAISE.
- key: a best-effort routing/affinity hint; providers without one RAISE.
- resource: the id of a stored cache object (
CacheInfo.id) to reference; providers without the resource tier RAISE.
ToolChoice
dataclass
¶
How the model should use tools.
from_tools(allowed: Tool | Sequence[Tool | str], *, mode: ToolChoiceMode = 'auto', parallel: bool | None = None) -> 'ToolChoice'
classmethod
¶
Create a choice by explicitly converting Tool objects to names.
Config
dataclass
¶
Generation parameters.
Universal fields are typed. Provider-specific settings go in
extensions — a clearly-separated namespace that never pretends
to be part of the universal schema.
Request
dataclass
¶
Bases: _ModelRequest
A complete request to a foundation model.
The composed artifact sent to the model — conversation history, system instructions, available tools, and generation config.
TopLogprob
dataclass
¶
One scored alternative token at a decoding step.
bytes is the token's UTF-8 byte sequence when the provider reports
it (OpenAI); token_id is the vocabulary id when the provider
reports it (Gemini). Absence means "not reported", never zero.
TokenLogprob
dataclass
¶
The chosen token at one decoding step, with ranked alternatives.
top is the provider-reported ranked candidate list (descending
logprob). Providers differ on whether the chosen token appears in
top — no guarantee either way, and providers also differ on
whether the requested alternative count includes the chosen token
(Gemini documents that it does; OpenAI counts alternatives only).
lm15 preserves the provider-reported list as-is.
Usage
dataclass
¶
Token usage.
input_tokens and output_tokens are common dimensions.
total_tokens is the provider-reported or billed total when present;
providers may include reasoning, audio, cache, or future token classes in
totals differently, so it is not forced to equal input + output.
reasoning_tokens is populated only when the provider reports an exact
separate reasoning/thinking token count. Some providers, notably Anthropic,
can return ThinkingPart content while reporting only combined
output_tokens; in that case reasoning_tokens remains None.
Every counter is int | None: None means "the provider did not
report this dimension", which is distinct from a reported 0.
Usage() with no arguments therefore means "nothing reported" and
serializes to {} (omitted entirely by enclosing serializers, per
docs/serde-rules.md).
total_tokens: an explicit value is preserved as provider telemetry.
When omitted, it auto-computes as input_tokens + output_tokens only
when BOTH are present; if either is None the total stays None.
Arithmetic over usage (e.g. InferencePricing.estimate) must treat
None as "unknown", never as zero.
Counters are provider-verbatim (spec/types.md, Usage, 2026-09-02): the
same field means different things across providers. input_tokens
includes cached tokens on OpenAI, Gemini, and xAI but not on Anthropic
(whose cache counters are disjoint); output_tokens includes
reasoning on OpenAI and Anthropic but not on Gemini and xAI. lm15 does
not normalise: that is what a bill reconciles against. A consumer
comparing across providers must apply the provider's rule from the
table in the spec; Response.provider_data and the router's
resolution say which provider answered.
Response
dataclass
¶
The composed artifact returned by a foundation model.
Response keeps only minimal convenience properties. Use
response.message.first(...) and response.message.parts_of(...)
for variant-specific content access.
logprobs_complete=False means local text editing could not preserve
scores for all retained text. Remaining scores describe whole original
tokens only; never infer a probability for the unscored text. True is
the default and does not promise the provider supplied scores at all.
text: str | None
property
¶
Concatenated assistant text, when the response has text.
Message.text is intentionally strict and only returns text for
pure-text messages. For model responses, citation and thinking parts
are metadata around the visible answer, so they do not make
Response.text unavailable.
data: Any
property
¶
The answer of a judgment request: the DataPart's value.
Falls back to the parsed JSON text of a plain structured-output
response (json) so response.data reads the same on a wire
that answered with text; None when there is neither.
probabilities: dict[str, dict[str, float]] | None
property
¶
Per-judgment distributions over the declared keys, or None when none was measured (never a fabricated one).
json: Any
property
¶
Parsed exact JSON text, or None when parsing fails.
Valid JSON null also returns None; use parse_json() when
parse failures should be reported distinctly.
expected(field_name: str) -> float | None
¶
Σ p·i over an ordered judgment's levels (Jev's score), or None.
parse_json(*, default: Any = _MISSING) -> Any
¶
Parse the response text as exact JSON.
FileUploadRequest
dataclass
¶
A file upload request.
Files are an ACCOUNT-scoped resource on every provider, so there is
no model field. Uploads can be backed by in-memory bytes or by a
local path. Path-backed uploads are lazy: LMs can stream from disk
instead of forcing the whole file into memory at construction time,
and they are runtime-only — the canonical serializer rejects them
because a local path is meaningless off this machine.
FileInfo
dataclass
¶
A snapshot of one provider-side stored file.
id is the canonical reference for this provider: paste it into a
media Part's file_id and the chat mapping places it on the wire
(OpenAI/Anthropic file ids verbatim; Gemini the file URI, because
Gemini requests address files by URI, not resource name).
Optional fields stay None when the provider does not report the
value (OpenAI reports no MIME type; only Gemini reports expiry).
downloadable is tri-state: True/False when the provider
states the capability, None when it does not say.
FilePage
dataclass
¶
One page of stored files plus an opaque continuation cursor.
next_cursor is provider-issued and opaque: pass it back to
file_list to fetch the next page; None means the listing is
complete.
CacheInfo
dataclass
¶
A snapshot of one provider-side stored cache object.
id is the provider's reference verbatim; place it in
CacheConfig.resource. tokens is the stored token count when
reported (storage cost = tokens x hours x the provider's rate).
expires_at is ISO-8601 UTC, normalized from the provider's form.
CachePage
dataclass
¶
One page of stored cache objects plus an opaque continuation cursor.
CachedPrefix
dataclass
¶
A reusable prompt beginning: cached = lm.cache(prefix).
prefix is a Request whose model, system, tools, and messages form
the reusable part (its config must be default: a cached object has no
temperature). resource is the stored object on providers with
that tier, None on providers that mark blocks or cache
automatically. Optional provider preserves a router destination;
prefix/resource models remain wire names, while suffix requests use
provider:wiremodel. Reuse with the same router configuration/account.
request(messages) builds a Request that appends
messages and sets the cache boundary at the seam; cached +
messages is Python sugar for it. The built Request and its wire
bytes are what the contract pins; the sugar is per-language.
cache_config() -> CacheConfig
¶
The CacheConfig that marks the seam between prefix and suffix.
request(messages: 'str | Message | Sequence[Message] | Request', *, config: Config | None = None) -> Request
¶
Append messages to the prefix and set the cache boundary.
messages may be a string (one user message), a Message, a
sequence of Messages, or a Request (same model, no system, no
tools: the prefix owns those). config supplies generation
settings; its cache must be unset (the prefix decides).
BatchRequest
dataclass
¶
A batch of model requests.
Each nested Request carries its own model. The optional top-level model is only a routing/default convenience; when omitted, it is inferred from the first nested request and does not constrain the rest of the batch.
BatchJobInfo
dataclass
¶
A snapshot of a provider-side batch job — the ticket.
id is the provider's job id, a plain string; store it anywhere.
created_at is normalized to an ISO-8601 UTC string when the
provider reports a creation time. Raw job state stays verbatim in
provider_data.
BatchEntry
dataclass
¶
The fate of one request in a batch, in submission order.
Partial failure is a first-class outcome: a completed job may mix
succeeded and errored entries. succeeded carries a full
canonical Response; errored carries a canonical ErrorDetail;
cancelled and expired carry neither.
ImageGenerationRequest
dataclass
¶
Bases: _PromptRequest
Text (and optionally input images, for edits) in; images out.
size takes the provider's own sizing vocabulary — like model
and voice, the field is portable but the values are not (OpenAI:
pixels; Gemini: aspect ratios). images are ordinary ImageParts
(inline data / url / file_id / path); adapters route them to the
provider's real edit door and raise where the wire has none.
ImageGenerationResponse
dataclass
¶
Generated images, plus any text the model said while drawing (Gemini routinely narrates; dropping it would be silent data loss).
SpeechGenerationRequest
dataclass
¶
Bases: _PromptRequest
Text-to-speech. Named for what every wire actually sells: speech.
voice and format take the provider's own vocabularies. An
omitted field means the SERVER decides (OpenAI's real default format
is MP3); lm15 injects no defaults of its own. format raises on
providers whose wire has no slot for it (Gemini: always PCM).
VideoGenerationRequest
dataclass
¶
Bases: _PromptRequest
Text (and optionally an input image) in; a video JOB out.
Video is job-shaped on every wire that sells it (Sora, Veo,
grok-imagine): submission returns a ticket, not bytes. seconds
maps to the provider's duration knob and raises where the wire has
none; images are input frames (image-to-video), ordinary
ImageParts.
VideoJobInfo
dataclass
¶
A snapshot of a provider-side video job — the ticket.
id is the provider's job id, a plain string; store it anywhere
(on xAI it is the ONLY copy — the wire has no list endpoint).
progress is a percentage when the provider reports one. Raw job
state stays verbatim in provider_data.
LiveServerUsageEvent
dataclass
¶
Billed usage for a response that did not end the turn.
A function-call response (the model waits for results; the turn stays
open) and a cancelled response (barge-in) both consume tokens the
provider reports. turn_end carries the usage of a completed turn;
this event carries the usage of everything else, so a session's bill
is the sum of every usage and turn_end event. Never a turn
boundary; dispatch loops ignore it.
ToolCallInfo
dataclass
¶
Callback view of a tool call without the part discriminator.
continuation_data(value: Message | Part | tuple[ContinuationState, ...], provider: str, kind: str) -> JsonObject | None
¶
Return provider-owned continuation data from a Message or Part.
This is a convenience helper for provider adapters and advanced callers. It returns the opaque JSON object attached to the first matching ContinuationState, or None when no match exists.
text(content: str, *, continuation: Sequence[ContinuationState] | ContinuationState | None = None) -> TextPart
¶
Create a text part.
data(value: JsonValue, *, probabilities: dict[str, dict[str, float]] | None = None, method: JudgmentMethod | None = None, continuation: Sequence[ContinuationState] | ContinuationState | None = None) -> DataPart
¶
Create a data part (structured input, or a judged answer).
tool_result(id: str, content: ToolResultContent, *, name: str | None = None, is_error: bool = False, continuation: Sequence[ContinuationState] | ContinuationState | None = None) -> ToolResultPart
¶
Create a tool result part.
content can be a sequence of parts, a single part, or a string (which becomes a TextPart). Raw bytes should be wrapped in a media part; use BinaryPart for arbitrary bytes that are not image/audio/video/document.