promptopskit 0.3.8 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -10
- package/SKILL.md +69 -6
- package/dist/chunk-F6UJ73Y6.js +113 -0
- package/dist/chunk-F6UJ73Y6.js.map +1 -0
- package/dist/chunk-HLNVKKMS.js +145 -0
- package/dist/chunk-HLNVKKMS.js.map +1 -0
- package/dist/{chunk-5OYWNRNC.js → chunk-JBLUPKY4.js} +191 -5
- package/dist/chunk-JBLUPKY4.js.map +1 -0
- package/dist/{chunk-UJU5XERR.js → chunk-L2V7LDKD.js} +25 -4
- package/dist/chunk-L2V7LDKD.js.map +1 -0
- package/dist/{chunk-E6NPZOJL.js → chunk-RY5DVTLV.js} +29 -4
- package/dist/chunk-RY5DVTLV.js.map +1 -0
- package/dist/chunk-UPTCSWK3.js +60 -0
- package/dist/chunk-UPTCSWK3.js.map +1 -0
- package/dist/{chunk-X73VG645.js → chunk-XNNDGDPI.js} +64 -3
- package/dist/chunk-XNNDGDPI.js.map +1 -0
- package/dist/cli/index.js +423 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +655 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -4
- package/dist/index.d.ts +7 -4
- package/dist/index.js +172 -11
- package/dist/index.js.map +1 -1
- package/dist/providers/anthropic.cjs +306 -10
- package/dist/providers/anthropic.cjs.map +1 -1
- package/dist/providers/anthropic.d.cts +2 -2
- package/dist/providers/anthropic.d.ts +2 -2
- package/dist/providers/anthropic.js +3 -3
- package/dist/providers/gemini.cjs +274 -6
- package/dist/providers/gemini.cjs.map +1 -1
- package/dist/providers/gemini.d.cts +2 -2
- package/dist/providers/gemini.d.ts +2 -2
- package/dist/providers/gemini.js +3 -3
- package/dist/providers/openai-responses.cjs +1245 -0
- package/dist/providers/openai-responses.cjs.map +1 -0
- package/dist/providers/openai-responses.d.cts +11 -0
- package/dist/providers/openai-responses.d.ts +11 -0
- package/dist/providers/openai-responses.js +9 -0
- package/dist/providers/openai-responses.js.map +1 -0
- package/dist/providers/openai.cjs +270 -6
- package/dist/providers/openai.cjs.map +1 -1
- package/dist/providers/openai.d.cts +2 -2
- package/dist/providers/openai.d.ts +2 -2
- package/dist/providers/openai.js +3 -3
- package/dist/providers/openrouter.cjs +305 -9
- package/dist/providers/openrouter.cjs.map +1 -1
- package/dist/providers/openrouter.d.cts +2 -2
- package/dist/providers/openrouter.d.ts +2 -2
- package/dist/providers/openrouter.js +4 -4
- package/dist/schema-B9A1zLRk.d.cts +2403 -0
- package/dist/schema-B9A1zLRk.d.ts +2403 -0
- package/dist/testing.cjs +264 -6
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +59 -2
- package/dist/testing.d.ts +59 -2
- package/dist/testing.js +70 -2
- package/dist/testing.js.map +1 -1
- package/dist/{types-fBvo_d4k.d.ts → types-DjGAwJxC.d.ts} +13 -1
- package/dist/{types-C6RG0Si0.d.cts → types-DpbzzZO2.d.cts} +13 -1
- package/dist/usagetap/index.d.cts +2 -2
- package/dist/usagetap/index.d.ts +2 -2
- package/package.json +12 -2
- package/dist/chunk-5OYWNRNC.js.map +0 -1
- package/dist/chunk-BBVVA27J.js +0 -92
- package/dist/chunk-BBVVA27J.js.map +0 -1
- package/dist/chunk-E2NXJLS6.js +0 -26
- package/dist/chunk-E2NXJLS6.js.map +0 -1
- package/dist/chunk-E6NPZOJL.js.map +0 -1
- package/dist/chunk-UJU5XERR.js.map +0 -1
- package/dist/chunk-X73VG645.js.map +0 -1
- package/dist/schema-DwzYbZba.d.cts +0 -859
- package/dist/schema-DwzYbZba.d.ts +0 -859
package/README.md
CHANGED
|
@@ -11,6 +11,31 @@ Your prompts are already in Git. PromptOpsKit makes them manageable. It replaces
|
|
|
11
11
|
|
|
12
12
|
Provider adapters for OpenAI, Anthropic, Gemini, and OpenRouter produce a ready-to-send **request body only** — no HTTP client, no auth, no headers. Your application owns transport, so PromptOpsKit slots into any stack without opinions about how you call the API.
|
|
13
13
|
|
|
14
|
+
## How is this different from GitHub Models?
|
|
15
|
+
|
|
16
|
+
GitHub Models is a great place to prototype prompts, compare models, and run evaluations inside GitHub.
|
|
17
|
+
|
|
18
|
+
PromptOpsKit is focused on the application runtime layer.
|
|
19
|
+
|
|
20
|
+
It helps you turn prompt behavior into repo-native assets that your application can load, validate, compose, compile, and render into provider-specific request bodies.
|
|
21
|
+
|
|
22
|
+
Use GitHub Models when you want:
|
|
23
|
+
|
|
24
|
+
- a GitHub-hosted prompt editor and playground
|
|
25
|
+
- side-by-side model comparisons
|
|
26
|
+
- structured evaluations inside GitHub
|
|
27
|
+
- `.prompt.yml` files for prompt experiments and evals
|
|
28
|
+
|
|
29
|
+
Use PromptOpsKit when you want:
|
|
30
|
+
|
|
31
|
+
- Markdown prompt assets with YAML metadata
|
|
32
|
+
- production input hardening and validation
|
|
33
|
+
- environment-specific model and parameter overrides
|
|
34
|
+
- reusable prompt composition
|
|
35
|
+
- compiled artifacts for deployment
|
|
36
|
+
- provider-specific request bodies for your own runtime code
|
|
37
|
+
- control over SDKs, auth, retries, routing, observability, and billing
|
|
38
|
+
|
|
14
39
|
### Why PromptOpsKit?
|
|
15
40
|
|
|
16
41
|
- **Centralized, not scattered** — each prompt is a single Markdown file that captures prompt text, model config, tool bindings, and context rules together.
|
|
@@ -44,9 +69,12 @@ This creates:
|
|
|
44
69
|
prompts/
|
|
45
70
|
├── defaults.md # Folder-level defaults (provider, model, metadata, system instructions)
|
|
46
71
|
├── hello.md # Sample prompt with variables
|
|
47
|
-
├── hello.test.yaml # Test sidecar with sample inputs
|
|
72
|
+
├── hello.test.yaml # Test sidecar with sample inputs and hardcoded responses
|
|
48
73
|
└── shared/
|
|
49
74
|
└── tone.md # Shared system instructions (included via composition)
|
|
75
|
+
|
|
76
|
+
tests/
|
|
77
|
+
└── hello.prompt.test.mjs # Executable starter test for the hello prompt
|
|
50
78
|
```
|
|
51
79
|
|
|
52
80
|
### 2. Write a prompt
|
|
@@ -69,9 +97,7 @@ context:
|
|
|
69
97
|
reject_secrets: true
|
|
70
98
|
- name: app_context
|
|
71
99
|
max_size: 2000
|
|
72
|
-
allow_regex:
|
|
73
|
-
pattern: "^[A-Za-z0-9 _-]+$"
|
|
74
|
-
flags: "i"
|
|
100
|
+
allow_regex: /^[A-Za-z0-9 _-]+$/i
|
|
75
101
|
includes:
|
|
76
102
|
- ./shared/tone.md
|
|
77
103
|
---
|
|
@@ -135,9 +161,11 @@ Supported values for `warnings.contextSize` are `auto`, `off`, `result-only`, `c
|
|
|
135
161
|
- **Composition** — `includes` to share system instructions across prompts, with circular detection
|
|
136
162
|
- **Folder defaults** — `defaults.md` inheritance for shared provider, model, metadata, and system instructions
|
|
137
163
|
- **Overrides** — Environment and tier-based overrides (base → env → tier → runtime)
|
|
138
|
-
- **
|
|
164
|
+
- **5 provider adapters** — OpenAI (Chat), OpenAI (Responses), Anthropic, Gemini, OpenRouter — body-only output
|
|
165
|
+
- **Provider-aware input caching controls** — optional `cache` front matter maps to OpenAI prompt cache hints, Anthropic `cache_control`, and Gemini `cachedContent`
|
|
166
|
+
- **Vendor escape hatch** — optional `raw.<provider>` blocks shallow-merge unmodeled request-body fields into the final provider payload
|
|
139
167
|
- **Validation** — Zod schema validation, Levenshtein-based "did you mean?" for typos, variable usage checks
|
|
140
|
-
- **Context hardening** —
|
|
168
|
+
- **Context hardening** — copyable `/pattern/i` regex literals, structured regexes with `return_message`, and built-in `non_empty` / `reject_secrets` validators
|
|
141
169
|
- **Optional short-circuit messages** — validators can return a structured `returnMessage` instead of throwing when configured
|
|
142
170
|
- **Context size guardrails** — optional per-input `max_size` metadata with non-blocking render-time warnings
|
|
143
171
|
- **Warning controls** — top-level config can suppress or emit context size warnings differently in dev and prod
|
|
@@ -193,6 +221,7 @@ Provider adapters are also available as direct imports:
|
|
|
193
221
|
|
|
194
222
|
```typescript
|
|
195
223
|
import { openaiAdapter } from 'promptopskit/openai';
|
|
224
|
+
import { openaiResponsesAdapter } from 'promptopskit/openai-responses';
|
|
196
225
|
import { anthropicAdapter } from 'promptopskit/anthropic';
|
|
197
226
|
import { geminiAdapter } from 'promptopskit/gemini';
|
|
198
227
|
import { openrouterAdapter } from 'promptopskit/openrouter';
|
|
@@ -223,6 +252,48 @@ const request = openaiAdapter.render(prompt, {
|
|
|
223
252
|
|
|
224
253
|
In browser or client-side code, keep provider credentials on the server. Use the rendered request body with your own server endpoint, server action, or edge function rather than calling a provider directly from the client.
|
|
225
254
|
|
|
255
|
+
### Provider-specific fields and raw passthrough
|
|
256
|
+
|
|
257
|
+
Use normalized fields first (`sampling`, `response`, `cache`, `tools`) so prompts stay portable. `response.schema` is the neutral JSON Schema path; adapters emit it as OpenAI/OpenRouter `response_format`, OpenAI Responses `text.format`, Anthropic `output_config.format`, and Gemini `generationConfig.responseJsonSchema`.
|
|
258
|
+
|
|
259
|
+
Use `provider_options` when PromptOpsKit has a known provider-specific mapping, such as Anthropic `top_k`, Gemini's native `response_schema`, or OpenRouter routing fields.
|
|
260
|
+
|
|
261
|
+
```yaml
|
|
262
|
+
response:
|
|
263
|
+
format: json
|
|
264
|
+
schema_name: support_reply
|
|
265
|
+
schema_description: Structured support reply
|
|
266
|
+
schema:
|
|
267
|
+
type: object
|
|
268
|
+
properties:
|
|
269
|
+
answer:
|
|
270
|
+
type: string
|
|
271
|
+
provider_options:
|
|
272
|
+
openrouter:
|
|
273
|
+
provider:
|
|
274
|
+
order: ["anthropic", "openai"]
|
|
275
|
+
transforms: ["middle-out"]
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
When a provider adds a body field PromptOpsKit does not model yet, use `raw`:
|
|
279
|
+
|
|
280
|
+
```yaml
|
|
281
|
+
raw:
|
|
282
|
+
openai:
|
|
283
|
+
service_tier: flex
|
|
284
|
+
anthropic:
|
|
285
|
+
service_tier: auto
|
|
286
|
+
gemini:
|
|
287
|
+
safetySettings:
|
|
288
|
+
- category: HARM_CATEGORY_DANGEROUS_CONTENT
|
|
289
|
+
threshold: BLOCK_ONLY_HIGH
|
|
290
|
+
openrouter:
|
|
291
|
+
usage:
|
|
292
|
+
include: true
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Each adapter reads only its matching raw block and shallow-merges it into the generated request body after normalized mappings. This is intentionally an escape hatch; prefer first-class fields when they exist.
|
|
296
|
+
|
|
226
297
|
On the server, adapters also provide async prompt-aware helpers so you can use the default `./prompts` and `./.generated-prompts/json` directories without creating a `PromptOpsKit` instance:
|
|
227
298
|
|
|
228
299
|
```typescript
|
|
@@ -472,11 +543,21 @@ Hello {{ name }}!`,
|
|
|
472
543
|
## Testing Helpers
|
|
473
544
|
|
|
474
545
|
```typescript
|
|
475
|
-
import {
|
|
546
|
+
import {
|
|
547
|
+
createHardcodedPromptResponder,
|
|
548
|
+
createMockAsset,
|
|
549
|
+
createMockResolvedAsset,
|
|
550
|
+
loadPromptTestSidecar,
|
|
551
|
+
parseTestPrompt,
|
|
552
|
+
} from 'promptopskit/testing';
|
|
476
553
|
|
|
477
554
|
const asset = createMockAsset({ model: 'gpt-5.4' });
|
|
478
555
|
const resolved = createMockResolvedAsset();
|
|
479
556
|
const parsed = parseTestPrompt('---\nid: test\nschema_version: 1\n---\n\nHello');
|
|
557
|
+
|
|
558
|
+
const sidecar = await loadPromptTestSidecar('./prompts/hello.test.yaml');
|
|
559
|
+
const respond = createHardcodedPromptResponder(sidecar);
|
|
560
|
+
const response = respond('basic-greeting');
|
|
480
561
|
```
|
|
481
562
|
|
|
482
563
|
## API Reference
|
|
@@ -501,7 +582,7 @@ Renders a prompt for a specific provider. Returns `{ resolved, request?, returnM
|
|
|
501
582
|
|--------|------|-------------|
|
|
502
583
|
| `path` | `string` | Prompt path (no extension), e.g. `'support/reply'` |
|
|
503
584
|
| `source` | `string` | Inline prompt source (alternative to path) |
|
|
504
|
-
| `provider` | `string` | `'openai'`, `'anthropic'`, `'gemini'`, `'openrouter'` |
|
|
585
|
+
| `provider` | `string` | `'openai'`, `'openai-responses'`, `'anthropic'`, `'gemini'`, `'openrouter'` |
|
|
505
586
|
| `variables` | `Record<string, string>` | Template variables |
|
|
506
587
|
| `onContextOverflow` | `(info) => string` | Optional callback to transform oversized context values before rendering |
|
|
507
588
|
| `environment` | `string` | Environment override name |
|
|
@@ -509,6 +590,7 @@ Renders a prompt for a specific provider. Returns `{ resolved, request?, returnM
|
|
|
509
590
|
| `history` | `Array<{ role, content }>` | Conversation history |
|
|
510
591
|
| `toolRegistry` | `Record<string, unknown>` | Tool definitions for resolving string tool references |
|
|
511
592
|
| `strict` | `boolean` | Fail on missing variables |
|
|
593
|
+
| `openaiResponses` | `object` | Optional Responses API extras (`previous_response_id`, `conversation`, `instructions`, `parallel_tool_calls`, `max_tool_calls`, `store`, `metadata`, `include`, `background`) |
|
|
512
594
|
|
|
513
595
|
### `kit.loadPrompt(path)` / `kit.resolvePrompt(path, options)` / `kit.validatePrompt(path)`
|
|
514
596
|
|
|
@@ -528,13 +610,16 @@ Prompt files use YAML front matter with these fields:
|
|
|
528
610
|
|-------|------|-------------|
|
|
529
611
|
| `id` | `string` | Unique prompt identifier (required) |
|
|
530
612
|
| `schema_version` | `number` | Schema version, currently `1` |
|
|
531
|
-
| `provider` | `string` | `openai`, `anthropic`, `gemini` (or `google`), `openrouter`, `any` |
|
|
613
|
+
| `provider` | `string` | `openai`, `openai-responses`, `anthropic`, `gemini` (or `google`), `openrouter`, `any` |
|
|
532
614
|
| `model` | `string` | Model name |
|
|
533
615
|
| `fallback_models` | `string[]` | Fallback model list |
|
|
534
616
|
| `reasoning` | `object` | `{ effort, budget_tokens }` |
|
|
535
617
|
| `sampling` | `object` | `{ temperature, top_p, frequency_penalty, presence_penalty, stop, max_output_tokens }` |
|
|
536
|
-
| `response` | `object` | `{ format, stream }` |
|
|
618
|
+
| `response` | `object` | `{ format, stream, schema, schema_name, schema_description, schema_strict }` |
|
|
619
|
+
| `cache` | `object` | Provider-specific cache controls (`openai`, `anthropic`, `gemini`/`google`) |
|
|
537
620
|
| `tools` | `array` | Tool references (string names or inline definitions) |
|
|
621
|
+
| `provider_options` | `object` | Provider-specific non-portable options (`anthropic`, `gemini`, `openrouter`) |
|
|
622
|
+
| `raw` | `object` | Provider-scoped request-body passthrough (`openai`, `openai-responses`, `anthropic`, `gemini`/`google`, `openrouter`) |
|
|
538
623
|
| `mcp` | `object` | MCP server references |
|
|
539
624
|
| `context` | `object` | `{ inputs, history }` — declare expected variables, with optional per-input `max_size`, `trim`, structured or literal `allow_regex`/`deny_regex`, and built-in `non_empty` / `reject_secrets` validators |
|
|
540
625
|
| `includes` | `string[]` | Paths to included prompt files |
|
|
@@ -542,6 +627,8 @@ Prompt files use YAML front matter with these fields:
|
|
|
542
627
|
| `tiers` | `object` | Named tier overrides |
|
|
543
628
|
| `metadata` | `object` | `{ owner, tags, review_required, stable }` |
|
|
544
629
|
|
|
630
|
+
For `allow_regex` and `deny_regex`, prefer unquoted `/pattern/i` literal form so regex escapes such as `\s` and `\b` stay copyable from tools like regex101. If you use structured `pattern:` form, use single-quoted YAML strings or double each backslash in double-quoted strings.
|
|
631
|
+
|
|
545
632
|
## Website
|
|
546
633
|
|
|
547
634
|
The `website/` directory contains a standalone marketing website for PromptOpsKit.
|
package/SKILL.md
CHANGED
|
@@ -8,7 +8,7 @@ description: Guidance for creating and editing promptopskit prompt files, defaul
|
|
|
8
8
|
This project uses **promptopskit** to manage LLM prompts as code.
|
|
9
9
|
Prompts live in markdown files with YAML front matter, are validated against
|
|
10
10
|
a schema, and render into provider-specific request bodies (OpenAI, Anthropic,
|
|
11
|
-
Gemini, OpenRouter). Follow these instructions when creating or editing prompts.
|
|
11
|
+
Gemini, OpenRouter, and OpenAI Responses). Follow these instructions when creating or editing prompts.
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
@@ -58,13 +58,16 @@ the fields required by that specific file:
|
|
|
58
58
|
| `id` | string | **yes** | Unique identifier for the prompt |
|
|
59
59
|
| `schema_version` | number | yes | Always `1` |
|
|
60
60
|
| `description` | string | no | Human-readable description |
|
|
61
|
-
| `provider` | enum | no | `openai`, `anthropic`, `google`, `gemini`, `openrouter`, or `any` |
|
|
61
|
+
| `provider` | enum | no | `openai`, `openai-responses`, `anthropic`, `google`, `gemini`, `openrouter`, or `any` |
|
|
62
62
|
| `model` | string | no | Model identifier (e.g. `gpt-5.4`, `claude-sonnet-4-20250514`) |
|
|
63
63
|
| `fallback_models` | string[] | no | Ordered fallback model list |
|
|
64
64
|
| `reasoning` | object | no | `{ effort: low|medium|high, budget_tokens: number }` |
|
|
65
65
|
| `sampling` | object | no | `{ temperature, top_p, frequency_penalty, presence_penalty, stop, max_output_tokens }` |
|
|
66
|
-
| `response` | object | no | `{ format: text|json|markdown, stream: boolean }` |
|
|
66
|
+
| `response` | object | no | `{ format: text|json|markdown, stream: boolean, schema?: object, schema_name?: string, schema_description?: string, schema_strict?: boolean }` |
|
|
67
|
+
| `cache` | object | no | Provider-specific cache controls (`openai`, `anthropic`, `gemini`/`google`) |
|
|
67
68
|
| `tools` | array | no | Tool names (strings) or inline definitions with `{ name, description, input_schema }` |
|
|
69
|
+
| `provider_options` | object | no | Provider-specific advanced options (`anthropic`, `gemini`, `openrouter`) |
|
|
70
|
+
| `raw` | object | no | Provider-scoped request-body passthrough for unmodeled vendor fields |
|
|
68
71
|
| `mcp` | object | no | `{ servers: [string | { name, config }] }` |
|
|
69
72
|
| `context.inputs` | `Array<string | { name, max_size?, trim?, allow_regex?, deny_regex?, non_empty?, reject_secrets? }>` | no | Declared variable names used in templates, with optional size budgets and runtime hardening controls |
|
|
70
73
|
| `context.history` | object | no | `{ max_items: number }` |
|
|
@@ -102,7 +105,9 @@ Rules:
|
|
|
102
105
|
- Use object-form inputs with `max_size` when a variable is likely to grow large and should trigger early warnings
|
|
103
106
|
- Use `trim` to enforce byte budgets before interpolation when `max_size` is set
|
|
104
107
|
- Use `allow_regex` for allowlist checks and `deny_regex` for blocklist checks on risky inputs
|
|
105
|
-
- Prefer
|
|
108
|
+
- Prefer unquoted `/pattern/i` literals for regex validators so backslash escapes such as `\s` and `\b` stay copyable from regex tools
|
|
109
|
+
- Use structured regexes like `{ pattern, flags, return_message }` when the validator needs a fallback message or separate flags
|
|
110
|
+
- In structured `pattern:` YAML, use single quotes for patterns with backslashes or double each backslash in double-quoted strings
|
|
106
111
|
- Use `non_empty: true` for required user text and `reject_secrets: true` for common secret redaction checks
|
|
107
112
|
- When the caller should receive a structured fallback message instead of an exception, use object form with `return_message` on `allow_regex`, `deny_regex`, `non_empty`, or `reject_secrets`
|
|
108
113
|
- Escape literal braces with `\{{` and `\}}`
|
|
@@ -124,7 +129,7 @@ At render time, callers can also pass `onContextOverflow` to transform oversized
|
|
|
124
129
|
|
|
125
130
|
If a validator declares `return_message`, `renderPrompt()` returns that message in a structured result and omits the provider request instead of throwing for that validation failure. Invalid regex definitions still fail during `validate` and `compile` as `POK013` prompt-authoring errors.
|
|
126
131
|
|
|
127
|
-
Malformed `allow_regex` and `deny_regex` values fail during `validate` and `compile`, not just at render time. When regex compilation fails, the error includes the prompt id, variable name, field name, and raw configured value.
|
|
132
|
+
Malformed `allow_regex` and `deny_regex` values fail during `validate` and `compile`, not just at render time. When regex compilation fails, the error includes the prompt id, variable name, field name, and raw configured value. Double-quoted YAML regex strings with raw backslashes fail as `POK013`; use `/pattern/i`, single-quoted `pattern: '...'`, or doubled backslashes.
|
|
128
133
|
|
|
129
134
|
Example: this is the minimal valid shape for a prompt that references
|
|
130
135
|
`{{ pull_request }}` even when provider/model are inherited from defaults:
|
|
@@ -182,6 +187,7 @@ prompts/
|
|
|
182
187
|
Supported default fields:
|
|
183
188
|
- `provider` (front matter) — default provider for the folder
|
|
184
189
|
- `model` (front matter) — default model for the folder
|
|
190
|
+
- `cache` (front matter) — default provider-specific cache hints
|
|
185
191
|
- `metadata` (front matter) — merged with prompt-local metadata
|
|
186
192
|
- `# System instructions` (body section) — used when the prompt has none
|
|
187
193
|
|
|
@@ -229,12 +235,69 @@ tiers:
|
|
|
229
235
|
```
|
|
230
236
|
|
|
231
237
|
Overridable fields: `model`, `fallback_models`, `reasoning`, `sampling`,
|
|
232
|
-
`response`, `tools`.
|
|
238
|
+
`response`, `cache`, `raw`, `tools`, `provider_options`.
|
|
233
239
|
|
|
234
240
|
Override application order: **base → environment → tier → runtime**.
|
|
235
241
|
|
|
236
242
|
---
|
|
237
243
|
|
|
244
|
+
## Provider-specific fields
|
|
245
|
+
|
|
246
|
+
Prefer portable fields first:
|
|
247
|
+
|
|
248
|
+
- Use `sampling` for common sampling controls
|
|
249
|
+
- Use `response.schema`, `response.schema_name`, `response.schema_description`, and `response.schema_strict` for structured output when possible
|
|
250
|
+
- Use `cache` for provider cache hints
|
|
251
|
+
- Use `tools` for tool definitions
|
|
252
|
+
|
|
253
|
+
Treat `response.schema` as the provider-neutral JSON Schema contract. The adapters emit it through provider-specific request fields: OpenAI/OpenRouter `response_format`, OpenAI Responses `text.format`, Anthropic `output_config.format`, and Gemini `generationConfig.responseJsonSchema`.
|
|
254
|
+
|
|
255
|
+
Use `provider_options` for known non-portable mappings:
|
|
256
|
+
|
|
257
|
+
```yaml
|
|
258
|
+
provider_options:
|
|
259
|
+
anthropic:
|
|
260
|
+
top_k: 40
|
|
261
|
+
tool_choice:
|
|
262
|
+
type: auto
|
|
263
|
+
output_config:
|
|
264
|
+
format:
|
|
265
|
+
type: json_schema
|
|
266
|
+
schema:
|
|
267
|
+
type: object
|
|
268
|
+
gemini:
|
|
269
|
+
# Use only when Gemini's native schema dialect is required.
|
|
270
|
+
response_schema:
|
|
271
|
+
type: object
|
|
272
|
+
response_json_schema:
|
|
273
|
+
type: object
|
|
274
|
+
openrouter:
|
|
275
|
+
provider:
|
|
276
|
+
order: ["anthropic", "openai"]
|
|
277
|
+
transforms: ["middle-out"]
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Use `raw` only when a vendor request-body field is important and PromptOpsKit does not model it yet:
|
|
281
|
+
|
|
282
|
+
```yaml
|
|
283
|
+
raw:
|
|
284
|
+
openai:
|
|
285
|
+
service_tier: flex
|
|
286
|
+
anthropic:
|
|
287
|
+
service_tier: auto
|
|
288
|
+
gemini:
|
|
289
|
+
safetySettings:
|
|
290
|
+
- category: HARM_CATEGORY_DANGEROUS_CONTENT
|
|
291
|
+
threshold: BLOCK_ONLY_HIGH
|
|
292
|
+
openrouter:
|
|
293
|
+
usage:
|
|
294
|
+
include: true
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Raw blocks are provider-scoped (`openai`, `openai-responses`/`openai_responses`, `anthropic`, `gemini`/`google`, `openrouter`) and are shallow-merged into the final request body after normalized fields. When adding `raw`, include a short note in `# Notes` explaining why a first-class field is not being used.
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
238
301
|
## Test sidecars
|
|
239
302
|
|
|
240
303
|
Create a `.test.yaml` file alongside a prompt to define test cases:
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import {
|
|
2
|
+
applyRawProviderBody,
|
|
3
|
+
renderSections,
|
|
4
|
+
resolveAssetForProvider,
|
|
5
|
+
withPromptInputSupport
|
|
6
|
+
} from "./chunk-XNNDGDPI.js";
|
|
7
|
+
|
|
8
|
+
// src/providers/openai-responses.ts
|
|
9
|
+
var openaiResponsesAdapter = withPromptInputSupport({
|
|
10
|
+
name: "openai-responses",
|
|
11
|
+
validate(asset, runtime) {
|
|
12
|
+
const resolvedAsset = resolveAssetForProvider(asset, runtime);
|
|
13
|
+
const errors = [];
|
|
14
|
+
const warnings = [];
|
|
15
|
+
if (!resolvedAsset.model) {
|
|
16
|
+
errors.push("OpenAI Responses adapter requires a model to be specified.");
|
|
17
|
+
}
|
|
18
|
+
if (resolvedAsset.reasoning?.budget_tokens !== void 0) {
|
|
19
|
+
warnings.push("OpenAI Responses uses reasoning.effort, not budget_tokens. budget_tokens will be ignored.");
|
|
20
|
+
}
|
|
21
|
+
if (resolvedAsset.response?.schema !== void 0 && resolvedAsset.response?.format !== "json") {
|
|
22
|
+
warnings.push("OpenAI Responses response.schema requires response.format: json. schema will still be applied as JSON schema output.");
|
|
23
|
+
}
|
|
24
|
+
if (runtime?.openaiResponses?.conversation !== void 0 && runtime?.openaiResponses?.previous_response_id !== void 0) {
|
|
25
|
+
errors.push('OpenAI Responses options "conversation" and "previous_response_id" cannot both be set.');
|
|
26
|
+
}
|
|
27
|
+
return { valid: errors.length === 0, errors, warnings };
|
|
28
|
+
},
|
|
29
|
+
render(asset, runtime) {
|
|
30
|
+
const resolvedAsset = resolveAssetForProvider(asset, runtime);
|
|
31
|
+
const sections = renderSections(resolvedAsset, {
|
|
32
|
+
variables: runtime.variables,
|
|
33
|
+
strict: runtime.strict
|
|
34
|
+
});
|
|
35
|
+
const responseOptions = runtime.openaiResponses;
|
|
36
|
+
const input = [];
|
|
37
|
+
if (runtime.history) {
|
|
38
|
+
for (const msg of runtime.history) {
|
|
39
|
+
input.push({ role: msg.role, content: msg.content });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (sections.prompt_template) {
|
|
43
|
+
input.push({ role: "user", content: sections.prompt_template });
|
|
44
|
+
}
|
|
45
|
+
const body = {
|
|
46
|
+
model: resolvedAsset.model,
|
|
47
|
+
input
|
|
48
|
+
};
|
|
49
|
+
if (responseOptions?.instructions !== void 0) {
|
|
50
|
+
body.instructions = responseOptions.instructions;
|
|
51
|
+
} else if (sections.system_instructions) {
|
|
52
|
+
body.instructions = sections.system_instructions;
|
|
53
|
+
}
|
|
54
|
+
if (resolvedAsset.sampling?.temperature !== void 0) body.temperature = resolvedAsset.sampling.temperature;
|
|
55
|
+
if (resolvedAsset.sampling?.top_p !== void 0) body.top_p = resolvedAsset.sampling.top_p;
|
|
56
|
+
if (resolvedAsset.sampling?.frequency_penalty !== void 0) body.frequency_penalty = resolvedAsset.sampling.frequency_penalty;
|
|
57
|
+
if (resolvedAsset.sampling?.presence_penalty !== void 0) body.presence_penalty = resolvedAsset.sampling.presence_penalty;
|
|
58
|
+
if (resolvedAsset.sampling?.stop !== void 0) body.stop = resolvedAsset.sampling.stop;
|
|
59
|
+
if (resolvedAsset.sampling?.max_output_tokens !== void 0) body.max_output_tokens = resolvedAsset.sampling.max_output_tokens;
|
|
60
|
+
if (resolvedAsset.reasoning?.effort) {
|
|
61
|
+
body.reasoning = { effort: resolvedAsset.reasoning.effort };
|
|
62
|
+
}
|
|
63
|
+
if (resolvedAsset.response?.schema) {
|
|
64
|
+
body.text = {
|
|
65
|
+
format: {
|
|
66
|
+
type: "json_schema",
|
|
67
|
+
name: resolvedAsset.response.schema_name ?? `${resolvedAsset.id}_response`,
|
|
68
|
+
...resolvedAsset.response.schema_description ? { description: resolvedAsset.response.schema_description } : {},
|
|
69
|
+
schema: resolvedAsset.response.schema,
|
|
70
|
+
strict: resolvedAsset.response.schema_strict ?? true
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
} else if (resolvedAsset.response?.format === "json") {
|
|
74
|
+
body.text = { format: { type: "json_object" } };
|
|
75
|
+
}
|
|
76
|
+
if (resolvedAsset.response?.stream !== void 0) {
|
|
77
|
+
body.stream = resolvedAsset.response.stream;
|
|
78
|
+
}
|
|
79
|
+
if (resolvedAsset.tools && resolvedAsset.tools.length > 0) {
|
|
80
|
+
body.tools = resolvedAsset.tools.map((tool) => {
|
|
81
|
+
if (typeof tool === "string") {
|
|
82
|
+
const def = runtime.toolRegistry?.[tool];
|
|
83
|
+
if (def) return def;
|
|
84
|
+
return { type: "function", name: tool };
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
type: "function",
|
|
88
|
+
name: tool.name,
|
|
89
|
+
description: tool.description,
|
|
90
|
+
parameters: tool.input_schema
|
|
91
|
+
};
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
if (responseOptions?.previous_response_id !== void 0) body.previous_response_id = responseOptions.previous_response_id;
|
|
95
|
+
if (responseOptions?.conversation !== void 0) body.conversation = responseOptions.conversation;
|
|
96
|
+
if (responseOptions?.parallel_tool_calls !== void 0) body.parallel_tool_calls = responseOptions.parallel_tool_calls;
|
|
97
|
+
if (responseOptions?.max_tool_calls !== void 0) body.max_tool_calls = responseOptions.max_tool_calls;
|
|
98
|
+
if (responseOptions?.store !== void 0) body.store = responseOptions.store;
|
|
99
|
+
if (responseOptions?.metadata !== void 0) body.metadata = responseOptions.metadata;
|
|
100
|
+
if (responseOptions?.include !== void 0) body.include = responseOptions.include;
|
|
101
|
+
if (responseOptions?.background !== void 0) body.background = responseOptions.background;
|
|
102
|
+
return {
|
|
103
|
+
body: applyRawProviderBody(body, resolvedAsset, "openai-responses"),
|
|
104
|
+
provider: "openai-responses",
|
|
105
|
+
model: resolvedAsset.model ?? "unknown"
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
export {
|
|
111
|
+
openaiResponsesAdapter
|
|
112
|
+
};
|
|
113
|
+
//# sourceMappingURL=chunk-F6UJ73Y6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/providers/openai-responses.ts"],"sourcesContent":["import type { ResolvedPromptAsset } from '../schema/index.js';\nimport type {\n ProviderAdapter,\n ProviderRequest,\n ValidationResult,\n RuntimeRenderOptions,\n} from './types.js';\nimport { renderSections } from '../renderer/index.js';\nimport { resolveAssetForProvider } from './resolve-asset.js';\nimport { withPromptInputSupport } from './prompt-input.js';\nimport { applyRawProviderBody } from './raw.js';\n\n/**\n * OpenAI Responses provider adapter.\n * Produces request bodies compatible with the OpenAI Responses API.\n */\nexport const openaiResponsesAdapter: ProviderAdapter = withPromptInputSupport({\n name: 'openai-responses',\n\n validate(asset: ResolvedPromptAsset, runtime?: RuntimeRenderOptions): ValidationResult {\n const resolvedAsset = resolveAssetForProvider(asset, runtime);\n const errors: string[] = [];\n const warnings: string[] = [];\n\n if (!resolvedAsset.model) {\n errors.push('OpenAI Responses adapter requires a model to be specified.');\n }\n\n if (resolvedAsset.reasoning?.budget_tokens !== undefined) {\n warnings.push('OpenAI Responses uses reasoning.effort, not budget_tokens. budget_tokens will be ignored.');\n }\n\n if (resolvedAsset.response?.schema !== undefined && resolvedAsset.response?.format !== 'json') {\n warnings.push('OpenAI Responses response.schema requires response.format: json. schema will still be applied as JSON schema output.');\n }\n\n if (runtime?.openaiResponses?.conversation !== undefined && runtime?.openaiResponses?.previous_response_id !== undefined) {\n errors.push('OpenAI Responses options \"conversation\" and \"previous_response_id\" cannot both be set.');\n }\n\n return { valid: errors.length === 0, errors, warnings };\n },\n\n render(asset: ResolvedPromptAsset, runtime: RuntimeRenderOptions): ProviderRequest {\n const resolvedAsset = resolveAssetForProvider(asset, runtime);\n const sections = renderSections(resolvedAsset, {\n variables: runtime.variables,\n strict: runtime.strict,\n });\n const responseOptions = runtime.openaiResponses;\n\n const input: Array<Record<string, unknown>> = [];\n\n if (runtime.history) {\n for (const msg of runtime.history) {\n input.push({ role: msg.role, content: msg.content });\n }\n }\n\n if (sections.prompt_template) {\n input.push({ role: 'user', content: sections.prompt_template });\n }\n\n const body: Record<string, unknown> = {\n model: resolvedAsset.model,\n input,\n };\n\n if (responseOptions?.instructions !== undefined) {\n body.instructions = responseOptions.instructions;\n } else if (sections.system_instructions) {\n body.instructions = sections.system_instructions;\n }\n\n // Sampling params\n if (resolvedAsset.sampling?.temperature !== undefined) body.temperature = resolvedAsset.sampling.temperature;\n if (resolvedAsset.sampling?.top_p !== undefined) body.top_p = resolvedAsset.sampling.top_p;\n if (resolvedAsset.sampling?.frequency_penalty !== undefined) body.frequency_penalty = resolvedAsset.sampling.frequency_penalty;\n if (resolvedAsset.sampling?.presence_penalty !== undefined) body.presence_penalty = resolvedAsset.sampling.presence_penalty;\n if (resolvedAsset.sampling?.stop !== undefined) body.stop = resolvedAsset.sampling.stop;\n if (resolvedAsset.sampling?.max_output_tokens !== undefined) body.max_output_tokens = resolvedAsset.sampling.max_output_tokens;\n\n // Reasoning\n if (resolvedAsset.reasoning?.effort) {\n body.reasoning = { effort: resolvedAsset.reasoning.effort };\n }\n\n // Structured output / response format\n if (resolvedAsset.response?.schema) {\n body.text = {\n format: {\n type: 'json_schema',\n name: resolvedAsset.response.schema_name ?? `${resolvedAsset.id}_response`,\n ...(resolvedAsset.response.schema_description ? { description: resolvedAsset.response.schema_description } : {}),\n schema: resolvedAsset.response.schema,\n strict: resolvedAsset.response.schema_strict ?? true,\n },\n };\n } else if (resolvedAsset.response?.format === 'json') {\n body.text = { format: { type: 'json_object' } };\n }\n\n // Streaming\n if (resolvedAsset.response?.stream !== undefined) {\n body.stream = resolvedAsset.response.stream;\n }\n\n // Tools\n if (resolvedAsset.tools && resolvedAsset.tools.length > 0) {\n body.tools = resolvedAsset.tools.map((tool) => {\n if (typeof tool === 'string') {\n const def = runtime.toolRegistry?.[tool];\n if (def) return def;\n return { type: 'function', name: tool };\n }\n return {\n type: 'function',\n name: tool.name,\n description: tool.description,\n parameters: tool.input_schema,\n };\n });\n }\n\n if (responseOptions?.previous_response_id !== undefined) body.previous_response_id = responseOptions.previous_response_id;\n if (responseOptions?.conversation !== undefined) body.conversation = responseOptions.conversation;\n if (responseOptions?.parallel_tool_calls !== undefined) body.parallel_tool_calls = responseOptions.parallel_tool_calls;\n if (responseOptions?.max_tool_calls !== undefined) body.max_tool_calls = responseOptions.max_tool_calls;\n if (responseOptions?.store !== undefined) body.store = responseOptions.store;\n if (responseOptions?.metadata !== undefined) body.metadata = responseOptions.metadata;\n if (responseOptions?.include !== undefined) body.include = responseOptions.include;\n if (responseOptions?.background !== undefined) body.background = responseOptions.background;\n\n return {\n body: applyRawProviderBody(body, resolvedAsset, 'openai-responses'),\n provider: 'openai-responses',\n model: resolvedAsset.model ?? 'unknown',\n };\n },\n});\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import {
|
|
2
|
+
applyRawProviderBody,
|
|
3
|
+
renderSections,
|
|
4
|
+
resolveAssetForProvider,
|
|
5
|
+
withPromptInputSupport
|
|
6
|
+
} from "./chunk-XNNDGDPI.js";
|
|
7
|
+
|
|
8
|
+
// src/providers/anthropic.ts
|
|
9
|
+
var anthropicAdapter = withPromptInputSupport({
|
|
10
|
+
name: "anthropic",
|
|
11
|
+
validate(asset, runtime) {
|
|
12
|
+
const resolvedAsset = resolveAssetForProvider(asset, runtime);
|
|
13
|
+
const errors = [];
|
|
14
|
+
const warnings = [];
|
|
15
|
+
if (!resolvedAsset.model) {
|
|
16
|
+
errors.push("Anthropic adapter requires a model to be specified.");
|
|
17
|
+
}
|
|
18
|
+
if (resolvedAsset.sampling?.frequency_penalty !== void 0) {
|
|
19
|
+
warnings.push("Anthropic does not support frequency_penalty. It will be ignored.");
|
|
20
|
+
}
|
|
21
|
+
if (resolvedAsset.sampling?.presence_penalty !== void 0) {
|
|
22
|
+
warnings.push("Anthropic does not support presence_penalty. It will be ignored.");
|
|
23
|
+
}
|
|
24
|
+
if (resolvedAsset.reasoning?.effort !== void 0) {
|
|
25
|
+
warnings.push("Anthropic uses budget_tokens for thinking, not effort. effort will be mapped approximately.");
|
|
26
|
+
}
|
|
27
|
+
if (resolvedAsset.response?.schema !== void 0 && resolvedAsset.response?.format !== "json") {
|
|
28
|
+
warnings.push("Anthropic response.schema is mapped to output_config.format and should usually be paired with response.format: json.");
|
|
29
|
+
}
|
|
30
|
+
if (resolvedAsset.provider_options?.anthropic?.top_k !== void 0 && resolvedAsset.provider_options.anthropic.top_k < 0) {
|
|
31
|
+
errors.push("Anthropic provider_options.top_k must be >= 0.");
|
|
32
|
+
}
|
|
33
|
+
return { valid: errors.length === 0, errors, warnings };
|
|
34
|
+
},
|
|
35
|
+
render(asset, runtime) {
|
|
36
|
+
const resolvedAsset = resolveAssetForProvider(asset, runtime);
|
|
37
|
+
const sections = renderSections(resolvedAsset, {
|
|
38
|
+
variables: runtime.variables,
|
|
39
|
+
strict: runtime.strict
|
|
40
|
+
});
|
|
41
|
+
const messages = [];
|
|
42
|
+
const anthropicCacheConfig = resolvedAsset.cache?.anthropic;
|
|
43
|
+
const cacheType = anthropicCacheConfig?.type ?? "ephemeral";
|
|
44
|
+
const cacheControl = anthropicCacheConfig ? {
|
|
45
|
+
type: cacheType,
|
|
46
|
+
...anthropicCacheConfig.ttl ? { ttl: anthropicCacheConfig.ttl } : {}
|
|
47
|
+
} : void 0;
|
|
48
|
+
const cacheMode = anthropicCacheConfig?.mode ?? "automatic";
|
|
49
|
+
if (runtime.history) {
|
|
50
|
+
for (const msg of runtime.history) {
|
|
51
|
+
messages.push({ role: msg.role, content: msg.content });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (sections.prompt_template) {
|
|
55
|
+
if (cacheControl && cacheMode === "explicit" && anthropicCacheConfig?.cache_prompt_template) {
|
|
56
|
+
messages.push({
|
|
57
|
+
role: "user",
|
|
58
|
+
content: [{ type: "text", text: sections.prompt_template, cache_control: cacheControl }]
|
|
59
|
+
});
|
|
60
|
+
} else {
|
|
61
|
+
messages.push({ role: "user", content: sections.prompt_template });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const body = {
|
|
65
|
+
model: resolvedAsset.model,
|
|
66
|
+
messages
|
|
67
|
+
};
|
|
68
|
+
if (sections.system_instructions) {
|
|
69
|
+
if (cacheControl && cacheMode === "explicit" && anthropicCacheConfig?.cache_system_instructions !== false) {
|
|
70
|
+
body.system = [{ type: "text", text: sections.system_instructions, cache_control: cacheControl }];
|
|
71
|
+
} else {
|
|
72
|
+
body.system = sections.system_instructions;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (resolvedAsset.sampling?.temperature !== void 0) body.temperature = resolvedAsset.sampling.temperature;
|
|
76
|
+
if (resolvedAsset.sampling?.top_p !== void 0) body.top_p = resolvedAsset.sampling.top_p;
|
|
77
|
+
if (resolvedAsset.sampling?.stop !== void 0) body.stop_sequences = resolvedAsset.sampling.stop;
|
|
78
|
+
if (resolvedAsset.sampling?.max_output_tokens !== void 0) {
|
|
79
|
+
body.max_tokens = resolvedAsset.sampling.max_output_tokens;
|
|
80
|
+
} else {
|
|
81
|
+
body.max_tokens = 4096;
|
|
82
|
+
}
|
|
83
|
+
if (resolvedAsset.reasoning?.budget_tokens) {
|
|
84
|
+
body.thinking = {
|
|
85
|
+
type: "enabled",
|
|
86
|
+
budget_tokens: resolvedAsset.reasoning.budget_tokens
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (resolvedAsset.provider_options?.anthropic?.top_k !== void 0) {
|
|
90
|
+
body.top_k = resolvedAsset.provider_options.anthropic.top_k;
|
|
91
|
+
}
|
|
92
|
+
if (resolvedAsset.provider_options?.anthropic?.output_config !== void 0) {
|
|
93
|
+
body.output_config = resolvedAsset.provider_options.anthropic.output_config;
|
|
94
|
+
} else if (resolvedAsset.response?.schema !== void 0) {
|
|
95
|
+
body.output_config = {
|
|
96
|
+
format: {
|
|
97
|
+
type: "json_schema",
|
|
98
|
+
schema: resolvedAsset.response.schema
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
if (resolvedAsset.response?.stream !== void 0) {
|
|
103
|
+
body.stream = resolvedAsset.response.stream;
|
|
104
|
+
}
|
|
105
|
+
if (cacheControl && cacheMode === "automatic") {
|
|
106
|
+
body.cache_control = cacheControl;
|
|
107
|
+
}
|
|
108
|
+
if (resolvedAsset.tools && resolvedAsset.tools.length > 0) {
|
|
109
|
+
body.tools = resolvedAsset.tools.map((tool) => {
|
|
110
|
+
if (typeof tool === "string") {
|
|
111
|
+
const def = runtime.toolRegistry?.[tool];
|
|
112
|
+
if (def) {
|
|
113
|
+
if (cacheControl && cacheMode === "explicit" && anthropicCacheConfig?.cache_tools) {
|
|
114
|
+
return { ...def, cache_control: cacheControl };
|
|
115
|
+
}
|
|
116
|
+
return def;
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
name: tool,
|
|
120
|
+
...cacheControl && cacheMode === "explicit" && anthropicCacheConfig?.cache_tools ? { cache_control: cacheControl } : {}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
name: tool.name,
|
|
125
|
+
description: tool.description,
|
|
126
|
+
input_schema: tool.input_schema ?? { type: "object", properties: {} },
|
|
127
|
+
...cacheControl && cacheMode === "explicit" && anthropicCacheConfig?.cache_tools ? { cache_control: cacheControl } : {}
|
|
128
|
+
};
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
if (resolvedAsset.provider_options?.anthropic?.tool_choice !== void 0) {
|
|
132
|
+
body.tool_choice = resolvedAsset.provider_options.anthropic.tool_choice;
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
body: applyRawProviderBody(body, resolvedAsset, "anthropic"),
|
|
136
|
+
provider: "anthropic",
|
|
137
|
+
model: resolvedAsset.model ?? "unknown"
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
export {
|
|
143
|
+
anthropicAdapter
|
|
144
|
+
};
|
|
145
|
+
//# sourceMappingURL=chunk-HLNVKKMS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/providers/anthropic.ts"],"sourcesContent":["import type { ResolvedPromptAsset } from '../schema/index.js';\nimport type {\n ProviderAdapter,\n ProviderRequest,\n ValidationResult,\n RuntimeRenderOptions,\n} from './types.js';\nimport { renderSections } from '../renderer/index.js';\nimport { resolveAssetForProvider } from './resolve-asset.js';\nimport { withPromptInputSupport } from './prompt-input.js';\nimport { applyRawProviderBody } from './raw.js';\n\n/**\n * Anthropic provider adapter.\n * Produces request bodies compatible with the Anthropic Messages API.\n */\nexport const anthropicAdapter: ProviderAdapter = withPromptInputSupport({\n name: 'anthropic',\n\n validate(asset: ResolvedPromptAsset, runtime?: RuntimeRenderOptions): ValidationResult {\n const resolvedAsset = resolveAssetForProvider(asset, runtime);\n const errors: string[] = [];\n const warnings: string[] = [];\n\n if (!resolvedAsset.model) {\n errors.push('Anthropic adapter requires a model to be specified.');\n }\n\n if (resolvedAsset.sampling?.frequency_penalty !== undefined) {\n warnings.push('Anthropic does not support frequency_penalty. It will be ignored.');\n }\n if (resolvedAsset.sampling?.presence_penalty !== undefined) {\n warnings.push('Anthropic does not support presence_penalty. It will be ignored.');\n }\n if (resolvedAsset.reasoning?.effort !== undefined) {\n warnings.push('Anthropic uses budget_tokens for thinking, not effort. effort will be mapped approximately.');\n }\n if (resolvedAsset.response?.schema !== undefined && resolvedAsset.response?.format !== 'json') {\n warnings.push('Anthropic response.schema is mapped to output_config.format and should usually be paired with response.format: json.');\n }\n\n if (resolvedAsset.provider_options?.anthropic?.top_k !== undefined && resolvedAsset.provider_options.anthropic.top_k < 0) {\n errors.push('Anthropic provider_options.top_k must be >= 0.');\n }\n\n return { valid: errors.length === 0, errors, warnings };\n },\n\n render(asset: ResolvedPromptAsset, runtime: RuntimeRenderOptions): ProviderRequest {\n const resolvedAsset = resolveAssetForProvider(asset, runtime);\n const sections = renderSections(resolvedAsset, {\n variables: runtime.variables,\n strict: runtime.strict,\n });\n\n const messages: Array<Record<string, unknown>> = [];\n const anthropicCacheConfig = resolvedAsset.cache?.anthropic;\n const cacheType = anthropicCacheConfig?.type ?? 'ephemeral';\n const cacheControl = anthropicCacheConfig\n ? {\n type: cacheType,\n ...(anthropicCacheConfig.ttl ? { ttl: anthropicCacheConfig.ttl } : {}),\n }\n : undefined;\n const cacheMode = anthropicCacheConfig?.mode ?? 'automatic';\n\n // History\n if (runtime.history) {\n for (const msg of runtime.history) {\n messages.push({ role: msg.role, content: msg.content });\n }\n }\n\n // User message (prompt template)\n if (sections.prompt_template) {\n if (cacheControl && cacheMode === 'explicit' && anthropicCacheConfig?.cache_prompt_template) {\n messages.push({\n role: 'user',\n content: [{ type: 'text', text: sections.prompt_template, cache_control: cacheControl }],\n });\n } else {\n messages.push({ role: 'user', content: sections.prompt_template });\n }\n }\n\n const body: Record<string, unknown> = {\n model: resolvedAsset.model,\n messages,\n };\n\n // System goes as top-level field in Anthropic\n if (sections.system_instructions) {\n if (cacheControl && cacheMode === 'explicit' && anthropicCacheConfig?.cache_system_instructions !== false) {\n body.system = [{ type: 'text', text: sections.system_instructions, cache_control: cacheControl }];\n } else {\n body.system = sections.system_instructions;\n }\n }\n\n // Sampling params\n if (resolvedAsset.sampling?.temperature !== undefined) body.temperature = resolvedAsset.sampling.temperature;\n if (resolvedAsset.sampling?.top_p !== undefined) body.top_p = resolvedAsset.sampling.top_p;\n if (resolvedAsset.sampling?.stop !== undefined) body.stop_sequences = resolvedAsset.sampling.stop;\n if (resolvedAsset.sampling?.max_output_tokens !== undefined) {\n body.max_tokens = resolvedAsset.sampling.max_output_tokens;\n } else {\n // Anthropic requires max_tokens\n body.max_tokens = 4096;\n }\n\n // Thinking/reasoning\n if (resolvedAsset.reasoning?.budget_tokens) {\n body.thinking = {\n type: 'enabled',\n budget_tokens: resolvedAsset.reasoning.budget_tokens,\n };\n }\n\n // Provider-specific options\n if (resolvedAsset.provider_options?.anthropic?.top_k !== undefined) {\n body.top_k = resolvedAsset.provider_options.anthropic.top_k;\n }\n\n if (resolvedAsset.provider_options?.anthropic?.output_config !== undefined) {\n body.output_config = resolvedAsset.provider_options.anthropic.output_config;\n } else if (resolvedAsset.response?.schema !== undefined) {\n body.output_config = {\n format: {\n type: 'json_schema',\n schema: resolvedAsset.response.schema,\n },\n };\n }\n\n // Streaming\n if (resolvedAsset.response?.stream !== undefined) {\n body.stream = resolvedAsset.response.stream;\n }\n\n if (cacheControl && cacheMode === 'automatic') {\n body.cache_control = cacheControl;\n }\n\n // Tools\n if (resolvedAsset.tools && resolvedAsset.tools.length > 0) {\n body.tools = resolvedAsset.tools.map((tool) => {\n if (typeof tool === 'string') {\n const def = runtime.toolRegistry?.[tool];\n if (def) {\n if (cacheControl && cacheMode === 'explicit' && anthropicCacheConfig?.cache_tools) {\n return { ...(def as Record<string, unknown>), cache_control: cacheControl };\n }\n return def;\n }\n return {\n name: tool,\n ...(cacheControl && cacheMode === 'explicit' && anthropicCacheConfig?.cache_tools\n ? { cache_control: cacheControl }\n : {}),\n };\n }\n return {\n name: tool.name,\n description: tool.description,\n input_schema: tool.input_schema ?? { type: 'object', properties: {} },\n ...(cacheControl && cacheMode === 'explicit' && anthropicCacheConfig?.cache_tools\n ? { cache_control: cacheControl }\n : {}),\n };\n });\n }\n\n if (resolvedAsset.provider_options?.anthropic?.tool_choice !== undefined) {\n body.tool_choice = resolvedAsset.provider_options.anthropic.tool_choice;\n }\n\n return {\n body: applyRawProviderBody(body, resolvedAsset, 'anthropic'),\n provider: 'anthropic',\n model: resolvedAsset.model ?? 'unknown',\n };\n },\n});\n"],"mappings":"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","names":[]}
|