@nymbalhq/ai-anthropic 1.5.0 → 1.7.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/LICENSE +21 -0
- package/README.md +10 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +418 -21
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 nymbal contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# @nymbalhq/ai-anthropic
|
|
2
|
+
|
|
3
|
+
Anthropic AI for Nymbal: the `anthropic` ai provider.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx nymbal add ai anthropic
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
`nymbal add` installs it, sets it in `nymbal.config.ts` and lists the variables it needs in `.env.example`.
|
|
10
|
+
See [Adapters](https://github.com/nymbalhq/nymbal/blob/main/apps/docs/src/content/docs/adapters.md).
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ interface AnthropicAiAdapterConfig {
|
|
|
5
5
|
model?: string;
|
|
6
6
|
/** Where the Messages API is; Anthropic's own when omitted (a stand-in for tests). */
|
|
7
7
|
baseUrl?: string;
|
|
8
|
+
/** How long one chat() waits for its answer, or one chatStream() for its end; 60 000 when omitted. */
|
|
9
|
+
timeoutMs?: number;
|
|
8
10
|
logger: Logger;
|
|
9
11
|
}
|
|
10
12
|
declare function createAnthropicAiAdapter(config: AnthropicAiAdapterConfig): AiAdapter;
|
|
@@ -16,8 +18,8 @@ declare const provider = "anthropic";
|
|
|
16
18
|
/** The fields the admin's Settings may fill when the config (and, for the key, ANTHROPIC_API_KEY) leaves them unset. */
|
|
17
19
|
declare const settings: AdapterSetting[];
|
|
18
20
|
/**
|
|
19
|
-
* The kernel's entry: `ai: { provider: 'anthropic', config: { apiKey?, model?, baseUrl? } }`, the key
|
|
20
|
-
* ANTHROPIC_API_KEY.
|
|
21
|
+
* The kernel's entry: `ai: { provider: 'anthropic', config: { apiKey?, model?, baseUrl?, timeoutMs? } }`, the key
|
|
22
|
+
* falling back to ANTHROPIC_API_KEY.
|
|
21
23
|
*/
|
|
22
24
|
declare function createAdapter(ctx: AdapterContext): AiAdapter;
|
|
23
25
|
|
package/dist/index.js
CHANGED
|
@@ -1,21 +1,373 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import {
|
|
3
|
+
ConfigError as ConfigError2
|
|
4
|
+
} from "@nymbalhq/types";
|
|
5
|
+
|
|
6
|
+
// src/anthropic-adapter.ts
|
|
7
|
+
import { AdapterError as AdapterError2 } from "@nymbalhq/types";
|
|
8
|
+
|
|
9
|
+
// src/chat.ts
|
|
10
|
+
import {
|
|
11
|
+
AdapterError,
|
|
3
12
|
ConfigError
|
|
4
13
|
} from "@nymbalhq/types";
|
|
14
|
+
var DEFAULT_MAX_TOKENS = 1024;
|
|
15
|
+
var DEFAULT_TIMEOUT_MS = 6e4;
|
|
16
|
+
var TOOL_NAME = /^[A-Za-z0-9_-]{1,64}$/;
|
|
17
|
+
function timeoutOf(value) {
|
|
18
|
+
if (value === void 0) return DEFAULT_TIMEOUT_MS;
|
|
19
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
20
|
+
throw new ConfigError("commerce.ai.config.timeoutMs is not a positive number of milliseconds", {
|
|
21
|
+
context: { slot: "ai" }
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
27
|
+
var recOf = (value) => isRecord(value) ? value : {};
|
|
28
|
+
function checkChat(provider2, messages, opts) {
|
|
29
|
+
const refuse = (why) => new AdapterError("invalid_request", `${provider2} chat refused: ${why}`, {
|
|
30
|
+
context: { provider: provider2 }
|
|
31
|
+
});
|
|
32
|
+
if (!Array.isArray(messages) || messages.length === 0) throw refuse("no messages");
|
|
33
|
+
let open = [];
|
|
34
|
+
for (const [at, message] of messages.entries()) {
|
|
35
|
+
const turn = recOf(message);
|
|
36
|
+
const role = turn["role"];
|
|
37
|
+
if (role !== "user" && role !== "assistant" && role !== "tool") {
|
|
38
|
+
throw refuse(`messages[${at}].role is not user, assistant or tool`);
|
|
39
|
+
}
|
|
40
|
+
if (at === 0 && role !== "user") throw refuse("the first message is not the user\u2019s");
|
|
41
|
+
const content = turn["content"];
|
|
42
|
+
if (typeof content !== "string") throw refuse(`messages[${at}].content is not text`);
|
|
43
|
+
if (role === "tool") {
|
|
44
|
+
const id = turn["toolCallId"];
|
|
45
|
+
if (typeof id !== "string" || !open.includes(id)) {
|
|
46
|
+
throw refuse(`messages[${at}] answers no open call of the assistant turn before it`);
|
|
47
|
+
}
|
|
48
|
+
if (content === "") throw refuse(`messages[${at}].content is empty`);
|
|
49
|
+
open = open.filter((o) => o !== id);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (open.length > 0) throw refuse(`messages[${at}] starts with a call left unanswered`);
|
|
53
|
+
const calls = role === "assistant" ? callIdsOf(turn["toolCalls"], at, refuse) : [];
|
|
54
|
+
if (content === "" && calls.length === 0) throw refuse(`messages[${at}].content is empty`);
|
|
55
|
+
open = calls;
|
|
56
|
+
}
|
|
57
|
+
checkTools(opts?.tools, refuse);
|
|
58
|
+
const maxTokens = opts?.maxTokens ?? DEFAULT_MAX_TOKENS;
|
|
59
|
+
if (!Number.isInteger(maxTokens) || maxTokens <= 0) {
|
|
60
|
+
throw refuse("maxTokens is not a positive integer");
|
|
61
|
+
}
|
|
62
|
+
return maxTokens;
|
|
63
|
+
}
|
|
64
|
+
function callIdsOf(value, at, refuse) {
|
|
65
|
+
if (value === void 0) return [];
|
|
66
|
+
if (!Array.isArray(value)) throw refuse(`messages[${at}].toolCalls is not a list`);
|
|
67
|
+
const ids = [];
|
|
68
|
+
for (const [n, call] of value.entries()) {
|
|
69
|
+
const c = recOf(call);
|
|
70
|
+
const id = c["id"];
|
|
71
|
+
if (typeof id !== "string" || id === "" || ids.includes(id)) {
|
|
72
|
+
throw refuse(`messages[${at}].toolCalls[${n}].id is empty or repeated`);
|
|
73
|
+
}
|
|
74
|
+
if (typeof c["name"] !== "string" || !TOOL_NAME.test(c["name"])) {
|
|
75
|
+
throw refuse(`messages[${at}].toolCalls[${n}].name is outside ${TOOL_NAME.source}`);
|
|
76
|
+
}
|
|
77
|
+
if (!isRecord(c["args"])) throw refuse(`messages[${at}].toolCalls[${n}].args is not an object`);
|
|
78
|
+
ids.push(id);
|
|
79
|
+
}
|
|
80
|
+
return ids;
|
|
81
|
+
}
|
|
82
|
+
function checkTools(value, refuse) {
|
|
83
|
+
if (value === void 0) return;
|
|
84
|
+
if (!Array.isArray(value)) throw refuse("tools is not a list");
|
|
85
|
+
const names = /* @__PURE__ */ new Set();
|
|
86
|
+
for (const [at, tool] of value.entries()) {
|
|
87
|
+
const t = recOf(tool);
|
|
88
|
+
const name = t["name"];
|
|
89
|
+
if (typeof name !== "string" || !TOOL_NAME.test(name)) {
|
|
90
|
+
throw refuse(`tools[${at}].name is outside ${TOOL_NAME.source}`);
|
|
91
|
+
}
|
|
92
|
+
if (names.has(name)) throw refuse(`tools[${at}].name is taken by an earlier tool`);
|
|
93
|
+
names.add(name);
|
|
94
|
+
if (typeof t["description"] !== "string") throw refuse(`tools[${at}].description is not text`);
|
|
95
|
+
if (!isRecord(t["inputSchema"])) throw refuse(`tools[${at}].inputSchema is not an object`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function callOf(id, name, args) {
|
|
99
|
+
if (typeof id !== "string" || id === "" || typeof name !== "string" || name === "")
|
|
100
|
+
return void 0;
|
|
101
|
+
return isRecord(args) ? { id, name, args } : void 0;
|
|
102
|
+
}
|
|
103
|
+
var timedOut = (provider2, timeoutMs) => new AdapterError("timeout", `${provider2} gave no answer within ${timeoutMs} ms`, {
|
|
104
|
+
context: { provider: provider2 }
|
|
105
|
+
});
|
|
106
|
+
var cancelled = (provider2) => new AdapterError("cancelled", `${provider2} request cancelled by the caller`, {
|
|
107
|
+
context: { provider: provider2 }
|
|
108
|
+
});
|
|
109
|
+
var unreachable = (provider2) => new AdapterError("unavailable", `${provider2} could not be reached`, { context: { provider: provider2 } });
|
|
110
|
+
var brokenOff = (provider2) => new AdapterError("unavailable", `${provider2} broke off its answer`, { context: { provider: provider2 } });
|
|
111
|
+
var unreadable = (provider2, status, what) => new AdapterError("unexpected_response", `${provider2} answered ${status} ${what}`, {
|
|
112
|
+
context: { provider: provider2, status }
|
|
113
|
+
});
|
|
114
|
+
function codeOf(status) {
|
|
115
|
+
if (status === 401 || status === 403) return "auth_failed";
|
|
116
|
+
if (status === 429) return "rate_limited";
|
|
117
|
+
if (status >= 400 && status < 500) return "invalid_request";
|
|
118
|
+
return "unavailable";
|
|
119
|
+
}
|
|
120
|
+
var answered = (provider2, status) => new AdapterError(codeOf(status), `${provider2} answered ${status}`, {
|
|
121
|
+
context: { provider: provider2, status }
|
|
122
|
+
});
|
|
123
|
+
function jsonOf(text) {
|
|
124
|
+
try {
|
|
125
|
+
return JSON.parse(text);
|
|
126
|
+
} catch {
|
|
127
|
+
return void 0;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function lifetime(provider2, timeoutMs, caller) {
|
|
131
|
+
const controller = new AbortController();
|
|
132
|
+
let reason;
|
|
133
|
+
const end = (error) => {
|
|
134
|
+
if (reason !== void 0) return;
|
|
135
|
+
reason = error;
|
|
136
|
+
controller.abort();
|
|
137
|
+
};
|
|
138
|
+
const timer = setTimeout(() => end(timedOut(provider2, timeoutMs)), timeoutMs);
|
|
139
|
+
const onAbort = () => end(cancelled(provider2));
|
|
140
|
+
if (caller?.aborted === true) onAbort();
|
|
141
|
+
else caller?.addEventListener("abort", onAbort, { once: true });
|
|
142
|
+
return {
|
|
143
|
+
signal: controller.signal,
|
|
144
|
+
ended: () => reason,
|
|
145
|
+
close: () => {
|
|
146
|
+
clearTimeout(timer);
|
|
147
|
+
caller?.removeEventListener("abort", onAbort);
|
|
148
|
+
controller.abort();
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
async function post(provider2, url, headers, body, life) {
|
|
153
|
+
let response;
|
|
154
|
+
try {
|
|
155
|
+
response = await globalThis.fetch(url, {
|
|
156
|
+
method: "POST",
|
|
157
|
+
headers: { ...headers, "content-type": "application/json" },
|
|
158
|
+
body,
|
|
159
|
+
signal: life.signal
|
|
160
|
+
});
|
|
161
|
+
} catch {
|
|
162
|
+
throw life.ended() ?? unreachable(provider2);
|
|
163
|
+
}
|
|
164
|
+
if (response.status < 200 || response.status > 299) throw answered(provider2, response.status);
|
|
165
|
+
return response;
|
|
166
|
+
}
|
|
167
|
+
async function bodyText(provider2, response, life) {
|
|
168
|
+
try {
|
|
169
|
+
return await response.text();
|
|
170
|
+
} catch {
|
|
171
|
+
throw life.ended() ?? brokenOff(provider2);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// src/sse.ts
|
|
176
|
+
async function* sseEvents(body) {
|
|
177
|
+
const reader = body.getReader();
|
|
178
|
+
const decoder = new TextDecoder();
|
|
179
|
+
let buffer = "";
|
|
180
|
+
let event = "";
|
|
181
|
+
let data = [];
|
|
182
|
+
try {
|
|
183
|
+
for (; ; ) {
|
|
184
|
+
const { done, value } = await reader.read();
|
|
185
|
+
let text = buffer + (done ? decoder.decode() : decoder.decode(value, { stream: true }));
|
|
186
|
+
const held = !done && text.endsWith("\r") ? "\r" : "";
|
|
187
|
+
if (held !== "") text = text.slice(0, -1);
|
|
188
|
+
const lines = text.split(/\r\n|\r|\n/);
|
|
189
|
+
buffer = (lines.pop() ?? "") + held;
|
|
190
|
+
for (const line of lines) {
|
|
191
|
+
if (line === "") {
|
|
192
|
+
if (data.length > 0)
|
|
193
|
+
yield { event: event === "" ? "message" : event, data: data.join("\n") };
|
|
194
|
+
event = "";
|
|
195
|
+
data = [];
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
if (line.startsWith(":")) continue;
|
|
199
|
+
const colon = line.indexOf(":");
|
|
200
|
+
const field = colon === -1 ? line : line.slice(0, colon);
|
|
201
|
+
const raw = colon === -1 ? "" : line.slice(colon + 1);
|
|
202
|
+
const value2 = raw.startsWith(" ") ? raw.slice(1) : raw;
|
|
203
|
+
if (field === "event") event = value2;
|
|
204
|
+
else if (field === "data") data.push(value2);
|
|
205
|
+
}
|
|
206
|
+
if (done) return;
|
|
207
|
+
}
|
|
208
|
+
} finally {
|
|
209
|
+
reader.releaseLock();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
5
212
|
|
|
6
213
|
// src/anthropic-adapter.ts
|
|
7
|
-
|
|
8
|
-
|
|
214
|
+
var DEFAULT_BASE_URL = "https://api.anthropic.com";
|
|
215
|
+
var API_VERSION = "2023-06-01";
|
|
216
|
+
var PROVIDER = "anthropic";
|
|
217
|
+
function stopReasonOf(wire) {
|
|
218
|
+
if (wire === "end_turn" || wire === "stop_sequence") return "end";
|
|
219
|
+
if (wire === "max_tokens") return "max_tokens";
|
|
220
|
+
if (wire === "tool_use") return "tool_use";
|
|
221
|
+
return "other";
|
|
222
|
+
}
|
|
223
|
+
var count = (value) => typeof value === "number" ? value : 0;
|
|
224
|
+
function wireMessages(messages) {
|
|
225
|
+
const out = [];
|
|
226
|
+
let results;
|
|
227
|
+
for (const m of messages) {
|
|
228
|
+
if (m.role === "tool") {
|
|
229
|
+
const result = {
|
|
230
|
+
type: "tool_result",
|
|
231
|
+
tool_use_id: m.toolCallId,
|
|
232
|
+
content: m.content,
|
|
233
|
+
...m.isError === true && { is_error: true }
|
|
234
|
+
};
|
|
235
|
+
if (results === void 0) {
|
|
236
|
+
results = [result];
|
|
237
|
+
out.push({ role: "user", content: results });
|
|
238
|
+
} else results.push(result);
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
results = void 0;
|
|
242
|
+
const calls = m.role === "assistant" ? m.toolCalls ?? [] : [];
|
|
243
|
+
out.push(
|
|
244
|
+
calls.length === 0 ? { role: m.role, content: m.content } : {
|
|
245
|
+
role: m.role,
|
|
246
|
+
content: [
|
|
247
|
+
...m.content === "" ? [] : [{ type: "text", text: m.content }],
|
|
248
|
+
...calls.map((c) => ({ type: "tool_use", id: c.id, name: c.name, input: c.args }))
|
|
249
|
+
]
|
|
250
|
+
}
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
return out;
|
|
254
|
+
}
|
|
9
255
|
function createAnthropicAiAdapter(config) {
|
|
10
256
|
const model = config.model ?? "claude-sonnet-4-6";
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
257
|
+
const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
258
|
+
const base = config.baseUrl ?? DEFAULT_BASE_URL;
|
|
259
|
+
const url = `${base.endsWith("/") ? base.slice(0, -1) : base}/v1/messages`;
|
|
260
|
+
const headers = { "x-api-key": config.apiKey, "anthropic-version": API_VERSION };
|
|
261
|
+
function requestOf(messages, opts, stream) {
|
|
262
|
+
const maxTokens = checkChat(PROVIDER, messages, opts);
|
|
263
|
+
const system = opts?.system;
|
|
264
|
+
const tools = opts?.tools ?? [];
|
|
265
|
+
return JSON.stringify({
|
|
266
|
+
model,
|
|
267
|
+
max_tokens: maxTokens,
|
|
268
|
+
messages: wireMessages(messages),
|
|
269
|
+
...system !== void 0 && system !== "" && { system },
|
|
270
|
+
...tools.length > 0 && {
|
|
271
|
+
tools: tools.map((t) => ({
|
|
272
|
+
name: t.name,
|
|
273
|
+
description: t.description,
|
|
274
|
+
input_schema: t.inputSchema
|
|
275
|
+
}))
|
|
276
|
+
},
|
|
277
|
+
...stream && { stream: true }
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
function resultOf(status, text, toolCalls, stop, usage, reported) {
|
|
281
|
+
if (text === "" && toolCalls.length === 0) throw unreadable(PROVIDER, status, "with no text");
|
|
282
|
+
return {
|
|
283
|
+
text,
|
|
284
|
+
stopReason: stopReasonOf(stop),
|
|
285
|
+
usage,
|
|
286
|
+
model: typeof reported === "string" && reported !== "" ? reported : model,
|
|
287
|
+
toolCalls
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
async function* streamed(status, body, life) {
|
|
291
|
+
const events = sseEvents(body);
|
|
292
|
+
let reported;
|
|
293
|
+
let inputTokens = 0;
|
|
294
|
+
let outputTokens = 0;
|
|
295
|
+
let stop;
|
|
296
|
+
let text = "";
|
|
297
|
+
const toolCalls = [];
|
|
298
|
+
const building = /* @__PURE__ */ new Map();
|
|
299
|
+
try {
|
|
300
|
+
for (; ; ) {
|
|
301
|
+
let next;
|
|
302
|
+
try {
|
|
303
|
+
next = await events.next();
|
|
304
|
+
} catch {
|
|
305
|
+
throw life.ended() ?? brokenOff(PROVIDER);
|
|
306
|
+
}
|
|
307
|
+
if (next.done === true) throw life.ended() ?? brokenOff(PROVIDER);
|
|
308
|
+
if (next.value.event === "error") throw brokenOff(PROVIDER);
|
|
309
|
+
const e = jsonOf(next.value.data);
|
|
310
|
+
if (!isRecord(e)) throw unreadable(PROVIDER, status, "with an event that is not JSON");
|
|
311
|
+
if (e["type"] === "message_start") {
|
|
312
|
+
const message = recOf(e["message"]);
|
|
313
|
+
reported = message["model"];
|
|
314
|
+
inputTokens = count(recOf(message["usage"])["input_tokens"]);
|
|
315
|
+
} else if (e["type"] === "content_block_start") {
|
|
316
|
+
const block = recOf(e["content_block"]);
|
|
317
|
+
if (block["type"] === "tool_use") {
|
|
318
|
+
building.set(e["index"], { id: block["id"], name: block["name"], json: "" });
|
|
319
|
+
}
|
|
320
|
+
} else if (e["type"] === "content_block_delta") {
|
|
321
|
+
const delta = recOf(e["delta"]);
|
|
322
|
+
const piece = delta["partial_json"];
|
|
323
|
+
if (delta["type"] === "text_delta" && typeof delta["text"] === "string") {
|
|
324
|
+
if (delta["text"] !== "") {
|
|
325
|
+
text += delta["text"];
|
|
326
|
+
yield { type: "text", delta: delta["text"] };
|
|
327
|
+
}
|
|
328
|
+
} else if (delta["type"] === "input_json_delta" && typeof piece === "string") {
|
|
329
|
+
const call = building.get(e["index"]);
|
|
330
|
+
if (call !== void 0) call.json += piece;
|
|
331
|
+
}
|
|
332
|
+
} else if (e["type"] === "content_block_stop") {
|
|
333
|
+
const built = building.get(e["index"]);
|
|
334
|
+
if (built !== void 0) {
|
|
335
|
+
building.delete(e["index"]);
|
|
336
|
+
const call = callOf(built.id, built.name, built.json === "" ? {} : jsonOf(built.json));
|
|
337
|
+
if (call === void 0) {
|
|
338
|
+
throw unreadable(PROVIDER, status, "with a tool call whose input is not an object");
|
|
339
|
+
}
|
|
340
|
+
toolCalls.push(call);
|
|
341
|
+
yield { type: "tool_call", call };
|
|
342
|
+
}
|
|
343
|
+
} else if (e["type"] === "message_delta") {
|
|
344
|
+
stop = recOf(e["delta"])["stop_reason"];
|
|
345
|
+
const out = recOf(e["usage"])["output_tokens"];
|
|
346
|
+
if (typeof out === "number") outputTokens = out;
|
|
347
|
+
} else if (e["type"] === "message_stop") {
|
|
348
|
+
yield {
|
|
349
|
+
type: "done",
|
|
350
|
+
result: resultOf(
|
|
351
|
+
status,
|
|
352
|
+
text,
|
|
353
|
+
toolCalls,
|
|
354
|
+
stop,
|
|
355
|
+
{ inputTokens, outputTokens },
|
|
356
|
+
reported
|
|
357
|
+
)
|
|
358
|
+
};
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
} finally {
|
|
363
|
+
await events.return(void 0);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
const adapter = {
|
|
16
367
|
kind: "ai",
|
|
17
|
-
providerName:
|
|
18
|
-
|
|
368
|
+
providerName: PROVIDER,
|
|
369
|
+
endpoint: base,
|
|
370
|
+
capabilities: () => ["complete", "chat", "tools", "stream"],
|
|
19
371
|
producesEvents: [],
|
|
20
372
|
consumesEvents: [],
|
|
21
373
|
async initialize() {
|
|
@@ -24,25 +376,69 @@ function createAnthropicAiAdapter(config) {
|
|
|
24
376
|
return { status: "healthy", detail: `Anthropic AI adapter using model ${model}` };
|
|
25
377
|
},
|
|
26
378
|
async embed() {
|
|
27
|
-
throw new
|
|
379
|
+
throw new AdapterError2(
|
|
28
380
|
"not_configured",
|
|
29
381
|
"Embedding not yet supported by the Anthropic adapter"
|
|
30
382
|
);
|
|
31
383
|
},
|
|
32
384
|
async complete(prompt, opts) {
|
|
33
|
-
const maxTokens = opts?.maxTokens
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
385
|
+
const maxTokens = opts?.maxTokens;
|
|
386
|
+
const result = await adapter.chat(
|
|
387
|
+
[{ role: "user", content: prompt }],
|
|
388
|
+
maxTokens === void 0 ? {} : { maxTokens }
|
|
389
|
+
);
|
|
390
|
+
return result.text;
|
|
391
|
+
},
|
|
392
|
+
async chat(messages, opts) {
|
|
393
|
+
const request = requestOf(messages, opts, false);
|
|
394
|
+
const life = lifetime(PROVIDER, timeoutMs, opts?.signal);
|
|
395
|
+
let status;
|
|
396
|
+
let text;
|
|
397
|
+
try {
|
|
398
|
+
const response = await post(PROVIDER, url, headers, request, life);
|
|
399
|
+
status = response.status;
|
|
400
|
+
text = await bodyText(PROVIDER, response, life);
|
|
401
|
+
} finally {
|
|
402
|
+
life.close();
|
|
403
|
+
}
|
|
404
|
+
const body = jsonOf(text);
|
|
405
|
+
if (!isRecord(body)) throw unreadable(PROVIDER, status, "with a body that is not JSON");
|
|
406
|
+
const blocks = Array.isArray(body["content"]) ? body["content"].map(recOf) : [];
|
|
407
|
+
const toolCalls = [];
|
|
408
|
+
for (const block of blocks.filter((b) => b["type"] === "tool_use")) {
|
|
409
|
+
const call = callOf(block["id"], block["name"], block["input"]);
|
|
410
|
+
if (call === void 0) {
|
|
411
|
+
throw unreadable(PROVIDER, status, "with a tool call whose input is not an object");
|
|
412
|
+
}
|
|
413
|
+
toolCalls.push(call);
|
|
414
|
+
}
|
|
415
|
+
const answer = blocks.map((b) => b["type"] === "text" && typeof b["text"] === "string" ? b["text"] : "").join("");
|
|
416
|
+
const usage = recOf(body["usage"]);
|
|
417
|
+
return resultOf(
|
|
418
|
+
status,
|
|
419
|
+
answer,
|
|
420
|
+
toolCalls,
|
|
421
|
+
body["stop_reason"],
|
|
422
|
+
{
|
|
423
|
+
inputTokens: count(usage["input_tokens"]),
|
|
424
|
+
outputTokens: count(usage["output_tokens"])
|
|
425
|
+
},
|
|
426
|
+
body["model"]
|
|
427
|
+
);
|
|
428
|
+
},
|
|
429
|
+
async *chatStream(messages, opts) {
|
|
430
|
+
const request = requestOf(messages, opts, true);
|
|
431
|
+
const life = lifetime(PROVIDER, timeoutMs, opts?.signal);
|
|
432
|
+
try {
|
|
433
|
+
const response = await post(PROVIDER, url, headers, request, life);
|
|
434
|
+
if (response.body === null) throw unreadable(PROVIDER, response.status, "with no body");
|
|
435
|
+
yield* streamed(response.status, response.body, life);
|
|
436
|
+
} finally {
|
|
437
|
+
life.close();
|
|
42
438
|
}
|
|
43
|
-
return block.text;
|
|
44
439
|
}
|
|
45
440
|
};
|
|
441
|
+
return adapter;
|
|
46
442
|
}
|
|
47
443
|
|
|
48
444
|
// src/index.ts
|
|
@@ -54,7 +450,7 @@ var settings = [
|
|
|
54
450
|
];
|
|
55
451
|
function createAdapter(ctx) {
|
|
56
452
|
if (ctx.slot !== "ai") {
|
|
57
|
-
throw new
|
|
453
|
+
throw new ConfigError2(`@nymbalhq/ai-anthropic serves ai, not ${ctx.slot}`, {
|
|
58
454
|
code: "adapter.wrong_slot",
|
|
59
455
|
context: { slot: ctx.slot }
|
|
60
456
|
});
|
|
@@ -66,6 +462,7 @@ function createAdapter(ctx) {
|
|
|
66
462
|
apiKey: typeof apiKey === "string" && apiKey !== "" ? apiKey : ctx.env["ANTHROPIC_API_KEY"] ?? "",
|
|
67
463
|
...typeof model === "string" && { model },
|
|
68
464
|
...typeof baseUrl === "string" && baseUrl !== "" && { baseUrl },
|
|
465
|
+
timeoutMs: timeoutOf(ctx.config["timeoutMs"]),
|
|
69
466
|
logger: ctx.logger
|
|
70
467
|
});
|
|
71
468
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/anthropic-adapter.ts"],"sourcesContent":["import {\n ConfigError,\n type AdapterContext,\n type AdapterSetting,\n type AiAdapter,\n} from '@nymbalhq/types'\nimport { createAnthropicAiAdapter, type AnthropicAiAdapterConfig } from './anthropic-adapter.js'\n\n/** What this package serves: the kernel refuses it on any other slot or provider. */\nexport const slot = 'ai'\nexport const provider = 'anthropic'\n\nexport { createAnthropicAiAdapter, type AnthropicAiAdapterConfig }\n\n/** The fields the admin's Settings may fill when the config (and, for the key, ANTHROPIC_API_KEY) leaves them unset. */\nexport const settings: AdapterSetting[] = [\n { path: 'apiKey', label: 'API key', secret: true, env: 'ANTHROPIC_API_KEY' },\n { path: 'baseUrl', label: 'API base URL', secret: false },\n]\n\n/**\n * The kernel's entry: `ai: { provider: 'anthropic', config: { apiKey?, model?, baseUrl? } }`, the key falling back to\n * ANTHROPIC_API_KEY.\n */\nexport function createAdapter(ctx: AdapterContext): AiAdapter {\n if (ctx.slot !== 'ai') {\n throw new ConfigError(`@nymbalhq/ai-anthropic serves ai, not ${ctx.slot}`, {\n code: 'adapter.wrong_slot',\n context: { slot: ctx.slot },\n })\n }\n const apiKey = ctx.config['apiKey']\n const model = ctx.config['model']\n const baseUrl = ctx.config['baseUrl']\n return createAnthropicAiAdapter({\n apiKey:\n typeof apiKey === 'string' && apiKey !== '' ? apiKey : (ctx.env['ANTHROPIC_API_KEY'] ?? ''),\n ...(typeof model === 'string' && { model }),\n ...(typeof baseUrl === 'string' && baseUrl !== '' && { baseUrl }),\n logger: ctx.logger,\n })\n}\n","import Anthropic from '@anthropic-ai/sdk'\nimport type { AiAdapter, Logger } from '@nymbalhq/types'\nimport { AdapterError } from '@nymbalhq/types'\n\nexport interface AnthropicAiAdapterConfig {\n apiKey: string\n model?: string\n /** Where the Messages API is; Anthropic's own when omitted (a stand-in for tests). */\n baseUrl?: string\n logger: Logger\n}\n\nexport function createAnthropicAiAdapter(config: AnthropicAiAdapterConfig): AiAdapter {\n const model = config.model ?? 'claude-sonnet-4-6'\n const client = new Anthropic({\n apiKey: config.apiKey,\n ...(config.baseUrl !== undefined && { baseURL: config.baseUrl }),\n })\n\n return {\n kind: 'ai',\n providerName: 'anthropic',\n capabilities: () => ['complete'],\n producesEvents: [],\n consumesEvents: [],\n async initialize() {\n // no-op: the client is constructed above\n },\n async healthCheck() {\n return { status: 'healthy', detail: `Anthropic AI adapter using model ${model}` }\n },\n\n async embed() {\n throw new AdapterError(\n 'not_configured',\n 'Embedding not yet supported by the Anthropic adapter',\n )\n },\n\n async complete(prompt, opts) {\n const maxTokens = opts?.maxTokens ?? 1024\n const message = await client.messages.create({\n model,\n max_tokens: maxTokens,\n messages: [{ role: 'user', content: prompt }],\n })\n const block = message.content[0]\n if (!block || block.type !== 'text') {\n throw new AdapterError('unexpected_response', 'Anthropic returned no text content')\n }\n return block.text\n },\n }\n}\n"],"mappings":";AAAA;AAAA,EACE;AAAA,OAIK;;;ACLP,OAAO,eAAe;AAEtB,SAAS,oBAAoB;AAUtB,SAAS,yBAAyB,QAA6C;AACpF,QAAM,QAAQ,OAAO,SAAS;AAC9B,QAAM,SAAS,IAAI,UAAU;AAAA,IAC3B,QAAQ,OAAO;AAAA,IACf,GAAI,OAAO,YAAY,UAAa,EAAE,SAAS,OAAO,QAAQ;AAAA,EAChE,CAAC;AAED,SAAO;AAAA,IACL,MAAM;AAAA,IACN,cAAc;AAAA,IACd,cAAc,MAAM,CAAC,UAAU;AAAA,IAC/B,gBAAgB,CAAC;AAAA,IACjB,gBAAgB,CAAC;AAAA,IACjB,MAAM,aAAa;AAAA,IAEnB;AAAA,IACA,MAAM,cAAc;AAClB,aAAO,EAAE,QAAQ,WAAW,QAAQ,oCAAoC,KAAK,GAAG;AAAA,IAClF;AAAA,IAEA,MAAM,QAAQ;AACZ,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM,SAAS,QAAQ,MAAM;AAC3B,YAAM,YAAY,MAAM,aAAa;AACrC,YAAM,UAAU,MAAM,OAAO,SAAS,OAAO;AAAA,QAC3C;AAAA,QACA,YAAY;AAAA,QACZ,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,OAAO,CAAC;AAAA,MAC9C,CAAC;AACD,YAAM,QAAQ,QAAQ,QAAQ,CAAC;AAC/B,UAAI,CAAC,SAAS,MAAM,SAAS,QAAQ;AACnC,cAAM,IAAI,aAAa,uBAAuB,oCAAoC;AAAA,MACpF;AACA,aAAO,MAAM;AAAA,IACf;AAAA,EACF;AACF;;;AD5CO,IAAM,OAAO;AACb,IAAM,WAAW;AAKjB,IAAM,WAA6B;AAAA,EACxC,EAAE,MAAM,UAAU,OAAO,WAAW,QAAQ,MAAM,KAAK,oBAAoB;AAAA,EAC3E,EAAE,MAAM,WAAW,OAAO,gBAAgB,QAAQ,MAAM;AAC1D;AAMO,SAAS,cAAc,KAAgC;AAC5D,MAAI,IAAI,SAAS,MAAM;AACrB,UAAM,IAAI,YAAY,yCAAyC,IAAI,IAAI,IAAI;AAAA,MACzE,MAAM;AAAA,MACN,SAAS,EAAE,MAAM,IAAI,KAAK;AAAA,IAC5B,CAAC;AAAA,EACH;AACA,QAAM,SAAS,IAAI,OAAO,QAAQ;AAClC,QAAM,QAAQ,IAAI,OAAO,OAAO;AAChC,QAAM,UAAU,IAAI,OAAO,SAAS;AACpC,SAAO,yBAAyB;AAAA,IAC9B,QACE,OAAO,WAAW,YAAY,WAAW,KAAK,SAAU,IAAI,IAAI,mBAAmB,KAAK;AAAA,IAC1F,GAAI,OAAO,UAAU,YAAY,EAAE,MAAM;AAAA,IACzC,GAAI,OAAO,YAAY,YAAY,YAAY,MAAM,EAAE,QAAQ;AAAA,IAC/D,QAAQ,IAAI;AAAA,EACd,CAAC;AACH;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/anthropic-adapter.ts","../src/chat.ts","../src/sse.ts"],"sourcesContent":["import {\n ConfigError,\n type AdapterContext,\n type AdapterSetting,\n type AiAdapter,\n} from '@nymbalhq/types'\nimport { createAnthropicAiAdapter, type AnthropicAiAdapterConfig } from './anthropic-adapter.js'\nimport { timeoutOf } from './chat.js'\n\n/** What this package serves: the kernel refuses it on any other slot or provider. */\nexport const slot = 'ai'\nexport const provider = 'anthropic'\n\nexport { createAnthropicAiAdapter, type AnthropicAiAdapterConfig }\n\n/** The fields the admin's Settings may fill when the config (and, for the key, ANTHROPIC_API_KEY) leaves them unset. */\nexport const settings: AdapterSetting[] = [\n { path: 'apiKey', label: 'API key', secret: true, env: 'ANTHROPIC_API_KEY' },\n { path: 'baseUrl', label: 'API base URL', secret: false },\n]\n\n/**\n * The kernel's entry: `ai: { provider: 'anthropic', config: { apiKey?, model?, baseUrl?, timeoutMs? } }`, the key\n * falling back to ANTHROPIC_API_KEY.\n */\nexport function createAdapter(ctx: AdapterContext): AiAdapter {\n if (ctx.slot !== 'ai') {\n throw new ConfigError(`@nymbalhq/ai-anthropic serves ai, not ${ctx.slot}`, {\n code: 'adapter.wrong_slot',\n context: { slot: ctx.slot },\n })\n }\n const apiKey = ctx.config['apiKey']\n const model = ctx.config['model']\n const baseUrl = ctx.config['baseUrl']\n return createAnthropicAiAdapter({\n apiKey:\n typeof apiKey === 'string' && apiKey !== '' ? apiKey : (ctx.env['ANTHROPIC_API_KEY'] ?? ''),\n ...(typeof model === 'string' && { model }),\n ...(typeof baseUrl === 'string' && baseUrl !== '' && { baseUrl }),\n timeoutMs: timeoutOf(ctx.config['timeoutMs']),\n logger: ctx.logger,\n })\n}\n","import type {\n AiAdapter,\n ChatMessage,\n ChatOptions,\n ChatResult,\n ChatStopReason,\n ChatStreamEvent,\n ChatToolCall,\n Logger,\n} from '@nymbalhq/types'\nimport { AdapterError } from '@nymbalhq/types'\nimport {\n bodyText,\n brokenOff,\n callOf,\n checkChat,\n DEFAULT_TIMEOUT_MS,\n isRecord,\n jsonOf,\n lifetime,\n post,\n recOf,\n unreadable,\n type Lifetime,\n} from './chat.js'\nimport { sseEvents, type SseEvent } from './sse.js'\n\nexport const DEFAULT_BASE_URL = 'https://api.anthropic.com'\nconst API_VERSION = '2023-06-01'\n\nexport interface AnthropicAiAdapterConfig {\n apiKey: string\n model?: string\n /** Where the Messages API is; Anthropic's own when omitted (a stand-in for tests). */\n baseUrl?: string\n /** How long one chat() waits for its answer, or one chatStream() for its end; 60 000 when omitted. */\n timeoutMs?: number\n logger: Logger\n}\n\nconst PROVIDER = 'anthropic'\n\nfunction stopReasonOf(wire: unknown): ChatStopReason {\n if (wire === 'end_turn' || wire === 'stop_sequence') return 'end'\n if (wire === 'max_tokens') return 'max_tokens'\n if (wire === 'tool_use') return 'tool_use'\n return 'other'\n}\n\nconst count = (value: unknown): number => (typeof value === 'number' ? value : 0)\n\n/** The conversation as Anthropic's turns: calls as `tool_use` blocks, each run of tool turns as one user turn. */\nfunction wireMessages(messages: readonly ChatMessage[]): unknown[] {\n const out: unknown[] = []\n let results: unknown[] | undefined\n for (const m of messages) {\n if (m.role === 'tool') {\n const result = {\n type: 'tool_result',\n tool_use_id: m.toolCallId,\n content: m.content,\n ...(m.isError === true && { is_error: true }),\n }\n if (results === undefined) {\n results = [result]\n out.push({ role: 'user', content: results })\n } else results.push(result)\n continue\n }\n results = undefined\n const calls = m.role === 'assistant' ? (m.toolCalls ?? []) : []\n out.push(\n calls.length === 0\n ? { role: m.role, content: m.content }\n : {\n role: m.role,\n content: [\n ...(m.content === '' ? [] : [{ type: 'text', text: m.content }]),\n ...calls.map((c) => ({ type: 'tool_use', id: c.id, name: c.name, input: c.args })),\n ],\n },\n )\n }\n return out\n}\n\nexport function createAnthropicAiAdapter(config: AnthropicAiAdapterConfig): AiAdapter {\n const model = config.model ?? 'claude-sonnet-4-6'\n const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS\n const base = config.baseUrl ?? DEFAULT_BASE_URL\n const url = `${base.endsWith('/') ? base.slice(0, -1) : base}/v1/messages`\n const headers = { 'x-api-key': config.apiKey, 'anthropic-version': API_VERSION }\n\n function requestOf(\n messages: readonly ChatMessage[],\n opts: ChatOptions | undefined,\n stream: boolean,\n ): string {\n const maxTokens = checkChat(PROVIDER, messages, opts)\n const system = opts?.system\n const tools = opts?.tools ?? []\n return JSON.stringify({\n model,\n max_tokens: maxTokens,\n messages: wireMessages(messages),\n ...(system !== undefined && system !== '' && { system }),\n ...(tools.length > 0 && {\n tools: tools.map((t) => ({\n name: t.name,\n description: t.description,\n input_schema: t.inputSchema,\n })),\n }),\n ...(stream && { stream: true }),\n })\n }\n\n function resultOf(\n status: number,\n text: string,\n toolCalls: ChatToolCall[],\n stop: unknown,\n usage: { inputTokens: number; outputTokens: number },\n reported: unknown,\n ): ChatResult {\n if (text === '' && toolCalls.length === 0) throw unreadable(PROVIDER, status, 'with no text')\n return {\n text,\n stopReason: stopReasonOf(stop),\n usage,\n model: typeof reported === 'string' && reported !== '' ? reported : model,\n toolCalls,\n }\n }\n\n async function* streamed(\n status: number,\n body: ReadableStream<Uint8Array>,\n life: Lifetime,\n ): AsyncGenerator<ChatStreamEvent> {\n const events = sseEvents(body)\n let reported: unknown\n let inputTokens = 0\n let outputTokens = 0\n let stop: unknown\n let text = ''\n const toolCalls: ChatToolCall[] = []\n const building = new Map<unknown, { id: unknown; name: unknown; json: string }>()\n try {\n for (;;) {\n let next: IteratorResult<SseEvent>\n try {\n next = await events.next()\n } catch {\n throw life.ended() ?? brokenOff(PROVIDER)\n }\n // Ended before message_stop: the answer is not complete.\n if (next.done === true) throw life.ended() ?? brokenOff(PROVIDER)\n if (next.value.event === 'error') throw brokenOff(PROVIDER)\n const e = jsonOf(next.value.data)\n if (!isRecord(e)) throw unreadable(PROVIDER, status, 'with an event that is not JSON')\n if (e['type'] === 'message_start') {\n const message = recOf(e['message'])\n reported = message['model']\n inputTokens = count(recOf(message['usage'])['input_tokens'])\n } else if (e['type'] === 'content_block_start') {\n const block = recOf(e['content_block'])\n if (block['type'] === 'tool_use') {\n building.set(e['index'], { id: block['id'], name: block['name'], json: '' })\n }\n } else if (e['type'] === 'content_block_delta') {\n const delta = recOf(e['delta'])\n const piece = delta['partial_json']\n if (delta['type'] === 'text_delta' && typeof delta['text'] === 'string') {\n if (delta['text'] !== '') {\n text += delta['text']\n yield { type: 'text', delta: delta['text'] }\n }\n } else if (delta['type'] === 'input_json_delta' && typeof piece === 'string') {\n const call = building.get(e['index'])\n if (call !== undefined) call.json += piece\n }\n } else if (e['type'] === 'content_block_stop') {\n const built = building.get(e['index'])\n if (built !== undefined) {\n building.delete(e['index'])\n const call = callOf(built.id, built.name, built.json === '' ? {} : jsonOf(built.json))\n if (call === undefined) {\n throw unreadable(PROVIDER, status, 'with a tool call whose input is not an object')\n }\n toolCalls.push(call)\n yield { type: 'tool_call', call }\n }\n } else if (e['type'] === 'message_delta') {\n stop = recOf(e['delta'])['stop_reason']\n const out = recOf(e['usage'])['output_tokens']\n if (typeof out === 'number') outputTokens = out\n } else if (e['type'] === 'message_stop') {\n yield {\n type: 'done',\n result: resultOf(\n status,\n text,\n toolCalls,\n stop,\n { inputTokens, outputTokens },\n reported,\n ),\n }\n return\n }\n }\n } finally {\n await events.return(undefined)\n }\n }\n\n const adapter: AiAdapter = {\n kind: 'ai',\n providerName: PROVIDER,\n endpoint: base,\n capabilities: () => ['complete', 'chat', 'tools', 'stream'],\n producesEvents: [],\n consumesEvents: [],\n async initialize() {\n // no-op: each chat() is one request\n },\n async healthCheck() {\n return { status: 'healthy', detail: `Anthropic AI adapter using model ${model}` }\n },\n\n async embed() {\n throw new AdapterError(\n 'not_configured',\n 'Embedding not yet supported by the Anthropic adapter',\n )\n },\n\n async complete(prompt, opts) {\n const maxTokens = opts?.maxTokens\n const result = await adapter.chat(\n [{ role: 'user', content: prompt }],\n maxTokens === undefined ? {} : { maxTokens },\n )\n return result.text\n },\n\n async chat(messages, opts): Promise<ChatResult> {\n const request = requestOf(messages, opts, false)\n const life = lifetime(PROVIDER, timeoutMs, opts?.signal)\n let status: number\n let text: string\n try {\n const response = await post(PROVIDER, url, headers, request, life)\n status = response.status\n text = await bodyText(PROVIDER, response, life)\n } finally {\n life.close()\n }\n const body = jsonOf(text)\n if (!isRecord(body)) throw unreadable(PROVIDER, status, 'with a body that is not JSON')\n const blocks = Array.isArray(body['content']) ? (body['content'] as unknown[]).map(recOf) : []\n const toolCalls: ChatToolCall[] = []\n for (const block of blocks.filter((b) => b['type'] === 'tool_use')) {\n const call = callOf(block['id'], block['name'], block['input'])\n if (call === undefined) {\n throw unreadable(PROVIDER, status, 'with a tool call whose input is not an object')\n }\n toolCalls.push(call)\n }\n const answer = blocks\n .map((b) => (b['type'] === 'text' && typeof b['text'] === 'string' ? b['text'] : ''))\n .join('')\n const usage = recOf(body['usage'])\n return resultOf(\n status,\n answer,\n toolCalls,\n body['stop_reason'],\n {\n inputTokens: count(usage['input_tokens']),\n outputTokens: count(usage['output_tokens']),\n },\n body['model'],\n )\n },\n\n async *chatStream(messages, opts) {\n const request = requestOf(messages, opts, true)\n const life = lifetime(PROVIDER, timeoutMs, opts?.signal)\n try {\n const response = await post(PROVIDER, url, headers, request, life)\n if (response.body === null) throw unreadable(PROVIDER, response.status, 'with no body')\n yield* streamed(response.status, response.body, life)\n } finally {\n life.close()\n }\n },\n }\n return adapter\n}\n","import {\n AdapterError,\n ConfigError,\n type ChatMessage,\n type ChatOptions,\n type ChatToolCall,\n} from '@nymbalhq/types'\n\n// What every ai provider's chat() does alike: the input it refuses before a request, the time it waits, and the\n// errors it throws. An error carries the provider and the HTTP status and nothing else: no cause, no headers, no\n// request and no provider message, which may echo the key or the conversation.\n\nexport const DEFAULT_MAX_TOKENS = 1024\nexport const DEFAULT_TIMEOUT_MS = 60_000\nconst TOOL_NAME = /^[A-Za-z0-9_-]{1,64}$/\n\n/** The block's `timeoutMs`, or the default when it is unset. */\nexport function timeoutOf(value: unknown): number {\n if (value === undefined) return DEFAULT_TIMEOUT_MS\n if (typeof value !== 'number' || !Number.isInteger(value) || value <= 0) {\n throw new ConfigError('commerce.ai.config.timeoutMs is not a positive number of milliseconds', {\n context: { slot: 'ai' },\n })\n }\n return value\n}\n\ntype Rec = Record<string, unknown>\n\nexport const isRecord = (value: unknown): value is Rec =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\nexport const recOf = (value: unknown): Rec => (isRecord(value) ? value : {})\n\n/** The maxTokens to send, once the input is one the provider would take. */\nexport function checkChat(\n provider: string,\n messages: readonly ChatMessage[],\n opts: ChatOptions | undefined,\n): number {\n const refuse = (why: string) =>\n new AdapterError('invalid_request', `${provider} chat refused: ${why}`, {\n context: { provider },\n })\n if (!Array.isArray(messages) || messages.length === 0) throw refuse('no messages')\n // The calls of the nearest assistant turn that no tool turn has answered yet.\n let open: string[] = []\n for (const [at, message] of messages.entries()) {\n const turn = recOf(message)\n const role = turn['role']\n if (role !== 'user' && role !== 'assistant' && role !== 'tool') {\n throw refuse(`messages[${at}].role is not user, assistant or tool`)\n }\n if (at === 0 && role !== 'user') throw refuse('the first message is not the user’s')\n const content = turn['content']\n if (typeof content !== 'string') throw refuse(`messages[${at}].content is not text`)\n if (role === 'tool') {\n const id = turn['toolCallId']\n if (typeof id !== 'string' || !open.includes(id)) {\n throw refuse(`messages[${at}] answers no open call of the assistant turn before it`)\n }\n if (content === '') throw refuse(`messages[${at}].content is empty`)\n open = open.filter((o) => o !== id)\n continue\n }\n if (open.length > 0) throw refuse(`messages[${at}] starts with a call left unanswered`)\n const calls = role === 'assistant' ? callIdsOf(turn['toolCalls'], at, refuse) : []\n if (content === '' && calls.length === 0) throw refuse(`messages[${at}].content is empty`)\n open = calls\n }\n checkTools(opts?.tools, refuse)\n const maxTokens = opts?.maxTokens ?? DEFAULT_MAX_TOKENS\n if (!Number.isInteger(maxTokens) || maxTokens <= 0) {\n throw refuse('maxTokens is not a positive integer')\n }\n return maxTokens\n}\n\n/** An assistant turn's call ids, once each call is a well-formed one. */\nfunction callIdsOf(value: unknown, at: number, refuse: (why: string) => AdapterError): string[] {\n if (value === undefined) return []\n if (!Array.isArray(value)) throw refuse(`messages[${at}].toolCalls is not a list`)\n const ids: string[] = []\n for (const [n, call] of (value as unknown[]).entries()) {\n const c = recOf(call)\n const id = c['id']\n if (typeof id !== 'string' || id === '' || ids.includes(id)) {\n throw refuse(`messages[${at}].toolCalls[${n}].id is empty or repeated`)\n }\n if (typeof c['name'] !== 'string' || !TOOL_NAME.test(c['name'])) {\n throw refuse(`messages[${at}].toolCalls[${n}].name is outside ${TOOL_NAME.source}`)\n }\n if (!isRecord(c['args'])) throw refuse(`messages[${at}].toolCalls[${n}].args is not an object`)\n ids.push(id)\n }\n return ids\n}\n\nfunction checkTools(value: unknown, refuse: (why: string) => AdapterError): void {\n if (value === undefined) return\n if (!Array.isArray(value)) throw refuse('tools is not a list')\n const names = new Set<string>()\n for (const [at, tool] of (value as unknown[]).entries()) {\n const t = recOf(tool)\n const name = t['name']\n if (typeof name !== 'string' || !TOOL_NAME.test(name)) {\n throw refuse(`tools[${at}].name is outside ${TOOL_NAME.source}`)\n }\n if (names.has(name)) throw refuse(`tools[${at}].name is taken by an earlier tool`)\n names.add(name)\n if (typeof t['description'] !== 'string') throw refuse(`tools[${at}].description is not text`)\n if (!isRecord(t['inputSchema'])) throw refuse(`tools[${at}].inputSchema is not an object`)\n }\n}\n\n/** A call the provider made, or undefined when its id or name is missing or its arguments are not a JSON object. */\nexport function callOf(id: unknown, name: unknown, args: unknown): ChatToolCall | undefined {\n if (typeof id !== 'string' || id === '' || typeof name !== 'string' || name === '')\n return undefined\n return isRecord(args) ? { id, name, args } : undefined\n}\n\nexport const timedOut = (provider: string, timeoutMs: number): AdapterError =>\n new AdapterError('timeout', `${provider} gave no answer within ${timeoutMs} ms`, {\n context: { provider },\n })\n\nexport const cancelled = (provider: string): AdapterError =>\n new AdapterError('cancelled', `${provider} request cancelled by the caller`, {\n context: { provider },\n })\n\nexport const unreachable = (provider: string): AdapterError =>\n new AdapterError('unavailable', `${provider} could not be reached`, { context: { provider } })\n\n/** A stream that ended, or reported an error, before its answer was complete. */\nexport const brokenOff = (provider: string): AdapterError =>\n new AdapterError('unavailable', `${provider} broke off its answer`, { context: { provider } })\n\n/** A 2xx the adapter cannot read an answer from. */\nexport const unreadable = (provider: string, status: number, what: string): AdapterError =>\n new AdapterError('unexpected_response', `${provider} answered ${status} ${what}`, {\n context: { provider, status },\n })\n\nfunction codeOf(status: number): string {\n if (status === 401 || status === 403) return 'auth_failed'\n if (status === 429) return 'rate_limited'\n if (status >= 400 && status < 500) return 'invalid_request'\n return 'unavailable'\n}\n\n/** A non-2xx answer, classified by its status. */\nexport const answered = (provider: string, status: number): AdapterError =>\n new AdapterError(codeOf(status), `${provider} answered ${status}`, {\n context: { provider, status },\n })\n\n/** A JSON body, or undefined when the text is not JSON. */\nexport function jsonOf(text: string): unknown {\n try {\n return JSON.parse(text) as unknown\n } catch {\n return undefined\n }\n}\n\n/** One request from its send to its last byte: the signal it is sent with, and why it was ended early. */\nexport interface Lifetime {\n signal: AbortSignal\n /** The timeout or the caller's abort, once either has ended the request. */\n ended(): AdapterError | undefined\n /** Clears the timer and closes the connection if it is still open. */\n close(): void\n}\n\n/** Bounds a request by `timeoutMs` (`adapter.timeout`) and by the caller's `signal` (`adapter.cancelled`). */\nexport function lifetime(provider: string, timeoutMs: number, caller?: AbortSignal): Lifetime {\n const controller = new AbortController()\n let reason: AdapterError | undefined\n // The reason is set before the abort, so the request's own abort error is read as it.\n const end = (error: AdapterError) => {\n if (reason !== undefined) return\n reason = error\n controller.abort()\n }\n const timer = setTimeout(() => end(timedOut(provider, timeoutMs)), timeoutMs)\n const onAbort = () => end(cancelled(provider))\n if (caller?.aborted === true) onAbort()\n else caller?.addEventListener('abort', onAbort, { once: true })\n return {\n signal: controller.signal,\n ended: () => reason,\n close: () => {\n clearTimeout(timer)\n caller?.removeEventListener('abort', onAbort)\n controller.abort()\n },\n }\n}\n\n/** POSTs `body` as JSON: the 2xx response, or the error a failure or a non-2xx answer is. */\nexport async function post(\n provider: string,\n url: string,\n headers: Record<string, string>,\n body: string,\n life: Lifetime,\n): Promise<Response> {\n let response: Response\n try {\n response = await globalThis.fetch(url, {\n method: 'POST',\n headers: { ...headers, 'content-type': 'application/json' },\n body,\n signal: life.signal,\n })\n } catch {\n // fetch's own error may quote the request's headers, the key among them: none of it goes on.\n throw life.ended() ?? unreachable(provider)\n }\n // The body is left unread (it may echo the key or the conversation); close() drops the connection.\n if (response.status < 200 || response.status > 299) throw answered(provider, response.status)\n return response\n}\n\n/** The whole body as text, or the error a body cut short is. */\nexport async function bodyText(\n provider: string,\n response: Response,\n life: Lifetime,\n): Promise<string> {\n try {\n return await response.text()\n } catch {\n throw life.ended() ?? brokenOff(provider)\n }\n}\n","// A server-sent events reader for a provider's streamed answer, the same in every ai provider package. It yields each\n// event once its blank line has arrived; a read that fails (a broken connection, an abort) rejects as it is, for the\n// adapter to classify.\n\n/** One event: its `event` field (`message` when it has none) and its `data` lines joined by a newline. */\nexport interface SseEvent {\n event: string\n data: string\n}\n\nexport async function* sseEvents(body: ReadableStream<Uint8Array>): AsyncGenerator<SseEvent> {\n const reader = body.getReader()\n const decoder = new TextDecoder()\n let buffer = ''\n let event = ''\n let data: string[] = []\n try {\n for (;;) {\n const { done, value } = await reader.read()\n let text = buffer + (done ? decoder.decode() : decoder.decode(value, { stream: true }))\n // A CR at the end is held back, as the LF that may make it one line end can be in the next chunk.\n const held = !done && text.endsWith('\\r') ? '\\r' : ''\n if (held !== '') text = text.slice(0, -1)\n const lines = text.split(/\\r\\n|\\r|\\n/)\n // The last piece is a line not yet ended.\n buffer = (lines.pop() ?? '') + held\n for (const line of lines) {\n if (line === '') {\n if (data.length > 0)\n yield { event: event === '' ? 'message' : event, data: data.join('\\n') }\n event = ''\n data = []\n continue\n }\n if (line.startsWith(':')) continue\n const colon = line.indexOf(':')\n const field = colon === -1 ? line : line.slice(0, colon)\n const raw = colon === -1 ? '' : line.slice(colon + 1)\n const value = raw.startsWith(' ') ? raw.slice(1) : raw\n if (field === 'event') event = value\n else if (field === 'data') data.push(value)\n }\n // An event whose blank line never came is incomplete, as the standard has it, and is dropped.\n if (done) return\n }\n } finally {\n reader.releaseLock()\n }\n}\n"],"mappings":"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","names":["ConfigError","AdapterError","provider","value","AdapterError","ConfigError"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nymbalhq/ai-anthropic",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Anthropic AI for Nymbal: the `anthropic` ai provider",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@anthropic-ai/sdk": "^0.39.0",
|
|
35
|
-
"@nymbalhq/types": "1.
|
|
35
|
+
"@nymbalhq/types": "1.7.0",
|
|
36
36
|
"@typescript/native": "npm:typescript@^7.0.2",
|
|
37
37
|
"tsup": "^9.0.0",
|
|
38
38
|
"typescript": "npm:@typescript/typescript6@^6.0.2",
|