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 aiin a provider package'sprovides/ai.jadpotakesgenerate:andmodels:, both required, andstream:andembed:(Streams, embeddings and the OpenAI and Anthropic packages). Any other key, like every mistake in the mapping, ispackage.provider_invalidwhere it is written.ai: { connection: }naming a connection whose package has noprovide aiisai.settings_invalid.generate:takescall:,input:,answer:,usage:andschema:, required, andrefused_when:andcut_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 (nodecode:). Its fields' decoders and encoders aren't run: a package function has no site at anaicall.input:maps each field of the operation's input, every required one, by its name, sent by itswire_name::request.model, the model's name after its provider (gpt-4.1-miniforopenai/gpt-4.1-mini, since the connection is the provider; the built-in driver sends theModelIdwhole, as vLLM names a model), onto text;request.max_outputonto a whole number;request.instructionsonto text;{ each: request.messages as: { } }onto a list of objects,item.roleonto text or an enum anditem.contentonto text;settings.<name>, a setting the package declares, given by each connection; or text, a whole number or a flag written there. Whererequest.instructionsis mapped the instructions go there andrequest.messagesholds the rest; otherwise the messages open with them as thesystemmessage, as Chat Completions takes them.request.schemaisn't mapped: the schema goes whereschema: { at: }says. No other nested object is mapped.answer:andrefused_when:are places in the operation's output,response.<field>,firststepping into a list's first item, read from the answer decoded by the package's types:answer:ends at text, and the call isrefusedwhenrefused_when:'s place holds anything butnone, blank text orfalse.cut_off_when: { path: equals: }is read on the answer as sent, asfailed_when:is, itspath:dotted by the members' spellings on the wire and a list's items by number, checked against the output type; it ends the callover_budget.usage:namesinput:andoutput:, required, andcached:, each a place in the output that is a whole number or aTokenCount, or0for a count the provider never gives; the budgets count them.schema: { mode: at: }:mode:is required. WithAiSchema.strict_subsetorAiSchema.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 asresponse_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 aTbuilt onText. WithAiSchema.none,at:is refused. The example above writesat: "response_format"for that reason. Withjson_onlyandnonethe 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 isschema: { 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 ofmodel:, its name after its provider, andtokens:, 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_longis 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'stokens:ends the calltoo_longwith 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, somisconfigured, as every one of them is.- The fake. In
jadpo testandjadpo calltheaifake 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 devfakes 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 (sourcegenerated), or with a condition scripted byjadpo calls script ai.<prompt> <condition>(sourcescripted). Only the five conditions can be scripted, and the prompt is named as a test'sfakes:names it.- The sandbox.
development: { sandbox: [<prompt>] }on theaiconnection 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 ofDriver.openai_compatibleisn't faked, so scripting one iscli.calls_arguments.
Markdown for agents: /docs/a-package-filling-ai.md.Built from Jadpo main at a707efb.