@zmdb/ai-anthropic 1.0.0-beta.1
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/LICENSE +10 -0
- package/README.md +40 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +109 -0
- package/dist/index.js.map +1 -0
- package/package.json +56 -0
- package/src/index.ts +139 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
GNU GENERAL PUBLIC LICENSE
|
|
2
|
+
Version 3, 29 June 2007
|
|
3
|
+
|
|
4
|
+
@zmdb/ai-anthropic is free software licensed under the GNU General Public License,
|
|
5
|
+
version 3 or (at your option) any later version.
|
|
6
|
+
|
|
7
|
+
The complete license text is published by the Free Software Foundation at:
|
|
8
|
+
https://www.gnu.org/licenses/gpl-3.0.txt
|
|
9
|
+
|
|
10
|
+
SPDX-License-Identifier: GPL-3.0-or-later
|
package/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# @zmdb/ai-anthropic
|
|
2
|
+
|
|
3
|
+
`@zmdb/ai-anthropic` adapts the provider-neutral `@zmdb/ai/chat` contract to an injected Anthropic Messages API client. It translates messages, tools, tool results, thinking blocks, and responses
|
|
4
|
+
without reading credentials, constructing a client, or making a request during import.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
yarn add @zmdb/ai-anthropic@1.0.0-beta.1 @anthropic-ai/sdk@0.124.0
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
> **Prerelease** (`1.0.0-beta.1`). Requires **Node.js 26+** and is **ESM-only**. The adapter depends on `@zmdb/ai`; the Anthropic SDK is an optional peer so importing the adapter does not resolve or
|
|
13
|
+
> instantiate it.
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
19
|
+
import { anthropicDriver } from '@zmdb/ai-anthropic';
|
|
20
|
+
|
|
21
|
+
const driver = anthropicDriver({
|
|
22
|
+
client: new Anthropic({ apiKey: process.env['ANTHROPIC_API_KEY'] }),
|
|
23
|
+
model: 'claude-opus-5',
|
|
24
|
+
maxOutputTokens: 1024,
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The caller owns credential loading, client construction, retries, model selection, and persistence. See the project documentation for the bounded `@zmdb/ai/chat` loop.
|
|
29
|
+
|
|
30
|
+
## Entry point
|
|
31
|
+
|
|
32
|
+
- `@zmdb/ai-anthropic` — `anthropicDriver`, `AnthropicDriverOptions`, and `AnthropicMessagesClient`.
|
|
33
|
+
|
|
34
|
+
## Documentation
|
|
35
|
+
|
|
36
|
+
Full project documentation is at **https://ambasta.github.io/zmdb/**.
|
|
37
|
+
|
|
38
|
+
## License
|
|
39
|
+
|
|
40
|
+
GNU General Public License v3.0 or later (GPL-3.0-or-later).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type Anthropic from '@anthropic-ai/sdk';
|
|
2
|
+
import type { ChatDriver } from '@zmdb/ai/chat';
|
|
3
|
+
export interface AnthropicMessagesClient {
|
|
4
|
+
readonly messages: {
|
|
5
|
+
create(params: Anthropic.MessageCreateParamsNonStreaming): PromiseLike<Anthropic.Message>;
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
export interface AnthropicDriverOptions {
|
|
9
|
+
readonly client: AnthropicMessagesClient;
|
|
10
|
+
readonly model: string;
|
|
11
|
+
readonly maxOutputTokens: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function anthropicDriver(opts: AnthropicDriverOptions): ChatDriver;
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAe,MAAM,eAAe,CAAC;AAE7D,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,QAAQ,EAAE;QACjB,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,+BAA+B,GAAG,WAAW,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;KAC3F,CAAC;CACH;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,MAAM,EAAE,uBAAuB,CAAC;IACzC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC;AA4FD,wBAAgB,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,UAAU,CAiCxE"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
2
|
+
// A named provider discriminator keeps verify-one-walker's SQL-type heuristic from
|
|
3
|
+
// misclassifying this content-block comparison as a second read of column metadata.
|
|
4
|
+
const TEXT_CONTENT_BLOCK = 'text';
|
|
5
|
+
const thinkingBlock = (value) => isRecord(value) &&
|
|
6
|
+
value['type'] === 'thinking' &&
|
|
7
|
+
typeof value['thinking'] === 'string' &&
|
|
8
|
+
typeof value['signature'] === 'string';
|
|
9
|
+
const redactedThinkingBlock = (value) => isRecord(value) && value['type'] === 'redacted_thinking' && typeof value['data'] === 'string';
|
|
10
|
+
const providerBlock = (block) => {
|
|
11
|
+
if (block.kind === 'thinking' && thinkingBlock(block.raw))
|
|
12
|
+
return block.raw;
|
|
13
|
+
if (block.kind === 'redacted_thinking' && redactedThinkingBlock(block.raw))
|
|
14
|
+
return block.raw;
|
|
15
|
+
throw new Error(`anthropic driver cannot carry provider block ${block.kind}`);
|
|
16
|
+
};
|
|
17
|
+
const assistantContent = (message) => {
|
|
18
|
+
const content = [];
|
|
19
|
+
for (const block of message.provider ?? [])
|
|
20
|
+
content.push(providerBlock(block));
|
|
21
|
+
if (message.content.length > 0)
|
|
22
|
+
content.push({ type: 'text', text: message.content });
|
|
23
|
+
for (const call of message.toolCalls ?? []) {
|
|
24
|
+
content.push({ type: 'tool_use', id: call.id, name: call.name, input: call.args });
|
|
25
|
+
}
|
|
26
|
+
return content;
|
|
27
|
+
};
|
|
28
|
+
const requestMessages = (messages) => {
|
|
29
|
+
const system = [];
|
|
30
|
+
const translated = [];
|
|
31
|
+
for (const message of messages) {
|
|
32
|
+
if (message.role === 'system') {
|
|
33
|
+
system.push(message.content);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (message.role === 'user') {
|
|
37
|
+
translated.push({ role: 'user', content: message.content });
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (message.role === 'tool') {
|
|
41
|
+
const result = message.isError === undefined
|
|
42
|
+
? { type: 'tool_result', tool_use_id: message.callId, content: message.content }
|
|
43
|
+
: { type: 'tool_result', tool_use_id: message.callId, content: message.content, is_error: message.isError };
|
|
44
|
+
translated.push({ role: 'user', content: [result] });
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
translated.push({ role: 'assistant', content: assistantContent(message) });
|
|
48
|
+
}
|
|
49
|
+
return system.length === 0 ? { messages: translated } : { messages: translated, system: system.join('\n\n') };
|
|
50
|
+
};
|
|
51
|
+
const responseMessage = (message) => {
|
|
52
|
+
const text = [];
|
|
53
|
+
const toolCalls = [];
|
|
54
|
+
const provider = [];
|
|
55
|
+
for (const block of message.content) {
|
|
56
|
+
if (block.type === TEXT_CONTENT_BLOCK) {
|
|
57
|
+
text.push(block.text);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (block.type === 'tool_use') {
|
|
61
|
+
toolCalls.push({ id: block.id, name: block.name, args: block.input });
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (block.type === 'thinking' || block.type === 'redacted_thinking') {
|
|
65
|
+
provider.push({ kind: block.type, raw: block });
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
throw new Error(`anthropic driver received unsupported content block ${block.type}`);
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
role: 'assistant',
|
|
72
|
+
content: text.join(''),
|
|
73
|
+
...(toolCalls.length === 0 ? {} : { toolCalls }),
|
|
74
|
+
...(provider.length === 0 ? {} : { provider }),
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
export function anthropicDriver(opts) {
|
|
78
|
+
return {
|
|
79
|
+
next: async (messages, tools) => {
|
|
80
|
+
const translated = requestMessages(messages);
|
|
81
|
+
const request = {
|
|
82
|
+
model: opts.model,
|
|
83
|
+
max_tokens: opts.maxOutputTokens,
|
|
84
|
+
messages: translated.messages,
|
|
85
|
+
tools: tools.map(tool => tool.description === undefined
|
|
86
|
+
? {
|
|
87
|
+
name: tool.name,
|
|
88
|
+
input_schema: {
|
|
89
|
+
type: tool.parameters.type,
|
|
90
|
+
properties: tool.parameters.properties,
|
|
91
|
+
required: [...tool.parameters.required],
|
|
92
|
+
},
|
|
93
|
+
}
|
|
94
|
+
: {
|
|
95
|
+
name: tool.name,
|
|
96
|
+
description: tool.description,
|
|
97
|
+
input_schema: {
|
|
98
|
+
type: tool.parameters.type,
|
|
99
|
+
properties: tool.parameters.properties,
|
|
100
|
+
required: [...tool.parameters.required],
|
|
101
|
+
},
|
|
102
|
+
}),
|
|
103
|
+
...(translated.system === undefined ? {} : { system: translated.system }),
|
|
104
|
+
};
|
|
105
|
+
return responseMessage(await opts.client.messages.create(request));
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zmdb/ai-anthropic",
|
|
3
|
+
"version": "1.0.0-beta.1",
|
|
4
|
+
"description": "Anthropic Messages API chat driver for the provider-neutral @zmdb/ai runtime.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ai",
|
|
7
|
+
"anthropic",
|
|
8
|
+
"chat",
|
|
9
|
+
"llm",
|
|
10
|
+
"typescript",
|
|
11
|
+
"zmdb"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/ambasta/zmdb#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/ambasta/zmdb/issues"
|
|
16
|
+
},
|
|
17
|
+
"license": "GPL-3.0-or-later",
|
|
18
|
+
"author": "zmdb contributors",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/ambasta/zmdb.git",
|
|
22
|
+
"directory": "packages/ai-anthropic"
|
|
23
|
+
},
|
|
24
|
+
"type": "module",
|
|
25
|
+
"sideEffects": false,
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"import": "./dist/index.js"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public",
|
|
34
|
+
"tag": "beta"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "node ../../scripts/build-package.mjs",
|
|
38
|
+
"test": "vitest run"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@zmdb/ai": "1.0.0-beta.1"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"@anthropic-ai/sdk": "0.124.0"
|
|
45
|
+
},
|
|
46
|
+
"peerDependenciesMeta": {
|
|
47
|
+
"@anthropic-ai/sdk": {
|
|
48
|
+
"optional": true
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=26"
|
|
53
|
+
},
|
|
54
|
+
"main": "./dist/index.js",
|
|
55
|
+
"types": "./dist/index.d.ts"
|
|
56
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type Anthropic from '@anthropic-ai/sdk';
|
|
2
|
+
import type { ChatDriver, ChatMessage } from '@zmdb/ai/chat';
|
|
3
|
+
|
|
4
|
+
export interface AnthropicMessagesClient {
|
|
5
|
+
readonly messages: {
|
|
6
|
+
create(params: Anthropic.MessageCreateParamsNonStreaming): PromiseLike<Anthropic.Message>;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface AnthropicDriverOptions {
|
|
11
|
+
readonly client: AnthropicMessagesClient;
|
|
12
|
+
readonly model: string;
|
|
13
|
+
readonly maxOutputTokens: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type AssistantMessage = Extract<ChatMessage, { readonly role: 'assistant' }>;
|
|
17
|
+
type ProviderPassthrough = NonNullable<AssistantMessage['provider']>[number];
|
|
18
|
+
|
|
19
|
+
const isRecord = (value: unknown): value is Readonly<Record<string, unknown>> =>
|
|
20
|
+
typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
21
|
+
|
|
22
|
+
// A named provider discriminator keeps verify-one-walker's SQL-type heuristic from
|
|
23
|
+
// misclassifying this content-block comparison as a second read of column metadata.
|
|
24
|
+
const TEXT_CONTENT_BLOCK = 'text';
|
|
25
|
+
|
|
26
|
+
const thinkingBlock = (value: unknown): value is Anthropic.ThinkingBlockParam =>
|
|
27
|
+
isRecord(value) &&
|
|
28
|
+
value['type'] === 'thinking' &&
|
|
29
|
+
typeof value['thinking'] === 'string' &&
|
|
30
|
+
typeof value['signature'] === 'string';
|
|
31
|
+
|
|
32
|
+
const redactedThinkingBlock = (value: unknown): value is Anthropic.RedactedThinkingBlockParam =>
|
|
33
|
+
isRecord(value) && value['type'] === 'redacted_thinking' && typeof value['data'] === 'string';
|
|
34
|
+
|
|
35
|
+
const providerBlock = (block: ProviderPassthrough): Anthropic.ContentBlockParam => {
|
|
36
|
+
if (block.kind === 'thinking' && thinkingBlock(block.raw)) return block.raw;
|
|
37
|
+
if (block.kind === 'redacted_thinking' && redactedThinkingBlock(block.raw)) return block.raw;
|
|
38
|
+
throw new Error(`anthropic driver cannot carry provider block ${block.kind}`);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const assistantContent = (message: AssistantMessage): Anthropic.ContentBlockParam[] => {
|
|
42
|
+
const content: Anthropic.ContentBlockParam[] = [];
|
|
43
|
+
for (const block of message.provider ?? []) content.push(providerBlock(block));
|
|
44
|
+
if (message.content.length > 0) content.push({ type: 'text', text: message.content });
|
|
45
|
+
for (const call of message.toolCalls ?? []) {
|
|
46
|
+
content.push({ type: 'tool_use', id: call.id, name: call.name, input: call.args });
|
|
47
|
+
}
|
|
48
|
+
return content;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const requestMessages = (
|
|
52
|
+
messages: readonly ChatMessage[],
|
|
53
|
+
): { readonly messages: Anthropic.MessageParam[]; readonly system?: string } => {
|
|
54
|
+
const system: string[] = [];
|
|
55
|
+
const translated: Anthropic.MessageParam[] = [];
|
|
56
|
+
for (const message of messages) {
|
|
57
|
+
if (message.role === 'system') {
|
|
58
|
+
system.push(message.content);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (message.role === 'user') {
|
|
62
|
+
translated.push({ role: 'user', content: message.content });
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (message.role === 'tool') {
|
|
66
|
+
const result: Anthropic.ToolResultBlockParam =
|
|
67
|
+
message.isError === undefined
|
|
68
|
+
? { type: 'tool_result', tool_use_id: message.callId, content: message.content }
|
|
69
|
+
: { type: 'tool_result', tool_use_id: message.callId, content: message.content, is_error: message.isError };
|
|
70
|
+
translated.push({ role: 'user', content: [result] });
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
translated.push({ role: 'assistant', content: assistantContent(message) });
|
|
74
|
+
}
|
|
75
|
+
return system.length === 0 ? { messages: translated } : { messages: translated, system: system.join('\n\n') };
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const responseMessage = (message: Anthropic.Message): ChatMessage => {
|
|
79
|
+
const text: string[] = [];
|
|
80
|
+
const toolCalls: { readonly id: string; readonly name: string; readonly args: unknown }[] = [];
|
|
81
|
+
const provider: ProviderPassthrough[] = [];
|
|
82
|
+
for (const block of message.content) {
|
|
83
|
+
if (block.type === TEXT_CONTENT_BLOCK) {
|
|
84
|
+
text.push(block.text);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (block.type === 'tool_use') {
|
|
88
|
+
toolCalls.push({ id: block.id, name: block.name, args: block.input });
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (block.type === 'thinking' || block.type === 'redacted_thinking') {
|
|
92
|
+
provider.push({ kind: block.type, raw: block });
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
throw new Error(`anthropic driver received unsupported content block ${block.type}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
role: 'assistant',
|
|
100
|
+
content: text.join(''),
|
|
101
|
+
...(toolCalls.length === 0 ? {} : { toolCalls }),
|
|
102
|
+
...(provider.length === 0 ? {} : { provider }),
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export function anthropicDriver(opts: AnthropicDriverOptions): ChatDriver {
|
|
107
|
+
return {
|
|
108
|
+
next: async (messages, tools) => {
|
|
109
|
+
const translated = requestMessages(messages);
|
|
110
|
+
const request: Anthropic.MessageCreateParamsNonStreaming = {
|
|
111
|
+
model: opts.model,
|
|
112
|
+
max_tokens: opts.maxOutputTokens,
|
|
113
|
+
messages: translated.messages,
|
|
114
|
+
tools: tools.map(tool =>
|
|
115
|
+
tool.description === undefined
|
|
116
|
+
? {
|
|
117
|
+
name: tool.name,
|
|
118
|
+
input_schema: {
|
|
119
|
+
type: tool.parameters.type,
|
|
120
|
+
properties: tool.parameters.properties,
|
|
121
|
+
required: [...tool.parameters.required],
|
|
122
|
+
},
|
|
123
|
+
}
|
|
124
|
+
: {
|
|
125
|
+
name: tool.name,
|
|
126
|
+
description: tool.description,
|
|
127
|
+
input_schema: {
|
|
128
|
+
type: tool.parameters.type,
|
|
129
|
+
properties: tool.parameters.properties,
|
|
130
|
+
required: [...tool.parameters.required],
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
),
|
|
134
|
+
...(translated.system === undefined ? {} : { system: translated.system }),
|
|
135
|
+
};
|
|
136
|
+
return responseMessage(await opts.client.messages.create(request));
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|