# Framework Format Reference ## Purpose This document is the canonical contract for Promptkit prompt-definition, profile, and schema files. The [Go package consumer guide](consumers/pkg-promptkit.md) explains how to select these sources and invoke the engine. The [OpenAI-compatible integration contract](integrations/openai-compatible-chat.md) owns the resulting outbound wire behavior. Prompt and profile sources recursively discover files ending in `.yaml` or `.yml`. Each prompt-definition and profile file contains exactly one YAML document; comments and trailing whitespace are allowed. YAML decoding is strict: unknown fields are errors for the selected definition. Definitions are selected by their YAML `id`, not their file name or directory. ## Prompt Definitions A prompt definition describes inputs, Go-template messages, an optional default profile, and an output contract. ```yaml id: meeting.summary version: "1.0.0" default_profile: local-summary description: Summarize a synthetic meeting note. session_id: '{{.session}}' inputs: - name: note required: true content_type: text/plain description: Meeting note to summarize. messages: - role: system content: Return a concise summary. cache_control: type: ephemeral ttl: 1h - role: user content_file: ./summary.user.md output: format: markdown validation_mode: basic repair_attempts: 0 ``` | Field | Required | Meaning | | --- | --- | --- | | `id` | yes | Non-empty prompt identifier used by `RunRequest.PromptID`. | | `version` | yes | Non-empty version selected by an optional `RunRequest.PromptVersion`. | | `default_profile` | no | Non-empty profile ID used when the request omits `ProfileID`. | | `description` | no | Human-readable description. | | `session_id` | no | Go template rendered from request variables and input helpers. | | `inputs` | no | Declared input metadata. | | `messages` | yes | One or more chat-message templates. | | `output` | yes | Output format and validation settings. | When a request omits a version, the selected prompt ID must identify exactly one definition. When it supplies a version, the ID and version pair must be unique. Exact prompt inspection uses this same configured source, strict decoding, referenced content-file resolution, and ID/version selection. It reports the selected definition's declared metadata without changing the prompt format or executing the definition. ### Inputs Each `inputs` item has these fields: | Field | Required | Meaning | | --- | --- | --- | | `name` | yes | Non-empty name used by the request input map and `input` template helper. Names must be unique. | | `required` | no | When true, preparation fails if the request omits the input. The default is false. | | `content_type` | no | Expected media-type metadata. | | `description` | no | Human-readable input description. | Requests supply inputs as inline or file-backed `ArtifactRef` values. Declared required inputs must be present. A template reference also requires the named input to exist, whether or not it was declared. Extra request inputs are allowed. ### Messages And Templates Each message has a non-empty `role` and exactly one of: - `content`, containing an inline Go template; or - `content_file`, naming a file whose contents are the Go template. `content_file` must be a relative path. It resolves from the directory that contains the prompt file and must remain within the configured prompt source root; parent components are allowed only when the resolved target remains inside that root. Absolute paths and paths that escape the root are rejected. Operating-system directory and single-file sources also reject symlink targets outside the root, while injected `fs.FS` sources apply containment in that filesystem's relative path namespace. For `WithPromptFile`, the source root is the directory containing the selected prompt file. Promptkit uses the parsed path text exactly after checking separately that it is not blank, so leading and trailing whitespace can name real filesystem entries. Request variables are the template data, so a variable named `audience` is referenced as `{{.audience}}`. The `{{input "note"}}` helper renders the body of a named input. Missing variables and input references are errors. The optional `session_id` uses the same template data and input helper. Its rendered value is trimmed, omitted when empty, and limited to 256 Unicode code points. A nonblank direct request session ID bypasses this template completely; a blank direct value leaves the template behavior unchanged. ### Cache Control `cache_control` is optional and has these fields: | Field | Required | Values | | --- | --- | --- | | `type` | yes | `ephemeral` | | `ttl` | no | Empty or `1h` | Promptkit preserves cache-control metadata on the rendered message. The outbound integration determines its wire representation. ### Output Contract | Field | Required | Values or behavior | | --- | --- | --- | | `format` | yes | `text`, `markdown`, or `json`. | | `validation_mode` | yes | `none`, `basic`, `json`, or `json_schema`. | | `schema_path` | for `json_schema` | Path to a schema in the configured schema source. | | `repair_attempts` | no | Integer zero or greater; omitted means zero. | The validation modes behave as follows: - `none` skips content validation; - `basic` requires non-empty generated content; - `json` requires valid JSON; and - `json_schema` requires valid JSON that satisfies the selected schema. `format` controls output artifact metadata. JSON Schema mode also supplies the schema to compatible model clients as structured-output metadata. The public engine does not install an output repairer, so its validation is single-pass even when a positive `repair_attempts` value is present. A request-level `OutputContract` replaces the complete prompt output contract. It does not merge individual fields. If its format is empty, Promptkit uses `text`. ## Profile Definitions A profile supplies model execution settings: ```yaml id: local-summary backend: openrouter model: example-model temperature: 0.2 max_tokens: 500 top_p: 0.95 timeout_seconds: 90 service_tier: flex reasoning_effort: medium extra_params: provider_option: enabled ``` | Field | Required | Meaning | | --- | --- | --- | | `id` | yes | Profile identifier, trimmed before selection and publication. It must be non-empty after trimming and unique within one source after normalization. | | `backend` | unless `endpoint` is present | Backend registry ID. It is trimmed and registry membership is checked when the profile is prepared or inspected. | | `endpoint` | unless `backend` is present | OpenAI-compatible base URL, including an API version path when required. A nonempty value is trimmed and must be absolute HTTP or HTTPS with a host and without user information, a query, or a fragment. When both connection fields are present, this overrides the backend endpoint without changing backend identity. | | `model` | yes | Non-empty provider model name. | | `temperature` | no | Number from 0 through 2. | | `max_tokens` | no | Integer zero or greater. | | `top_p` | no | Number from 0 through 1. | | `timeout_seconds` | no | Per-generation deadline in whole seconds; integer zero or greater. | | `service_tier` | no | Provider-specific request tier. | | `reasoning_effort` | no | Provider-specific reasoning setting. | | `api_key_env` | no | Name of an environment variable containing the API key. | | `extra_params` | no | JSON-compatible provider-specific outbound fields. | Raw `api_key` is prohibited in profile YAML. Store only an environment variable name in `api_key_env`. Promptkit does not infer a backend from a model or endpoint. Endpoint-only profiles remain supported and have no effective backend ID. The engine always provides the built-in `openrouter` ID. Consumers can add engine-scoped IDs with [`WithBackend`](../backends.go); exact registration validation belongs to its GoDoc. `extra_params` accepts null, booleans, finite numbers, strings, arrays, and objects with string keys. Keys must be non-empty. With the built-in client, they also cannot collide with the standard fields listed in the [outbound request contract](integrations/openai-compatible-chat.md#request-body). Excessively deep or large JSON-shaped values are rejected for safety. ### Defaults And Overrides Execution settings resolve in this order: 1. the framework timeout baseline; 2. the selected backend, when the profile names one; 3. the selected profile; and 4. request `ExecutionTargetOverride` values. The framework baseline is: | Setting | Default | | --- | --- | | `temperature` | Unspecified and omitted from compatible provider requests unless a profile or runtime override selects it. | | `max_tokens` | Unspecified and omitted from compatible provider requests unless a profile or runtime override selects it. | | `top_p` | Unspecified and omitted from compatible provider requests unless a profile or runtime override selects it. | | `timeout_seconds` | `600` | Numeric zero in a file or in-memory profile does not select a numeric value. For `temperature`, `max_tokens`, and `top_p`, it leaves the provider control unspecified. For `timeout_seconds`, it retains the framework deadline. Numeric request overrides use pointers, so an explicit zero is retained and sent to compatible providers. In particular, an explicit request `timeout_seconds` of zero disables the per-generation deadline while leaving the caller context and transport timeout intact. Non-empty profile strings replace backend defaults, and non-empty request strings replace both. Request reasoning is the exception: a nil `ReasoningEffort` pointer inherits the profile, a pointer to a nonblank string trims and replaces it, and a pointer to a blank string clears it. Backend identity is retained when either layer overrides the endpoint, so the override also retains any engine-local capacity policy configured for that backend. Capacity configuration belongs to the Go [`Backend` API](../backends.go), not prompt or profile YAML. A non-empty `extra_params` map at each layer replaces the entire lower-precedence map rather than merging keys. The [outbound integration contract](integrations/openai-compatible-chat.md) defines how the effective settings are serialized. ### Source And Profile Precedence An explicit request profile ID takes precedence over the prompt's `default_profile`. If neither is present, preparation fails. Exact profile inspection instead takes one explicit profile ID and does not use a prompt default. Profile sources resolve matching IDs in this order: 1. in-memory profiles supplied with `WithProfiles`; 2. the ordinary configured source selected by a profile file, `fs.FS`, or configured profile directory; 3. application fallback profiles supplied with `WithFallbackProfileFS`; and 4. embedded built-in profiles. A profile source supplies a complete definition; definitions and their fields are not merged across sources. A higher-precedence source falls back only when the requested profile ID is absent. An invalid matching profile is an error and does not fall back. In-memory `Profile` values follow the same ranges as YAML profiles. They use `APIKeyRequired` for request-scoped credentials instead of `api_key_env`. Preparation and exact profile inspection use this same source precedence. ## Built-In Profile Catalog Every built-in selects the `openrouter` backend. The engine's built-in backend registry supplies `https://openrouter.ai/api/v1` and the environment-variable name `OPENROUTER_API_KEY`, so individual profiles contain only model and generation settings. Built-in profile files do not repeat those connection values. A configured, application fallback, or in-memory profile with the same profile ID takes precedence. | Provider | ID | Model | | --- | --- | --- | | aion-labs | `aion-2` | `aion-labs/aion-2.0` | | anthropic | `claude-fable-latest` | `~anthropic/claude-fable-latest` | | anthropic | `claude-haiku-latest` | `~anthropic/claude-haiku-latest` | | anthropic | `claude-opus-latest` | `~anthropic/claude-opus-latest` | | anthropic | `claude-sonnet-latest` | `~anthropic/claude-sonnet-latest` | | deepseek | `deepseek-3-2` | `deepseek/deepseek-v3.2` | | deepseek | `deepseek-4-flash` | `deepseek/deepseek-v4-flash` | | deepseek | `deepseek-4-pro` | `deepseek/deepseek-v4-pro` | | google | `gemini-2-flash` | `google/gemini-2.5-flash` | | google | `gemini-2-flash-lite` | `google/gemini-2.5-flash-lite` | | google | `gemini-2-pro` | `google/gemini-2.5-pro` | | google | `gemini-3-flash-lite` | `google/gemini-3.1-flash-lite` | | google | `gemini-flash-latest` | `~google/gemini-flash-latest` | | google | `gemini-pro-latest` | `~google/gemini-pro-latest` | | google | `gemma-4-31b` | `google/gemma-4-31b-it:exacto` | | minimax | `minimax-m2` | `minimax/minimax-m2.5` | | minimax | `minimax-m3` | `minimax/minimax-m3` | | mistral | `mistral-large-2512` | `mistralai/mistral-large-2512` | | mistral | `mistral-medium-3-5` | `mistralai/mistral-medium-3-5` | | mistral | `mistral-small-3` | `mistralai/mistral-small-3.2-24b-instruct` | | mistral | `mistral-small-4` | `mistralai/mistral-small-2603` | | nvidia | `nemotron-3-ultra` | `nvidia/nemotron-3-ultra-550b-a55b` | | openai | `gpt-5-mini` | `openai/gpt-5.4-mini` | | openai | `gpt-5-nano` | `openai/gpt-5.4-nano` | ## Schemas Schemas are JSON documents selected by a prompt or request `schema_path`. For a directory or `fs.FS` source, paths resolve within the configured source root. Referenced nested schemas resolve relative to the owning schema document. `WithSchemaFile` exposes one schema, addressed by its base name. An unreadable, invalid, or unresolvable schema produces an operational validation error. Generated content that is valid JSON but does not satisfy the schema produces a failed validation result. ## Credentials Credential values belong at the request or environment boundary, never in prompt, profile, schema, or example files: - a file profile names an environment variable with `api_key_env`; - an in-memory profile may set `APIKeyRequired`; - a request can provide a direct `APIKey` or override `APIKeyEnv`; and - a direct request key takes precedence over environment lookup. After a direct request key, the credential-source precedence is request `APIKeyEnv`, profile `api_key_env`, then the backend default. An in-memory profile with `APIKeyRequired` clears an inherited backend environment name and requires a direct key unless the request explicitly supplies `APIKeyEnv`. Promptkit validates required credential availability during preparation. Direct keys are excluded from JSON results and redacted by public string formatters. Environment-variable names may appear in prepared metadata, but their values do not.