# AI

Your app can ask a language model for an answer, a stream of text or an
embedding (a list of numbers that stands for a text's meaning). What
comes back is a value you can trust as much as a request body: it has a
type, it has been checked against that type's rules, it stayed within a
budget, it is faked in tests, and Jadpo tracks any personal data it was
made from.

Jadpo itself provides the `ai` interface your code calls, typed answers,
budgets, privacy checks and test fakes. A model provider, such as OpenAI
or Anthropic, is reached through a provider package
([External services](external-services.md)). Anything beyond that core,
such as tool calling, images or agent frameworks, is a package too.

| Part | In short |
|---|---|
| The capability | `ai`, one API in code, filled by a provider package through `provides/ai.jadpo` or by the built-in OpenAI-compatible driver for local models |
| Operations | `ai.generate(prompt, as: T)`, `ai.stream(prompt)`, `ai.embed(text, as: V)`; tool calling left to packages |
| Models | Named in `ai: { models: }`, each a `ModelId` setting; never a literal in code |
| Typed output | The provider's JSON-schema mode where it has one, the schema in the instructions where not; always decoded by `json.read`'s decoding; one bounded repair retry |
| Prompts | `type X = Prompt { ... }` with its text in `prompts/<name>.prompt`; placeholders checked as translations' are; free text only ever sent as delimited data |
| Embeddings | A `Vector { dimensions: n  distance: ... }` field in a pgvector column, an HNSW index derived from the queries, `order: [F nearest v]` returning `Nearest<T>` |
| Privacy | Every personal-data category sent to a model listed on the connection; `to_models: false` on a field; prompts and answers never logged by default |
| Limits | Output tokens per call; tokens per request; tokens and an estimated cost per day, for the app and per caller |
| Tests | One fake for every `ai` call; scripted answers and recorded ones; never a real model |
