@kindgi/adapter-model-anthropic 0.1.2 → 0.1.4-rc.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/dist/cost.d.ts +8 -13
- package/dist/cost.d.ts.map +1 -1
- package/dist/cost.js +13 -14
- package/dist/cost.js.map +1 -1
- package/dist/provider.d.ts +1 -1
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +15 -2
- package/dist/provider.js.map +1 -1
- package/package.json +2 -2
- package/src/cost.ts +15 -19
- package/src/provider.ts +29 -11
package/dist/cost.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type Anthropic from '@anthropic-ai/sdk';
|
|
2
|
+
import type { UsageCounters } from '@kindgi/capabilities';
|
|
2
3
|
/**
|
|
3
4
|
* Cost multipliers for prompt-cache tokens. Anthropic prices cache
|
|
4
5
|
* activity as a multiple of the base input rate:
|
|
@@ -39,19 +40,13 @@ export interface CostRates {
|
|
|
39
40
|
*/
|
|
40
41
|
export declare function computeCostUsd(usage: Anthropic.Usage, rates: CostRates): number;
|
|
41
42
|
/**
|
|
42
|
-
*
|
|
43
|
-
* `UsageCounters` shape.
|
|
43
|
+
* Anthropic's four-way token split as the framework's `UsageCounters`:
|
|
44
44
|
*
|
|
45
|
-
* - `promptTokens` =
|
|
46
|
-
* cache-
|
|
47
|
-
* - `
|
|
48
|
-
* - `
|
|
49
|
-
*
|
|
50
|
-
* field, so we don't need to bake cache math into `promptTokens`).
|
|
45
|
+
* - `promptTokens` = every input token = regular + cache-write +
|
|
46
|
+
* cache-read (`input_tokens` excludes both cache counts);
|
|
47
|
+
* - `cacheReadTokens` / `cacheWriteTokens` = the cache's parts of it;
|
|
48
|
+
* - `completionTokens` = output tokens. Anthropic counts thinking in
|
|
49
|
+
* them and doesn't report it apart, so there's no `reasoningTokens`.
|
|
51
50
|
*/
|
|
52
|
-
export declare function toFrameworkUsage(usage: Anthropic.Usage):
|
|
53
|
-
readonly promptTokens: number;
|
|
54
|
-
readonly completionTokens: number;
|
|
55
|
-
readonly cachedTokens?: number;
|
|
56
|
-
};
|
|
51
|
+
export declare function toFrameworkUsage(usage: Anthropic.Usage): UsageCounters;
|
|
57
52
|
//# sourceMappingURL=cost.d.ts.map
|
package/dist/cost.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cost.d.ts","sourceRoot":"","sources":["../src/cost.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"cost.d.ts","sourceRoot":"","sources":["../src/cost.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,sCAAsC,OAAO,CAAC;AAC3D,eAAO,MAAM,6BAA6B,MAAM,CAAC;AAEjD,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,wBAAwB,EAAE,MAAM,CAAC;IAC1C,QAAQ,CAAC,6BAA6B,CAAC,EAAE,MAAM,CAAC;IAChD,QAAQ,CAAC,yBAAyB,CAAC,EAAE,MAAM,CAAC;CAC7C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,GAAG,MAAM,CAoB/E;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,CAAC,KAAK,GAAG,aAAa,CAUtE"}
|
package/dist/cost.js
CHANGED
|
@@ -49,24 +49,23 @@ export function computeCostUsd(usage, rates) {
|
|
|
49
49
|
return inputCost + outputCost;
|
|
50
50
|
}
|
|
51
51
|
/**
|
|
52
|
-
*
|
|
53
|
-
* `UsageCounters` shape.
|
|
52
|
+
* Anthropic's four-way token split as the framework's `UsageCounters`:
|
|
54
53
|
*
|
|
55
|
-
* - `promptTokens` =
|
|
56
|
-
* cache-
|
|
57
|
-
* - `
|
|
58
|
-
* - `
|
|
59
|
-
*
|
|
60
|
-
* field, so we don't need to bake cache math into `promptTokens`).
|
|
54
|
+
* - `promptTokens` = every input token = regular + cache-write +
|
|
55
|
+
* cache-read (`input_tokens` excludes both cache counts);
|
|
56
|
+
* - `cacheReadTokens` / `cacheWriteTokens` = the cache's parts of it;
|
|
57
|
+
* - `completionTokens` = output tokens. Anthropic counts thinking in
|
|
58
|
+
* them and doesn't report it apart, so there's no `reasoningTokens`.
|
|
61
59
|
*/
|
|
62
60
|
export function toFrameworkUsage(usage) {
|
|
63
|
-
const
|
|
64
|
-
const cacheRead = usage.cache_read_input_tokens
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
promptTokens,
|
|
61
|
+
const cacheWrite = usage.cache_creation_input_tokens;
|
|
62
|
+
const cacheRead = usage.cache_read_input_tokens;
|
|
63
|
+
return {
|
|
64
|
+
promptTokens: usage.input_tokens + (cacheWrite ?? 0) + (cacheRead ?? 0),
|
|
68
65
|
completionTokens: usage.output_tokens,
|
|
66
|
+
// A part the vendor reports is kept, 0 included.
|
|
67
|
+
...(typeof cacheRead === 'number' && { cacheReadTokens: cacheRead }),
|
|
68
|
+
...(typeof cacheWrite === 'number' && { cacheWriteTokens: cacheWrite }),
|
|
69
69
|
};
|
|
70
|
-
return cacheRead > 0 ? { ...base, cachedTokens: cacheRead } : base;
|
|
71
70
|
}
|
|
72
71
|
//# sourceMappingURL=cost.js.map
|
package/dist/cost.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cost.js","sourceRoot":"","sources":["../src/cost.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,iCAAiC;
|
|
1
|
+
{"version":3,"file":"cost.js","sourceRoot":"","sources":["../src/cost.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,iCAAiC;AAKjC;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,sCAAsC,GAAG,IAAI,CAAC;AAC3D,MAAM,CAAC,MAAM,6BAA6B,GAAG,GAAG,CAAC;AASjD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,cAAc,CAAC,KAAsB,EAAE,KAAgB;IACrE,MAAM,SAAS,GAAG,KAAK,CAAC,oBAAoB,CAAC;IAC7C,MAAM,UAAU,GAAG,KAAK,CAAC,wBAAwB,CAAC;IAClD,MAAM,kBAAkB,GACtB,KAAK,CAAC,6BAA6B,IAAI,sCAAsC,CAAC;IAChF,MAAM,cAAc,GAAG,KAAK,CAAC,yBAAyB,IAAI,6BAA6B,CAAC;IAExF,MAAM,WAAW,GAAG,KAAK,CAAC,YAAY,CAAC;IACvC,MAAM,YAAY,GAAG,KAAK,CAAC,aAAa,CAAC;IACzC,MAAM,aAAa,GAAG,KAAK,CAAC,2BAA2B,IAAI,CAAC,CAAC;IAC7D,MAAM,SAAS,GAAG,KAAK,CAAC,uBAAuB,IAAI,CAAC,CAAC;IAErD,MAAM,SAAS,GACb,CAAC,WAAW,GAAG,SAAS;QACtB,aAAa,GAAG,SAAS,GAAG,kBAAkB;QAC9C,SAAS,GAAG,SAAS,GAAG,cAAc,CAAC;QACzC,IAAI,CAAC;IACP,MAAM,UAAU,GAAG,CAAC,YAAY,GAAG,UAAU,CAAC,GAAG,IAAI,CAAC;IAEtD,OAAO,SAAS,GAAG,UAAU,CAAC;AAChC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAsB;IACrD,MAAM,UAAU,GAAG,KAAK,CAAC,2BAA2B,CAAC;IACrD,MAAM,SAAS,GAAG,KAAK,CAAC,uBAAuB,CAAC;IAChD,OAAO;QACL,YAAY,EAAE,KAAK,CAAC,YAAY,GAAG,CAAC,UAAU,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,CAAC;QACvE,gBAAgB,EAAE,KAAK,CAAC,aAAa;QACrC,iDAAiD;QACjD,GAAG,CAAC,OAAO,SAAS,KAAK,QAAQ,IAAI,EAAE,eAAe,EAAE,SAAS,EAAE,CAAC;QACpE,GAAG,CAAC,OAAO,UAAU,KAAK,QAAQ,IAAI,EAAE,gBAAgB,EAAE,UAAU,EAAE,CAAC;KACxE,CAAC;AACJ,CAAC"}
|
package/dist/provider.d.ts
CHANGED
|
@@ -51,7 +51,7 @@ export interface AnthropicProviderOptions {
|
|
|
51
51
|
* Additional SDK client options (timeouts, custom fetch, headers,
|
|
52
52
|
* etc.). `apiKey` and `baseURL` from the top level always win.
|
|
53
53
|
*/
|
|
54
|
-
readonly clientOptions?: Omit<ConstructorParameters<typeof Anthropic>[0]
|
|
54
|
+
readonly clientOptions?: Omit<NonNullable<ConstructorParameters<typeof Anthropic>[0]>, 'apiKey' | 'baseURL'>;
|
|
55
55
|
/**
|
|
56
56
|
* Dependency-injected SDK client. When present, `apiKey` /
|
|
57
57
|
* `baseURL` / `clientOptions` are ignored on construction (tests
|
package/dist/provider.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAGA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAE1C,OAAO,KAAK,EAGV,SAAS,EACT,aAAa,EACb,gBAAgB,EACjB,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAGA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAE1C,OAAO,KAAK,EAGV,SAAS,EACT,aAAa,EACb,gBAAgB,EACjB,MAAM,sBAAsB,CAAC;AAW9B;;;;;;;;GAQG;AACH,MAAM,WAAW,kBAAmB,SAAQ,SAAS;IACnD,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC,GAAG;QACjC,QAAQ,CAAC,6BAA6B,CAAC,EAAE,MAAM,CAAC;QAChD,QAAQ,CAAC,yBAAyB,CAAC,EAAE,MAAM,CAAC;KAC7C,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3D;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAC,GAAG;QACpD,QAAQ,CAAC,MAAM,EAAE,SAAS,kBAAkB,EAAE,CAAC;KAChD,CAAC;IACF,8DAA8D;IAC9D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,IAAI,CAC3B,WAAW,CAAC,qBAAqB,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,EACvD,QAAQ,GAAG,SAAS,CACrB,CAAC;IACF;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;CAC7B;AASD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,wBAAwB,GAAG,aAAa,CA0GxF"}
|
package/dist/provider.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// Copyright (C) 2026 Kindgi Inc.
|
|
3
3
|
import Anthropic from '@anthropic-ai/sdk';
|
|
4
|
+
import { createAttemptCounter } from '@kindgi/capabilities/attempts';
|
|
4
5
|
import { computeCostUsd, toFrameworkUsage } from './cost.js';
|
|
5
6
|
import { fromAnthropicResponse, mapStopReason, toAnthropicMessages, toAnthropicTools, } from './translate.js';
|
|
6
7
|
/**
|
|
@@ -27,6 +28,9 @@ export function createAnthropicProvider(options) {
|
|
|
27
28
|
// keepalive — throwing it away on every invoke would tank latency).
|
|
28
29
|
// When the resolver returns a new value (key rotation), rebuild.
|
|
29
30
|
let cached;
|
|
31
|
+
// Counts each call's HTTP attempts, the SDK's own retries included (its
|
|
32
|
+
// retry policy stays the SDK's), through the `fetch` the client sends with.
|
|
33
|
+
const attempts = createAttemptCounter(options.clientOptions?.fetch);
|
|
30
34
|
if (options.client !== undefined) {
|
|
31
35
|
// Dep-injected client — pin to sentinel key so resolveClient below
|
|
32
36
|
// always returns the same instance regardless of what the resolver
|
|
@@ -48,6 +52,7 @@ export function createAnthropicProvider(options) {
|
|
|
48
52
|
apiKey: key,
|
|
49
53
|
...(options.baseURL !== undefined && { baseURL: options.baseURL }),
|
|
50
54
|
...(options.clientOptions ?? {}),
|
|
55
|
+
fetch: attempts.fetch,
|
|
51
56
|
});
|
|
52
57
|
cached = { key, client };
|
|
53
58
|
return client;
|
|
@@ -68,14 +73,15 @@ export function createAnthropicProvider(options) {
|
|
|
68
73
|
: undefined;
|
|
69
74
|
const requestOptions = input.abortSignal !== undefined ? { signal: input.abortSignal } : {};
|
|
70
75
|
const maxTokens = input.maxOutputTokens ?? modelInfo.maxOutputTokens ?? DEFAULT_MAX_TOKENS;
|
|
71
|
-
const
|
|
76
|
+
const counted = await attempts.count(() => client.messages.create({
|
|
72
77
|
model: input.model,
|
|
73
78
|
max_tokens: maxTokens,
|
|
74
79
|
...(system !== undefined && { system }),
|
|
75
80
|
messages: [...messages],
|
|
76
81
|
...(tools !== undefined && { tools: [...tools] }),
|
|
77
82
|
...(input.temperature !== undefined && { temperature: input.temperature }),
|
|
78
|
-
}, requestOptions);
|
|
83
|
+
}, requestOptions));
|
|
84
|
+
const response = counted.value;
|
|
79
85
|
const durationMs = Date.now() - startedAt;
|
|
80
86
|
const message = fromAnthropicResponse(response);
|
|
81
87
|
const usage = toFrameworkUsage(response.usage);
|
|
@@ -87,6 +93,13 @@ export function createAnthropicProvider(options) {
|
|
|
87
93
|
costUsd,
|
|
88
94
|
durationMs,
|
|
89
95
|
provider: { id: metadata.id, model: input.model },
|
|
96
|
+
...(response.model !== undefined && { servedModel: response.model }),
|
|
97
|
+
...(typeof response._request_id === 'string' && {
|
|
98
|
+
providerRequestId: response._request_id,
|
|
99
|
+
}),
|
|
100
|
+
// An injected client sends with its own fetch: nothing was counted.
|
|
101
|
+
...(counted.attempts > 0 && { attempts: counted.attempts }),
|
|
102
|
+
rawUsage: { ...response.usage },
|
|
90
103
|
};
|
|
91
104
|
},
|
|
92
105
|
};
|
package/dist/provider.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,iCAAiC;AAEjC,OAAO,SAAS,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kindgi/adapter-model-anthropic",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4-rc.0",
|
|
4
4
|
"description": "Anthropic ModelProvider for @kindgi/capabilities. Wraps @anthropic-ai/sdk with the framework's provider-neutral interface: message-trail ↔ Messages API translation, prompt-caching-aware cost accounting, and lazy API-key resolution so per-tenant registries can close over a SecretStore scope. Non-streaming, tool-use enabled. Wire per-tenant in the provider registry (see @kindgi/capabilities).",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"README.md"
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@kindgi/capabilities": "0.1.
|
|
31
|
+
"@kindgi/capabilities": "0.1.4-rc.0",
|
|
32
32
|
"@anthropic-ai/sdk": "^0.68.0"
|
|
33
33
|
},
|
|
34
34
|
"engines": {
|
package/src/cost.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Copyright (C) 2026 Kindgi Inc.
|
|
3
3
|
|
|
4
4
|
import type Anthropic from '@anthropic-ai/sdk';
|
|
5
|
+
import type { UsageCounters } from '@kindgi/capabilities';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Cost multipliers for prompt-cache tokens. Anthropic prices cache
|
|
@@ -66,27 +67,22 @@ export function computeCostUsd(usage: Anthropic.Usage, rates: CostRates): number
|
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
/**
|
|
69
|
-
*
|
|
70
|
-
* `UsageCounters` shape.
|
|
70
|
+
* Anthropic's four-way token split as the framework's `UsageCounters`:
|
|
71
71
|
*
|
|
72
|
-
* - `promptTokens` =
|
|
73
|
-
* cache-
|
|
74
|
-
* - `
|
|
75
|
-
* - `
|
|
76
|
-
*
|
|
77
|
-
* field, so we don't need to bake cache math into `promptTokens`).
|
|
72
|
+
* - `promptTokens` = every input token = regular + cache-write +
|
|
73
|
+
* cache-read (`input_tokens` excludes both cache counts);
|
|
74
|
+
* - `cacheReadTokens` / `cacheWriteTokens` = the cache's parts of it;
|
|
75
|
+
* - `completionTokens` = output tokens. Anthropic counts thinking in
|
|
76
|
+
* them and doesn't report it apart, so there's no `reasoningTokens`.
|
|
78
77
|
*/
|
|
79
|
-
export function toFrameworkUsage(usage: Anthropic.Usage): {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const cacheCreation = usage.cache_creation_input_tokens ?? 0;
|
|
85
|
-
const cacheRead = usage.cache_read_input_tokens ?? 0;
|
|
86
|
-
const promptTokens = usage.input_tokens + cacheCreation + cacheRead;
|
|
87
|
-
const base = {
|
|
88
|
-
promptTokens,
|
|
78
|
+
export function toFrameworkUsage(usage: Anthropic.Usage): UsageCounters {
|
|
79
|
+
const cacheWrite = usage.cache_creation_input_tokens;
|
|
80
|
+
const cacheRead = usage.cache_read_input_tokens;
|
|
81
|
+
return {
|
|
82
|
+
promptTokens: usage.input_tokens + (cacheWrite ?? 0) + (cacheRead ?? 0),
|
|
89
83
|
completionTokens: usage.output_tokens,
|
|
84
|
+
// A part the vendor reports is kept, 0 included.
|
|
85
|
+
...(typeof cacheRead === 'number' && { cacheReadTokens: cacheRead }),
|
|
86
|
+
...(typeof cacheWrite === 'number' && { cacheWriteTokens: cacheWrite }),
|
|
90
87
|
};
|
|
91
|
-
return cacheRead > 0 ? { ...base, cachedTokens: cacheRead } : base;
|
|
92
88
|
}
|
package/src/provider.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
ModelProvider,
|
|
11
11
|
ProviderMetadata,
|
|
12
12
|
} from '@kindgi/capabilities';
|
|
13
|
+
import { createAttemptCounter } from '@kindgi/capabilities/attempts';
|
|
13
14
|
|
|
14
15
|
import { computeCostUsd, toFrameworkUsage } from './cost.js';
|
|
15
16
|
import {
|
|
@@ -71,7 +72,10 @@ export interface AnthropicProviderOptions {
|
|
|
71
72
|
* Additional SDK client options (timeouts, custom fetch, headers,
|
|
72
73
|
* etc.). `apiKey` and `baseURL` from the top level always win.
|
|
73
74
|
*/
|
|
74
|
-
readonly clientOptions?: Omit<
|
|
75
|
+
readonly clientOptions?: Omit<
|
|
76
|
+
NonNullable<ConstructorParameters<typeof Anthropic>[0]>,
|
|
77
|
+
'apiKey' | 'baseURL'
|
|
78
|
+
>;
|
|
75
79
|
/**
|
|
76
80
|
* Dependency-injected SDK client. When present, `apiKey` /
|
|
77
81
|
* `baseURL` / `clientOptions` are ignored on construction (tests
|
|
@@ -110,6 +114,9 @@ export function createAnthropicProvider(options: AnthropicProviderOptions): Mode
|
|
|
110
114
|
// keepalive — throwing it away on every invoke would tank latency).
|
|
111
115
|
// When the resolver returns a new value (key rotation), rebuild.
|
|
112
116
|
let cached: { readonly key: string; readonly client: Anthropic } | undefined;
|
|
117
|
+
// Counts each call's HTTP attempts, the SDK's own retries included (its
|
|
118
|
+
// retry policy stays the SDK's), through the `fetch` the client sends with.
|
|
119
|
+
const attempts = createAttemptCounter(options.clientOptions?.fetch);
|
|
113
120
|
if (options.client !== undefined) {
|
|
114
121
|
// Dep-injected client — pin to sentinel key so resolveClient below
|
|
115
122
|
// always returns the same instance regardless of what the resolver
|
|
@@ -130,6 +137,7 @@ export function createAnthropicProvider(options: AnthropicProviderOptions): Mode
|
|
|
130
137
|
apiKey: key,
|
|
131
138
|
...(options.baseURL !== undefined && { baseURL: options.baseURL }),
|
|
132
139
|
...(options.clientOptions ?? {}),
|
|
140
|
+
fetch: attempts.fetch,
|
|
133
141
|
});
|
|
134
142
|
cached = { key, client };
|
|
135
143
|
return client;
|
|
@@ -158,17 +166,20 @@ export function createAnthropicProvider(options: AnthropicProviderOptions): Mode
|
|
|
158
166
|
input.abortSignal !== undefined ? { signal: input.abortSignal } : {};
|
|
159
167
|
const maxTokens = input.maxOutputTokens ?? modelInfo.maxOutputTokens ?? DEFAULT_MAX_TOKENS;
|
|
160
168
|
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
169
|
+
const counted = await attempts.count(() =>
|
|
170
|
+
client.messages.create(
|
|
171
|
+
{
|
|
172
|
+
model: input.model,
|
|
173
|
+
max_tokens: maxTokens,
|
|
174
|
+
...(system !== undefined && { system }),
|
|
175
|
+
messages: [...messages],
|
|
176
|
+
...(tools !== undefined && { tools: [...tools] }),
|
|
177
|
+
...(input.temperature !== undefined && { temperature: input.temperature }),
|
|
178
|
+
},
|
|
179
|
+
requestOptions,
|
|
180
|
+
),
|
|
171
181
|
);
|
|
182
|
+
const response = counted.value;
|
|
172
183
|
|
|
173
184
|
const durationMs = Date.now() - startedAt;
|
|
174
185
|
const message = fromAnthropicResponse(response);
|
|
@@ -182,6 +193,13 @@ export function createAnthropicProvider(options: AnthropicProviderOptions): Mode
|
|
|
182
193
|
costUsd,
|
|
183
194
|
durationMs,
|
|
184
195
|
provider: { id: metadata.id, model: input.model },
|
|
196
|
+
...(response.model !== undefined && { servedModel: response.model }),
|
|
197
|
+
...(typeof response._request_id === 'string' && {
|
|
198
|
+
providerRequestId: response._request_id,
|
|
199
|
+
}),
|
|
200
|
+
// An injected client sends with its own fetch: nothing was counted.
|
|
201
|
+
...(counted.attempts > 0 && { attempts: counted.attempts }),
|
|
202
|
+
rawUsage: { ...response.usage },
|
|
185
203
|
};
|
|
186
204
|
},
|
|
187
205
|
};
|