@rudra-js/anthropic 0.6.0 → 0.7.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 +1 -1
- package/dist/anthropic-provider.d.ts +0 -21
- package/dist/anthropic-provider.d.ts.map +1 -1
- package/dist/anthropic-provider.js +0 -60
- package/dist/anthropic-provider.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/package.json +8 -7
- package/src/anthropic-provider.ts +8 -74
- package/src/index.ts +0 -2
package/README.md
CHANGED
|
@@ -1,35 +1,14 @@
|
|
|
1
1
|
import type { ComponentProvider } from '@rudra-js/core';
|
|
2
2
|
export interface AnthropicProviderOptions {
|
|
3
3
|
apiKey: string;
|
|
4
|
-
/** Defaults to the current Claude model this package was written against. */
|
|
5
4
|
model?: string;
|
|
6
5
|
maxTokens?: number;
|
|
7
6
|
baseUrl?: string;
|
|
8
|
-
/**
|
|
9
|
-
* Required when the key is identity-linked rather than workspace-scoped —
|
|
10
|
-
* such a key belongs to a person across several workspaces, so the API cannot
|
|
11
|
-
* infer which one a request acts in and rejects it with a 400.
|
|
12
|
-
*/
|
|
13
7
|
workspaceId?: string;
|
|
14
|
-
/**
|
|
15
|
-
* Sent as the request's `thinking`. Defaults to `{ type: 'disabled' }`,
|
|
16
|
-
* because core budgets 1500ms for the whole call and a model that reasons
|
|
17
|
-
* before answering does not finish inside it. Pass `null` to send nothing,
|
|
18
|
-
* which is what a model that rejects an explicit `disabled` needs, and raise
|
|
19
|
-
* `modelTimeoutMs` to match when you do.
|
|
20
|
-
*/
|
|
21
8
|
thinking?: {
|
|
22
9
|
type: 'adaptive' | 'disabled';
|
|
23
10
|
} | null;
|
|
24
|
-
/** Injected so the adapter is testable without a network or an SDK. */
|
|
25
11
|
fetch?: typeof globalThis.fetch;
|
|
26
12
|
}
|
|
27
|
-
/**
|
|
28
|
-
* Adapts the Anthropic Messages API to `ComponentProvider`.
|
|
29
|
-
*
|
|
30
|
-
* The tool schema is derived from the schema core exports rather than restated
|
|
31
|
-
* here: a second copy is a second vocabulary, and the drift would throw below
|
|
32
|
-
* and reach the generator as `provider-error` events.
|
|
33
|
-
*/
|
|
34
13
|
export declare function createAnthropicProvider(options: AnthropicProviderOptions): ComponentProvider;
|
|
35
14
|
//# sourceMappingURL=anthropic-provider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic-provider.d.ts","sourceRoot":"","sources":["../src/anthropic-provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,iBAAiB,EAIlB,MAAM,gBAAgB,CAAC;AAExB,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"anthropic-provider.d.ts","sourceRoot":"","sources":["../src/anthropic-provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,iBAAiB,EAIlB,MAAM,gBAAgB,CAAC;AAExB,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IAEf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,QAAQ,CAAC,EAAE;QAAE,IAAI,EAAE,UAAU,GAAG,UAAU,CAAA;KAAE,GAAG,IAAI,CAAC;IAEpD,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC;AAoCD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,wBAAwB,GAAG,iBAAiB,CAmG5F"}
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
const TOOL_NAME = 'emit_component_spec';
|
|
3
|
-
/**
|
|
4
|
-
* A model that reasons before answering draws on the same output budget as the
|
|
5
|
-
* tool call, and a cap close to what a spec needs leaves no room for it. The
|
|
6
|
-
* default is well above a typical spec's size rather than tuned to it.
|
|
7
|
-
*/
|
|
8
3
|
const DEFAULT_MAX_TOKENS = 8192;
|
|
9
4
|
function describeShape(input) {
|
|
10
5
|
if (input === null)
|
|
@@ -28,22 +23,10 @@ function isToolUseBlock(candidate) {
|
|
|
28
23
|
candidate['type'] === 'tool_use' &&
|
|
29
24
|
candidate['name'] === TOOL_NAME);
|
|
30
25
|
}
|
|
31
|
-
/**
|
|
32
|
-
* Adapts the Anthropic Messages API to `ComponentProvider`.
|
|
33
|
-
*
|
|
34
|
-
* The tool schema is derived from the schema core exports rather than restated
|
|
35
|
-
* here: a second copy is a second vocabulary, and the drift would throw below
|
|
36
|
-
* and reach the generator as `provider-error` events.
|
|
37
|
-
*/
|
|
38
26
|
export function createAnthropicProvider(options) {
|
|
39
27
|
const model = options.model ?? 'claude-sonnet-5';
|
|
40
28
|
const thinking = options.thinking === undefined ? { type: 'disabled' } : options.thinking;
|
|
41
29
|
const call = options.fetch ?? globalThis.fetch;
|
|
42
|
-
// Trimmed so a caller-supplied `baseUrl` ending in `/` cannot turn into
|
|
43
|
-
// `//v1/messages`. Done with a loop rather than `/\/+$/`: that pattern
|
|
44
|
-
// backtracks on a string of many trailing slashes, which is a denial of
|
|
45
|
-
// service in a published package even though the value comes from the caller
|
|
46
|
-
// rather than from a request.
|
|
47
30
|
let baseUrl = options.baseUrl ?? 'https://api.anthropic.com';
|
|
48
31
|
while (baseUrl.endsWith('/'))
|
|
49
32
|
baseUrl = baseUrl.slice(0, -1);
|
|
@@ -52,10 +35,6 @@ export function createAnthropicProvider(options) {
|
|
|
52
35
|
name: 'anthropic',
|
|
53
36
|
model,
|
|
54
37
|
async generate(request) {
|
|
55
|
-
// The half of obligation three that `fetch` does not cover. The real
|
|
56
|
-
// `fetch` rejects an already-aborted signal on its own, but `fetch` is an
|
|
57
|
-
// injected seam here, and a caller's own transport has no such duty — so
|
|
58
|
-
// without this, a call the caller has already given up on goes out.
|
|
59
38
|
request.signal.throwIfAborted();
|
|
60
39
|
const response = await send(call, `${baseUrl}/v1/messages`, {
|
|
61
40
|
method: 'POST',
|
|
@@ -65,26 +44,17 @@ export function createAnthropicProvider(options) {
|
|
|
65
44
|
'anthropic-version': '2023-06-01',
|
|
66
45
|
...(options.workspaceId ? { 'anthropic-workspace-id': options.workspaceId } : {}),
|
|
67
46
|
},
|
|
68
|
-
// The caller's deadline, handed straight to the transport: the contract
|
|
69
|
-
// asks an adapter to stop, not merely to stop caring about the answer.
|
|
70
47
|
signal: request.signal,
|
|
71
48
|
body: JSON.stringify({
|
|
72
49
|
model,
|
|
73
50
|
max_tokens: maxTokens,
|
|
74
51
|
...(thinking ? { thinking } : {}),
|
|
75
|
-
// Marked as the cached prefix. Anything per-shopper interpolated here
|
|
76
|
-
// would destroy the prompt cache hit rate.
|
|
77
52
|
system: [{ type: 'text', text: request.system, cache_control: { type: 'ephemeral' } }],
|
|
78
53
|
messages: [{ role: 'user', content: request.user }],
|
|
79
54
|
tools: [
|
|
80
55
|
{
|
|
81
56
|
name: TOOL_NAME,
|
|
82
57
|
description: 'Return the component specification.',
|
|
83
|
-
// "input" — a tool's input_schema describes what the model must
|
|
84
|
-
// produce as the tool call's argument, not core's own output. The
|
|
85
|
-
// two are not the same: "output" carries additionalProperties:
|
|
86
|
-
// false on every block and "input" does not. Pinned in
|
|
87
|
-
// tests/tool-schema.test.ts.
|
|
88
58
|
input_schema: z.toJSONSchema(request.schema, { io: 'input' }),
|
|
89
59
|
},
|
|
90
60
|
],
|
|
@@ -92,40 +62,25 @@ export function createAnthropicProvider(options) {
|
|
|
92
62
|
}),
|
|
93
63
|
});
|
|
94
64
|
if (!response.ok) {
|
|
95
|
-
// Status and the vendor's error category only. Its message quotes the
|
|
96
|
-
// request back, and for this framework that can be a shopper's own search
|
|
97
|
-
// terms — which an adopter's `console.error(err)` would then capture.
|
|
98
65
|
const category = await errorCategory(response);
|
|
99
66
|
throw new Error(`anthropic responded ${response.status}${category}`);
|
|
100
67
|
}
|
|
101
68
|
const parsed = await response.json();
|
|
102
|
-
// `response.json()` yields whatever the body held, and `null` is valid
|
|
103
|
-
// JSON — reading `stop_reason` off it would throw a TypeError naming this
|
|
104
|
-
// adapter rather than the vendor that sent it.
|
|
105
69
|
if (typeof parsed !== 'object' || parsed === null) {
|
|
106
70
|
throw new Error(`anthropic returned ${parsed === null ? 'null' : typeof parsed}, not an object`);
|
|
107
71
|
}
|
|
108
72
|
const body = parsed;
|
|
109
|
-
// Before the tool-block lookup: both are ordinary 200s with no tool_use,
|
|
110
|
-
// and reporting them as "no tool use" blames the model for a budget or a
|
|
111
|
-
// policy this adapter controls.
|
|
112
73
|
if (body.stop_reason === 'max_tokens') {
|
|
113
74
|
throw new Error(`anthropic stopped at the max_tokens budget (${maxTokens}) before returning a tool use`);
|
|
114
75
|
}
|
|
115
76
|
if (body.stop_reason === 'refusal') {
|
|
116
77
|
throw new Error('anthropic refused to answer (stop_reason: refusal)');
|
|
117
78
|
}
|
|
118
|
-
// `content` and each of its entries are untrusted shapes from here on:
|
|
119
|
-
// a malformed response should name the vendor, not crash on the
|
|
120
|
-
// adapter's own `.find`/`.type` access.
|
|
121
79
|
const blocks = Array.isArray(body.content) ? body.content : [];
|
|
122
80
|
const block = blocks.find(isToolUseBlock);
|
|
123
81
|
if (!block) {
|
|
124
82
|
throw new Error(`anthropic returned no ${TOOL_NAME} tool use`);
|
|
125
83
|
}
|
|
126
|
-
// Parsed against the caller's own schema. generatedSpecSchema has no
|
|
127
|
-
// refinements, so this catches type and enum violations — a block kind
|
|
128
|
-
// outside the closed set, a non-string headline — not refinement logic.
|
|
129
84
|
const asSent = request.schema.safeParse(block.input);
|
|
130
85
|
const usable = asSent.success ? asSent : request.schema.safeParse(onlyValue(block.input));
|
|
131
86
|
if (!usable.success) {
|
|
@@ -138,21 +93,11 @@ export function createAnthropicProvider(options) {
|
|
|
138
93
|
},
|
|
139
94
|
};
|
|
140
95
|
}
|
|
141
|
-
/**
|
|
142
|
-
* Calls the transport, and says what went wrong when it never answered.
|
|
143
|
-
*
|
|
144
|
-
* `fetch` reports every transport fault as the same `TypeError: fetch failed`
|
|
145
|
-
* and hides the reason in `cause` — so a refused connection, a DNS failure and
|
|
146
|
-
* a socket reset are indistinguishable in a log. An operator needs to tell
|
|
147
|
-
* those apart, and none of them carries request content.
|
|
148
|
-
*/
|
|
149
96
|
async function send(call, url, init) {
|
|
150
97
|
try {
|
|
151
98
|
return await call(url, init);
|
|
152
99
|
}
|
|
153
100
|
catch (error) {
|
|
154
|
-
// The caller's own deadline. It means something specific upstream, so it
|
|
155
|
-
// travels unchanged.
|
|
156
101
|
if (init.signal?.aborted)
|
|
157
102
|
throw error;
|
|
158
103
|
const cause = error instanceof Error ? (error.cause ?? error) : error;
|
|
@@ -162,7 +107,6 @@ async function send(call, url, init) {
|
|
|
162
107
|
throw new Error(`anthropic did not answer: ${detail}`, { cause: error });
|
|
163
108
|
}
|
|
164
109
|
}
|
|
165
|
-
/** The vendor's error category, never its message — the message quotes the request. */
|
|
166
110
|
async function errorCategory(response) {
|
|
167
111
|
try {
|
|
168
112
|
const body = JSON.parse(await response.text());
|
|
@@ -170,16 +114,12 @@ async function errorCategory(response) {
|
|
|
170
114
|
return typeof type === 'string' ? ` (${type})` : '';
|
|
171
115
|
}
|
|
172
116
|
catch {
|
|
173
|
-
// A body that is unreadable or not JSON tells us nothing extra. The status
|
|
174
|
-
// is still in the message.
|
|
175
117
|
return '';
|
|
176
118
|
}
|
|
177
119
|
}
|
|
178
120
|
function toUsage(usage) {
|
|
179
121
|
if (!usage)
|
|
180
122
|
return undefined;
|
|
181
|
-
// A JSON body is untrusted: `"input_tokens": "11"` must not become part of
|
|
182
|
-
// a cost figure that downstream code adds instead of concatenates.
|
|
183
123
|
const numberAt = (key) => {
|
|
184
124
|
const value = usage[key];
|
|
185
125
|
return typeof value === 'number' ? value : undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic-provider.js","sourceRoot":"","sources":["../src/anthropic-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"anthropic-provider.js","sourceRoot":"","sources":["../src/anthropic-provider.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,KAAK,wBAAwB,EAAE,MAAM,yBAAyB,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAiC,MAAM,yBAAyB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rudra-js/anthropic",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "An Anthropic adapter for @rudra-js/core, with no vendor SDK
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "An Anthropic adapter for @rudra-js/core, with no vendor SDK",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"anthropic",
|
|
7
7
|
"claude",
|
|
@@ -46,13 +46,14 @@
|
|
|
46
46
|
"publishConfig": {
|
|
47
47
|
"access": "public"
|
|
48
48
|
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"zod": "^4.0.0"
|
|
51
|
+
},
|
|
49
52
|
"peerDependencies": {
|
|
50
|
-
"@rudra-js/core": "^0.
|
|
51
|
-
"zod": "^4.5.0"
|
|
53
|
+
"@rudra-js/core": "^0.7.0"
|
|
52
54
|
},
|
|
53
55
|
"devDependencies": {
|
|
54
|
-
"@rudra-js/core": "0.
|
|
55
|
-
"@types/node": "^26.4.1"
|
|
56
|
-
"zod": "^4.6.5"
|
|
56
|
+
"@rudra-js/core": "0.7.0",
|
|
57
|
+
"@types/node": "^26.4.1"
|
|
57
58
|
}
|
|
58
59
|
}
|
|
@@ -8,35 +8,20 @@ import type {
|
|
|
8
8
|
|
|
9
9
|
export interface AnthropicProviderOptions {
|
|
10
10
|
apiKey: string;
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
model?: string;
|
|
13
13
|
maxTokens?: number;
|
|
14
14
|
baseUrl?: string;
|
|
15
|
-
|
|
16
|
-
* Required when the key is identity-linked rather than workspace-scoped —
|
|
17
|
-
* such a key belongs to a person across several workspaces, so the API cannot
|
|
18
|
-
* infer which one a request acts in and rejects it with a 400.
|
|
19
|
-
*/
|
|
15
|
+
|
|
20
16
|
workspaceId?: string;
|
|
21
|
-
|
|
22
|
-
* Sent as the request's `thinking`. Defaults to `{ type: 'disabled' }`,
|
|
23
|
-
* because core budgets 1500ms for the whole call and a model that reasons
|
|
24
|
-
* before answering does not finish inside it. Pass `null` to send nothing,
|
|
25
|
-
* which is what a model that rejects an explicit `disabled` needs, and raise
|
|
26
|
-
* `modelTimeoutMs` to match when you do.
|
|
27
|
-
*/
|
|
17
|
+
|
|
28
18
|
thinking?: { type: 'adaptive' | 'disabled' } | null;
|
|
29
|
-
|
|
19
|
+
|
|
30
20
|
fetch?: typeof globalThis.fetch;
|
|
31
21
|
}
|
|
32
22
|
|
|
33
23
|
const TOOL_NAME = 'emit_component_spec';
|
|
34
24
|
|
|
35
|
-
/**
|
|
36
|
-
* A model that reasons before answering draws on the same output budget as the
|
|
37
|
-
* tool call, and a cap close to what a spec needs leaves no room for it. The
|
|
38
|
-
* default is well above a typical spec's size rather than tuned to it.
|
|
39
|
-
*/
|
|
40
25
|
const DEFAULT_MAX_TOKENS = 8192;
|
|
41
26
|
|
|
42
27
|
interface ToolUseBlock {
|
|
@@ -69,23 +54,12 @@ function isToolUseBlock(candidate: unknown): candidate is ToolUseBlock {
|
|
|
69
54
|
);
|
|
70
55
|
}
|
|
71
56
|
|
|
72
|
-
/**
|
|
73
|
-
* Adapts the Anthropic Messages API to `ComponentProvider`.
|
|
74
|
-
*
|
|
75
|
-
* The tool schema is derived from the schema core exports rather than restated
|
|
76
|
-
* here: a second copy is a second vocabulary, and the drift would throw below
|
|
77
|
-
* and reach the generator as `provider-error` events.
|
|
78
|
-
*/
|
|
79
57
|
export function createAnthropicProvider(options: AnthropicProviderOptions): ComponentProvider {
|
|
80
58
|
const model = options.model ?? 'claude-sonnet-5';
|
|
81
59
|
const thinking =
|
|
82
60
|
options.thinking === undefined ? { type: 'disabled' as const } : options.thinking;
|
|
83
61
|
const call = options.fetch ?? globalThis.fetch;
|
|
84
|
-
|
|
85
|
-
// `//v1/messages`. Done with a loop rather than `/\/+$/`: that pattern
|
|
86
|
-
// backtracks on a string of many trailing slashes, which is a denial of
|
|
87
|
-
// service in a published package even though the value comes from the caller
|
|
88
|
-
// rather than from a request.
|
|
62
|
+
|
|
89
63
|
let baseUrl = options.baseUrl ?? 'https://api.anthropic.com';
|
|
90
64
|
while (baseUrl.endsWith('/')) baseUrl = baseUrl.slice(0, -1);
|
|
91
65
|
const maxTokens = options.maxTokens ?? DEFAULT_MAX_TOKENS;
|
|
@@ -95,10 +69,6 @@ export function createAnthropicProvider(options: AnthropicProviderOptions): Comp
|
|
|
95
69
|
model,
|
|
96
70
|
|
|
97
71
|
async generate(request: ProviderRequest): Promise<ProviderResult> {
|
|
98
|
-
// The half of obligation three that `fetch` does not cover. The real
|
|
99
|
-
// `fetch` rejects an already-aborted signal on its own, but `fetch` is an
|
|
100
|
-
// injected seam here, and a caller's own transport has no such duty — so
|
|
101
|
-
// without this, a call the caller has already given up on goes out.
|
|
102
72
|
request.signal.throwIfAborted();
|
|
103
73
|
|
|
104
74
|
const response = await send(call, `${baseUrl}/v1/messages`, {
|
|
@@ -109,26 +79,20 @@ export function createAnthropicProvider(options: AnthropicProviderOptions): Comp
|
|
|
109
79
|
'anthropic-version': '2023-06-01',
|
|
110
80
|
...(options.workspaceId ? { 'anthropic-workspace-id': options.workspaceId } : {}),
|
|
111
81
|
},
|
|
112
|
-
|
|
113
|
-
// asks an adapter to stop, not merely to stop caring about the answer.
|
|
82
|
+
|
|
114
83
|
signal: request.signal,
|
|
115
84
|
body: JSON.stringify({
|
|
116
85
|
model,
|
|
117
86
|
max_tokens: maxTokens,
|
|
118
87
|
...(thinking ? { thinking } : {}),
|
|
119
|
-
|
|
120
|
-
// would destroy the prompt cache hit rate.
|
|
88
|
+
|
|
121
89
|
system: [{ type: 'text', text: request.system, cache_control: { type: 'ephemeral' } }],
|
|
122
90
|
messages: [{ role: 'user', content: request.user }],
|
|
123
91
|
tools: [
|
|
124
92
|
{
|
|
125
93
|
name: TOOL_NAME,
|
|
126
94
|
description: 'Return the component specification.',
|
|
127
|
-
|
|
128
|
-
// produce as the tool call's argument, not core's own output. The
|
|
129
|
-
// two are not the same: "output" carries additionalProperties:
|
|
130
|
-
// false on every block and "input" does not. Pinned in
|
|
131
|
-
// tests/tool-schema.test.ts.
|
|
95
|
+
|
|
132
96
|
input_schema: z.toJSONSchema(request.schema, { io: 'input' }),
|
|
133
97
|
},
|
|
134
98
|
],
|
|
@@ -137,9 +101,6 @@ export function createAnthropicProvider(options: AnthropicProviderOptions): Comp
|
|
|
137
101
|
});
|
|
138
102
|
|
|
139
103
|
if (!response.ok) {
|
|
140
|
-
// Status and the vendor's error category only. Its message quotes the
|
|
141
|
-
// request back, and for this framework that can be a shopper's own search
|
|
142
|
-
// terms — which an adopter's `console.error(err)` would then capture.
|
|
143
104
|
const category = await errorCategory(response);
|
|
144
105
|
|
|
145
106
|
throw new Error(`anthropic responded ${response.status}${category}`);
|
|
@@ -147,9 +108,6 @@ export function createAnthropicProvider(options: AnthropicProviderOptions): Comp
|
|
|
147
108
|
|
|
148
109
|
const parsed: unknown = await response.json();
|
|
149
110
|
|
|
150
|
-
// `response.json()` yields whatever the body held, and `null` is valid
|
|
151
|
-
// JSON — reading `stop_reason` off it would throw a TypeError naming this
|
|
152
|
-
// adapter rather than the vendor that sent it.
|
|
153
111
|
if (typeof parsed !== 'object' || parsed === null) {
|
|
154
112
|
throw new Error(
|
|
155
113
|
`anthropic returned ${parsed === null ? 'null' : typeof parsed}, not an object`,
|
|
@@ -162,9 +120,6 @@ export function createAnthropicProvider(options: AnthropicProviderOptions): Comp
|
|
|
162
120
|
stop_reason?: string;
|
|
163
121
|
};
|
|
164
122
|
|
|
165
|
-
// Before the tool-block lookup: both are ordinary 200s with no tool_use,
|
|
166
|
-
// and reporting them as "no tool use" blames the model for a budget or a
|
|
167
|
-
// policy this adapter controls.
|
|
168
123
|
if (body.stop_reason === 'max_tokens') {
|
|
169
124
|
throw new Error(
|
|
170
125
|
`anthropic stopped at the max_tokens budget (${maxTokens}) before returning a tool use`,
|
|
@@ -174,9 +129,6 @@ export function createAnthropicProvider(options: AnthropicProviderOptions): Comp
|
|
|
174
129
|
throw new Error('anthropic refused to answer (stop_reason: refusal)');
|
|
175
130
|
}
|
|
176
131
|
|
|
177
|
-
// `content` and each of its entries are untrusted shapes from here on:
|
|
178
|
-
// a malformed response should name the vendor, not crash on the
|
|
179
|
-
// adapter's own `.find`/`.type` access.
|
|
180
132
|
const blocks = Array.isArray(body.content) ? body.content : [];
|
|
181
133
|
const block = blocks.find(isToolUseBlock);
|
|
182
134
|
|
|
@@ -184,9 +136,6 @@ export function createAnthropicProvider(options: AnthropicProviderOptions): Comp
|
|
|
184
136
|
throw new Error(`anthropic returned no ${TOOL_NAME} tool use`);
|
|
185
137
|
}
|
|
186
138
|
|
|
187
|
-
// Parsed against the caller's own schema. generatedSpecSchema has no
|
|
188
|
-
// refinements, so this catches type and enum violations — a block kind
|
|
189
|
-
// outside the closed set, a non-string headline — not refinement logic.
|
|
190
139
|
const asSent = request.schema.safeParse(block.input);
|
|
191
140
|
const usable = asSent.success ? asSent : request.schema.safeParse(onlyValue(block.input));
|
|
192
141
|
|
|
@@ -206,20 +155,10 @@ export function createAnthropicProvider(options: AnthropicProviderOptions): Comp
|
|
|
206
155
|
};
|
|
207
156
|
}
|
|
208
157
|
|
|
209
|
-
/**
|
|
210
|
-
* Calls the transport, and says what went wrong when it never answered.
|
|
211
|
-
*
|
|
212
|
-
* `fetch` reports every transport fault as the same `TypeError: fetch failed`
|
|
213
|
-
* and hides the reason in `cause` — so a refused connection, a DNS failure and
|
|
214
|
-
* a socket reset are indistinguishable in a log. An operator needs to tell
|
|
215
|
-
* those apart, and none of them carries request content.
|
|
216
|
-
*/
|
|
217
158
|
async function send(call: typeof globalThis.fetch, url: string, init: RequestInit) {
|
|
218
159
|
try {
|
|
219
160
|
return await call(url, init);
|
|
220
161
|
} catch (error) {
|
|
221
|
-
// The caller's own deadline. It means something specific upstream, so it
|
|
222
|
-
// travels unchanged.
|
|
223
162
|
if (init.signal?.aborted) throw error;
|
|
224
163
|
|
|
225
164
|
const cause = error instanceof Error ? (error.cause ?? error) : error;
|
|
@@ -232,15 +171,12 @@ async function send(call: typeof globalThis.fetch, url: string, init: RequestIni
|
|
|
232
171
|
}
|
|
233
172
|
}
|
|
234
173
|
|
|
235
|
-
/** The vendor's error category, never its message — the message quotes the request. */
|
|
236
174
|
async function errorCategory(response: Response): Promise<string> {
|
|
237
175
|
try {
|
|
238
176
|
const body: unknown = JSON.parse(await response.text());
|
|
239
177
|
const type = (body as { error?: { type?: unknown } })?.error?.type;
|
|
240
178
|
return typeof type === 'string' ? ` (${type})` : '';
|
|
241
179
|
} catch {
|
|
242
|
-
// A body that is unreadable or not JSON tells us nothing extra. The status
|
|
243
|
-
// is still in the message.
|
|
244
180
|
return '';
|
|
245
181
|
}
|
|
246
182
|
}
|
|
@@ -248,8 +184,6 @@ async function errorCategory(response: Response): Promise<string> {
|
|
|
248
184
|
function toUsage(usage: Record<string, unknown> | undefined): TokenUsage | undefined {
|
|
249
185
|
if (!usage) return undefined;
|
|
250
186
|
|
|
251
|
-
// A JSON body is untrusted: `"input_tokens": "11"` must not become part of
|
|
252
|
-
// a cost figure that downstream code adds instead of concatenates.
|
|
253
187
|
const numberAt = (key: string): number | undefined => {
|
|
254
188
|
const value = usage[key];
|
|
255
189
|
return typeof value === 'number' ? value : undefined;
|
package/src/index.ts
CHANGED