# Streams, embeddings and the OpenAI and Anthropic packages

How a provider package maps streams and embeddings, and what Jadpo's
own OpenAI and Anthropic packages send. You need the mapping rules only
to write a package; an app picks a package and connects it
([Connecting a provider](the-ai-capability.md#connecting-a-provider)).

- **`stream:`** takes `call:`, `input:` and `pieces:`, required, and
  `refused_when:`, `cut_off_when:`, `done_when:` and `usage:`. `call:` is
  an operation that changes nothing, as `generate:`'s, its output type
  the shape of one event's data; its input is mapped as `generate:`'s,
  and a value written there (`stream: true`) asks the provider to
  stream. `pieces: event.<field>...` is where an event's piece of text
  is, `first` a list's first item; `cut_off_when:` and `done_when:` are
  `{ path: equals: }` on each event as sent. A stream is whole at `data:
  [DONE]` or an event `done_when:` marks; an `error` event, or one with
  an `error` member, breaks it; an event that marks nothing and has no
  text (a ping, a thinking block's) is passed over. Its events are read
  on the answer as sent, not decoded, since a provider's events differ in
  shape; their type checks the mapping and the stream's recording.
  `usage:` is read and checked but not counted, since a stream is
  charged the whole of the most it could use.
- **`embed:`** takes `call:`, `input:`, `vectors:`, `vector:`, `batch:`
  and `usage:`, required, and `index:`. Its input maps `request.model`,
  `request.texts` (onto a list of text, and required) and
  `request.dimensions`, `ai: { embedding_dimensions: }`, besides
  settings and values; `vectors: response.<field>` is the answer's list
  of objects, `vector: item.<field>` each item's vector (a `Vector` or a
  list of numbers) and `index: item.<field>` its place among the texts.
  `batch:` is the most texts the provider takes at once (see the note at
  the end of this topic). A text longer than the
  model's context in the package's table is `too_long` before sending. The answer
  is decoded by the package's types, then kept in the built-in driver's
  form, `usage.prompt_tokens` from `usage: { input: }`, so a rerun, a
  record and the budget read it as theirs.
- **`refused_when:`** is a place that holds anything, or `{
  holds: <place>  path: "<member>"  equals: <value> }`, any of them
  saying so, since OpenAI marks a refusal with a field and its content
  filter with a finish reason.
- **`answer:`** reads every item of a list where a field follows the list
  with no `first` (`response.content.text`), their texts joined in order
  and the items with none left out, as Anthropic answers in content
  blocks, a thinking block among them with no text.
- **`schema: { envelope: }`** names the envelope's shape:
  `AiEnvelope.openai` (the default, `{"type": "json_schema",
  "json_schema": {"name", "strict", "schema"}}`) or
  `AiEnvelope.anthropic` (`{"type": "json_schema", "schema"}` at
  `output_config.format`), with the schema cut as
  [Generating, its fake and the local driver](generating-its-fake-and-the-local-driver.md)
  says, which Anthropic's structured outputs also take. Anthropic has no
  JSON-only mode, so `json_only` with its envelope, and any
  envelope with `none`, is `package.provider_invalid`.
- **`CredentialScheme.header`** sends the credential alone in the header
  `credential_header:` names (Anthropic's `x-api-key`), a new provider
  key required for it and only for it; and **`headers:`**, another new
  key, lists the fixed headers every request carries, each `"Name:
  value"` (Anthropic's `anthropic-version: 2023-06-01`). Neither may name
  a header the driver writes itself (`Authorization`, `Content-Type`,
  `Accept`, `User-Agent`, `Host`, `Content-Length` and AWS's own). The
  surface record lists both.
- **A sandbox's embeddings.** A connection's `development: { sandbox: }`
  may name `embed`, the operation embeddings are logged as, when it is
  the `ai:` connection and its package maps `embed:`.
- **A provider package's types** read its answers leniently (a `T?`
  member left out is `none`, a member the type doesn't declare ignored)
  whether or not a connection names the package, so `jadpo package
  check` decodes its recordings as an app does.
- **`ai: { prices: }`** sets a price for a model by its name in
  `models:`, overriding its package's table: `input:` and
  `output:`, required, and `cached:` (the input's when left out), each a
  `Money` setting giving the price per million tokens, all three in one
  currency (`misconfigured` at the call otherwise). A price for a model
  `models:` doesn't name, or one that isn't money, is
  `ai.settings_invalid`.
- **The OpenAI package**, `packages/openai` (`OpenAi.v1`), is written
  from OpenAI's API reference and model pages: Chat Completions, `POST
  /chat/completions` with `model`, `messages` (the instructions as the
  `system` message), `max_completion_tokens` (`max_tokens` is
  deprecated) and the strict `response_format`; the answer
  `choices.0.message.content`; refused for `message.refusal` or a
  `content_filter` finish, cut off for a `length` finish; usage
  `prompt_tokens`, `completion_tokens` and
  `prompt_tokens_details.cached_tokens`. It streams the same request with
  `"stream": true` (its own input type, so a request that doesn't stream
  sends no `stream`), each chunk's `choices.0.delta.content` a piece,
  ended by `data: [DONE]`; and embeds through `POST /embeddings` with
  `model`, `input` and `dimensions`, reading `data[i].embedding` by
  `index`, up to 2,048 texts a request. Its key is a bearer credential;
  the connection gives `host:`, `https://api.openai.com/v1`, so a
  compatible gateway or a test's server can stand in. Its failures are
  its operations' (`error.code`), and none is mapped. So a refusal of the
  request is `misconfigured`, and a `429` or `5xx` is `unavailable`, as
  for every package's operation.
- **The Anthropic package**, `packages/anthropic` (`Anthropic.v1`), is
  written from Anthropic's Messages reference and guides: `POST
  /v1/messages` with `model`, `max_tokens`, `system` (the instructions)
  and `messages`, the schema at `output_config.format` (its structured
  outputs, out of beta; `output_format` is deprecated); the answer every
  `text` block's text; refused for a `refusal` stop reason, cut off for
  `max_tokens`; usage `input_tokens`, `output_tokens` and
  `cache_read_input_tokens`. It streams the same request with `"stream":
  true`, each `content_block_delta`'s `delta.text` a piece, a
  `message_delta`'s `delta.stop_reason` marking a refusal or a cut-off,
  ended by `message_stop`. Its key goes in `x-api-key` and its version,
  `2023-06-01`, in `anthropic-version`; errors are `error.type`. Anthropic
  has no embeddings endpoint, so the package maps no `embed:`, and an
  app embedding through it is `ai.settings_invalid` at the call. Its
  current models think by default, and their thinking counts against
  `max_tokens`, so against `max_output:`; the package leaves thinking as
  each model sets it, since the setting that turns it down differs by
  model and a mapping writes no nested object.
- **The tables** come from the providers' own pages, and are refreshed
  whenever those change. They hold context windows of 1,050,000
  tokens for `gpt-6-astra`, `gpt-6.1-sol` and `gpt-6-luna`, 8,192 tokens
  a text for `text-embedding-3-small` and `-large`, and 1M (taken as
  1,000,000) for `claude-fable-5-1`, `claude-opus-5-5`,
  `claude-sonnet-5-5` and `claude-haiku-5-5`; standard prices per
  million input, cached and output tokens in US dollars, from 2026-10-20:
  OpenAI's 10.00, 1.00 and 50.00 (Astra), 2.00, 0.10 and 10.00 (Sol), and
  0.10, 0.01 and 0.50 (Luna), its embedding models' 0.02 and 0.13 input
  (no output; cached repeating input, which OpenAI doesn't publish);
  Anthropic's 10.00, 0.25 and 50.00 (Fable 5.1), 4.00, 0.20 and 20.00
  (Opus 5.5), 2.00, 0.10 and 10.00 (Sonnet 5.5), and 0.10, 0.01 and 0.50
  (Haiku 5.5, for prompts up to 100,000 tokens). Long-prompt prices
  (OpenAI's past 272,000 input tokens, Haiku 5.5's past 100,000),
  batch, fast and regional prices and cache writes aren't held; an app
  that needs them sets `ai: { prices: }`.
- **Recordings.** Each package keeps a recording of each operation,
  `recordings/<resource>.<operation>.json`, made by `jadpo package
  record` from the answer each provider's documentation shows, sent by a
  server in the test, never a live call: a stream's is one event, a
  piece of text. A test records them again from the same answers and
  compares them with the files, and `jadpo test` decodes them.
- **The documentation used:**
  - OpenAI: <https://developers.openai.com/api/reference/resources/chat/subresources/completions/methods/create>,
    <https://developers.openai.com/api/reference/resources/chat/subresources/completions/streaming-events>,
    <https://developers.openai.com/api/reference/resources/embeddings/methods/create>,
    <https://developers.openai.com/api/docs/guides/structured-outputs>,
    <https://developers.openai.com/api/docs/guides/error-codes>,
    <https://developers.openai.com/api/docs/models>, each model's page
    under <https://developers.openai.com/api/docs/models/> (`gpt-6-astra`,
    `gpt-6.1-sol`, `gpt-6-luna`, `text-embedding-3-small`,
    `text-embedding-3-large`), and
    <https://developers.openai.com/api/docs/pricing>.
  - Anthropic: <https://platform.claude.com/docs/en/api/messages/create>,
    <https://platform.claude.com/docs/en/build-with-claude/streaming>,
    <https://platform.claude.com/docs/en/build-with-claude/structured-outputs>,
    <https://platform.claude.com/docs/en/build-with-claude/thinking>,
    <https://platform.claude.com/docs/en/api/errors>,
    <https://platform.claude.com/docs/en/api/versioning>,
    <https://platform.claude.com/docs/en/about-claude/models/overview>,
    <https://platform.claude.com/docs/en/build-with-claude/context-windows>
    and <https://platform.claude.com/docs/en/about-claude/pricing>.

Not built yet: a `batch:` below 100, the most `ai.embed_all` sends, is
`toolchain.not_built_yet`, since every text goes in one request.
