Skip to content

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:

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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 input
  • assistant — model output
  • tool — tool execution results
  • developer — 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 native developer role; 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.