@sembl/provider-anthropic 0.3.0 → 0.5.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 +21 -1
- package/dist/index.cjs +35 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +35 -3
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -59,7 +59,9 @@ await coerce(input, { provider, schema, bundle });
|
|
|
59
59
|
| `client` | — | Pre-built SDK client. Takes precedence over `apiKey`/`baseURL`. |
|
|
60
60
|
| `apiKey` | `ANTHROPIC_API_KEY` | Falls back to the SDK's own env lookup. |
|
|
61
61
|
| `baseURL` | Anthropic production | Ignored when `client` is set. |
|
|
62
|
-
| `temperature` |
|
|
62
|
+
| `temperature` | not sent | Sent only when set. Newer models reject sampling parameters. |
|
|
63
|
+
| `thinking` | see note | `{ type: "disabled" }` for Claude 5 models; unset otherwise. |
|
|
64
|
+
| `requestOverrides` | — | Merged into the request body last. For the next model-specific parameter. |
|
|
63
65
|
| `maxTokens` | `4096` | Anthropic requires an explicit output budget. |
|
|
64
66
|
| `toolName` | `extract_<SchemaId>` | Sanitized to Anthropic's `^[a-zA-Z0-9_-]{1,64}$`. |
|
|
65
67
|
| `cachePrompt` | `false` | Cache the stable prefix — tool definition plus system prompt. |
|
|
@@ -67,6 +69,24 @@ await coerce(input, { provider, schema, bundle });
|
|
|
67
69
|
| `maxRetries` | `2` | Retries per call, handled by the SDK. |
|
|
68
70
|
| `timeoutMs` | `120000` | Per-attempt timeout. |
|
|
69
71
|
|
|
72
|
+
## Thinking and other model-specific parameters
|
|
73
|
+
|
|
74
|
+
Claude 5 models enable adaptive thinking by default, and the API rejects a
|
|
75
|
+
forced tool call while thinking is on. The provider therefore sends
|
|
76
|
+
`thinking: { type: "disabled" }` for any model id that names a Claude 5 model
|
|
77
|
+
(`claude-sonnet-5`, `claude-opus-5`, `claude-fable-5-1`, …) and nothing for
|
|
78
|
+
older models, which reject the parameter. Set `thinking` yourself to override
|
|
79
|
+
either way. When the next parameter of that kind appears, `requestOverrides`
|
|
80
|
+
lets you send it without waiting for a release:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
new AnthropicProvider({
|
|
84
|
+
model: "claude-sonnet-5",
|
|
85
|
+
apiKey,
|
|
86
|
+
requestOverrides: { metadata: { user_id: tenantId } },
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
70
90
|
## Prompt caching
|
|
71
91
|
|
|
72
92
|
Every call against the same schema sends the same tool definition and the same
|
package/dist/index.cjs
CHANGED
|
@@ -45,6 +45,9 @@ var import_sdk2 = __toESM(require("@anthropic-ai/sdk"), 1);
|
|
|
45
45
|
|
|
46
46
|
// src/anthropic-config.ts
|
|
47
47
|
var DEFAULT_MAX_TOKENS = 4096;
|
|
48
|
+
function isClaude5Model(model) {
|
|
49
|
+
return /claude-(?:fable|opus|sonnet|haiku)-5(?:[.-]|$)/i.test(model);
|
|
50
|
+
}
|
|
48
51
|
var DEFAULT_MAX_RETRIES = 2;
|
|
49
52
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
50
53
|
|
|
@@ -102,7 +105,30 @@ function toInputSchema(schema, bundle, resolvedEnums) {
|
|
|
102
105
|
}
|
|
103
106
|
|
|
104
107
|
// src/anthropic-provider.ts
|
|
108
|
+
function renderHistory(history, toolName) {
|
|
109
|
+
const messages = [];
|
|
110
|
+
let lastToolUseId = "";
|
|
111
|
+
history.forEach((turn, i) => {
|
|
112
|
+
if (turn.role === "assistant") {
|
|
113
|
+
lastToolUseId = `toolu_sembl_${i}`;
|
|
114
|
+
messages.push({
|
|
115
|
+
role: "assistant",
|
|
116
|
+
content: [{ type: "tool_use", id: lastToolUseId, name: toolName, input: turn.data }]
|
|
117
|
+
});
|
|
118
|
+
} else if (lastToolUseId) {
|
|
119
|
+
messages.push({
|
|
120
|
+
role: "user",
|
|
121
|
+
content: [{ type: "tool_result", tool_use_id: lastToolUseId, content: turn.text, is_error: true }]
|
|
122
|
+
});
|
|
123
|
+
} else {
|
|
124
|
+
messages.push({ role: "user", content: turn.text });
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
return messages;
|
|
128
|
+
}
|
|
105
129
|
var AnthropicProvider = class {
|
|
130
|
+
/** Repair turns are rendered as a tool call and its (failed) result. */
|
|
131
|
+
supportsHistory = true;
|
|
106
132
|
client;
|
|
107
133
|
config;
|
|
108
134
|
callOptions;
|
|
@@ -129,13 +155,18 @@ var AnthropicProvider = class {
|
|
|
129
155
|
request.bundle,
|
|
130
156
|
request.resolvedEnums
|
|
131
157
|
);
|
|
158
|
+
const thinking = this.config.thinking ?? (isClaude5Model(this.config.model) ? { type: "disabled" } : void 0);
|
|
132
159
|
const message = await this.send(
|
|
133
160
|
{
|
|
134
161
|
model: this.config.model,
|
|
135
162
|
max_tokens: maxTokens,
|
|
136
|
-
temperature: this.config.temperature
|
|
163
|
+
...this.config.temperature !== void 0 ? { temperature: this.config.temperature } : {},
|
|
164
|
+
...thinking ? { thinking } : {},
|
|
137
165
|
system: this.buildSystem(request.systemPrompt),
|
|
138
|
-
messages: [
|
|
166
|
+
messages: [
|
|
167
|
+
{ role: "user", content: request.userInput },
|
|
168
|
+
...renderHistory(request.history ?? [], toolName)
|
|
169
|
+
],
|
|
139
170
|
tools: [
|
|
140
171
|
{
|
|
141
172
|
name: toolName,
|
|
@@ -143,7 +174,8 @@ var AnthropicProvider = class {
|
|
|
143
174
|
input_schema: inputSchema
|
|
144
175
|
}
|
|
145
176
|
],
|
|
146
|
-
tool_choice: { type: "tool", name: toolName }
|
|
177
|
+
tool_choice: { type: "tool", name: toolName },
|
|
178
|
+
...this.config.requestOverrides
|
|
147
179
|
},
|
|
148
180
|
this.callOptions
|
|
149
181
|
);
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/anthropic-provider.ts","../src/anthropic-config.ts","../src/errors.ts","../src/schema-converter.ts"],"sourcesContent":["export { AnthropicProvider } from \"./anthropic-provider.js\";\nexport type { AnthropicProviderConfig } from \"./anthropic-config.js\";\nexport {\n DEFAULT_MAX_TOKENS,\n DEFAULT_MAX_RETRIES,\n DEFAULT_TIMEOUT_MS,\n} from \"./anthropic-config.js\";\nexport { AnthropicProviderError } from \"./errors.js\";\nexport type { ProviderErrorKind } from \"./errors.js\";\nexport { toInputSchema, toToolName } from \"./schema-converter.js\";\n","import Anthropic from \"@anthropic-ai/sdk\";\nimport type { Provider, ProviderRequest, ProviderResponse } from \"@sembl/core\";\nimport type { AnthropicProviderConfig } from \"./anthropic-config.js\";\nimport {\n DEFAULT_MAX_RETRIES,\n DEFAULT_MAX_TOKENS,\n DEFAULT_TIMEOUT_MS,\n} from \"./anthropic-config.js\";\nimport { AnthropicProviderError, toProviderError } from \"./errors.js\";\nimport { toInputSchema, toToolName } from \"./schema-converter.js\";\n\n/** Per-call overrides handed to the SDK alongside the request body. */\ninterface CallOptions {\n maxRetries?: number;\n timeout?: number;\n}\n\n/**\n * Anthropic provider implementation.\n *\n * Structured output is obtained by declaring the target schema as a single\n * tool and forcing the model to call it (`tool_choice: { type: \"tool\" }`), so\n * the arguments come back already parsed and shape-checked by the API — no\n * JSON scraped out of prose.\n *\n * Retries and timeouts are the SDK's (exponential backoff, `retry-after`\n * aware); this class only chooses the numbers and translates whatever comes\n * back out into an {@link AnthropicProviderError}.\n */\nexport class AnthropicProvider implements Provider {\n private client: Pick<Anthropic, \"messages\">;\n private config: AnthropicProviderConfig;\n private callOptions: CallOptions | undefined;\n\n constructor(config: AnthropicProviderConfig) {\n this.config = config;\n\n if (config.client) {\n // The host owns this client's transport policy, so only override it\n // per-call where the caller asked for something specific.\n this.client = config.client;\n this.callOptions =\n config.maxRetries === undefined && config.timeoutMs === undefined\n ? undefined\n : { maxRetries: config.maxRetries, timeout: config.timeoutMs };\n } else {\n this.client = new Anthropic({\n apiKey: config.apiKey,\n baseURL: config.baseURL,\n maxRetries: config.maxRetries ?? DEFAULT_MAX_RETRIES,\n timeout: config.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n });\n this.callOptions = undefined;\n }\n }\n\n async complete(request: ProviderRequest): Promise<ProviderResponse> {\n const toolName = this.config.toolName ?? toToolName(request.schema.id);\n const maxTokens = this.config.maxTokens ?? DEFAULT_MAX_TOKENS;\n const inputSchema = toInputSchema(\n request.schema,\n request.bundle,\n request.resolvedEnums,\n );\n\n const message = await this.send(\n {\n model: this.config.model,\n max_tokens: maxTokens,\n temperature: this.config.temperature ?? 0,\n system: this.buildSystem(request.systemPrompt),\n messages: [{ role: \"user\", content: request.userInput }],\n tools: [\n {\n name: toolName,\n description: request.schema.description,\n input_schema: inputSchema as Anthropic.Tool[\"input_schema\"],\n },\n ],\n tool_choice: { type: \"tool\", name: toolName },\n },\n this.callOptions,\n );\n\n const toolUse = message.content.find(\n (block): block is Anthropic.ToolUseBlock =>\n block.type === \"tool_use\" && block.name === toolName,\n );\n\n if (!toolUse) {\n if (message.stop_reason === \"max_tokens\") {\n throw new AnthropicProviderError(\n `Anthropic hit the ${maxTokens}-token output cap before completing the \"${toolName}\" call. ` +\n \"Raise maxTokens, or coerce into a smaller schema.\",\n { kind: \"truncated\", retryable: false, stopReason: \"max_tokens\" },\n );\n }\n throw new AnthropicProviderError(\n `Anthropic returned no \"${toolName}\" tool call (stop_reason: ${message.stop_reason ?? \"unknown\"})`,\n {\n kind: \"no_output\",\n retryable: false,\n stopReason: message.stop_reason ?? undefined,\n },\n );\n }\n\n return {\n data: toolUse.input as Record<string, unknown>,\n usage: {\n promptTokens: message.usage.input_tokens,\n completionTokens: message.usage.output_tokens,\n totalTokens: message.usage.input_tokens + message.usage.output_tokens,\n ...(message.usage.cache_read_input_tokens != null && {\n cacheReadTokens: message.usage.cache_read_input_tokens,\n }),\n ...(message.usage.cache_creation_input_tokens != null && {\n cacheWriteTokens: message.usage.cache_creation_input_tokens,\n }),\n },\n };\n }\n\n /**\n * The system prompt, marked as a cache breakpoint when caching is on.\n *\n * One breakpoint is enough: the API renders `tools` before `system`, so a\n * marker on the trailing system block covers the tool definition too — and\n * the user input, the only part that changes between calls, sits after it\n * in `messages` where it invalidates nothing.\n */\n private buildSystem(\n systemPrompt: string,\n ): string | Anthropic.TextBlockParam[] {\n if (!this.config.cachePrompt) return systemPrompt;\n\n return [\n {\n type: \"text\",\n text: systemPrompt,\n cache_control: { type: \"ephemeral\", ttl: this.config.cacheTtl ?? \"5m\" },\n },\n ];\n }\n\n /** Issue the call, translating SDK failures into typed provider errors. */\n private async send(\n body: Anthropic.MessageCreateParamsNonStreaming,\n options: CallOptions | undefined,\n ): Promise<Anthropic.Message> {\n try {\n return await this.client.messages.create(body, options);\n } catch (error) {\n throw toProviderError(error);\n }\n }\n}\n","import type Anthropic from \"@anthropic-ai/sdk\";\nimport type { ProviderConfig } from \"@sembl/core\";\n\n/**\n * Configuration specific to the Anthropic provider.\n *\n * Supply either `client` or `apiKey`. Prefer `client` when the host app\n * already resolves credentials its own way (Secret Manager, Vault, Bedrock,\n * a Vertex client) — the provider will reuse that client as-is rather than\n * constructing its own.\n */\nexport interface AnthropicProviderConfig extends ProviderConfig {\n /**\n * A pre-built Anthropic client. Takes precedence over `apiKey`/`baseURL`.\n * Also accepts an `AnthropicBedrock` / `AnthropicVertex` client — anything\n * exposing a compatible `messages.create`.\n */\n client?: Pick<Anthropic, \"messages\">;\n /** Anthropic API key. Ignored when `client` is supplied. */\n apiKey?: string;\n /** Base URL override. Ignored when `client` is supplied. */\n baseURL?: string;\n /**\n * Name given to the extraction tool the model is forced to call.\n * Defaults to a sanitized form of the schema id. Only override this if a\n * name shows up somewhere you care about (logs, prompt-cache keys).\n */\n toolName?: string;\n /**\n * Mark the stable prefix of the request — the tool definition and the\n * system prompt — as cacheable, so a run of calls against the same schema\n * pays to process it once instead of once per call.\n *\n * Off by default: a cache write costs more than an ordinary read of the\n * same tokens, so a single call, or a prefix below the model's minimum\n * cacheable length, comes out slightly behind. Turn it on for batches.\n * `ProviderResponse.usage.cacheReadTokens` says whether it is paying off.\n */\n cachePrompt?: boolean;\n /**\n * Lifetime of the cached prefix. Ignored unless `cachePrompt` is set.\n *\n * `\"5m\"` (the default) is refreshed by every read, so back-to-back calls\n * keep it alive indefinitely and it is the cheaper write. Choose `\"1h\"`\n * only for traffic with gaps longer than five minutes between calls — it\n * survives the gap, but the write costs roughly twice as much.\n */\n cacheTtl?: \"5m\" | \"1h\";\n /**\n * How many times the SDK retries a failed call before giving up. The SDK\n * retries connection errors, 408/409/429 and 5xx with exponential backoff\n * and honours `retry-after`, so there is nothing to hand-roll here.\n *\n * Defaults to {@link DEFAULT_MAX_RETRIES}. When a `client` is supplied,\n * leaving this unset keeps that client's own policy.\n */\n maxRetries?: number;\n /**\n * Timeout for a single attempt, in milliseconds. Retries each get their\n * own attempt, so the worst-case wall clock is roughly\n * `timeoutMs * (maxRetries + 1)` plus backoff.\n *\n * Defaults to {@link DEFAULT_TIMEOUT_MS}. When a `client` is supplied,\n * leaving this unset keeps that client's own policy.\n */\n timeoutMs?: number;\n}\n\n/** Anthropic requires an explicit output budget; this is used when none is set. */\nexport const DEFAULT_MAX_TOKENS = 4096;\n\n/** Matches the SDK's own default; stated here so it survives an SDK change. */\nexport const DEFAULT_MAX_RETRIES = 2;\n\n/**\n * Two minutes per attempt. The SDK's own default is ten, which is a long time\n * for a backend import to sit on one listing when the retry is cheap.\n */\nexport const DEFAULT_TIMEOUT_MS = 120_000;\n","import { APIConnectionError, APIError } from \"@anthropic-ai/sdk\";\n\n/**\n * Why a provider call failed, in the terms a caller can act on.\n *\n * A batch import wants to route these differently: `\"api\"` failures are worth\n * re-queueing, `\"truncated\"` needs a bigger output budget, and `\"no_output\"`\n * is a property of that one listing's content — retrying it changes nothing.\n *\n * The same three kinds are used by `@sembl/provider-openai`, so a caller that\n * branches on `kind` keeps working when the provider is swapped.\n */\nexport type ProviderErrorKind = \"api\" | \"truncated\" | \"no_output\";\n\n/**\n * Error thrown by the Anthropic provider.\n *\n * Branch on `kind` rather than matching the message — messages stay\n * diagnostic and are free to change.\n */\nexport class AnthropicProviderError extends Error {\n /** What class of failure this is. */\n public readonly kind: ProviderErrorKind;\n /**\n * Whether another attempt could plausibly succeed. The SDK has already\n * retried retryable transport failures (see `maxRetries`); this says only\n * that the failure was transient in nature, so a caller running a queue can\n * re-enqueue the item rather than dead-letter it.\n */\n public readonly retryable: boolean;\n /** HTTP status, when the failure came back as an API error. */\n public readonly status?: number;\n /** Anthropic's `stop_reason`, when the call returned a message we rejected. */\n public readonly stopReason?: string;\n\n constructor(\n message: string,\n options: {\n kind: ProviderErrorKind;\n retryable: boolean;\n status?: number;\n stopReason?: string;\n cause?: unknown;\n },\n ) {\n super(message, { cause: options.cause });\n this.name = \"AnthropicProviderError\";\n this.kind = options.kind;\n this.retryable = options.retryable;\n this.status = options.status;\n this.stopReason = options.stopReason;\n }\n}\n\n/**\n * Wrap an SDK-level failure as an `AnthropicProviderError`.\n *\n * Retryability is read off the SDK's own error classes rather than the\n * message: connection failures and timeouts are transient by construction,\n * and of the status codes only 408/409/429 and 5xx are worth another attempt —\n * the same set the SDK itself retries internally.\n */\nexport function toProviderError(error: unknown): AnthropicProviderError {\n if (error instanceof APIError) {\n const status = error.status;\n const retryable =\n error instanceof APIConnectionError ||\n status === undefined ||\n status === 408 ||\n status === 409 ||\n status === 429 ||\n status >= 500;\n\n return new AnthropicProviderError(\n `Anthropic request failed${status ? ` (${status})` : \"\"}: ${error.message}`,\n { kind: \"api\", retryable, status, cause: error },\n );\n }\n\n // Anything else (an AbortError, a bug in a caller-supplied client) is\n // surfaced with the same shape so callers only need one catch.\n return new AnthropicProviderError(\n `Anthropic request failed: ${error instanceof Error ? error.message : String(error)}`,\n { kind: \"api\", retryable: false, cause: error },\n );\n}\n","import type { RuntimeSchema, ResolvedEnums, SchemaBundle } from \"@sembl/core\";\nimport { runtimeSchemaToJsonSchema } from \"@sembl/core\";\n\n/** Anthropic tool names must match `^[a-zA-Z0-9_-]{1,64}$`. */\nexport function toToolName(schemaId: string): string {\n const cleaned = schemaId.replace(/[^a-zA-Z0-9_-]/g, \"_\").slice(0, 57);\n return `extract_${cleaned || \"schema\"}`.slice(0, 64);\n}\n\n/**\n * Convert a RuntimeSchema to an Anthropic tool `input_schema`.\n *\n * Unlike OpenAI structured outputs, Anthropic takes ordinary JSON Schema, so\n * optional fields are left out of `required` instead of being made nullable.\n * That keeps the model from inventing explicit `null`s for fields the source\n * text simply never mentioned — which matters for partial coercion, where an\n * absent field and a null field mean different things to the caller.\n */\nexport function toInputSchema(\n schema: RuntimeSchema,\n bundle?: SchemaBundle,\n resolvedEnums?: ResolvedEnums,\n): Record<string, unknown> {\n return runtimeSchemaToJsonSchema(schema, bundle, {\n dialect: \"standard\",\n resolvedEnums,\n });\n}\n"],"mappings":"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","names":["import_sdk","Anthropic"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/anthropic-provider.ts","../src/anthropic-config.ts","../src/errors.ts","../src/schema-converter.ts"],"sourcesContent":["export { AnthropicProvider } from \"./anthropic-provider.js\";\nexport type { AnthropicProviderConfig } from \"./anthropic-config.js\";\nexport {\n DEFAULT_MAX_TOKENS,\n DEFAULT_MAX_RETRIES,\n DEFAULT_TIMEOUT_MS,\n} from \"./anthropic-config.js\";\nexport { AnthropicProviderError } from \"./errors.js\";\nexport type { ProviderErrorKind } from \"./errors.js\";\nexport { toInputSchema, toToolName } from \"./schema-converter.js\";\n","import Anthropic from \"@anthropic-ai/sdk\";\nimport type { Provider, ProviderRequest, ProviderResponse, ProviderTurn } from \"@sembl/core\";\nimport type { AnthropicProviderConfig } from \"./anthropic-config.js\";\nimport {\n DEFAULT_MAX_RETRIES,\n DEFAULT_MAX_TOKENS,\n DEFAULT_TIMEOUT_MS,\n isClaude5Model,\n} from \"./anthropic-config.js\";\nimport { AnthropicProviderError, toProviderError } from \"./errors.js\";\nimport { toInputSchema, toToolName } from \"./schema-converter.js\";\n\n/**\n * Earlier turns as the API saw them: the model's rejected output as the\n * tool call it made, the correction as that call's result flagged as an\n * error. The forced tool choice then makes the model call the tool again,\n * which is the corrected attempt. Ids only have to match within the\n * conversation, so they are synthesised.\n */\nfunction renderHistory(history: readonly ProviderTurn[], toolName: string): Anthropic.MessageParam[] {\n const messages: Anthropic.MessageParam[] = [];\n let lastToolUseId = \"\";\n history.forEach((turn, i) => {\n if (turn.role === \"assistant\") {\n lastToolUseId = `toolu_sembl_${i}`;\n messages.push({\n role: \"assistant\",\n content: [{ type: \"tool_use\", id: lastToolUseId, name: toolName, input: turn.data }],\n });\n } else if (lastToolUseId) {\n messages.push({\n role: \"user\",\n content: [{ type: \"tool_result\", tool_use_id: lastToolUseId, content: turn.text, is_error: true }],\n });\n } else {\n messages.push({ role: \"user\", content: turn.text });\n }\n });\n return messages;\n}\n\n/** Per-call overrides handed to the SDK alongside the request body. */\ninterface CallOptions {\n maxRetries?: number;\n timeout?: number;\n}\n\n/**\n * Anthropic provider implementation.\n *\n * Structured output is obtained by declaring the target schema as a single\n * tool and forcing the model to call it (`tool_choice: { type: \"tool\" }`), so\n * the arguments come back already parsed and shape-checked by the API — no\n * JSON scraped out of prose.\n *\n * Retries and timeouts are the SDK's (exponential backoff, `retry-after`\n * aware); this class only chooses the numbers and translates whatever comes\n * back out into an {@link AnthropicProviderError}.\n */\nexport class AnthropicProvider implements Provider {\n /** Repair turns are rendered as a tool call and its (failed) result. */\n readonly supportsHistory = true;\n\n private client: Pick<Anthropic, \"messages\">;\n private config: AnthropicProviderConfig;\n private callOptions: CallOptions | undefined;\n\n constructor(config: AnthropicProviderConfig) {\n this.config = config;\n\n if (config.client) {\n // The host owns this client's transport policy, so only override it\n // per-call where the caller asked for something specific.\n this.client = config.client;\n this.callOptions =\n config.maxRetries === undefined && config.timeoutMs === undefined\n ? undefined\n : { maxRetries: config.maxRetries, timeout: config.timeoutMs };\n } else {\n this.client = new Anthropic({\n apiKey: config.apiKey,\n baseURL: config.baseURL,\n maxRetries: config.maxRetries ?? DEFAULT_MAX_RETRIES,\n timeout: config.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n });\n this.callOptions = undefined;\n }\n }\n\n async complete(request: ProviderRequest): Promise<ProviderResponse> {\n const toolName = this.config.toolName ?? toToolName(request.schema.id);\n const maxTokens = this.config.maxTokens ?? DEFAULT_MAX_TOKENS;\n const inputSchema = toInputSchema(\n request.schema,\n request.bundle,\n request.resolvedEnums,\n );\n\n // Sampling parameters go only when configured: newer models reject\n // `temperature` (and `top_p`/`top_k`) outright, and structured\n // extraction does not need them.\n const thinking = this.config.thinking ?? (isClaude5Model(this.config.model) ? { type: \"disabled\" as const } : undefined);\n const message = await this.send(\n {\n model: this.config.model,\n max_tokens: maxTokens,\n ...(this.config.temperature !== undefined ? { temperature: this.config.temperature } : {}),\n ...(thinking ? { thinking } : {}),\n system: this.buildSystem(request.systemPrompt),\n messages: [\n { role: \"user\", content: request.userInput },\n ...renderHistory(request.history ?? [], toolName),\n ],\n tools: [\n {\n name: toolName,\n description: request.schema.description,\n input_schema: inputSchema as Anthropic.Tool[\"input_schema\"],\n },\n ],\n tool_choice: { type: \"tool\", name: toolName },\n ...this.config.requestOverrides,\n },\n this.callOptions,\n );\n\n const toolUse = message.content.find(\n (block): block is Anthropic.ToolUseBlock =>\n block.type === \"tool_use\" && block.name === toolName,\n );\n\n if (!toolUse) {\n if (message.stop_reason === \"max_tokens\") {\n throw new AnthropicProviderError(\n `Anthropic hit the ${maxTokens}-token output cap before completing the \"${toolName}\" call. ` +\n \"Raise maxTokens, or coerce into a smaller schema.\",\n { kind: \"truncated\", retryable: false, stopReason: \"max_tokens\" },\n );\n }\n throw new AnthropicProviderError(\n `Anthropic returned no \"${toolName}\" tool call (stop_reason: ${message.stop_reason ?? \"unknown\"})`,\n {\n kind: \"no_output\",\n retryable: false,\n stopReason: message.stop_reason ?? undefined,\n },\n );\n }\n\n return {\n data: toolUse.input as Record<string, unknown>,\n usage: {\n promptTokens: message.usage.input_tokens,\n completionTokens: message.usage.output_tokens,\n totalTokens: message.usage.input_tokens + message.usage.output_tokens,\n ...(message.usage.cache_read_input_tokens != null && {\n cacheReadTokens: message.usage.cache_read_input_tokens,\n }),\n ...(message.usage.cache_creation_input_tokens != null && {\n cacheWriteTokens: message.usage.cache_creation_input_tokens,\n }),\n },\n };\n }\n\n /**\n * The system prompt, marked as a cache breakpoint when caching is on.\n *\n * One breakpoint is enough: the API renders `tools` before `system`, so a\n * marker on the trailing system block covers the tool definition too — and\n * the user input, the only part that changes between calls, sits after it\n * in `messages` where it invalidates nothing.\n */\n private buildSystem(\n systemPrompt: string,\n ): string | Anthropic.TextBlockParam[] {\n if (!this.config.cachePrompt) return systemPrompt;\n\n return [\n {\n type: \"text\",\n text: systemPrompt,\n cache_control: { type: \"ephemeral\", ttl: this.config.cacheTtl ?? \"5m\" },\n },\n ];\n }\n\n /** Issue the call, translating SDK failures into typed provider errors. */\n private async send(\n body: Anthropic.MessageCreateParamsNonStreaming,\n options: CallOptions | undefined,\n ): Promise<Anthropic.Message> {\n try {\n return await this.client.messages.create(body, options);\n } catch (error) {\n throw toProviderError(error);\n }\n }\n}\n","import type Anthropic from \"@anthropic-ai/sdk\";\nimport type { ProviderConfig } from \"@sembl/core\";\n\n/**\n * Configuration specific to the Anthropic provider.\n *\n * Supply either `client` or `apiKey`. Prefer `client` when the host app\n * already resolves credentials its own way (Secret Manager, Vault, Bedrock,\n * a Vertex client) — the provider will reuse that client as-is rather than\n * constructing its own.\n */\nexport interface AnthropicProviderConfig extends ProviderConfig {\n /**\n * A pre-built Anthropic client. Takes precedence over `apiKey`/`baseURL`.\n * Also accepts an `AnthropicBedrock` / `AnthropicVertex` client — anything\n * exposing a compatible `messages.create`.\n */\n client?: Pick<Anthropic, \"messages\">;\n /** Anthropic API key. Ignored when `client` is supplied. */\n apiKey?: string;\n /** Base URL override. Ignored when `client` is supplied. */\n baseURL?: string;\n /**\n * Name given to the extraction tool the model is forced to call.\n * Defaults to a sanitized form of the schema id. Only override this if a\n * name shows up somewhere you care about (logs, prompt-cache keys).\n */\n toolName?: string;\n /**\n * Mark the stable prefix of the request — the tool definition and the\n * system prompt — as cacheable, so a run of calls against the same schema\n * pays to process it once instead of once per call.\n *\n * Off by default: a cache write costs more than an ordinary read of the\n * same tokens, so a single call, or a prefix below the model's minimum\n * cacheable length, comes out slightly behind. Turn it on for batches.\n * `ProviderResponse.usage.cacheReadTokens` says whether it is paying off.\n */\n cachePrompt?: boolean;\n /**\n * Lifetime of the cached prefix. Ignored unless `cachePrompt` is set.\n *\n * `\"5m\"` (the default) is refreshed by every read, so back-to-back calls\n * keep it alive indefinitely and it is the cheaper write. Choose `\"1h\"`\n * only for traffic with gaps longer than five minutes between calls — it\n * survives the gap, but the write costs roughly twice as much.\n */\n cacheTtl?: \"5m\" | \"1h\";\n /**\n * Extended-thinking setting sent with every call.\n *\n * Claude 5 models turn adaptive thinking on by default, and a forced tool\n * call is rejected while it is on, so for those models the provider sends\n * `{ type: \"disabled\" }` unless this is set. Older models reject the\n * parameter outright, so nothing is sent for them by default. Set it\n * explicitly to override either behaviour.\n */\n thinking?: Anthropic.ThinkingConfigParam;\n /**\n * Fields merged into the request body last, after everything the provider\n * builds. The escape hatch for the next model-specific parameter, so a\n * caller does not have to wait for a library release — or wrap the client\n * — to send it.\n */\n requestOverrides?: Partial<Anthropic.MessageCreateParamsNonStreaming>;\n /**\n * How many times the SDK retries a failed call before giving up. The SDK\n * retries connection errors, 408/409/429 and 5xx with exponential backoff\n * and honours `retry-after`, so there is nothing to hand-roll here.\n *\n * Defaults to {@link DEFAULT_MAX_RETRIES}. When a `client` is supplied,\n * leaving this unset keeps that client's own policy.\n */\n maxRetries?: number;\n /**\n * Timeout for a single attempt, in milliseconds. Retries each get their\n * own attempt, so the worst-case wall clock is roughly\n * `timeoutMs * (maxRetries + 1)` plus backoff.\n *\n * Defaults to {@link DEFAULT_TIMEOUT_MS}. When a `client` is supplied,\n * leaving this unset keeps that client's own policy.\n */\n timeoutMs?: number;\n}\n\n/** Anthropic requires an explicit output budget; this is used when none is set. */\nexport const DEFAULT_MAX_TOKENS = 4096;\n\n/**\n * Whether a model id names a Claude 5 model. Those enable adaptive thinking\n * by default, which a forced tool call cannot be combined with.\n */\nexport function isClaude5Model(model: string): boolean {\n return /claude-(?:fable|opus|sonnet|haiku)-5(?:[.-]|$)/i.test(model);\n}\n\n/** Matches the SDK's own default; stated here so it survives an SDK change. */\nexport const DEFAULT_MAX_RETRIES = 2;\n\n/**\n * Two minutes per attempt. The SDK's own default is ten, which is a long time\n * for a backend import to sit on one listing when the retry is cheap.\n */\nexport const DEFAULT_TIMEOUT_MS = 120_000;\n","import { APIConnectionError, APIError } from \"@anthropic-ai/sdk\";\n\n/**\n * Why a provider call failed, in the terms a caller can act on.\n *\n * A batch import wants to route these differently: `\"api\"` failures are worth\n * re-queueing, `\"truncated\"` needs a bigger output budget, and `\"no_output\"`\n * is a property of that one listing's content — retrying it changes nothing.\n *\n * The same three kinds are used by `@sembl/provider-openai`, so a caller that\n * branches on `kind` keeps working when the provider is swapped.\n */\nexport type ProviderErrorKind = \"api\" | \"truncated\" | \"no_output\";\n\n/**\n * Error thrown by the Anthropic provider.\n *\n * Branch on `kind` rather than matching the message — messages stay\n * diagnostic and are free to change.\n */\nexport class AnthropicProviderError extends Error {\n /** What class of failure this is. */\n public readonly kind: ProviderErrorKind;\n /**\n * Whether another attempt could plausibly succeed. The SDK has already\n * retried retryable transport failures (see `maxRetries`); this says only\n * that the failure was transient in nature, so a caller running a queue can\n * re-enqueue the item rather than dead-letter it.\n */\n public readonly retryable: boolean;\n /** HTTP status, when the failure came back as an API error. */\n public readonly status?: number;\n /** Anthropic's `stop_reason`, when the call returned a message we rejected. */\n public readonly stopReason?: string;\n\n constructor(\n message: string,\n options: {\n kind: ProviderErrorKind;\n retryable: boolean;\n status?: number;\n stopReason?: string;\n cause?: unknown;\n },\n ) {\n super(message, { cause: options.cause });\n this.name = \"AnthropicProviderError\";\n this.kind = options.kind;\n this.retryable = options.retryable;\n this.status = options.status;\n this.stopReason = options.stopReason;\n }\n}\n\n/**\n * Wrap an SDK-level failure as an `AnthropicProviderError`.\n *\n * Retryability is read off the SDK's own error classes rather than the\n * message: connection failures and timeouts are transient by construction,\n * and of the status codes only 408/409/429 and 5xx are worth another attempt —\n * the same set the SDK itself retries internally.\n */\nexport function toProviderError(error: unknown): AnthropicProviderError {\n if (error instanceof APIError) {\n const status = error.status;\n const retryable =\n error instanceof APIConnectionError ||\n status === undefined ||\n status === 408 ||\n status === 409 ||\n status === 429 ||\n status >= 500;\n\n return new AnthropicProviderError(\n `Anthropic request failed${status ? ` (${status})` : \"\"}: ${error.message}`,\n { kind: \"api\", retryable, status, cause: error },\n );\n }\n\n // Anything else (an AbortError, a bug in a caller-supplied client) is\n // surfaced with the same shape so callers only need one catch.\n return new AnthropicProviderError(\n `Anthropic request failed: ${error instanceof Error ? error.message : String(error)}`,\n { kind: \"api\", retryable: false, cause: error },\n );\n}\n","import type { RuntimeSchema, ResolvedEnums, SchemaBundle } from \"@sembl/core\";\nimport { runtimeSchemaToJsonSchema } from \"@sembl/core\";\n\n/** Anthropic tool names must match `^[a-zA-Z0-9_-]{1,64}$`. */\nexport function toToolName(schemaId: string): string {\n const cleaned = schemaId.replace(/[^a-zA-Z0-9_-]/g, \"_\").slice(0, 57);\n return `extract_${cleaned || \"schema\"}`.slice(0, 64);\n}\n\n/**\n * Convert a RuntimeSchema to an Anthropic tool `input_schema`.\n *\n * Unlike OpenAI structured outputs, Anthropic takes ordinary JSON Schema, so\n * optional fields are left out of `required` instead of being made nullable.\n * That keeps the model from inventing explicit `null`s for fields the source\n * text simply never mentioned — which matters for partial coercion, where an\n * absent field and a null field mean different things to the caller.\n */\nexport function toInputSchema(\n schema: RuntimeSchema,\n bundle?: SchemaBundle,\n resolvedEnums?: ResolvedEnums,\n): Record<string, unknown> {\n return runtimeSchemaToJsonSchema(schema, bundle, {\n dialect: \"standard\",\n resolvedEnums,\n });\n}\n"],"mappings":"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","names":["import_sdk","Anthropic"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -46,6 +46,23 @@ interface AnthropicProviderConfig extends ProviderConfig {
|
|
|
46
46
|
* survives the gap, but the write costs roughly twice as much.
|
|
47
47
|
*/
|
|
48
48
|
cacheTtl?: "5m" | "1h";
|
|
49
|
+
/**
|
|
50
|
+
* Extended-thinking setting sent with every call.
|
|
51
|
+
*
|
|
52
|
+
* Claude 5 models turn adaptive thinking on by default, and a forced tool
|
|
53
|
+
* call is rejected while it is on, so for those models the provider sends
|
|
54
|
+
* `{ type: "disabled" }` unless this is set. Older models reject the
|
|
55
|
+
* parameter outright, so nothing is sent for them by default. Set it
|
|
56
|
+
* explicitly to override either behaviour.
|
|
57
|
+
*/
|
|
58
|
+
thinking?: Anthropic.ThinkingConfigParam;
|
|
59
|
+
/**
|
|
60
|
+
* Fields merged into the request body last, after everything the provider
|
|
61
|
+
* builds. The escape hatch for the next model-specific parameter, so a
|
|
62
|
+
* caller does not have to wait for a library release — or wrap the client
|
|
63
|
+
* — to send it.
|
|
64
|
+
*/
|
|
65
|
+
requestOverrides?: Partial<Anthropic.MessageCreateParamsNonStreaming>;
|
|
49
66
|
/**
|
|
50
67
|
* How many times the SDK retries a failed call before giving up. The SDK
|
|
51
68
|
* retries connection errors, 408/409/429 and 5xx with exponential backoff
|
|
@@ -88,6 +105,8 @@ declare const DEFAULT_TIMEOUT_MS = 120000;
|
|
|
88
105
|
* back out into an {@link AnthropicProviderError}.
|
|
89
106
|
*/
|
|
90
107
|
declare class AnthropicProvider implements Provider {
|
|
108
|
+
/** Repair turns are rendered as a tool call and its (failed) result. */
|
|
109
|
+
readonly supportsHistory = true;
|
|
91
110
|
private client;
|
|
92
111
|
private config;
|
|
93
112
|
private callOptions;
|
package/dist/index.d.ts
CHANGED
|
@@ -46,6 +46,23 @@ interface AnthropicProviderConfig extends ProviderConfig {
|
|
|
46
46
|
* survives the gap, but the write costs roughly twice as much.
|
|
47
47
|
*/
|
|
48
48
|
cacheTtl?: "5m" | "1h";
|
|
49
|
+
/**
|
|
50
|
+
* Extended-thinking setting sent with every call.
|
|
51
|
+
*
|
|
52
|
+
* Claude 5 models turn adaptive thinking on by default, and a forced tool
|
|
53
|
+
* call is rejected while it is on, so for those models the provider sends
|
|
54
|
+
* `{ type: "disabled" }` unless this is set. Older models reject the
|
|
55
|
+
* parameter outright, so nothing is sent for them by default. Set it
|
|
56
|
+
* explicitly to override either behaviour.
|
|
57
|
+
*/
|
|
58
|
+
thinking?: Anthropic.ThinkingConfigParam;
|
|
59
|
+
/**
|
|
60
|
+
* Fields merged into the request body last, after everything the provider
|
|
61
|
+
* builds. The escape hatch for the next model-specific parameter, so a
|
|
62
|
+
* caller does not have to wait for a library release — or wrap the client
|
|
63
|
+
* — to send it.
|
|
64
|
+
*/
|
|
65
|
+
requestOverrides?: Partial<Anthropic.MessageCreateParamsNonStreaming>;
|
|
49
66
|
/**
|
|
50
67
|
* How many times the SDK retries a failed call before giving up. The SDK
|
|
51
68
|
* retries connection errors, 408/409/429 and 5xx with exponential backoff
|
|
@@ -88,6 +105,8 @@ declare const DEFAULT_TIMEOUT_MS = 120000;
|
|
|
88
105
|
* back out into an {@link AnthropicProviderError}.
|
|
89
106
|
*/
|
|
90
107
|
declare class AnthropicProvider implements Provider {
|
|
108
|
+
/** Repair turns are rendered as a tool call and its (failed) result. */
|
|
109
|
+
readonly supportsHistory = true;
|
|
91
110
|
private client;
|
|
92
111
|
private config;
|
|
93
112
|
private callOptions;
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,9 @@ import Anthropic from "@anthropic-ai/sdk";
|
|
|
3
3
|
|
|
4
4
|
// src/anthropic-config.ts
|
|
5
5
|
var DEFAULT_MAX_TOKENS = 4096;
|
|
6
|
+
function isClaude5Model(model) {
|
|
7
|
+
return /claude-(?:fable|opus|sonnet|haiku)-5(?:[.-]|$)/i.test(model);
|
|
8
|
+
}
|
|
6
9
|
var DEFAULT_MAX_RETRIES = 2;
|
|
7
10
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
8
11
|
|
|
@@ -60,7 +63,30 @@ function toInputSchema(schema, bundle, resolvedEnums) {
|
|
|
60
63
|
}
|
|
61
64
|
|
|
62
65
|
// src/anthropic-provider.ts
|
|
66
|
+
function renderHistory(history, toolName) {
|
|
67
|
+
const messages = [];
|
|
68
|
+
let lastToolUseId = "";
|
|
69
|
+
history.forEach((turn, i) => {
|
|
70
|
+
if (turn.role === "assistant") {
|
|
71
|
+
lastToolUseId = `toolu_sembl_${i}`;
|
|
72
|
+
messages.push({
|
|
73
|
+
role: "assistant",
|
|
74
|
+
content: [{ type: "tool_use", id: lastToolUseId, name: toolName, input: turn.data }]
|
|
75
|
+
});
|
|
76
|
+
} else if (lastToolUseId) {
|
|
77
|
+
messages.push({
|
|
78
|
+
role: "user",
|
|
79
|
+
content: [{ type: "tool_result", tool_use_id: lastToolUseId, content: turn.text, is_error: true }]
|
|
80
|
+
});
|
|
81
|
+
} else {
|
|
82
|
+
messages.push({ role: "user", content: turn.text });
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
return messages;
|
|
86
|
+
}
|
|
63
87
|
var AnthropicProvider = class {
|
|
88
|
+
/** Repair turns are rendered as a tool call and its (failed) result. */
|
|
89
|
+
supportsHistory = true;
|
|
64
90
|
client;
|
|
65
91
|
config;
|
|
66
92
|
callOptions;
|
|
@@ -87,13 +113,18 @@ var AnthropicProvider = class {
|
|
|
87
113
|
request.bundle,
|
|
88
114
|
request.resolvedEnums
|
|
89
115
|
);
|
|
116
|
+
const thinking = this.config.thinking ?? (isClaude5Model(this.config.model) ? { type: "disabled" } : void 0);
|
|
90
117
|
const message = await this.send(
|
|
91
118
|
{
|
|
92
119
|
model: this.config.model,
|
|
93
120
|
max_tokens: maxTokens,
|
|
94
|
-
temperature: this.config.temperature
|
|
121
|
+
...this.config.temperature !== void 0 ? { temperature: this.config.temperature } : {},
|
|
122
|
+
...thinking ? { thinking } : {},
|
|
95
123
|
system: this.buildSystem(request.systemPrompt),
|
|
96
|
-
messages: [
|
|
124
|
+
messages: [
|
|
125
|
+
{ role: "user", content: request.userInput },
|
|
126
|
+
...renderHistory(request.history ?? [], toolName)
|
|
127
|
+
],
|
|
97
128
|
tools: [
|
|
98
129
|
{
|
|
99
130
|
name: toolName,
|
|
@@ -101,7 +132,8 @@ var AnthropicProvider = class {
|
|
|
101
132
|
input_schema: inputSchema
|
|
102
133
|
}
|
|
103
134
|
],
|
|
104
|
-
tool_choice: { type: "tool", name: toolName }
|
|
135
|
+
tool_choice: { type: "tool", name: toolName },
|
|
136
|
+
...this.config.requestOverrides
|
|
105
137
|
},
|
|
106
138
|
this.callOptions
|
|
107
139
|
);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/anthropic-provider.ts","../src/anthropic-config.ts","../src/errors.ts","../src/schema-converter.ts"],"sourcesContent":["import Anthropic from \"@anthropic-ai/sdk\";\nimport type { Provider, ProviderRequest, ProviderResponse } from \"@sembl/core\";\nimport type { AnthropicProviderConfig } from \"./anthropic-config.js\";\nimport {\n DEFAULT_MAX_RETRIES,\n DEFAULT_MAX_TOKENS,\n DEFAULT_TIMEOUT_MS,\n} from \"./anthropic-config.js\";\nimport { AnthropicProviderError, toProviderError } from \"./errors.js\";\nimport { toInputSchema, toToolName } from \"./schema-converter.js\";\n\n/** Per-call overrides handed to the SDK alongside the request body. */\ninterface CallOptions {\n maxRetries?: number;\n timeout?: number;\n}\n\n/**\n * Anthropic provider implementation.\n *\n * Structured output is obtained by declaring the target schema as a single\n * tool and forcing the model to call it (`tool_choice: { type: \"tool\" }`), so\n * the arguments come back already parsed and shape-checked by the API — no\n * JSON scraped out of prose.\n *\n * Retries and timeouts are the SDK's (exponential backoff, `retry-after`\n * aware); this class only chooses the numbers and translates whatever comes\n * back out into an {@link AnthropicProviderError}.\n */\nexport class AnthropicProvider implements Provider {\n private client: Pick<Anthropic, \"messages\">;\n private config: AnthropicProviderConfig;\n private callOptions: CallOptions | undefined;\n\n constructor(config: AnthropicProviderConfig) {\n this.config = config;\n\n if (config.client) {\n // The host owns this client's transport policy, so only override it\n // per-call where the caller asked for something specific.\n this.client = config.client;\n this.callOptions =\n config.maxRetries === undefined && config.timeoutMs === undefined\n ? undefined\n : { maxRetries: config.maxRetries, timeout: config.timeoutMs };\n } else {\n this.client = new Anthropic({\n apiKey: config.apiKey,\n baseURL: config.baseURL,\n maxRetries: config.maxRetries ?? DEFAULT_MAX_RETRIES,\n timeout: config.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n });\n this.callOptions = undefined;\n }\n }\n\n async complete(request: ProviderRequest): Promise<ProviderResponse> {\n const toolName = this.config.toolName ?? toToolName(request.schema.id);\n const maxTokens = this.config.maxTokens ?? DEFAULT_MAX_TOKENS;\n const inputSchema = toInputSchema(\n request.schema,\n request.bundle,\n request.resolvedEnums,\n );\n\n const message = await this.send(\n {\n model: this.config.model,\n max_tokens: maxTokens,\n temperature: this.config.temperature ?? 0,\n system: this.buildSystem(request.systemPrompt),\n messages: [{ role: \"user\", content: request.userInput }],\n tools: [\n {\n name: toolName,\n description: request.schema.description,\n input_schema: inputSchema as Anthropic.Tool[\"input_schema\"],\n },\n ],\n tool_choice: { type: \"tool\", name: toolName },\n },\n this.callOptions,\n );\n\n const toolUse = message.content.find(\n (block): block is Anthropic.ToolUseBlock =>\n block.type === \"tool_use\" && block.name === toolName,\n );\n\n if (!toolUse) {\n if (message.stop_reason === \"max_tokens\") {\n throw new AnthropicProviderError(\n `Anthropic hit the ${maxTokens}-token output cap before completing the \"${toolName}\" call. ` +\n \"Raise maxTokens, or coerce into a smaller schema.\",\n { kind: \"truncated\", retryable: false, stopReason: \"max_tokens\" },\n );\n }\n throw new AnthropicProviderError(\n `Anthropic returned no \"${toolName}\" tool call (stop_reason: ${message.stop_reason ?? \"unknown\"})`,\n {\n kind: \"no_output\",\n retryable: false,\n stopReason: message.stop_reason ?? undefined,\n },\n );\n }\n\n return {\n data: toolUse.input as Record<string, unknown>,\n usage: {\n promptTokens: message.usage.input_tokens,\n completionTokens: message.usage.output_tokens,\n totalTokens: message.usage.input_tokens + message.usage.output_tokens,\n ...(message.usage.cache_read_input_tokens != null && {\n cacheReadTokens: message.usage.cache_read_input_tokens,\n }),\n ...(message.usage.cache_creation_input_tokens != null && {\n cacheWriteTokens: message.usage.cache_creation_input_tokens,\n }),\n },\n };\n }\n\n /**\n * The system prompt, marked as a cache breakpoint when caching is on.\n *\n * One breakpoint is enough: the API renders `tools` before `system`, so a\n * marker on the trailing system block covers the tool definition too — and\n * the user input, the only part that changes between calls, sits after it\n * in `messages` where it invalidates nothing.\n */\n private buildSystem(\n systemPrompt: string,\n ): string | Anthropic.TextBlockParam[] {\n if (!this.config.cachePrompt) return systemPrompt;\n\n return [\n {\n type: \"text\",\n text: systemPrompt,\n cache_control: { type: \"ephemeral\", ttl: this.config.cacheTtl ?? \"5m\" },\n },\n ];\n }\n\n /** Issue the call, translating SDK failures into typed provider errors. */\n private async send(\n body: Anthropic.MessageCreateParamsNonStreaming,\n options: CallOptions | undefined,\n ): Promise<Anthropic.Message> {\n try {\n return await this.client.messages.create(body, options);\n } catch (error) {\n throw toProviderError(error);\n }\n }\n}\n","import type Anthropic from \"@anthropic-ai/sdk\";\nimport type { ProviderConfig } from \"@sembl/core\";\n\n/**\n * Configuration specific to the Anthropic provider.\n *\n * Supply either `client` or `apiKey`. Prefer `client` when the host app\n * already resolves credentials its own way (Secret Manager, Vault, Bedrock,\n * a Vertex client) — the provider will reuse that client as-is rather than\n * constructing its own.\n */\nexport interface AnthropicProviderConfig extends ProviderConfig {\n /**\n * A pre-built Anthropic client. Takes precedence over `apiKey`/`baseURL`.\n * Also accepts an `AnthropicBedrock` / `AnthropicVertex` client — anything\n * exposing a compatible `messages.create`.\n */\n client?: Pick<Anthropic, \"messages\">;\n /** Anthropic API key. Ignored when `client` is supplied. */\n apiKey?: string;\n /** Base URL override. Ignored when `client` is supplied. */\n baseURL?: string;\n /**\n * Name given to the extraction tool the model is forced to call.\n * Defaults to a sanitized form of the schema id. Only override this if a\n * name shows up somewhere you care about (logs, prompt-cache keys).\n */\n toolName?: string;\n /**\n * Mark the stable prefix of the request — the tool definition and the\n * system prompt — as cacheable, so a run of calls against the same schema\n * pays to process it once instead of once per call.\n *\n * Off by default: a cache write costs more than an ordinary read of the\n * same tokens, so a single call, or a prefix below the model's minimum\n * cacheable length, comes out slightly behind. Turn it on for batches.\n * `ProviderResponse.usage.cacheReadTokens` says whether it is paying off.\n */\n cachePrompt?: boolean;\n /**\n * Lifetime of the cached prefix. Ignored unless `cachePrompt` is set.\n *\n * `\"5m\"` (the default) is refreshed by every read, so back-to-back calls\n * keep it alive indefinitely and it is the cheaper write. Choose `\"1h\"`\n * only for traffic with gaps longer than five minutes between calls — it\n * survives the gap, but the write costs roughly twice as much.\n */\n cacheTtl?: \"5m\" | \"1h\";\n /**\n * How many times the SDK retries a failed call before giving up. The SDK\n * retries connection errors, 408/409/429 and 5xx with exponential backoff\n * and honours `retry-after`, so there is nothing to hand-roll here.\n *\n * Defaults to {@link DEFAULT_MAX_RETRIES}. When a `client` is supplied,\n * leaving this unset keeps that client's own policy.\n */\n maxRetries?: number;\n /**\n * Timeout for a single attempt, in milliseconds. Retries each get their\n * own attempt, so the worst-case wall clock is roughly\n * `timeoutMs * (maxRetries + 1)` plus backoff.\n *\n * Defaults to {@link DEFAULT_TIMEOUT_MS}. When a `client` is supplied,\n * leaving this unset keeps that client's own policy.\n */\n timeoutMs?: number;\n}\n\n/** Anthropic requires an explicit output budget; this is used when none is set. */\nexport const DEFAULT_MAX_TOKENS = 4096;\n\n/** Matches the SDK's own default; stated here so it survives an SDK change. */\nexport const DEFAULT_MAX_RETRIES = 2;\n\n/**\n * Two minutes per attempt. The SDK's own default is ten, which is a long time\n * for a backend import to sit on one listing when the retry is cheap.\n */\nexport const DEFAULT_TIMEOUT_MS = 120_000;\n","import { APIConnectionError, APIError } from \"@anthropic-ai/sdk\";\n\n/**\n * Why a provider call failed, in the terms a caller can act on.\n *\n * A batch import wants to route these differently: `\"api\"` failures are worth\n * re-queueing, `\"truncated\"` needs a bigger output budget, and `\"no_output\"`\n * is a property of that one listing's content — retrying it changes nothing.\n *\n * The same three kinds are used by `@sembl/provider-openai`, so a caller that\n * branches on `kind` keeps working when the provider is swapped.\n */\nexport type ProviderErrorKind = \"api\" | \"truncated\" | \"no_output\";\n\n/**\n * Error thrown by the Anthropic provider.\n *\n * Branch on `kind` rather than matching the message — messages stay\n * diagnostic and are free to change.\n */\nexport class AnthropicProviderError extends Error {\n /** What class of failure this is. */\n public readonly kind: ProviderErrorKind;\n /**\n * Whether another attempt could plausibly succeed. The SDK has already\n * retried retryable transport failures (see `maxRetries`); this says only\n * that the failure was transient in nature, so a caller running a queue can\n * re-enqueue the item rather than dead-letter it.\n */\n public readonly retryable: boolean;\n /** HTTP status, when the failure came back as an API error. */\n public readonly status?: number;\n /** Anthropic's `stop_reason`, when the call returned a message we rejected. */\n public readonly stopReason?: string;\n\n constructor(\n message: string,\n options: {\n kind: ProviderErrorKind;\n retryable: boolean;\n status?: number;\n stopReason?: string;\n cause?: unknown;\n },\n ) {\n super(message, { cause: options.cause });\n this.name = \"AnthropicProviderError\";\n this.kind = options.kind;\n this.retryable = options.retryable;\n this.status = options.status;\n this.stopReason = options.stopReason;\n }\n}\n\n/**\n * Wrap an SDK-level failure as an `AnthropicProviderError`.\n *\n * Retryability is read off the SDK's own error classes rather than the\n * message: connection failures and timeouts are transient by construction,\n * and of the status codes only 408/409/429 and 5xx are worth another attempt —\n * the same set the SDK itself retries internally.\n */\nexport function toProviderError(error: unknown): AnthropicProviderError {\n if (error instanceof APIError) {\n const status = error.status;\n const retryable =\n error instanceof APIConnectionError ||\n status === undefined ||\n status === 408 ||\n status === 409 ||\n status === 429 ||\n status >= 500;\n\n return new AnthropicProviderError(\n `Anthropic request failed${status ? ` (${status})` : \"\"}: ${error.message}`,\n { kind: \"api\", retryable, status, cause: error },\n );\n }\n\n // Anything else (an AbortError, a bug in a caller-supplied client) is\n // surfaced with the same shape so callers only need one catch.\n return new AnthropicProviderError(\n `Anthropic request failed: ${error instanceof Error ? error.message : String(error)}`,\n { kind: \"api\", retryable: false, cause: error },\n );\n}\n","import type { RuntimeSchema, ResolvedEnums, SchemaBundle } from \"@sembl/core\";\nimport { runtimeSchemaToJsonSchema } from \"@sembl/core\";\n\n/** Anthropic tool names must match `^[a-zA-Z0-9_-]{1,64}$`. */\nexport function toToolName(schemaId: string): string {\n const cleaned = schemaId.replace(/[^a-zA-Z0-9_-]/g, \"_\").slice(0, 57);\n return `extract_${cleaned || \"schema\"}`.slice(0, 64);\n}\n\n/**\n * Convert a RuntimeSchema to an Anthropic tool `input_schema`.\n *\n * Unlike OpenAI structured outputs, Anthropic takes ordinary JSON Schema, so\n * optional fields are left out of `required` instead of being made nullable.\n * That keeps the model from inventing explicit `null`s for fields the source\n * text simply never mentioned — which matters for partial coercion, where an\n * absent field and a null field mean different things to the caller.\n */\nexport function toInputSchema(\n schema: RuntimeSchema,\n bundle?: SchemaBundle,\n resolvedEnums?: ResolvedEnums,\n): Record<string, unknown> {\n return runtimeSchemaToJsonSchema(schema, bundle, {\n dialect: \"standard\",\n resolvedEnums,\n });\n}\n"],"mappings":";AAAA,OAAO,eAAe;;;ACqEf,IAAM,qBAAqB;AAG3B,IAAM,sBAAsB;AAM5B,IAAM,qBAAqB;;;AC9ElC,SAAS,oBAAoB,gBAAgB;AAoBtC,IAAM,yBAAN,cAAqC,MAAM;AAAA;AAAA,EAEhC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EAEhB,YACE,SACA,SAOA;AACA,UAAM,SAAS,EAAE,OAAO,QAAQ,MAAM,CAAC;AACvC,SAAK,OAAO;AACZ,SAAK,OAAO,QAAQ;AACpB,SAAK,YAAY,QAAQ;AACzB,SAAK,SAAS,QAAQ;AACtB,SAAK,aAAa,QAAQ;AAAA,EAC5B;AACF;AAUO,SAAS,gBAAgB,OAAwC;AACtE,MAAI,iBAAiB,UAAU;AAC7B,UAAM,SAAS,MAAM;AACrB,UAAM,YACJ,iBAAiB,sBACjB,WAAW,UACX,WAAW,OACX,WAAW,OACX,WAAW,OACX,UAAU;AAEZ,WAAO,IAAI;AAAA,MACT,2BAA2B,SAAS,KAAK,MAAM,MAAM,EAAE,KAAK,MAAM,OAAO;AAAA,MACzE,EAAE,MAAM,OAAO,WAAW,QAAQ,OAAO,MAAM;AAAA,IACjD;AAAA,EACF;AAIA,SAAO,IAAI;AAAA,IACT,6BAA6B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,IACnF,EAAE,MAAM,OAAO,WAAW,OAAO,OAAO,MAAM;AAAA,EAChD;AACF;;;ACpFA,SAAS,iCAAiC;AAGnC,SAAS,WAAW,UAA0B;AACnD,QAAM,UAAU,SAAS,QAAQ,mBAAmB,GAAG,EAAE,MAAM,GAAG,EAAE;AACpE,SAAO,WAAW,WAAW,QAAQ,GAAG,MAAM,GAAG,EAAE;AACrD;AAWO,SAAS,cACd,QACA,QACA,eACyB;AACzB,SAAO,0BAA0B,QAAQ,QAAQ;AAAA,IAC/C,SAAS;AAAA,IACT;AAAA,EACF,CAAC;AACH;;;AHEO,IAAM,oBAAN,MAA4C;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,QAAiC;AAC3C,SAAK,SAAS;AAEd,QAAI,OAAO,QAAQ;AAGjB,WAAK,SAAS,OAAO;AACrB,WAAK,cACH,OAAO,eAAe,UAAa,OAAO,cAAc,SACpD,SACA,EAAE,YAAY,OAAO,YAAY,SAAS,OAAO,UAAU;AAAA,IACnE,OAAO;AACL,WAAK,SAAS,IAAI,UAAU;AAAA,QAC1B,QAAQ,OAAO;AAAA,QACf,SAAS,OAAO;AAAA,QAChB,YAAY,OAAO,cAAc;AAAA,QACjC,SAAS,OAAO,aAAa;AAAA,MAC/B,CAAC;AACD,WAAK,cAAc;AAAA,IACrB;AAAA,EACF;AAAA,EAEA,MAAM,SAAS,SAAqD;AAClE,UAAM,WAAW,KAAK,OAAO,YAAY,WAAW,QAAQ,OAAO,EAAE;AACrE,UAAM,YAAY,KAAK,OAAO,aAAa;AAC3C,UAAM,cAAc;AAAA,MAClB,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA,IACV;AAEA,UAAM,UAAU,MAAM,KAAK;AAAA,MACzB;AAAA,QACE,OAAO,KAAK,OAAO;AAAA,QACnB,YAAY;AAAA,QACZ,aAAa,KAAK,OAAO,eAAe;AAAA,QACxC,QAAQ,KAAK,YAAY,QAAQ,YAAY;AAAA,QAC7C,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,QAAQ,UAAU,CAAC;AAAA,QACvD,OAAO;AAAA,UACL;AAAA,YACE,MAAM;AAAA,YACN,aAAa,QAAQ,OAAO;AAAA,YAC5B,cAAc;AAAA,UAChB;AAAA,QACF;AAAA,QACA,aAAa,EAAE,MAAM,QAAQ,MAAM,SAAS;AAAA,MAC9C;AAAA,MACA,KAAK;AAAA,IACP;AAEA,UAAM,UAAU,QAAQ,QAAQ;AAAA,MAC9B,CAAC,UACC,MAAM,SAAS,cAAc,MAAM,SAAS;AAAA,IAChD;AAEA,QAAI,CAAC,SAAS;AACZ,UAAI,QAAQ,gBAAgB,cAAc;AACxC,cAAM,IAAI;AAAA,UACR,qBAAqB,SAAS,4CAA4C,QAAQ;AAAA,UAElF,EAAE,MAAM,aAAa,WAAW,OAAO,YAAY,aAAa;AAAA,QAClE;AAAA,MACF;AACA,YAAM,IAAI;AAAA,QACR,0BAA0B,QAAQ,6BAA6B,QAAQ,eAAe,SAAS;AAAA,QAC/F;AAAA,UACE,MAAM;AAAA,UACN,WAAW;AAAA,UACX,YAAY,QAAQ,eAAe;AAAA,QACrC;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL,MAAM,QAAQ;AAAA,MACd,OAAO;AAAA,QACL,cAAc,QAAQ,MAAM;AAAA,QAC5B,kBAAkB,QAAQ,MAAM;AAAA,QAChC,aAAa,QAAQ,MAAM,eAAe,QAAQ,MAAM;AAAA,QACxD,GAAI,QAAQ,MAAM,2BAA2B,QAAQ;AAAA,UACnD,iBAAiB,QAAQ,MAAM;AAAA,QACjC;AAAA,QACA,GAAI,QAAQ,MAAM,+BAA+B,QAAQ;AAAA,UACvD,kBAAkB,QAAQ,MAAM;AAAA,QAClC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,YACN,cACqC;AACrC,QAAI,CAAC,KAAK,OAAO,YAAa,QAAO;AAErC,WAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN,eAAe,EAAE,MAAM,aAAa,KAAK,KAAK,OAAO,YAAY,KAAK;AAAA,MACxE;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAc,KACZ,MACA,SAC4B;AAC5B,QAAI;AACF,aAAO,MAAM,KAAK,OAAO,SAAS,OAAO,MAAM,OAAO;AAAA,IACxD,SAAS,OAAO;AACd,YAAM,gBAAgB,KAAK;AAAA,IAC7B;AAAA,EACF;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/anthropic-provider.ts","../src/anthropic-config.ts","../src/errors.ts","../src/schema-converter.ts"],"sourcesContent":["import Anthropic from \"@anthropic-ai/sdk\";\nimport type { Provider, ProviderRequest, ProviderResponse, ProviderTurn } from \"@sembl/core\";\nimport type { AnthropicProviderConfig } from \"./anthropic-config.js\";\nimport {\n DEFAULT_MAX_RETRIES,\n DEFAULT_MAX_TOKENS,\n DEFAULT_TIMEOUT_MS,\n isClaude5Model,\n} from \"./anthropic-config.js\";\nimport { AnthropicProviderError, toProviderError } from \"./errors.js\";\nimport { toInputSchema, toToolName } from \"./schema-converter.js\";\n\n/**\n * Earlier turns as the API saw them: the model's rejected output as the\n * tool call it made, the correction as that call's result flagged as an\n * error. The forced tool choice then makes the model call the tool again,\n * which is the corrected attempt. Ids only have to match within the\n * conversation, so they are synthesised.\n */\nfunction renderHistory(history: readonly ProviderTurn[], toolName: string): Anthropic.MessageParam[] {\n const messages: Anthropic.MessageParam[] = [];\n let lastToolUseId = \"\";\n history.forEach((turn, i) => {\n if (turn.role === \"assistant\") {\n lastToolUseId = `toolu_sembl_${i}`;\n messages.push({\n role: \"assistant\",\n content: [{ type: \"tool_use\", id: lastToolUseId, name: toolName, input: turn.data }],\n });\n } else if (lastToolUseId) {\n messages.push({\n role: \"user\",\n content: [{ type: \"tool_result\", tool_use_id: lastToolUseId, content: turn.text, is_error: true }],\n });\n } else {\n messages.push({ role: \"user\", content: turn.text });\n }\n });\n return messages;\n}\n\n/** Per-call overrides handed to the SDK alongside the request body. */\ninterface CallOptions {\n maxRetries?: number;\n timeout?: number;\n}\n\n/**\n * Anthropic provider implementation.\n *\n * Structured output is obtained by declaring the target schema as a single\n * tool and forcing the model to call it (`tool_choice: { type: \"tool\" }`), so\n * the arguments come back already parsed and shape-checked by the API — no\n * JSON scraped out of prose.\n *\n * Retries and timeouts are the SDK's (exponential backoff, `retry-after`\n * aware); this class only chooses the numbers and translates whatever comes\n * back out into an {@link AnthropicProviderError}.\n */\nexport class AnthropicProvider implements Provider {\n /** Repair turns are rendered as a tool call and its (failed) result. */\n readonly supportsHistory = true;\n\n private client: Pick<Anthropic, \"messages\">;\n private config: AnthropicProviderConfig;\n private callOptions: CallOptions | undefined;\n\n constructor(config: AnthropicProviderConfig) {\n this.config = config;\n\n if (config.client) {\n // The host owns this client's transport policy, so only override it\n // per-call where the caller asked for something specific.\n this.client = config.client;\n this.callOptions =\n config.maxRetries === undefined && config.timeoutMs === undefined\n ? undefined\n : { maxRetries: config.maxRetries, timeout: config.timeoutMs };\n } else {\n this.client = new Anthropic({\n apiKey: config.apiKey,\n baseURL: config.baseURL,\n maxRetries: config.maxRetries ?? DEFAULT_MAX_RETRIES,\n timeout: config.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n });\n this.callOptions = undefined;\n }\n }\n\n async complete(request: ProviderRequest): Promise<ProviderResponse> {\n const toolName = this.config.toolName ?? toToolName(request.schema.id);\n const maxTokens = this.config.maxTokens ?? DEFAULT_MAX_TOKENS;\n const inputSchema = toInputSchema(\n request.schema,\n request.bundle,\n request.resolvedEnums,\n );\n\n // Sampling parameters go only when configured: newer models reject\n // `temperature` (and `top_p`/`top_k`) outright, and structured\n // extraction does not need them.\n const thinking = this.config.thinking ?? (isClaude5Model(this.config.model) ? { type: \"disabled\" as const } : undefined);\n const message = await this.send(\n {\n model: this.config.model,\n max_tokens: maxTokens,\n ...(this.config.temperature !== undefined ? { temperature: this.config.temperature } : {}),\n ...(thinking ? { thinking } : {}),\n system: this.buildSystem(request.systemPrompt),\n messages: [\n { role: \"user\", content: request.userInput },\n ...renderHistory(request.history ?? [], toolName),\n ],\n tools: [\n {\n name: toolName,\n description: request.schema.description,\n input_schema: inputSchema as Anthropic.Tool[\"input_schema\"],\n },\n ],\n tool_choice: { type: \"tool\", name: toolName },\n ...this.config.requestOverrides,\n },\n this.callOptions,\n );\n\n const toolUse = message.content.find(\n (block): block is Anthropic.ToolUseBlock =>\n block.type === \"tool_use\" && block.name === toolName,\n );\n\n if (!toolUse) {\n if (message.stop_reason === \"max_tokens\") {\n throw new AnthropicProviderError(\n `Anthropic hit the ${maxTokens}-token output cap before completing the \"${toolName}\" call. ` +\n \"Raise maxTokens, or coerce into a smaller schema.\",\n { kind: \"truncated\", retryable: false, stopReason: \"max_tokens\" },\n );\n }\n throw new AnthropicProviderError(\n `Anthropic returned no \"${toolName}\" tool call (stop_reason: ${message.stop_reason ?? \"unknown\"})`,\n {\n kind: \"no_output\",\n retryable: false,\n stopReason: message.stop_reason ?? undefined,\n },\n );\n }\n\n return {\n data: toolUse.input as Record<string, unknown>,\n usage: {\n promptTokens: message.usage.input_tokens,\n completionTokens: message.usage.output_tokens,\n totalTokens: message.usage.input_tokens + message.usage.output_tokens,\n ...(message.usage.cache_read_input_tokens != null && {\n cacheReadTokens: message.usage.cache_read_input_tokens,\n }),\n ...(message.usage.cache_creation_input_tokens != null && {\n cacheWriteTokens: message.usage.cache_creation_input_tokens,\n }),\n },\n };\n }\n\n /**\n * The system prompt, marked as a cache breakpoint when caching is on.\n *\n * One breakpoint is enough: the API renders `tools` before `system`, so a\n * marker on the trailing system block covers the tool definition too — and\n * the user input, the only part that changes between calls, sits after it\n * in `messages` where it invalidates nothing.\n */\n private buildSystem(\n systemPrompt: string,\n ): string | Anthropic.TextBlockParam[] {\n if (!this.config.cachePrompt) return systemPrompt;\n\n return [\n {\n type: \"text\",\n text: systemPrompt,\n cache_control: { type: \"ephemeral\", ttl: this.config.cacheTtl ?? \"5m\" },\n },\n ];\n }\n\n /** Issue the call, translating SDK failures into typed provider errors. */\n private async send(\n body: Anthropic.MessageCreateParamsNonStreaming,\n options: CallOptions | undefined,\n ): Promise<Anthropic.Message> {\n try {\n return await this.client.messages.create(body, options);\n } catch (error) {\n throw toProviderError(error);\n }\n }\n}\n","import type Anthropic from \"@anthropic-ai/sdk\";\nimport type { ProviderConfig } from \"@sembl/core\";\n\n/**\n * Configuration specific to the Anthropic provider.\n *\n * Supply either `client` or `apiKey`. Prefer `client` when the host app\n * already resolves credentials its own way (Secret Manager, Vault, Bedrock,\n * a Vertex client) — the provider will reuse that client as-is rather than\n * constructing its own.\n */\nexport interface AnthropicProviderConfig extends ProviderConfig {\n /**\n * A pre-built Anthropic client. Takes precedence over `apiKey`/`baseURL`.\n * Also accepts an `AnthropicBedrock` / `AnthropicVertex` client — anything\n * exposing a compatible `messages.create`.\n */\n client?: Pick<Anthropic, \"messages\">;\n /** Anthropic API key. Ignored when `client` is supplied. */\n apiKey?: string;\n /** Base URL override. Ignored when `client` is supplied. */\n baseURL?: string;\n /**\n * Name given to the extraction tool the model is forced to call.\n * Defaults to a sanitized form of the schema id. Only override this if a\n * name shows up somewhere you care about (logs, prompt-cache keys).\n */\n toolName?: string;\n /**\n * Mark the stable prefix of the request — the tool definition and the\n * system prompt — as cacheable, so a run of calls against the same schema\n * pays to process it once instead of once per call.\n *\n * Off by default: a cache write costs more than an ordinary read of the\n * same tokens, so a single call, or a prefix below the model's minimum\n * cacheable length, comes out slightly behind. Turn it on for batches.\n * `ProviderResponse.usage.cacheReadTokens` says whether it is paying off.\n */\n cachePrompt?: boolean;\n /**\n * Lifetime of the cached prefix. Ignored unless `cachePrompt` is set.\n *\n * `\"5m\"` (the default) is refreshed by every read, so back-to-back calls\n * keep it alive indefinitely and it is the cheaper write. Choose `\"1h\"`\n * only for traffic with gaps longer than five minutes between calls — it\n * survives the gap, but the write costs roughly twice as much.\n */\n cacheTtl?: \"5m\" | \"1h\";\n /**\n * Extended-thinking setting sent with every call.\n *\n * Claude 5 models turn adaptive thinking on by default, and a forced tool\n * call is rejected while it is on, so for those models the provider sends\n * `{ type: \"disabled\" }` unless this is set. Older models reject the\n * parameter outright, so nothing is sent for them by default. Set it\n * explicitly to override either behaviour.\n */\n thinking?: Anthropic.ThinkingConfigParam;\n /**\n * Fields merged into the request body last, after everything the provider\n * builds. The escape hatch for the next model-specific parameter, so a\n * caller does not have to wait for a library release — or wrap the client\n * — to send it.\n */\n requestOverrides?: Partial<Anthropic.MessageCreateParamsNonStreaming>;\n /**\n * How many times the SDK retries a failed call before giving up. The SDK\n * retries connection errors, 408/409/429 and 5xx with exponential backoff\n * and honours `retry-after`, so there is nothing to hand-roll here.\n *\n * Defaults to {@link DEFAULT_MAX_RETRIES}. When a `client` is supplied,\n * leaving this unset keeps that client's own policy.\n */\n maxRetries?: number;\n /**\n * Timeout for a single attempt, in milliseconds. Retries each get their\n * own attempt, so the worst-case wall clock is roughly\n * `timeoutMs * (maxRetries + 1)` plus backoff.\n *\n * Defaults to {@link DEFAULT_TIMEOUT_MS}. When a `client` is supplied,\n * leaving this unset keeps that client's own policy.\n */\n timeoutMs?: number;\n}\n\n/** Anthropic requires an explicit output budget; this is used when none is set. */\nexport const DEFAULT_MAX_TOKENS = 4096;\n\n/**\n * Whether a model id names a Claude 5 model. Those enable adaptive thinking\n * by default, which a forced tool call cannot be combined with.\n */\nexport function isClaude5Model(model: string): boolean {\n return /claude-(?:fable|opus|sonnet|haiku)-5(?:[.-]|$)/i.test(model);\n}\n\n/** Matches the SDK's own default; stated here so it survives an SDK change. */\nexport const DEFAULT_MAX_RETRIES = 2;\n\n/**\n * Two minutes per attempt. The SDK's own default is ten, which is a long time\n * for a backend import to sit on one listing when the retry is cheap.\n */\nexport const DEFAULT_TIMEOUT_MS = 120_000;\n","import { APIConnectionError, APIError } from \"@anthropic-ai/sdk\";\n\n/**\n * Why a provider call failed, in the terms a caller can act on.\n *\n * A batch import wants to route these differently: `\"api\"` failures are worth\n * re-queueing, `\"truncated\"` needs a bigger output budget, and `\"no_output\"`\n * is a property of that one listing's content — retrying it changes nothing.\n *\n * The same three kinds are used by `@sembl/provider-openai`, so a caller that\n * branches on `kind` keeps working when the provider is swapped.\n */\nexport type ProviderErrorKind = \"api\" | \"truncated\" | \"no_output\";\n\n/**\n * Error thrown by the Anthropic provider.\n *\n * Branch on `kind` rather than matching the message — messages stay\n * diagnostic and are free to change.\n */\nexport class AnthropicProviderError extends Error {\n /** What class of failure this is. */\n public readonly kind: ProviderErrorKind;\n /**\n * Whether another attempt could plausibly succeed. The SDK has already\n * retried retryable transport failures (see `maxRetries`); this says only\n * that the failure was transient in nature, so a caller running a queue can\n * re-enqueue the item rather than dead-letter it.\n */\n public readonly retryable: boolean;\n /** HTTP status, when the failure came back as an API error. */\n public readonly status?: number;\n /** Anthropic's `stop_reason`, when the call returned a message we rejected. */\n public readonly stopReason?: string;\n\n constructor(\n message: string,\n options: {\n kind: ProviderErrorKind;\n retryable: boolean;\n status?: number;\n stopReason?: string;\n cause?: unknown;\n },\n ) {\n super(message, { cause: options.cause });\n this.name = \"AnthropicProviderError\";\n this.kind = options.kind;\n this.retryable = options.retryable;\n this.status = options.status;\n this.stopReason = options.stopReason;\n }\n}\n\n/**\n * Wrap an SDK-level failure as an `AnthropicProviderError`.\n *\n * Retryability is read off the SDK's own error classes rather than the\n * message: connection failures and timeouts are transient by construction,\n * and of the status codes only 408/409/429 and 5xx are worth another attempt —\n * the same set the SDK itself retries internally.\n */\nexport function toProviderError(error: unknown): AnthropicProviderError {\n if (error instanceof APIError) {\n const status = error.status;\n const retryable =\n error instanceof APIConnectionError ||\n status === undefined ||\n status === 408 ||\n status === 409 ||\n status === 429 ||\n status >= 500;\n\n return new AnthropicProviderError(\n `Anthropic request failed${status ? ` (${status})` : \"\"}: ${error.message}`,\n { kind: \"api\", retryable, status, cause: error },\n );\n }\n\n // Anything else (an AbortError, a bug in a caller-supplied client) is\n // surfaced with the same shape so callers only need one catch.\n return new AnthropicProviderError(\n `Anthropic request failed: ${error instanceof Error ? error.message : String(error)}`,\n { kind: \"api\", retryable: false, cause: error },\n );\n}\n","import type { RuntimeSchema, ResolvedEnums, SchemaBundle } from \"@sembl/core\";\nimport { runtimeSchemaToJsonSchema } from \"@sembl/core\";\n\n/** Anthropic tool names must match `^[a-zA-Z0-9_-]{1,64}$`. */\nexport function toToolName(schemaId: string): string {\n const cleaned = schemaId.replace(/[^a-zA-Z0-9_-]/g, \"_\").slice(0, 57);\n return `extract_${cleaned || \"schema\"}`.slice(0, 64);\n}\n\n/**\n * Convert a RuntimeSchema to an Anthropic tool `input_schema`.\n *\n * Unlike OpenAI structured outputs, Anthropic takes ordinary JSON Schema, so\n * optional fields are left out of `required` instead of being made nullable.\n * That keeps the model from inventing explicit `null`s for fields the source\n * text simply never mentioned — which matters for partial coercion, where an\n * absent field and a null field mean different things to the caller.\n */\nexport function toInputSchema(\n schema: RuntimeSchema,\n bundle?: SchemaBundle,\n resolvedEnums?: ResolvedEnums,\n): Record<string, unknown> {\n return runtimeSchemaToJsonSchema(schema, bundle, {\n dialect: \"standard\",\n resolvedEnums,\n });\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sembl/provider-anthropic",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Anthropic provider for SEMBL, using forced tool calls for structured output.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"llm",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"provenance": true
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@sembl/core": "0.
|
|
55
|
+
"@sembl/core": "0.5.0"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
58
|
"@anthropic-ai/sdk": ">=0.30.0"
|