@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.
@@ -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>;