Jadpo is experimental and not ready for production. These docs follow the compiler's main branch as of 30 Oct 2026. What's built · Install 0.1.0
jadpo
Reference: AI

AI

A package filling ai

The full rules for a provider package's provides/ai.jadpo, which maps ai's operations onto the provider's. An app only names the package's connection in ai: { connection: }; the overview is under How a provider fills it.

  • provide ai in a provider package's provides/ai.jadpo takes generate: and models:, both required, and stream: and embed: (Streams, embeddings and the OpenAI and Anthropic packages). Any other key, like every mistake in the mapping, is package.provider_invalid where it is written. ai: { connection: } naming a connection whose package has no provide ai is ai.settings_invalid.
  • generate: takes call:, input:, answer:, usage: and schema:, required, and refused_when: and cut_off_when:. call: is one of the package's operations that changes nothing (changes: false), since generation changes nothing at the provider, and whose answer its types read (no decode:). Its fields' decoders and encoders aren't run: a package function has no site at an ai call.
  • input: maps each field of the operation's input, every required one, by its name, sent by its wire_name:: request.model, the model's name after its provider (gpt-4.1-mini for openai/gpt-4.1-mini, since the connection is the provider; the built-in driver sends the ModelId whole, as vLLM names a model), onto text; request.max_output onto a whole number; request.instructions onto text; { each: request.messages as: { } } onto a list of objects, item.role onto text or an enum and item.content onto text; settings.<name>, a setting the package declares, given by each connection; or text, a whole number or a flag written there. Where request.instructions is mapped the instructions go there and request.messages holds the rest; otherwise the messages open with them as the system message, as Chat Completions takes them. request.schema isn't mapped: the schema goes where schema: { at: } says. No other nested object is mapped.
  • answer: and refused_when: are places in the operation's output, response.<field>, first stepping into a list's first item, read from the answer decoded by the package's types: answer: ends at text, and the call is refused when refused_when:'s place holds anything but none, blank text or false. cut_off_when: { path: equals: } is read on the answer as sent, as failed_when: is, its path: dotted by the members' spellings on the wire and a list's items by number, checked against the output type; it ends the call over_budget.
  • usage: names input: and output:, required, and cached:, each a place in the output that is a whole number or a TokenCount, or 0 for a count the provider never gives; the budgets count them.
  • schema: { mode: at: }: mode: is required. With AiSchema.strict_subset or AiSchema.json_only, at: is required and names, dotted, the member of the request Jadpo writes the mode's envelope in, the one the built-in driver sends as response_format: {"type": "json_schema", "json_schema": {"name": <prompt>, "strict": true, "schema": ...}}, the schema cut as Generating, its fake and the local driver says, or {"type": "json_object"}; nothing for a T built on Text. With AiSchema.none, at: is refused. The example above writes at: "response_format" for that reason. With json_only and none the schema goes in the instructions, under # Answer format, whole, and a JSON answer in a Markdown code fence is read without the fence. Anthropic's envelope is schema: { envelope: AiEnvelope.anthropic }; Gemini's waits for a package that needs it.
  • models: { context: } names <library>.<constant>, a constant of the package's own libraries listing each model as an object of model:, its name after its provider, and tokens:, its context window; the mapping reading it counts as using the constant. prices: is optional, checked to name such a constant, and read by the cost estimate.
  • too_long is checked before sending, before the call takes a step: the rendered messages (the schema in the instructions included) are taken to be a token for each four bytes of UTF-8, rounded up, OpenAI's rule of thumb, and a total over the model's tokens: ends the call too_long with nothing sent. A model the table doesn't list isn't checked. max_output: isn't added to the total, as the page counts only the prompt. A provider's own refusal of a long prompt is one of its operation's failures, so misconfigured, as every one of them is.
  • The fake. In jadpo test and jadpo call the ai fake stands in the package's connection too, ahead of the package's fake, answering in the built-in driver's form whatever the connection, as Generating, its fake and the local driver says. An answer from the connection's own driver or its sandbox is read through the mapping. The request is still made from the mapping, so what would be sent is checked and logged.
  • jadpo dev fakes the package's connection, as it fakes every provider call (Fakes and the development call log). Each prompt is answered with a generated value of the call's type (source generated), or with a condition scripted by jadpo calls script ai.<prompt> <condition> (source scripted). Only the five conditions can be scripted, and the prompt is named as a test's fakes: names it.
  • The sandbox. development: { sandbox: [<prompt>] } on the ai connection names the app's own prompts beside the package's operations. Those go to the provider's sandbox with the developer's credential, never live, as a sandboxed operation does, under the app's own limits (How budgets are counted). A prompt asked of Driver.openai_compatible isn't faked, so scripting one is cli.calls_arguments.

Markdown for agents: /docs/a-package-filling-ai.md.Built from Jadpo main at a707efb.