@veryfront/ext-llm-anthropic 0.1.985

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.
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Anthropic provider - implements the {@link LLMProvider} contract for
3
+ * Anthropic's Messages API (direct + via Veryfront Cloud / Bedrock-compatible
4
+ * proxies).
5
+ *
6
+ * Ported from `src/provider/runtime-loader.ts` as part of PR 12.
7
+ *
8
+ * @module extensions/ext-llm-anthropic/anthropic-provider
9
+ */
10
+ import { buildProviderError, createAnthropicRequestInit, createWarningCollector, getAnthropicMessagesUrl, isNumberArray, mergeUsage, parseRetryAfterMs, ProviderError, ProviderOverloadedError, ProviderQuotaError, ProviderRateLimitError, ProviderRequestError, readRecord, requestJson, requestStream, stringifyJsonValue, TOOL_INPUT_PENDING_THRESHOLD_MS, withToolInputStatusTransitions, } from "veryfront/provider/shared";
11
+ import { buildAnthropicMessagesRequest, } from "./anthropic-request-builder.js";
12
+ import { extractAnthropicUsage, normalizeAnthropicFinishReason, streamAnthropicCompatibleParts, } from "./anthropic-stream.js";
13
+ export { buildProviderError, isNumberArray, mergeUsage, parseRetryAfterMs, ProviderError, ProviderOverloadedError, ProviderQuotaError, ProviderRateLimitError, ProviderRequestError, TOOL_INPUT_PENDING_THRESHOLD_MS, withToolInputStatusTransitions, };
14
+ /**
15
+ * Best-effort camelCase normalization of a single Anthropic citation
16
+ * record. Handles the union of fields across web_search_result_location,
17
+ * web_fetch_result_location, char_location, page_location, and
18
+ * content_block_location citation kinds - see
19
+ * https://docs.claude.com/en/docs/build-with-claude/citations
20
+ */
21
+ function normalizeAnthropicCitation(raw) {
22
+ const r = readRecord(raw);
23
+ if (!r)
24
+ return undefined;
25
+ const typeStr = typeof r.type === "string" ? r.type : undefined;
26
+ if (!typeStr)
27
+ return undefined;
28
+ const out = { type: typeStr };
29
+ if (typeof r.cited_text === "string")
30
+ out.citedText = r.cited_text;
31
+ if (typeof r.url === "string")
32
+ out.url = r.url;
33
+ if (typeof r.title === "string")
34
+ out.title = r.title;
35
+ if (typeof r.start_char_index === "number")
36
+ out.startCharIndex = r.start_char_index;
37
+ if (typeof r.end_char_index === "number")
38
+ out.endCharIndex = r.end_char_index;
39
+ if (typeof r.start_block_index === "number")
40
+ out.startBlockIndex = r.start_block_index;
41
+ if (typeof r.end_block_index === "number")
42
+ out.endBlockIndex = r.end_block_index;
43
+ if (typeof r.start_page_number === "number")
44
+ out.startPageNumber = r.start_page_number;
45
+ if (typeof r.end_page_number === "number")
46
+ out.endPageNumber = r.end_page_number;
47
+ if (typeof r.document_index === "number")
48
+ out.documentIndex = r.document_index;
49
+ if (typeof r.document_title === "string")
50
+ out.documentTitle = r.document_title;
51
+ return out;
52
+ }
53
+ function buildAnthropicGenerateResult(payload) {
54
+ const record = readRecord(payload);
55
+ const content = Array.isArray(record?.content) ? record.content : [];
56
+ const normalized = [];
57
+ for (const blockValue of content) {
58
+ const block = readRecord(blockValue);
59
+ const blockType = typeof block?.type === "string" ? block.type : undefined;
60
+ if (blockType === "text" && typeof block?.text === "string" && block.text.length > 0) {
61
+ const citationsRaw = Array.isArray(block.citations) ? block.citations : undefined;
62
+ const citations = citationsRaw
63
+ ?.flatMap((c) => {
64
+ const normalizedCitation = normalizeAnthropicCitation(c);
65
+ return normalizedCitation ? [normalizedCitation] : [];
66
+ });
67
+ normalized.push({
68
+ type: "text",
69
+ text: block.text,
70
+ ...(citations && citations.length > 0 ? { citations } : {}),
71
+ });
72
+ continue;
73
+ }
74
+ // Thinking blocks carry the cleartext trace plus a signature that
75
+ // Anthropic uses to verify on subsequent turns. Surfacing both lets
76
+ // callers persist them as `reasoning` content parts and replay on
77
+ // the next turn so Claude can continue from the same thinking.
78
+ if (blockType === "thinking") {
79
+ normalized.push({
80
+ type: "reasoning",
81
+ ...(typeof block?.thinking === "string" ? { text: block.thinking } : {}),
82
+ ...(typeof block?.signature === "string" ? { signature: block.signature } : {}),
83
+ });
84
+ continue;
85
+ }
86
+ // Redacted thinking blocks arrive when Claude's safety classifier
87
+ // hides the trace. Pass the encrypted blob through opaquely so the
88
+ // caller can replay it on the next turn (Anthropic still needs the
89
+ // blob to verify continuity even though it can't read it).
90
+ if (blockType === "redacted_thinking" && typeof block?.data === "string") {
91
+ normalized.push({
92
+ type: "reasoning",
93
+ redactedData: block.data,
94
+ });
95
+ continue;
96
+ }
97
+ if ((blockType === "tool_use" || blockType === "server_tool_use") &&
98
+ typeof block?.id === "string" &&
99
+ typeof block?.name === "string") {
100
+ normalized.push({
101
+ type: "tool-call",
102
+ toolCallId: block.id,
103
+ toolName: block.name,
104
+ input: stringifyJsonValue(block.input ?? {}),
105
+ });
106
+ continue;
107
+ }
108
+ if (blockType === "web_search_tool_result" &&
109
+ typeof block?.tool_use_id === "string" &&
110
+ Array.isArray(block?.content)) {
111
+ normalized.push({
112
+ type: "tool-result",
113
+ toolCallId: block.tool_use_id,
114
+ toolName: "web_search",
115
+ result: block.content,
116
+ });
117
+ }
118
+ if (blockType === "web_fetch_tool_result" &&
119
+ typeof block?.tool_use_id === "string" &&
120
+ readRecord(block?.content)) {
121
+ normalized.push({
122
+ type: "tool-result",
123
+ toolCallId: block.tool_use_id,
124
+ toolName: "web_fetch",
125
+ result: block.content,
126
+ });
127
+ }
128
+ }
129
+ return {
130
+ content: normalized,
131
+ finishReason: normalizeAnthropicFinishReason(record?.stop_reason),
132
+ usage: extractAnthropicUsage(payload),
133
+ };
134
+ }
135
+ export function createAnthropicModelRuntime(config, modelId) {
136
+ const fetchImpl = config.fetch ?? globalThis.fetch;
137
+ return {
138
+ provider: config.name ?? "anthropic",
139
+ modelId,
140
+ specificationVersion: "v3",
141
+ supportedUrls: {},
142
+ doGenerate(optionsForRuntime) {
143
+ const options = optionsForRuntime;
144
+ const url = getAnthropicMessagesUrl(config.baseURL);
145
+ const warnings = createWarningCollector();
146
+ const body = buildAnthropicMessagesRequest(modelId, config.name ?? "anthropic", options, false, warnings);
147
+ return requestJson({
148
+ url,
149
+ fetchImpl,
150
+ providerLabel: config.name ?? "anthropic",
151
+ providerKind: "anthropic",
152
+ init: createAnthropicRequestInit({
153
+ apiKey: config.apiKey,
154
+ authToken: config.authToken,
155
+ extraHeaders: options.headers,
156
+ body: JSON.stringify(body),
157
+ signal: options.abortSignal,
158
+ }),
159
+ }).then((payload) => {
160
+ const drained = warnings.drain();
161
+ return {
162
+ ...buildAnthropicGenerateResult(payload),
163
+ ...(drained.length > 0 ? { warnings: drained } : {}),
164
+ };
165
+ });
166
+ },
167
+ doStream(optionsForRuntime) {
168
+ const options = optionsForRuntime;
169
+ const url = getAnthropicMessagesUrl(config.baseURL);
170
+ const warnings = createWarningCollector();
171
+ const body = buildAnthropicMessagesRequest(modelId, config.name ?? "anthropic", options, true, warnings);
172
+ return requestStream({
173
+ url,
174
+ fetchImpl,
175
+ providerLabel: config.name ?? "anthropic",
176
+ providerKind: "anthropic",
177
+ init: createAnthropicRequestInit({
178
+ apiKey: config.apiKey,
179
+ authToken: config.authToken,
180
+ extraHeaders: options.headers,
181
+ enableFineGrainedToolStreaming: true,
182
+ body: JSON.stringify(body),
183
+ signal: options.abortSignal,
184
+ }),
185
+ }).then((responseStream) => {
186
+ const drained = warnings.drain();
187
+ return {
188
+ stream: ReadableStream.from(withToolInputStatusTransitions(streamAnthropicCompatibleParts(responseStream))),
189
+ ...(drained.length > 0 ? { warnings: drained } : {}),
190
+ };
191
+ });
192
+ },
193
+ };
194
+ }
195
+ export class AnthropicProvider {
196
+ id = "anthropic";
197
+ createModel(modelId, config) {
198
+ return createAnthropicModelRuntime({
199
+ apiKey: config.credential,
200
+ authToken: typeof config.authToken === "string" ? config.authToken : undefined,
201
+ baseURL: config.baseURL,
202
+ name: config.name ?? "anthropic",
203
+ fetch: config.fetch,
204
+ }, modelId);
205
+ }
206
+ }
@@ -0,0 +1,98 @@
1
+ import type { RuntimePromptMessage } from "veryfront/provider/shared";
2
+ type ProviderCacheTtl = boolean | "5m" | "1h";
3
+ type ProviderCacheControlOption = {
4
+ system?: ProviderCacheTtl;
5
+ tools?: ProviderCacheTtl;
6
+ };
7
+ type ProviderReasoningEffort = "low" | "medium" | "high" | "max";
8
+ type ProviderReasoningOption = {
9
+ enabled?: boolean;
10
+ effort?: ProviderReasoningEffort;
11
+ budgetTokens?: number;
12
+ };
13
+ export type RuntimeToolDefinition = {
14
+ type: "function";
15
+ name: string;
16
+ description?: string;
17
+ inputSchema: unknown;
18
+ } | {
19
+ type: "provider";
20
+ name: string;
21
+ id: `${string}.${string}`;
22
+ args: Record<string, unknown>;
23
+ };
24
+ export type OpenAICompatibleLanguageOptions = {
25
+ prompt: RuntimePromptMessage[];
26
+ maxOutputTokens?: number;
27
+ temperature?: number;
28
+ topP?: number;
29
+ topK?: number;
30
+ stopSequences?: string[];
31
+ tools?: RuntimeToolDefinition[];
32
+ toolChoice?: unknown;
33
+ seed?: number;
34
+ presencePenalty?: number;
35
+ frequencyPenalty?: number;
36
+ headers?: HeadersInit;
37
+ providerOptions?: Record<string, unknown>;
38
+ includeRawChunks?: boolean;
39
+ abortSignal?: AbortSignal;
40
+ cacheControl?: ProviderCacheControlOption;
41
+ reasoning?: ProviderReasoningOption;
42
+ userId?: string;
43
+ requestLabels?: Record<string, string>;
44
+ serviceTier?: "auto" | "default" | "flex" | "scale";
45
+ parallelToolCalls?: boolean;
46
+ responseFormat?: {
47
+ type: "text";
48
+ } | {
49
+ type: "json";
50
+ } | {
51
+ type: "json_schema";
52
+ name: string;
53
+ schema: unknown;
54
+ description?: string;
55
+ strict?: boolean;
56
+ };
57
+ anthropicContainer?: unknown;
58
+ googleCachedContent?: string;
59
+ googleSafetySettings?: Array<{
60
+ category: string;
61
+ threshold: string;
62
+ }>;
63
+ mcpServers?: Array<Record<string, unknown>>;
64
+ };
65
+ type WarningCollector = {
66
+ push(warning: {
67
+ type: "unsupported-setting" | "other";
68
+ setting?: string;
69
+ details?: string;
70
+ provider: string;
71
+ }): void;
72
+ drain(): Array<{
73
+ type: "unsupported-setting" | "other";
74
+ setting?: string;
75
+ details?: string;
76
+ provider: string;
77
+ }>;
78
+ };
79
+ type AnthropicCompatibleMessage = {
80
+ role: "user" | "assistant";
81
+ content: Array<Record<string, unknown>>;
82
+ };
83
+ type AnthropicCompatibleRequest = {
84
+ model: string;
85
+ messages: AnthropicCompatibleMessage[];
86
+ max_tokens: number;
87
+ stream?: boolean;
88
+ system?: string | Array<Record<string, unknown>>;
89
+ temperature?: number;
90
+ top_p?: number;
91
+ stop_sequences?: string[];
92
+ tools?: Array<Record<string, unknown>>;
93
+ tool_choice?: unknown;
94
+ [key: string]: unknown;
95
+ };
96
+ export declare function buildAnthropicMessagesRequest(modelId: string, providerName: string, options: OpenAICompatibleLanguageOptions, stream: boolean, warnings: WarningCollector): AnthropicCompatibleRequest;
97
+ export {};
98
+ //# sourceMappingURL=anthropic-request-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anthropic-request-builder.d.ts","sourceRoot":"","sources":["../src/anthropic-request-builder.ts"],"names":[],"mappings":"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"}