@node-llm/core 0.4.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 +43 -20
- package/dist/chat/Chat.d.ts +1 -1
- package/dist/chat/Chat.d.ts.map +1 -1
- package/dist/chat/Chat.js +2 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/llm.d.ts.map +1 -1
- package/dist/llm.js +4 -0
- package/dist/providers/Provider.d.ts +1 -0
- package/dist/providers/Provider.d.ts.map +1 -1
- package/dist/providers/anthropic/AnthropicProvider.d.ts +32 -0
- package/dist/providers/anthropic/AnthropicProvider.d.ts.map +1 -0
- package/dist/providers/anthropic/AnthropicProvider.js +51 -0
- package/dist/providers/anthropic/Capabilities.d.ts +47 -0
- package/dist/providers/anthropic/Capabilities.d.ts.map +1 -0
- package/dist/providers/anthropic/Capabilities.js +153 -0
- package/dist/providers/anthropic/Chat.d.ts +8 -0
- package/dist/providers/anthropic/Chat.d.ts.map +1 -0
- package/dist/providers/anthropic/Chat.js +93 -0
- package/dist/providers/anthropic/Errors.d.ts +2 -0
- package/dist/providers/anthropic/Errors.d.ts.map +1 -0
- package/dist/providers/anthropic/Errors.js +33 -0
- package/dist/providers/anthropic/Models.d.ts +8 -0
- package/dist/providers/anthropic/Models.d.ts.map +1 -0
- package/dist/providers/anthropic/Models.js +63 -0
- package/dist/providers/anthropic/Streaming.d.ts +8 -0
- package/dist/providers/anthropic/Streaming.d.ts.map +1 -0
- package/dist/providers/anthropic/Streaming.js +104 -0
- package/dist/providers/anthropic/Utils.d.ts +5 -0
- package/dist/providers/anthropic/Utils.d.ts.map +1 -0
- package/dist/providers/anthropic/Utils.js +125 -0
- package/dist/providers/anthropic/index.d.ts +2 -0
- package/dist/providers/anthropic/index.d.ts.map +1 -0
- package/dist/providers/anthropic/index.js +11 -0
- package/dist/providers/anthropic/types.d.ts +57 -0
- package/dist/providers/anthropic/types.d.ts.map +1 -0
- package/dist/providers/anthropic/types.js +1 -0
- package/dist/providers/gemini/Capabilities.d.ts.map +1 -1
- package/dist/providers/gemini/Capabilities.js +5 -2
- package/dist/providers/gemini/Chat.d.ts +1 -0
- package/dist/providers/gemini/Chat.d.ts.map +1 -1
- package/dist/providers/gemini/Chat.js +43 -4
- package/dist/providers/gemini/GeminiProvider.d.ts +2 -1
- package/dist/providers/gemini/GeminiProvider.d.ts.map +1 -1
- package/dist/providers/gemini/GeminiProvider.js +3 -0
- package/dist/providers/gemini/Streaming.d.ts +1 -0
- package/dist/providers/gemini/Streaming.d.ts.map +1 -1
- package/dist/providers/gemini/Streaming.js +34 -4
- package/dist/providers/registry.js +1 -1
- package/dist/utils/FileLoader.d.ts.map +1 -1
- package/dist/utils/FileLoader.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Capabilities } from "./Capabilities.js";
|
|
2
|
+
import { handleAnthropicError } from "./Errors.js";
|
|
3
|
+
import { formatSystemPrompt, formatMessages } from "./Utils.js";
|
|
4
|
+
export class AnthropicChat {
|
|
5
|
+
baseUrl;
|
|
6
|
+
apiKey;
|
|
7
|
+
constructor(baseUrl, apiKey) {
|
|
8
|
+
this.baseUrl = baseUrl;
|
|
9
|
+
this.apiKey = apiKey;
|
|
10
|
+
}
|
|
11
|
+
async execute(request) {
|
|
12
|
+
const model = request.model;
|
|
13
|
+
const maxTokens = request.max_tokens || Capabilities.getMaxOutputTokens(model) || 4096;
|
|
14
|
+
const systemPrompt = formatSystemPrompt(request.messages);
|
|
15
|
+
const messages = formatMessages(request.messages);
|
|
16
|
+
let system = systemPrompt;
|
|
17
|
+
if (request.response_format) {
|
|
18
|
+
let schemaText = "";
|
|
19
|
+
if (request.response_format.type === "json_schema" && request.response_format.json_schema?.schema) {
|
|
20
|
+
schemaText = "\nSchema:\n" + JSON.stringify(request.response_format.json_schema.schema, null, 2);
|
|
21
|
+
}
|
|
22
|
+
const instruction = `CRITICAL: Respond ONLY with a valid JSON object matching the requested schema.${schemaText}\n\nDo not include any other text or explanation.`;
|
|
23
|
+
system = system ? `${system}\n\n${instruction}` : instruction;
|
|
24
|
+
}
|
|
25
|
+
const body = {
|
|
26
|
+
model: model,
|
|
27
|
+
messages: messages,
|
|
28
|
+
max_tokens: maxTokens,
|
|
29
|
+
system: system,
|
|
30
|
+
stream: false, // For now, no streaming
|
|
31
|
+
};
|
|
32
|
+
if (request.temperature !== undefined) {
|
|
33
|
+
body.temperature = request.temperature;
|
|
34
|
+
}
|
|
35
|
+
// Map tools
|
|
36
|
+
if (request.tools && request.tools.length > 0) {
|
|
37
|
+
body.tools = request.tools.map(tool => ({
|
|
38
|
+
name: tool.function.name,
|
|
39
|
+
description: tool.function.description,
|
|
40
|
+
input_schema: tool.function.parameters
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
// Check if any message contains PDF content to add beta header
|
|
44
|
+
const hasPdf = messages.some(msg => Array.isArray(msg.content) && msg.content.some(block => block.type === "document"));
|
|
45
|
+
const headers = {
|
|
46
|
+
"x-api-key": this.apiKey,
|
|
47
|
+
"anthropic-version": "2023-06-01",
|
|
48
|
+
"content-type": "application/json",
|
|
49
|
+
...request.headers,
|
|
50
|
+
};
|
|
51
|
+
if (hasPdf) {
|
|
52
|
+
headers["anthropic-beta"] = "pdfs-2024-09-25";
|
|
53
|
+
}
|
|
54
|
+
const response = await fetch(`${this.baseUrl}/messages`, {
|
|
55
|
+
method: "POST",
|
|
56
|
+
headers: headers,
|
|
57
|
+
body: JSON.stringify(body),
|
|
58
|
+
});
|
|
59
|
+
if (!response.ok) {
|
|
60
|
+
await handleAnthropicError(response, model);
|
|
61
|
+
}
|
|
62
|
+
const json = (await response.json());
|
|
63
|
+
const contentBlocks = json.content;
|
|
64
|
+
// Extract text content and tool calls
|
|
65
|
+
let content = null;
|
|
66
|
+
const toolCalls = []; // Using any for ToolCall matching Provider.ts interface
|
|
67
|
+
for (const block of contentBlocks) {
|
|
68
|
+
if (block.type === "text") {
|
|
69
|
+
if (content === null)
|
|
70
|
+
content = "";
|
|
71
|
+
content += block.text;
|
|
72
|
+
}
|
|
73
|
+
else if (block.type === "tool_use") {
|
|
74
|
+
toolCalls.push({
|
|
75
|
+
id: block.id,
|
|
76
|
+
type: "function",
|
|
77
|
+
function: {
|
|
78
|
+
name: block.name,
|
|
79
|
+
arguments: JSON.stringify(block.input)
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const usage = json.usage ? {
|
|
85
|
+
input_tokens: json.usage.input_tokens,
|
|
86
|
+
output_tokens: json.usage.output_tokens,
|
|
87
|
+
total_tokens: json.usage.input_tokens + json.usage.output_tokens,
|
|
88
|
+
cached_tokens: json.usage.cache_read_input_tokens,
|
|
89
|
+
cache_creation_tokens: json.usage.cache_creation_input_tokens,
|
|
90
|
+
} : undefined;
|
|
91
|
+
return { content, usage, tool_calls: toolCalls.length > 0 ? toolCalls : undefined };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Errors.d.ts","sourceRoot":"","sources":["../../../src/providers/anthropic/Errors.ts"],"names":[],"mappings":"AASA,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAsC9F"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { BadRequestError, AuthenticationError, RateLimitError, ServerError, ServiceUnavailableError, APIError } from "../../errors/index.js";
|
|
2
|
+
export async function handleAnthropicError(response, modelId) {
|
|
3
|
+
const status = response.status;
|
|
4
|
+
let body;
|
|
5
|
+
let message = `Anthropic error (${status})`;
|
|
6
|
+
try {
|
|
7
|
+
body = await response.json();
|
|
8
|
+
if (body?.error?.message) {
|
|
9
|
+
message = body.error.message;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
body = await response.text().catch(() => "Unknown error");
|
|
14
|
+
message = `Anthropic error (${status}): ${body}`;
|
|
15
|
+
}
|
|
16
|
+
const provider = "anthropic";
|
|
17
|
+
if (status === 400) {
|
|
18
|
+
throw new BadRequestError(message, body, provider, modelId);
|
|
19
|
+
}
|
|
20
|
+
if (status === 401 || status === 403) {
|
|
21
|
+
throw new AuthenticationError(message, status, body, provider);
|
|
22
|
+
}
|
|
23
|
+
if (status === 429) {
|
|
24
|
+
throw new RateLimitError(message, body, provider, modelId);
|
|
25
|
+
}
|
|
26
|
+
if (status === 502 || status === 503 || status === 529) {
|
|
27
|
+
throw new ServiceUnavailableError(message, status, body, provider, modelId);
|
|
28
|
+
}
|
|
29
|
+
if (status >= 500) {
|
|
30
|
+
throw new ServerError(message, status, body, provider, modelId);
|
|
31
|
+
}
|
|
32
|
+
throw new APIError(message, status, body, provider, modelId);
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Models.d.ts","sourceRoot":"","sources":["../../../src/providers/anthropic/Models.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAG3C,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,OAAO;IAAU,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAxC,OAAO,EAAE,MAAM,EAAmB,MAAM,EAAE,MAAM;IAEvE,OAAO,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;CAwDtC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Capabilities, ANTHROPIC_MODELS } from "./Capabilities.js";
|
|
2
|
+
export class AnthropicModels {
|
|
3
|
+
baseUrl;
|
|
4
|
+
apiKey;
|
|
5
|
+
constructor(baseUrl, apiKey) {
|
|
6
|
+
this.baseUrl = baseUrl;
|
|
7
|
+
this.apiKey = apiKey;
|
|
8
|
+
}
|
|
9
|
+
async execute() {
|
|
10
|
+
try {
|
|
11
|
+
const response = await fetch(`${this.baseUrl}/models`, {
|
|
12
|
+
method: "GET",
|
|
13
|
+
headers: {
|
|
14
|
+
"x-api-key": this.apiKey,
|
|
15
|
+
"anthropic-version": "2023-06-01",
|
|
16
|
+
"content-type": "application/json",
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
if (!response.ok) {
|
|
20
|
+
throw new Error(`Failed to list models: ${response.statusText}`);
|
|
21
|
+
}
|
|
22
|
+
const data = await response.json();
|
|
23
|
+
const models = data.data || [];
|
|
24
|
+
return models.map((model) => {
|
|
25
|
+
const id = model.id;
|
|
26
|
+
const createdAt = new Date(model.created_at);
|
|
27
|
+
return {
|
|
28
|
+
id: id,
|
|
29
|
+
name: model.display_name || id,
|
|
30
|
+
provider: "anthropic",
|
|
31
|
+
family: Capabilities.getFamily(id),
|
|
32
|
+
context_window: Capabilities.getContextWindow(id),
|
|
33
|
+
max_output_tokens: Capabilities.getMaxOutputTokens(id),
|
|
34
|
+
modalities: Capabilities.getModalities(id),
|
|
35
|
+
capabilities: Capabilities.getCapabilities(id),
|
|
36
|
+
pricing: Capabilities.getPricing(id),
|
|
37
|
+
created_at: createdAt
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
// Fallback to currently defined static models if API list fails
|
|
43
|
+
return Object.entries(ANTHROPIC_MODELS).filter(([key]) => key !== "other").map(([key, def]) => {
|
|
44
|
+
let id = key.replace(/_/g, "-");
|
|
45
|
+
if (id.includes("claude-3-5"))
|
|
46
|
+
id = "claude-3-5-sonnet-20240620";
|
|
47
|
+
else if (id.includes("claude-3-haiku"))
|
|
48
|
+
id = "claude-3-haiku-20240307";
|
|
49
|
+
return {
|
|
50
|
+
id: id,
|
|
51
|
+
name: id,
|
|
52
|
+
provider: "anthropic",
|
|
53
|
+
family: Capabilities.getFamily(id),
|
|
54
|
+
context_window: def.contextWindow,
|
|
55
|
+
max_output_tokens: def.maxOutputTokens,
|
|
56
|
+
modalities: Capabilities.getModalities(id),
|
|
57
|
+
capabilities: Capabilities.getCapabilities(id),
|
|
58
|
+
pricing: Capabilities.getPricing(id)
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ChatRequest, ChatChunk } from "../Provider.js";
|
|
2
|
+
export declare class AnthropicStreaming {
|
|
3
|
+
private readonly baseUrl;
|
|
4
|
+
private readonly apiKey;
|
|
5
|
+
constructor(baseUrl: string, apiKey: string);
|
|
6
|
+
execute(request: ChatRequest): AsyncGenerator<ChatChunk>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=Streaming.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Streaming.d.ts","sourceRoot":"","sources":["../../../src/providers/anthropic/Streaming.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAMxD,qBAAa,kBAAkB;IACjB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAAU,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAxC,OAAO,EAAE,MAAM,EAAmB,MAAM,EAAE,MAAM;IAEtE,OAAO,CAAC,OAAO,EAAE,WAAW,GAAG,cAAc,CAAC,SAAS,CAAC;CAgHhE"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { Capabilities } from "./Capabilities.js";
|
|
2
|
+
import { handleAnthropicError } from "./Errors.js";
|
|
3
|
+
import { formatSystemPrompt, formatMessages } from "./Utils.js";
|
|
4
|
+
export class AnthropicStreaming {
|
|
5
|
+
baseUrl;
|
|
6
|
+
apiKey;
|
|
7
|
+
constructor(baseUrl, apiKey) {
|
|
8
|
+
this.baseUrl = baseUrl;
|
|
9
|
+
this.apiKey = apiKey;
|
|
10
|
+
}
|
|
11
|
+
async *execute(request) {
|
|
12
|
+
const model = request.model;
|
|
13
|
+
const maxTokens = request.max_tokens || Capabilities.getMaxOutputTokens(model) || 4096;
|
|
14
|
+
const systemPrompt = formatSystemPrompt(request.messages);
|
|
15
|
+
const messages = formatMessages(request.messages);
|
|
16
|
+
const body = {
|
|
17
|
+
model: model,
|
|
18
|
+
messages: messages,
|
|
19
|
+
max_tokens: maxTokens,
|
|
20
|
+
system: systemPrompt,
|
|
21
|
+
stream: true,
|
|
22
|
+
};
|
|
23
|
+
if (request.temperature !== undefined) {
|
|
24
|
+
body.temperature = request.temperature;
|
|
25
|
+
}
|
|
26
|
+
if (request.tools && request.tools.length > 0) {
|
|
27
|
+
body.tools = request.tools.map(tool => ({
|
|
28
|
+
name: tool.function.name,
|
|
29
|
+
description: tool.function.description,
|
|
30
|
+
input_schema: tool.function.parameters
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
// Check if any message contains PDF content to add beta header
|
|
34
|
+
const hasPdf = messages.some(msg => Array.isArray(msg.content) && msg.content.some(block => block.type === "document"));
|
|
35
|
+
const headers = {
|
|
36
|
+
"x-api-key": this.apiKey,
|
|
37
|
+
"anthropic-version": "2023-06-01",
|
|
38
|
+
"content-type": "application/json",
|
|
39
|
+
...request.headers,
|
|
40
|
+
};
|
|
41
|
+
if (hasPdf) {
|
|
42
|
+
headers["anthropic-beta"] = "pdfs-2024-09-25";
|
|
43
|
+
}
|
|
44
|
+
const response = await fetch(`${this.baseUrl}/messages`, {
|
|
45
|
+
method: "POST",
|
|
46
|
+
headers: headers,
|
|
47
|
+
body: JSON.stringify(body),
|
|
48
|
+
});
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
await handleAnthropicError(response, model);
|
|
51
|
+
}
|
|
52
|
+
if (!response.body) {
|
|
53
|
+
throw new Error("No response body for streaming");
|
|
54
|
+
}
|
|
55
|
+
const reader = response.body.getReader();
|
|
56
|
+
const decoder = new TextDecoder();
|
|
57
|
+
let buffer = "";
|
|
58
|
+
while (true) {
|
|
59
|
+
const { value, done } = await reader.read();
|
|
60
|
+
if (done)
|
|
61
|
+
break;
|
|
62
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
63
|
+
buffer += chunk;
|
|
64
|
+
const lines = buffer.split("\n\n");
|
|
65
|
+
buffer = lines.pop() || "";
|
|
66
|
+
for (const line of lines) {
|
|
67
|
+
const trimmed = line.trim();
|
|
68
|
+
if (!trimmed.startsWith("event: "))
|
|
69
|
+
continue;
|
|
70
|
+
// Format is:
|
|
71
|
+
// event: type
|
|
72
|
+
// data: json
|
|
73
|
+
const parts = trimmed.split("\n");
|
|
74
|
+
const eventLine = parts[0];
|
|
75
|
+
const dataLine = parts.find(p => p.startsWith("data: "));
|
|
76
|
+
if (!dataLine || !eventLine)
|
|
77
|
+
continue;
|
|
78
|
+
const eventType = eventLine.replace("event: ", "").trim();
|
|
79
|
+
const dataStr = dataLine.replace("data: ", "").trim();
|
|
80
|
+
try {
|
|
81
|
+
const data = JSON.parse(dataStr);
|
|
82
|
+
// Handle different event types from Anthropic
|
|
83
|
+
if (eventType === "content_block_delta") {
|
|
84
|
+
if (data.delta && data.delta.type === "text_delta") {
|
|
85
|
+
yield { content: data.delta.text };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else if (eventType === "message_start") {
|
|
89
|
+
// Could extract initial usage here
|
|
90
|
+
}
|
|
91
|
+
else if (eventType === "message_delta") {
|
|
92
|
+
// Update usage or stop reason
|
|
93
|
+
}
|
|
94
|
+
else if (eventType === "error") {
|
|
95
|
+
throw new Error(`Stream error: ${data.error?.message}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch (e) {
|
|
99
|
+
// console.warn("Parse error", e);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Message } from "../../chat/Message.js";
|
|
2
|
+
import { AnthropicMessage } from "./types.js";
|
|
3
|
+
export declare function formatSystemPrompt(messages: Message[]): string | undefined;
|
|
4
|
+
export declare function formatMessages(requestMessages: Message[]): AnthropicMessage[];
|
|
5
|
+
//# sourceMappingURL=Utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Utils.d.ts","sourceRoot":"","sources":["../../../src/providers/anthropic/Utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEhD,OAAO,EAAE,gBAAgB,EAAyB,MAAM,YAAY,CAAC;AAErE,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,GAAG,SAAS,CAiB1E;AAED,wBAAgB,cAAc,CAAC,eAAe,EAAE,OAAO,EAAE,GAAG,gBAAgB,EAAE,CAyB7E"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
export function formatSystemPrompt(messages) {
|
|
2
|
+
let systemPrompt;
|
|
3
|
+
for (const msg of messages) {
|
|
4
|
+
if (msg.role === "system") {
|
|
5
|
+
if (typeof msg.content === "string") {
|
|
6
|
+
systemPrompt = msg.content;
|
|
7
|
+
}
|
|
8
|
+
else if (Array.isArray(msg.content)) {
|
|
9
|
+
systemPrompt = msg.content
|
|
10
|
+
.filter((p) => p.type === "text")
|
|
11
|
+
.map(p => p.text)
|
|
12
|
+
.join("\n");
|
|
13
|
+
}
|
|
14
|
+
else if (msg.content) {
|
|
15
|
+
systemPrompt = String(msg.content);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return systemPrompt;
|
|
20
|
+
}
|
|
21
|
+
export function formatMessages(requestMessages) {
|
|
22
|
+
const messages = [];
|
|
23
|
+
for (const msg of requestMessages) {
|
|
24
|
+
if (msg.role === "system")
|
|
25
|
+
continue;
|
|
26
|
+
const formatted = formatSingleMessage(msg);
|
|
27
|
+
const lastMsg = messages[messages.length - 1];
|
|
28
|
+
if (lastMsg && lastMsg.role === formatted.role) {
|
|
29
|
+
// Merge content
|
|
30
|
+
let existingContent = Array.isArray(lastMsg.content)
|
|
31
|
+
? lastMsg.content
|
|
32
|
+
: [{ type: "text", text: lastMsg.content }];
|
|
33
|
+
const newContent = Array.isArray(formatted.content)
|
|
34
|
+
? formatted.content
|
|
35
|
+
: [{ type: "text", text: formatted.content }];
|
|
36
|
+
lastMsg.content = [...existingContent, ...newContent];
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
messages.push(formatted);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return messages;
|
|
43
|
+
}
|
|
44
|
+
function formatSingleMessage(msg) {
|
|
45
|
+
const role = msg.role === "user" ? "user" : "assistant";
|
|
46
|
+
// Handle Tool Responses (role: "tool")
|
|
47
|
+
if (msg.role === "tool") {
|
|
48
|
+
return {
|
|
49
|
+
role: "user",
|
|
50
|
+
content: [{
|
|
51
|
+
type: "tool_result",
|
|
52
|
+
tool_use_id: msg.tool_call_id,
|
|
53
|
+
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
|
|
54
|
+
}]
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
// Handle Assistant Messages (which might have tool_calls)
|
|
58
|
+
if (msg.role === "assistant" && msg.tool_calls) {
|
|
59
|
+
const blocks = [];
|
|
60
|
+
// Assistant text content
|
|
61
|
+
const text = String(msg.content || "");
|
|
62
|
+
if (text.length > 0) {
|
|
63
|
+
blocks.push({ type: "text", text });
|
|
64
|
+
}
|
|
65
|
+
// Assistant tool uses
|
|
66
|
+
for (const toolCall of msg.tool_calls) {
|
|
67
|
+
blocks.push({
|
|
68
|
+
type: "tool_use",
|
|
69
|
+
id: toolCall.id,
|
|
70
|
+
name: toolCall.function.name,
|
|
71
|
+
input: JSON.parse(toolCall.function.arguments)
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return { role: "assistant", content: blocks };
|
|
75
|
+
}
|
|
76
|
+
const contentText = String(msg.content || "");
|
|
77
|
+
if (contentText && typeof msg.content === "string") {
|
|
78
|
+
return { role, content: contentText };
|
|
79
|
+
}
|
|
80
|
+
if (contentText && msg.content instanceof String) {
|
|
81
|
+
return { role, content: contentText };
|
|
82
|
+
}
|
|
83
|
+
// Handle multimodal content (images)
|
|
84
|
+
const blocks = [];
|
|
85
|
+
if (Array.isArray(msg.content)) {
|
|
86
|
+
for (const part of msg.content) {
|
|
87
|
+
if (part.type === "text") {
|
|
88
|
+
blocks.push({ type: "text", text: part.text });
|
|
89
|
+
}
|
|
90
|
+
else if (part.type === "image_url") {
|
|
91
|
+
const url = part.image_url.url;
|
|
92
|
+
if (url.startsWith("data:")) {
|
|
93
|
+
const parts = url.split(",");
|
|
94
|
+
const meta = parts[0];
|
|
95
|
+
const data = parts.slice(1).join(",");
|
|
96
|
+
if (meta && data) {
|
|
97
|
+
const mimeMatch = meta.match(/:(.*?);/);
|
|
98
|
+
const mediaType = mimeMatch ? mimeMatch[1] : "image/jpeg";
|
|
99
|
+
if (mediaType === "application/pdf") {
|
|
100
|
+
blocks.push({
|
|
101
|
+
type: "document",
|
|
102
|
+
source: {
|
|
103
|
+
type: "base64",
|
|
104
|
+
media_type: "application/pdf",
|
|
105
|
+
data: data
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
blocks.push({
|
|
111
|
+
type: "image",
|
|
112
|
+
source: {
|
|
113
|
+
type: "base64",
|
|
114
|
+
media_type: mediaType || "image/jpeg",
|
|
115
|
+
data: data
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return { role, content: blocks };
|
|
125
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/anthropic/index.ts"],"names":[],"mappings":"AAGA,wBAAgB,yBAAyB,SAQxC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { providerRegistry } from "../registry.js";
|
|
2
|
+
import { AnthropicProvider } from "./AnthropicProvider.js";
|
|
3
|
+
export function registerAnthropicProvider() {
|
|
4
|
+
providerRegistry.register("anthropic", () => {
|
|
5
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
6
|
+
if (!apiKey) {
|
|
7
|
+
throw new Error("ANTHROPIC_API_KEY environment variable is not set");
|
|
8
|
+
}
|
|
9
|
+
return new AnthropicProvider({ apiKey: apiKey.trim() });
|
|
10
|
+
});
|
|
11
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export interface AnthropicMessage {
|
|
2
|
+
role: "user" | "assistant";
|
|
3
|
+
content: string | Array<AnthropicContentBlock>;
|
|
4
|
+
}
|
|
5
|
+
export interface AnthropicContentBlock {
|
|
6
|
+
type: "text" | "image" | "tool_use" | "tool_result" | "document";
|
|
7
|
+
text?: string;
|
|
8
|
+
source?: {
|
|
9
|
+
type: "base64";
|
|
10
|
+
media_type: string;
|
|
11
|
+
data: string;
|
|
12
|
+
};
|
|
13
|
+
id?: string;
|
|
14
|
+
name?: string;
|
|
15
|
+
input?: any;
|
|
16
|
+
tool_use_id?: string;
|
|
17
|
+
content?: string | Array<AnthropicContentBlock>;
|
|
18
|
+
is_error?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface AnthropicMessageRequest {
|
|
21
|
+
model: string;
|
|
22
|
+
messages: AnthropicMessage[];
|
|
23
|
+
max_tokens: number;
|
|
24
|
+
system?: string;
|
|
25
|
+
metadata?: any;
|
|
26
|
+
stop_sequences?: string[];
|
|
27
|
+
stream?: boolean;
|
|
28
|
+
temperature?: number;
|
|
29
|
+
top_p?: number;
|
|
30
|
+
top_k?: number;
|
|
31
|
+
tools?: any[];
|
|
32
|
+
tool_choice?: any;
|
|
33
|
+
}
|
|
34
|
+
export interface AnthropicUsage {
|
|
35
|
+
input_tokens: number;
|
|
36
|
+
output_tokens: number;
|
|
37
|
+
cache_creation_input_tokens?: number;
|
|
38
|
+
cache_read_input_tokens?: number;
|
|
39
|
+
}
|
|
40
|
+
export interface AnthropicMessageResponse {
|
|
41
|
+
id: string;
|
|
42
|
+
type: "message";
|
|
43
|
+
role: "assistant";
|
|
44
|
+
content: AnthropicContentBlock[];
|
|
45
|
+
model: string;
|
|
46
|
+
stop_reason: "end_turn" | "max_tokens" | "stop_sequence" | "tool_use" | null;
|
|
47
|
+
stop_sequence: string | null;
|
|
48
|
+
usage: AnthropicUsage;
|
|
49
|
+
}
|
|
50
|
+
export interface AnthropicErrorResponse {
|
|
51
|
+
type: "error";
|
|
52
|
+
error: {
|
|
53
|
+
type: string;
|
|
54
|
+
message: string;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/providers/anthropic/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,KAAK,CAAC,qBAAqB,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,UAAU,GAAG,aAAa,GAAG,UAAU,CAAC;IACjE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE;QACP,IAAI,EAAE,QAAQ,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAChD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC;IACd,WAAW,CAAC,EAAE,GAAG,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,qBAAqB,EAAE,CAAC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,UAAU,GAAG,YAAY,GAAG,eAAe,GAAG,UAAU,GAAG,IAAI,CAAC;IAC7E,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,cAAc,CAAC;CACvB;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Capabilities.d.ts","sourceRoot":"","sources":["../../../src/providers/gemini/Capabilities.ts"],"names":[],"mappings":"AAAA,qBAAa,YAAY;IACvB,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAoBvD,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAiBzD,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAQ/C,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAQ9C,MAAM,CAAC,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;
|
|
1
|
+
{"version":3,"file":"Capabilities.d.ts","sourceRoot":"","sources":["../../../src/providers/gemini/Capabilities.ts"],"names":[],"mappings":"AAAA,qBAAa,YAAY;IACvB,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAoBvD,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAiBzD,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAQ/C,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAQ9C,MAAM,CAAC,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAQzD,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAIjD,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAKnD,MAAM,CAAC,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAKxD,MAAM,CAAC,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAKtD,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAInD,MAAM,CAAC,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI;IAIzG,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAWzC,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE;IAY5E,MAAM,CAAC,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;IAUjD,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM;;;;;;;;IA0BjC,MAAM,CAAC,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAIjD,OAAO,CAAC,MAAM,CAAC,gBAAgB;CAGhC"}
|
|
@@ -49,8 +49,11 @@ export class Capabilities {
|
|
|
49
49
|
return !!id.match(/gemini|pro|flash/);
|
|
50
50
|
}
|
|
51
51
|
static supportsStructuredOutput(modelId) {
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
const id = this.normalizeModelId(modelId);
|
|
53
|
+
if (id.match(/text-embedding|embedding-001|aqa|imagen/)) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
return true;
|
|
54
57
|
}
|
|
55
58
|
static supportsJsonMode(modelId) {
|
|
56
59
|
return this.supportsStructuredOutput(modelId);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/providers/gemini/Chat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAM3D,qBAAa,UAAU;IACT,OAAO,CAAC,QAAQ,CAAC,OAAO;IAAU,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAxC,OAAO,EAAE,MAAM,EAAmB,MAAM,EAAE,MAAM;IAEvE,OAAO,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/providers/gemini/Chat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAM3D,qBAAa,UAAU;IACT,OAAO,CAAC,QAAQ,CAAC,OAAO;IAAU,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAxC,OAAO,EAAE,MAAM,EAAmB,MAAM,EAAE,MAAM;IAEvE,OAAO,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IA0F1D,OAAO,CAAC,cAAc;CAwBvB"}
|
|
@@ -12,12 +12,31 @@ export class GeminiChat {
|
|
|
12
12
|
const temperature = Capabilities.normalizeTemperature(request.temperature, request.model);
|
|
13
13
|
const url = `${this.baseUrl}/models/${request.model}:generateContent?key=${this.apiKey}`;
|
|
14
14
|
const { contents, systemInstructionParts } = await GeminiChatUtils.convertMessages(request.messages);
|
|
15
|
+
const generationConfig = {
|
|
16
|
+
temperature: temperature ?? undefined,
|
|
17
|
+
maxOutputTokens: request.max_tokens,
|
|
18
|
+
};
|
|
19
|
+
if (request.response_format?.type === "json_object") {
|
|
20
|
+
generationConfig.responseMimeType = "application/json";
|
|
21
|
+
}
|
|
22
|
+
else if (request.response_format?.type === "json_schema") {
|
|
23
|
+
generationConfig.responseMimeType = "application/json";
|
|
24
|
+
if (request.response_format.json_schema?.schema) {
|
|
25
|
+
generationConfig.responseSchema = request.response_format.json_schema.schema;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (request.response_format?.type === "json_object") {
|
|
29
|
+
generationConfig.responseMimeType = "application/json";
|
|
30
|
+
}
|
|
31
|
+
else if (request.response_format?.type === "json_schema") {
|
|
32
|
+
generationConfig.responseMimeType = "application/json";
|
|
33
|
+
if (request.response_format.json_schema?.schema) {
|
|
34
|
+
generationConfig.responseSchema = this.sanitizeSchema(request.response_format.json_schema.schema);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
15
37
|
const payload = {
|
|
16
38
|
contents,
|
|
17
|
-
generationConfig
|
|
18
|
-
temperature: temperature ?? undefined,
|
|
19
|
-
maxOutputTokens: request.max_tokens,
|
|
20
|
-
},
|
|
39
|
+
generationConfig,
|
|
21
40
|
};
|
|
22
41
|
if (systemInstructionParts.length > 0) {
|
|
23
42
|
payload.systemInstruction = { parts: systemInstructionParts };
|
|
@@ -66,4 +85,24 @@ export class GeminiChat {
|
|
|
66
85
|
} : undefined;
|
|
67
86
|
return { content, tool_calls, usage };
|
|
68
87
|
}
|
|
88
|
+
sanitizeSchema(schema) {
|
|
89
|
+
if (typeof schema !== "object" || schema === null)
|
|
90
|
+
return schema;
|
|
91
|
+
const sanitized = { ...schema };
|
|
92
|
+
// Remove unsupported fields
|
|
93
|
+
delete sanitized.additionalProperties;
|
|
94
|
+
delete sanitized.$schema;
|
|
95
|
+
delete sanitized.$id;
|
|
96
|
+
delete sanitized.definitions;
|
|
97
|
+
// Recursively sanitize
|
|
98
|
+
if (sanitized.properties) {
|
|
99
|
+
for (const key in sanitized.properties) {
|
|
100
|
+
sanitized.properties[key] = this.sanitizeSchema(sanitized.properties[key]);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (sanitized.items) {
|
|
104
|
+
sanitized.items = this.sanitizeSchema(sanitized.items);
|
|
105
|
+
}
|
|
106
|
+
return sanitized;
|
|
107
|
+
}
|
|
69
108
|
}
|