@agentier/anthropic 0.1.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/index.d.ts +7 -0
- package/dist/index.js +250 -0
- package/dist/mapper.d.ts +66 -0
- package/dist/provider.d.ts +24 -0
- package/dist/types.d.ts +23 -0
- package/package.json +29 -0
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
// src/mapper.ts
|
|
2
|
+
function toAnthropicMessages(messages) {
|
|
3
|
+
let system;
|
|
4
|
+
const result = [];
|
|
5
|
+
for (const msg of messages) {
|
|
6
|
+
if (msg.role === "system") {
|
|
7
|
+
system = msg.content ?? undefined;
|
|
8
|
+
continue;
|
|
9
|
+
}
|
|
10
|
+
if (msg.role === "user") {
|
|
11
|
+
result.push({ role: "user", content: msg.content ?? "" });
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
if (msg.role === "assistant") {
|
|
15
|
+
const content = [];
|
|
16
|
+
if (msg.content) {
|
|
17
|
+
content.push({ type: "text", text: msg.content });
|
|
18
|
+
}
|
|
19
|
+
if (msg.toolCalls) {
|
|
20
|
+
for (const tc of msg.toolCalls) {
|
|
21
|
+
content.push({
|
|
22
|
+
type: "tool_use",
|
|
23
|
+
id: tc.id,
|
|
24
|
+
name: tc.name,
|
|
25
|
+
input: tc.arguments
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
result.push({
|
|
30
|
+
role: "assistant",
|
|
31
|
+
content: content.length === 1 && content[0].type === "text" ? content[0].text : content
|
|
32
|
+
});
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (msg.role === "tool") {
|
|
36
|
+
const lastMsg = result[result.length - 1];
|
|
37
|
+
const toolResultBlock = {
|
|
38
|
+
type: "tool_result",
|
|
39
|
+
tool_use_id: msg.toolCallId ?? "",
|
|
40
|
+
content: msg.content ?? ""
|
|
41
|
+
};
|
|
42
|
+
if (lastMsg && lastMsg.role === "user" && Array.isArray(lastMsg.content)) {
|
|
43
|
+
lastMsg.content.push(toolResultBlock);
|
|
44
|
+
} else {
|
|
45
|
+
result.push({ role: "user", content: [toolResultBlock] });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return { system, messages: result };
|
|
50
|
+
}
|
|
51
|
+
function toAnthropicTools(tools) {
|
|
52
|
+
return tools.map((t) => ({
|
|
53
|
+
name: t.name,
|
|
54
|
+
description: t.description,
|
|
55
|
+
input_schema: t.parameters
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
function fromAnthropicResponse(content) {
|
|
59
|
+
let text = "";
|
|
60
|
+
const toolCalls = [];
|
|
61
|
+
for (const block of content) {
|
|
62
|
+
if (block.type === "text") {
|
|
63
|
+
text += block.text;
|
|
64
|
+
} else if (block.type === "tool_use") {
|
|
65
|
+
toolCalls.push({
|
|
66
|
+
id: block.id,
|
|
67
|
+
name: block.name,
|
|
68
|
+
arguments: block.input
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
text: text || null,
|
|
74
|
+
toolCalls
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/provider.ts
|
|
79
|
+
function anthropic(config) {
|
|
80
|
+
const baseUrl = (config.baseUrl ?? "https://api.anthropic.com").replace(/\/$/, "");
|
|
81
|
+
const apiVersion = config.apiVersion ?? "2023-06-01";
|
|
82
|
+
const fetchFn = config.fetch ?? globalThis.fetch;
|
|
83
|
+
function buildHeaders() {
|
|
84
|
+
return {
|
|
85
|
+
"Content-Type": "application/json",
|
|
86
|
+
"x-api-key": config.apiKey,
|
|
87
|
+
"anthropic-version": apiVersion,
|
|
88
|
+
...config.defaultHeaders
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function buildBody(params, stream = false) {
|
|
92
|
+
const { system, messages } = toAnthropicMessages(params.messages);
|
|
93
|
+
const body = {
|
|
94
|
+
model: params.model,
|
|
95
|
+
messages,
|
|
96
|
+
max_tokens: params.maxOutputTokens ?? 4096,
|
|
97
|
+
stream
|
|
98
|
+
};
|
|
99
|
+
if (system)
|
|
100
|
+
body.system = system;
|
|
101
|
+
if (params.tools && params.tools.length > 0) {
|
|
102
|
+
body.tools = toAnthropicTools(params.tools);
|
|
103
|
+
}
|
|
104
|
+
if (params.temperature !== undefined)
|
|
105
|
+
body.temperature = params.temperature;
|
|
106
|
+
if (params.topP !== undefined)
|
|
107
|
+
body.top_p = params.topP;
|
|
108
|
+
return body;
|
|
109
|
+
}
|
|
110
|
+
const provider = {
|
|
111
|
+
name: "anthropic",
|
|
112
|
+
async chat(params) {
|
|
113
|
+
const response = await fetchFn(`${baseUrl}/v1/messages`, {
|
|
114
|
+
method: "POST",
|
|
115
|
+
headers: buildHeaders(),
|
|
116
|
+
body: JSON.stringify(buildBody(params, false)),
|
|
117
|
+
signal: params.signal
|
|
118
|
+
});
|
|
119
|
+
if (!response.ok) {
|
|
120
|
+
const errorBody = await response.text();
|
|
121
|
+
throw new Error(`Anthropic API error (${response.status}): ${errorBody}`);
|
|
122
|
+
}
|
|
123
|
+
const data = await response.json();
|
|
124
|
+
const { text, toolCalls } = fromAnthropicResponse(data.content);
|
|
125
|
+
return {
|
|
126
|
+
content: text,
|
|
127
|
+
toolCalls,
|
|
128
|
+
usage: {
|
|
129
|
+
inputTokens: data.usage?.input_tokens ?? 0,
|
|
130
|
+
outputTokens: data.usage?.output_tokens ?? 0
|
|
131
|
+
},
|
|
132
|
+
raw: data
|
|
133
|
+
};
|
|
134
|
+
},
|
|
135
|
+
async* stream(params) {
|
|
136
|
+
const response = await fetchFn(`${baseUrl}/v1/messages`, {
|
|
137
|
+
method: "POST",
|
|
138
|
+
headers: buildHeaders(),
|
|
139
|
+
body: JSON.stringify(buildBody(params, true)),
|
|
140
|
+
signal: params.signal
|
|
141
|
+
});
|
|
142
|
+
if (!response.ok) {
|
|
143
|
+
const errorBody = await response.text();
|
|
144
|
+
throw new Error(`Anthropic API error (${response.status}): ${errorBody}`);
|
|
145
|
+
}
|
|
146
|
+
const reader = response.body?.getReader();
|
|
147
|
+
if (!reader)
|
|
148
|
+
throw new Error("No response body");
|
|
149
|
+
const decoder = new TextDecoder;
|
|
150
|
+
let buffer = "";
|
|
151
|
+
let fullContent = "";
|
|
152
|
+
const toolCalls = [];
|
|
153
|
+
let currentToolUse = null;
|
|
154
|
+
let inputTokens = 0;
|
|
155
|
+
let outputTokens = 0;
|
|
156
|
+
try {
|
|
157
|
+
while (true) {
|
|
158
|
+
const { done, value } = await reader.read();
|
|
159
|
+
if (done)
|
|
160
|
+
break;
|
|
161
|
+
buffer += decoder.decode(value, { stream: true });
|
|
162
|
+
const lines = buffer.split(`
|
|
163
|
+
`);
|
|
164
|
+
buffer = lines.pop() ?? "";
|
|
165
|
+
for (const line of lines) {
|
|
166
|
+
const trimmed = line.trim();
|
|
167
|
+
if (!trimmed.startsWith("data: "))
|
|
168
|
+
continue;
|
|
169
|
+
const data = trimmed.slice(6);
|
|
170
|
+
try {
|
|
171
|
+
const event = JSON.parse(data);
|
|
172
|
+
switch (event.type) {
|
|
173
|
+
case "content_block_start": {
|
|
174
|
+
const block = event.content_block;
|
|
175
|
+
if (block.type === "tool_use") {
|
|
176
|
+
currentToolUse = {
|
|
177
|
+
id: block.id,
|
|
178
|
+
name: block.name,
|
|
179
|
+
inputJson: ""
|
|
180
|
+
};
|
|
181
|
+
yield {
|
|
182
|
+
type: "tool_call_start",
|
|
183
|
+
id: block.id,
|
|
184
|
+
name: block.name
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
case "content_block_delta": {
|
|
190
|
+
const delta = event.delta;
|
|
191
|
+
if (delta.type === "text_delta") {
|
|
192
|
+
fullContent += delta.text;
|
|
193
|
+
yield { type: "token", text: delta.text };
|
|
194
|
+
} else if (delta.type === "input_json_delta" && currentToolUse) {
|
|
195
|
+
currentToolUse.inputJson += delta.partial_json;
|
|
196
|
+
yield {
|
|
197
|
+
type: "tool_call_delta",
|
|
198
|
+
id: currentToolUse.id,
|
|
199
|
+
argumentsDelta: delta.partial_json
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
case "content_block_stop": {
|
|
205
|
+
if (currentToolUse) {
|
|
206
|
+
const call = {
|
|
207
|
+
id: currentToolUse.id,
|
|
208
|
+
name: currentToolUse.name,
|
|
209
|
+
arguments: currentToolUse.inputJson ? JSON.parse(currentToolUse.inputJson) : {}
|
|
210
|
+
};
|
|
211
|
+
toolCalls.push(call);
|
|
212
|
+
yield { type: "tool_call_end", id: currentToolUse.id, call };
|
|
213
|
+
currentToolUse = null;
|
|
214
|
+
}
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
case "message_delta": {
|
|
218
|
+
if (event.usage) {
|
|
219
|
+
outputTokens = event.usage.output_tokens ?? outputTokens;
|
|
220
|
+
}
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
case "message_start": {
|
|
224
|
+
if (event.message?.usage) {
|
|
225
|
+
inputTokens = event.message.usage.input_tokens ?? 0;
|
|
226
|
+
}
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
} catch {}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
} finally {
|
|
234
|
+
reader.releaseLock();
|
|
235
|
+
}
|
|
236
|
+
yield {
|
|
237
|
+
type: "done",
|
|
238
|
+
response: {
|
|
239
|
+
content: fullContent || null,
|
|
240
|
+
toolCalls,
|
|
241
|
+
usage: { inputTokens, outputTokens }
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
return provider;
|
|
247
|
+
}
|
|
248
|
+
export {
|
|
249
|
+
anthropic
|
|
250
|
+
};
|
package/dist/mapper.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Message, ToolCall, ToolJsonSchema } from '@agentier/core';
|
|
2
|
+
/** Valid roles in the Anthropic Messages API (system is handled separately). */
|
|
3
|
+
export type AnthropicRole = 'user' | 'assistant';
|
|
4
|
+
/** A plain text content block. */
|
|
5
|
+
export interface AnthropicTextBlock {
|
|
6
|
+
type: 'text';
|
|
7
|
+
text: string;
|
|
8
|
+
}
|
|
9
|
+
/** A tool-use content block representing an assistant's request to invoke a tool. */
|
|
10
|
+
export interface AnthropicToolUseBlock {
|
|
11
|
+
type: 'tool_use';
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
input: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
/** A tool-result content block carrying the output of a previously invoked tool. */
|
|
17
|
+
export interface AnthropicToolResultBlock {
|
|
18
|
+
type: 'tool_result';
|
|
19
|
+
tool_use_id: string;
|
|
20
|
+
content: string;
|
|
21
|
+
}
|
|
22
|
+
/** Union of all content block types used in Anthropic messages. */
|
|
23
|
+
export type AnthropicContentBlock = AnthropicTextBlock | AnthropicToolUseBlock | AnthropicToolResultBlock;
|
|
24
|
+
/** Wire format for an Anthropic message (role + mixed content blocks). */
|
|
25
|
+
export interface AnthropicMessage {
|
|
26
|
+
role: AnthropicRole;
|
|
27
|
+
content: string | AnthropicContentBlock[];
|
|
28
|
+
}
|
|
29
|
+
/** Wire format for an Anthropic tool definition. */
|
|
30
|
+
export interface AnthropicTool {
|
|
31
|
+
name: string;
|
|
32
|
+
description: string;
|
|
33
|
+
input_schema: Record<string, unknown>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Converts agenti `Message` objects into the Anthropic Messages API format.
|
|
37
|
+
*
|
|
38
|
+
* System messages are extracted into a separate `system` string (Anthropic
|
|
39
|
+
* treats system prompts as a top-level parameter, not a message). Tool result
|
|
40
|
+
* messages are folded into the preceding `user` turn when possible, matching
|
|
41
|
+
* the Anthropic expectation that tool results appear inside user messages.
|
|
42
|
+
*
|
|
43
|
+
* @param messages - Array of provider-agnostic messages.
|
|
44
|
+
* @returns An object with an optional `system` prompt and an array of Anthropic messages.
|
|
45
|
+
*/
|
|
46
|
+
export declare function toAnthropicMessages(messages: Message[]): {
|
|
47
|
+
system?: string;
|
|
48
|
+
messages: AnthropicMessage[];
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Converts agenti tool schemas into the Anthropic tool definition format.
|
|
52
|
+
*
|
|
53
|
+
* @param tools - Array of provider-agnostic tool JSON schemas.
|
|
54
|
+
* @returns Array of Anthropic tool definitions with `input_schema`.
|
|
55
|
+
*/
|
|
56
|
+
export declare function toAnthropicTools(tools: ToolJsonSchema[]): AnthropicTool[];
|
|
57
|
+
/**
|
|
58
|
+
* Extracts text and tool calls from Anthropic response content blocks.
|
|
59
|
+
*
|
|
60
|
+
* @param content - Array of content blocks from the Anthropic API response.
|
|
61
|
+
* @returns Parsed text (or `null` if empty) and an array of agenti `ToolCall` objects.
|
|
62
|
+
*/
|
|
63
|
+
export declare function fromAnthropicResponse(content: AnthropicContentBlock[]): {
|
|
64
|
+
text: string | null;
|
|
65
|
+
toolCalls: ToolCall[];
|
|
66
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ModelProvider } from '@agentier/core';
|
|
2
|
+
import type { AnthropicProviderConfig } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Creates an Anthropic {@link ModelProvider}.
|
|
5
|
+
*
|
|
6
|
+
* Supports both blocking `chat` and streaming `stream` completions via the
|
|
7
|
+
* Anthropic Messages API (`/v1/messages`). Handles the Anthropic-specific
|
|
8
|
+
* SSE event protocol (`content_block_start`, `content_block_delta`, etc.).
|
|
9
|
+
*
|
|
10
|
+
* @param config - Provider configuration including API key and optional overrides.
|
|
11
|
+
* @returns A `ModelProvider` wired to the Anthropic Messages API.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { anthropic } from '@agentier/provider-anthropic'
|
|
16
|
+
*
|
|
17
|
+
* const provider = anthropic({ apiKey: process.env.ANTHROPIC_API_KEY! })
|
|
18
|
+
* const response = await provider.chat({
|
|
19
|
+
* model: 'claude-sonnet-4-20250514',
|
|
20
|
+
* messages: [{ role: 'user', content: 'Hello!' }],
|
|
21
|
+
* })
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare function anthropic(config: AnthropicProviderConfig): ModelProvider;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for the Anthropic provider.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* const config: AnthropicProviderConfig = {
|
|
7
|
+
* apiKey: process.env.ANTHROPIC_API_KEY!,
|
|
8
|
+
* apiVersion: '2023-06-01',
|
|
9
|
+
* }
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export interface AnthropicProviderConfig {
|
|
13
|
+
/** Anthropic API key sent via the `x-api-key` header. */
|
|
14
|
+
apiKey: string;
|
|
15
|
+
/** Base URL for API requests. Defaults to `https://api.anthropic.com`. */
|
|
16
|
+
baseUrl?: string;
|
|
17
|
+
/** Anthropic API version sent via the `anthropic-version` header. Defaults to `'2023-06-01'`. */
|
|
18
|
+
apiVersion?: string;
|
|
19
|
+
/** Additional headers merged into every request. */
|
|
20
|
+
defaultHeaders?: Record<string, string>;
|
|
21
|
+
/** Custom `fetch` implementation, useful for proxies or test doubles. */
|
|
22
|
+
fetch?: typeof globalThis.fetch;
|
|
23
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agentier/anthropic",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "bun build ./src/index.ts --outdir ./dist --target node",
|
|
18
|
+
"test": "bun test",
|
|
19
|
+
"typecheck": "tsc --noEmit"
|
|
20
|
+
},
|
|
21
|
+
"peerDependencies": {
|
|
22
|
+
"@agentier/core": "^0.1.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@agentier/core": "workspace:*",
|
|
26
|
+
"typescript": "^5.5.0",
|
|
27
|
+
"@types/bun": "latest"
|
|
28
|
+
}
|
|
29
|
+
}
|