Typed output
ai.generate(prompt, as: T) never hands back a value that hasn't passed
every one of T's rules. You write the type; Jadpo turns it into a
schema, asks for an answer in that shape, and decodes the answer the way
it decodes a request body. The steps, in order:
- The schema. The compiler writes
T's JSON Schema when it builds your app. It is the same schema the API record and OpenAPI use (OpenAPI), with each field's///description, so the model sees what each field means. - Asking. Where the package's
schema:mode isstrict_subset, Jadpo sends the schema in the provider's structured-output mode, cut to the keywords the provider enforces: types, required fields, enums and nesting. Limits it doesn't enforce, such asmax_length,patternor aruleacross fields, are checked in step 3. Withjson_onlyornone, Jadpo adds the schema to the instructions under a fixed heading and asks for JSON alone. - Decoding. The answer goes through the same decoding as
json.read(…, as: T), every constraint and named rule included. There is no second way in. ATbuilt onTextasks for plain text and skips the schema. - One repair. If decoding fails and
attempts:allows, Jadpo asks once more with the previous answer and theDecodeProblem(its path and reason, never a value from the prompt) appended as a correction. The default is 2 attempts in all and the most is 3; each attempt counts against every limit. - The result. A decoded
T, ormalformedcarrying the lastDecodeProblem. A refusal or a cut-off ends the call at once.
The built-in driver's mode is strict_subset. It sends OpenAPI's schema
for T, cut to what the mode enforces. A T whose JSON isn't an
object, such as an enum or a list, is asked for as the member value of
one. A package's mode may be json_only or none. The details are under
Generating, its fake and the local driver
and A package filling ai.
as: takes most types, but not all:
as: takes |
Refused, and why |
|---|---|
Any type json.read takes: objects, lists, enums, Text types, the standard types |
Secret values (ApiKey, Password and the rest): configuration.secret_flow, a model never makes a secret |
References (X.id): a model can't know which records exist; pass the candidates in and ask for an enum or a field the code looks up (semantic.ai_output_type) |
|
File, Bytes, PresentationText: not something JSON from a model can be |
A model's answer is input from outside, like a request body: typed and checked, but never proof of anything. Policy never grants access because of it, and its text is escaped wherever it is shown, as any user's text is.
Since a model can't name records, a reply that cites help articles asks for their slugs, a field of text, picked from the articles the prompt was given. Here the action first embeds the question, finds the nearest articles (Embeddings and search) and passes them to the prompt:
// domains/support/types/support_answer.jadpotype ArticleSlug = Text { max_length: 120}/// What the assistant says back, and which articles it drew on.type SupportAnswer = Output { /// False when the articles given don't answer the question. resolved_by_articles: Bool /// The reply to show the customer, at most a few short paragraphs. reply: Text { max_length: 2000 } /// The slugs of the articles the reply used, from those given. cited_slugs: List<ArticleSlug> { max_length: 5 }}
// domains/support/actions/answer_customer.jadpouse support/types { AssistantUnavailable, CustomerQuestion, SupportAnswer }use support/prompts { AnswerQuestion }/// Answers a customer's question from the closest help articles.action answer_customer(asked: CustomerQuestion) fails AssistantUnavailable -> SupportAnswer { var question_meaning: ArticleMeaning = attempt ai.embed(asked.text, as: ArticleMeaning) { timed_out: AssistantUnavailable over_budget: AssistantUnavailable too_long: AssistantUnavailable } var nearest_excerpts: List<Nearest<ArticleExcerpt>> = closest_articles(question_meaning) return attempt ai.generate( AnswerQuestion { question: asked.text articles: list.map(nearest_excerpts, found -> found.item) reader_locale: asked.locale }, as: SupportAnswer, max_output: 600 ) { refused: AssistantUnavailable malformed: AssistantUnavailable timed_out: AssistantUnavailable over_budget: AssistantUnavailable too_long: AssistantUnavailable }}
Markdown for agents: /docs/typed-output.md.Built from Jadpo main at a707efb.