@revojs/ai 0.1.58
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 +617 -0
- package/dist/index.js +76 -0
- package/dist/module/index.d.ts +3 -0
- package/dist/module/index.js +47 -0
- package/dist/protocols/anthropic-messages.d.ts +7 -0
- package/dist/protocols/anthropic-messages.js +377 -0
- package/dist/protocols/google-models.d.ts +7 -0
- package/dist/protocols/google-models.js +331 -0
- package/dist/protocols/openai-chat-completions.d.ts +7 -0
- package/dist/protocols/openai-chat-completions.js +334 -0
- package/dist/protocols/openai-responses.d.ts +7 -0
- package/dist/protocols/openai-responses.js +409 -0
- package/dist/providers/anthropic.d.ts +29 -0
- package/dist/providers/anthropic.js +40 -0
- package/dist/providers/deepseek.d.ts +15 -0
- package/dist/providers/deepseek.js +26 -0
- package/dist/providers/google.d.ts +25 -0
- package/dist/providers/google.js +36 -0
- package/dist/providers/moonshotai.d.ts +21 -0
- package/dist/providers/moonshotai.js +32 -0
- package/dist/providers/openai.d.ts +29 -0
- package/dist/providers/openai.js +40 -0
- package/dist/providers/opencode.d.ts +163 -0
- package/dist/providers/opencode.js +183 -0
- package/dist/providers/xai.d.ts +17 -0
- package/dist/providers/xai.js +28 -0
- package/dist/types/index.d.ts +14 -0
- package/package.json +37 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { defineModule, useModule } from "revojs/module";
|
|
2
|
+
//#region src/module/index.ts
|
|
3
|
+
var module_default = defineModule({
|
|
4
|
+
name: "ai",
|
|
5
|
+
config: () => ({}),
|
|
6
|
+
setup() {
|
|
7
|
+
const { fromModule } = useModule(import.meta.url);
|
|
8
|
+
return {
|
|
9
|
+
variables: { server: {
|
|
10
|
+
anthropicApiKey: {
|
|
11
|
+
type: "string",
|
|
12
|
+
optional: true
|
|
13
|
+
},
|
|
14
|
+
deepseekApiKey: {
|
|
15
|
+
type: "string",
|
|
16
|
+
optional: true
|
|
17
|
+
},
|
|
18
|
+
googleApiKey: {
|
|
19
|
+
type: "string",
|
|
20
|
+
optional: true
|
|
21
|
+
},
|
|
22
|
+
moonshotaiApiKey: {
|
|
23
|
+
type: "string",
|
|
24
|
+
optional: true
|
|
25
|
+
},
|
|
26
|
+
openaiApiKey: {
|
|
27
|
+
type: "string",
|
|
28
|
+
optional: true
|
|
29
|
+
},
|
|
30
|
+
opencodeApiKey: {
|
|
31
|
+
type: "string",
|
|
32
|
+
optional: true
|
|
33
|
+
},
|
|
34
|
+
xaiApiKey: {
|
|
35
|
+
type: "string",
|
|
36
|
+
optional: true
|
|
37
|
+
}
|
|
38
|
+
} },
|
|
39
|
+
sources: { providers: {
|
|
40
|
+
match: ["**/*.{js,ts}", "!**/*.d.ts"],
|
|
41
|
+
from: ["./providers", fromModule("../providers")]
|
|
42
|
+
} }
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
//#endregion
|
|
47
|
+
export { module_default as default };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type LanguageModelCallOptions, type LanguageModelGenerateResult, type LanguageModelStreamResult, type Protocol } from "../index";
|
|
2
|
+
export interface MessagesConfig {
|
|
3
|
+
baseURL: string;
|
|
4
|
+
apiKey: string | undefined;
|
|
5
|
+
}
|
|
6
|
+
export declare function messagesGenerateProtocol(config: MessagesConfig): Protocol<LanguageModelCallOptions, LanguageModelGenerateResult>;
|
|
7
|
+
export declare function messagesStreamProtocol(config: MessagesConfig): Protocol<LanguageModelCallOptions, LanguageModelStreamResult>;
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import { mergeHeaders, readDataFrames, toStream } from "../index.js";
|
|
2
|
+
import { toJsonSchema } from "revojs/shared";
|
|
3
|
+
//#region src/protocols/anthropic-messages.ts
|
|
4
|
+
function toUsage(inputTokens, cacheRead, cacheWrite, outputTokens) {
|
|
5
|
+
const raw = {};
|
|
6
|
+
if (inputTokens !== void 0) raw.input_tokens = inputTokens;
|
|
7
|
+
if (cacheRead !== void 0) raw.cache_read_input_tokens = cacheRead;
|
|
8
|
+
if (cacheWrite !== void 0) raw.cache_creation_input_tokens = cacheWrite;
|
|
9
|
+
if (outputTokens !== void 0) raw.output_tokens = outputTokens;
|
|
10
|
+
return {
|
|
11
|
+
inputTokens: {
|
|
12
|
+
total: inputTokens,
|
|
13
|
+
noCache: inputTokens !== void 0 && cacheRead !== void 0 && cacheWrite !== void 0 ? Math.max(0, inputTokens - cacheRead - cacheWrite) : void 0,
|
|
14
|
+
cacheRead,
|
|
15
|
+
cacheWrite
|
|
16
|
+
},
|
|
17
|
+
outputTokens: {
|
|
18
|
+
total: outputTokens,
|
|
19
|
+
text: void 0,
|
|
20
|
+
reasoning: void 0
|
|
21
|
+
},
|
|
22
|
+
raw
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function toFinishReason(raw) {
|
|
26
|
+
return {
|
|
27
|
+
unified: raw === "max_tokens" ? "length" : raw === "refusal" ? "content-filter" : raw === "tool_use" ? "tool-calls" : "stop",
|
|
28
|
+
raw: raw ?? void 0
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function toParsedInput(input) {
|
|
32
|
+
try {
|
|
33
|
+
return JSON.parse(input || "{}");
|
|
34
|
+
} catch {
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function toResultValue(output) {
|
|
39
|
+
if (output.type === "text" || output.type === "error-text") return output.value;
|
|
40
|
+
const value = "value" in output ? output.value : void 0;
|
|
41
|
+
return JSON.stringify(value ?? null);
|
|
42
|
+
}
|
|
43
|
+
function toBody(options, stream) {
|
|
44
|
+
let system = "";
|
|
45
|
+
const warnings = [];
|
|
46
|
+
const wire = [];
|
|
47
|
+
let pendingUser;
|
|
48
|
+
const flushUser = () => {
|
|
49
|
+
if (pendingUser !== void 0 && pendingUser.length > 0) wire.push({
|
|
50
|
+
role: "user",
|
|
51
|
+
content: pendingUser
|
|
52
|
+
});
|
|
53
|
+
pendingUser = void 0;
|
|
54
|
+
};
|
|
55
|
+
for (const message of options.prompt) {
|
|
56
|
+
if (message.role === "system") {
|
|
57
|
+
flushUser();
|
|
58
|
+
system += (system.length > 0 ? "\n" : "") + message.content;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (message.role === "user") {
|
|
62
|
+
flushUser();
|
|
63
|
+
const content = message.content.flatMap((part) => {
|
|
64
|
+
if (part.type === "text") return [{
|
|
65
|
+
type: "text",
|
|
66
|
+
text: part.text
|
|
67
|
+
}];
|
|
68
|
+
if (part.data.type === "url") return [{
|
|
69
|
+
type: "image",
|
|
70
|
+
source: {
|
|
71
|
+
type: "url",
|
|
72
|
+
url: part.data.url.toString()
|
|
73
|
+
}
|
|
74
|
+
}];
|
|
75
|
+
if (part.data.type === "data" && typeof part.data.data === "string") return [{
|
|
76
|
+
type: "image",
|
|
77
|
+
source: {
|
|
78
|
+
type: "base64",
|
|
79
|
+
media_type: part.mediaType,
|
|
80
|
+
data: part.data.data
|
|
81
|
+
}
|
|
82
|
+
}];
|
|
83
|
+
warnings.push({
|
|
84
|
+
type: "unsupported",
|
|
85
|
+
feature: `user file (${part.mediaType}) message part`
|
|
86
|
+
});
|
|
87
|
+
return [];
|
|
88
|
+
});
|
|
89
|
+
if (content.length > 0) wire.push({
|
|
90
|
+
role: "user",
|
|
91
|
+
content
|
|
92
|
+
});
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (message.role === "tool") {
|
|
96
|
+
for (const part of message.content) {
|
|
97
|
+
if (part.type !== "tool-result") continue;
|
|
98
|
+
(pendingUser ??= []).push({
|
|
99
|
+
type: "tool_result",
|
|
100
|
+
tool_use_id: part.toolCallId,
|
|
101
|
+
content: toResultValue(part.output)
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
flushUser();
|
|
107
|
+
const blocks = [];
|
|
108
|
+
for (const part of message.content) {
|
|
109
|
+
if (part.type === "text") {
|
|
110
|
+
if (part.text.length > 0) blocks.push({
|
|
111
|
+
type: "text",
|
|
112
|
+
text: part.text
|
|
113
|
+
});
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (part.type === "reasoning") {
|
|
117
|
+
blocks.push({
|
|
118
|
+
type: "thinking",
|
|
119
|
+
thinking: part.text
|
|
120
|
+
});
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (part.type === "tool-call") {
|
|
124
|
+
blocks.push({
|
|
125
|
+
type: "tool_use",
|
|
126
|
+
id: part.toolCallId,
|
|
127
|
+
name: part.toolName,
|
|
128
|
+
input: toParsedInput(typeof part.input === "string" ? part.input : JSON.stringify(part.input ?? {}))
|
|
129
|
+
});
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (part.type !== "tool-result") warnings.push({
|
|
133
|
+
type: "compatibility",
|
|
134
|
+
feature: `assistant ${part.type} message part`
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
if (blocks.length > 0) wire.push({
|
|
138
|
+
role: "assistant",
|
|
139
|
+
content: blocks
|
|
140
|
+
});
|
|
141
|
+
for (const part of message.content) if (part.type === "tool-result") (pendingUser ??= []).push({
|
|
142
|
+
type: "tool_result",
|
|
143
|
+
tool_use_id: part.toolCallId,
|
|
144
|
+
content: toResultValue(part.output)
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
flushUser();
|
|
148
|
+
return [{
|
|
149
|
+
model: options.model,
|
|
150
|
+
max_tokens: options.maxOutputTokens ?? 8192,
|
|
151
|
+
stream,
|
|
152
|
+
...options.temperature !== void 0 ? { temperature: options.temperature } : {},
|
|
153
|
+
...options.topP !== void 0 ? { top_p: options.topP } : {},
|
|
154
|
+
...options.topK !== void 0 ? { top_k: options.topK } : {},
|
|
155
|
+
...options.stopSequences !== void 0 ? { stop_sequences: options.stopSequences } : {},
|
|
156
|
+
...system.length > 0 ? { system } : {},
|
|
157
|
+
...options.tools !== void 0 && options.tools.length > 0 ? { tools: options.tools.map((tool) => tool.type === "provider" ? {
|
|
158
|
+
type: "custom",
|
|
159
|
+
name: `${tool.id}.${tool.name}`,
|
|
160
|
+
...tool.args
|
|
161
|
+
} : {
|
|
162
|
+
name: tool.name,
|
|
163
|
+
description: tool.description,
|
|
164
|
+
input_schema: toJsonSchema(tool.inputSchema)
|
|
165
|
+
}) } : {},
|
|
166
|
+
...options.toolChoice !== void 0 ? { tool_choice: options.toolChoice.type === "tool" ? {
|
|
167
|
+
type: "tool",
|
|
168
|
+
name: options.toolChoice.toolName
|
|
169
|
+
} : { type: options.toolChoice.type } } : {},
|
|
170
|
+
...options.providerOptions?.anthropic,
|
|
171
|
+
messages: wire
|
|
172
|
+
}, warnings];
|
|
173
|
+
}
|
|
174
|
+
async function* readParts(response, warnings) {
|
|
175
|
+
yield {
|
|
176
|
+
type: "stream-start",
|
|
177
|
+
warnings
|
|
178
|
+
};
|
|
179
|
+
const active = /* @__PURE__ */ new Map();
|
|
180
|
+
const complete = /* @__PURE__ */ new Map();
|
|
181
|
+
let streamCount = 0;
|
|
182
|
+
let metadataSent = false;
|
|
183
|
+
let inputTokens;
|
|
184
|
+
let cacheRead;
|
|
185
|
+
let cacheWrite;
|
|
186
|
+
let outputTokens;
|
|
187
|
+
let stopReason;
|
|
188
|
+
for await (const data of readDataFrames(response)) {
|
|
189
|
+
let chunk;
|
|
190
|
+
try {
|
|
191
|
+
chunk = JSON.parse(data);
|
|
192
|
+
} catch {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (chunk.type === "error") throw new Error(chunk.error?.message ?? "Unknown AI provider error");
|
|
196
|
+
if (!metadataSent && chunk.type === "message_start") {
|
|
197
|
+
metadataSent = true;
|
|
198
|
+
yield {
|
|
199
|
+
type: "response-metadata",
|
|
200
|
+
id: chunk.id,
|
|
201
|
+
modelId: chunk.model
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
if (chunk.type === "message_start") {
|
|
205
|
+
inputTokens = chunk.message?.usage?.input_tokens;
|
|
206
|
+
cacheRead = chunk.message?.usage?.cache_read_input_tokens;
|
|
207
|
+
cacheWrite = chunk.message?.usage?.cache_creation_input_tokens;
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
if (chunk.type === "content_block_start") {
|
|
211
|
+
const index = chunk.index ?? 0;
|
|
212
|
+
const kind = chunk.content_block?.type ?? "";
|
|
213
|
+
const streamId = `anthropic-${kind}-${streamCount++}`;
|
|
214
|
+
const record = {
|
|
215
|
+
index,
|
|
216
|
+
kind,
|
|
217
|
+
streamId,
|
|
218
|
+
callId: chunk.content_block?.id ?? "",
|
|
219
|
+
name: chunk.content_block?.name ?? "",
|
|
220
|
+
json: ""
|
|
221
|
+
};
|
|
222
|
+
active.set(index, record);
|
|
223
|
+
if (kind === "tool_use") yield {
|
|
224
|
+
type: "tool-input-start",
|
|
225
|
+
id: streamId,
|
|
226
|
+
toolName: record.name
|
|
227
|
+
};
|
|
228
|
+
else if (kind === "thinking") yield {
|
|
229
|
+
type: "reasoning-start",
|
|
230
|
+
id: streamId
|
|
231
|
+
};
|
|
232
|
+
else yield {
|
|
233
|
+
type: "text-start",
|
|
234
|
+
id: streamId
|
|
235
|
+
};
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
const record = chunk.index === void 0 ? void 0 : active.get(chunk.index);
|
|
239
|
+
if (chunk.type === "content_block_delta" && record !== void 0 && chunk.delta !== void 0) {
|
|
240
|
+
if (chunk.delta.type === "input_json_delta" && typeof chunk.delta.partial_json === "string") {
|
|
241
|
+
record.json += chunk.delta.partial_json;
|
|
242
|
+
yield {
|
|
243
|
+
type: "tool-input-delta",
|
|
244
|
+
id: record.streamId,
|
|
245
|
+
delta: chunk.delta.partial_json
|
|
246
|
+
};
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
if (chunk.delta.type === "text_delta" && typeof chunk.delta.text === "string" && chunk.delta.text.length > 0) {
|
|
250
|
+
yield {
|
|
251
|
+
type: "text-delta",
|
|
252
|
+
id: record.streamId,
|
|
253
|
+
delta: chunk.delta.text
|
|
254
|
+
};
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
if (chunk.delta.type === "thinking_delta" && typeof chunk.delta.thinking === "string" && chunk.delta.thinking.length > 0) yield {
|
|
258
|
+
type: "reasoning-delta",
|
|
259
|
+
id: record.streamId,
|
|
260
|
+
delta: chunk.delta.thinking
|
|
261
|
+
};
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
if (chunk.type === "content_block_stop" && record !== void 0 && chunk.index !== void 0) {
|
|
265
|
+
active.delete(chunk.index);
|
|
266
|
+
complete.set(chunk.index, record);
|
|
267
|
+
if (record.kind === "tool_use") yield {
|
|
268
|
+
type: "tool-input-end",
|
|
269
|
+
id: record.streamId
|
|
270
|
+
};
|
|
271
|
+
else if (record.kind === "thinking") yield {
|
|
272
|
+
type: "reasoning-end",
|
|
273
|
+
id: record.streamId
|
|
274
|
+
};
|
|
275
|
+
else yield {
|
|
276
|
+
type: "text-end",
|
|
277
|
+
id: record.streamId
|
|
278
|
+
};
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
if (chunk.type === "message_delta") {
|
|
282
|
+
if (typeof chunk.delta?.stop_reason === "string") stopReason = chunk.delta.stop_reason;
|
|
283
|
+
if (typeof chunk.usage?.output_tokens === "number") outputTokens = chunk.usage.output_tokens;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
for (const record of active.values()) if (record.kind === "tool_use") yield {
|
|
287
|
+
type: "tool-input-end",
|
|
288
|
+
id: record.streamId
|
|
289
|
+
};
|
|
290
|
+
else if (record.kind === "thinking") yield {
|
|
291
|
+
type: "reasoning-end",
|
|
292
|
+
id: record.streamId
|
|
293
|
+
};
|
|
294
|
+
else yield {
|
|
295
|
+
type: "text-end",
|
|
296
|
+
id: record.streamId
|
|
297
|
+
};
|
|
298
|
+
for (const record of [...complete.values()].sort((left, right) => left.index - right.index)) if (record.callId.length > 0) yield {
|
|
299
|
+
type: "tool-call",
|
|
300
|
+
toolCallId: record.callId,
|
|
301
|
+
toolName: record.name,
|
|
302
|
+
input: record.json
|
|
303
|
+
};
|
|
304
|
+
yield {
|
|
305
|
+
type: "finish",
|
|
306
|
+
usage: toUsage(inputTokens, cacheRead, cacheWrite, outputTokens),
|
|
307
|
+
finishReason: toFinishReason(stopReason)
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
async function post(config, options, body) {
|
|
311
|
+
const response = await fetch(`${config.baseURL.replace(/\/+$/, "")}/messages`, {
|
|
312
|
+
method: "POST",
|
|
313
|
+
headers: {
|
|
314
|
+
"Content-Type": "application/json",
|
|
315
|
+
...mergeHeaders(config.apiKey === void 0 ? {} : { "x-api-key": config.apiKey }, { "anthropic-version": "2023-06-01" }, options.headers)
|
|
316
|
+
},
|
|
317
|
+
body: JSON.stringify(body),
|
|
318
|
+
signal: options.abortSignal
|
|
319
|
+
});
|
|
320
|
+
if (!response.ok) throw new Error(`AI provider responded with ${response.status}: ${(await response.text()).slice(0, 500)}`);
|
|
321
|
+
return response;
|
|
322
|
+
}
|
|
323
|
+
function messagesGenerateProtocol(config) {
|
|
324
|
+
return async (options) => {
|
|
325
|
+
const [body, warnings] = toBody(options, false);
|
|
326
|
+
const response = await post(config, options, body);
|
|
327
|
+
const data = await response.json();
|
|
328
|
+
const content = [];
|
|
329
|
+
for (const part of data.content ?? []) {
|
|
330
|
+
if (part.type === "text" && typeof part.text === "string") {
|
|
331
|
+
content.push({
|
|
332
|
+
type: "text",
|
|
333
|
+
text: part.text
|
|
334
|
+
});
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
if (part.type === "thinking" && typeof part.thinking === "string") {
|
|
338
|
+
content.push({
|
|
339
|
+
type: "reasoning",
|
|
340
|
+
text: part.thinking
|
|
341
|
+
});
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
if (part.type === "tool_use" && typeof part.id === "string" && typeof part.name === "string") content.push({
|
|
345
|
+
type: "tool-call",
|
|
346
|
+
toolCallId: part.id,
|
|
347
|
+
toolName: part.name,
|
|
348
|
+
input: JSON.stringify(part.input ?? {})
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
return {
|
|
352
|
+
content,
|
|
353
|
+
finishReason: toFinishReason(data.stop_reason),
|
|
354
|
+
usage: toUsage(data.usage?.input_tokens, data.usage?.cache_read_input_tokens, data.usage?.cache_creation_input_tokens, data.usage?.output_tokens),
|
|
355
|
+
request: { body },
|
|
356
|
+
response: {
|
|
357
|
+
id: data.id,
|
|
358
|
+
modelId: data.model,
|
|
359
|
+
headers: Object.fromEntries(response.headers.entries())
|
|
360
|
+
},
|
|
361
|
+
warnings
|
|
362
|
+
};
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
function messagesStreamProtocol(config) {
|
|
366
|
+
return async (options) => {
|
|
367
|
+
const [body, warnings] = toBody(options, true);
|
|
368
|
+
const response = await post(config, options, body);
|
|
369
|
+
return {
|
|
370
|
+
request: { body },
|
|
371
|
+
response: { headers: Object.fromEntries(response.headers.entries()) },
|
|
372
|
+
stream: toStream(() => readParts(response, warnings))
|
|
373
|
+
};
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
//#endregion
|
|
377
|
+
export { messagesGenerateProtocol, messagesStreamProtocol };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type LanguageModelCallOptions, type LanguageModelGenerateResult, type LanguageModelStreamResult, type Protocol } from "../index";
|
|
2
|
+
export interface GeminiConfig {
|
|
3
|
+
baseURL: string;
|
|
4
|
+
apiKey: string | undefined;
|
|
5
|
+
}
|
|
6
|
+
export declare function geminiGenerateProtocol(config: GeminiConfig): Protocol<LanguageModelCallOptions, LanguageModelGenerateResult>;
|
|
7
|
+
export declare function geminiStreamProtocol(config: GeminiConfig): Protocol<LanguageModelCallOptions, LanguageModelStreamResult>;
|