@absolutejs/ai 0.0.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/README.md +6 -0
- package/dist/ai/client/index.js +682 -0
- package/dist/ai/client/index.js.map +15 -0
- package/dist/ai/index.js +3317 -0
- package/dist/ai/index.js.map +28 -0
- package/dist/ai/providers/anthropic.js +371 -0
- package/dist/ai/providers/anthropic.js.map +10 -0
- package/dist/ai/providers/gemini.js +260 -0
- package/dist/ai/providers/gemini.js.map +10 -0
- package/dist/ai/providers/ollama.js +221 -0
- package/dist/ai/providers/ollama.js.map +10 -0
- package/dist/ai/providers/openai.js +322 -0
- package/dist/ai/providers/openai.js.map +10 -0
- package/dist/ai/providers/openaiCompatible.js +360 -0
- package/dist/ai/providers/openaiCompatible.js.map +11 -0
- package/dist/ai/providers/openaiResponses.js +381 -0
- package/dist/ai/providers/openaiResponses.js.map +10 -0
- package/dist/src/ai/client/actions.d.ts +186 -0
- package/dist/src/ai/client/connection.d.ts +9 -0
- package/dist/src/ai/client/createAIStream.d.ts +12 -0
- package/dist/src/ai/client/index.d.ts +4 -0
- package/dist/src/ai/client/messageStore.d.ts +12 -0
- package/dist/src/ai/conversationManager.d.ts +19 -0
- package/dist/src/ai/htmxRenderers.d.ts +3 -0
- package/dist/src/ai/index.d.ts +19 -0
- package/dist/src/ai/memoryStore.d.ts +2 -0
- package/dist/src/ai/protocol.d.ts +4 -0
- package/dist/src/ai/providers/anthropic.d.ts +3 -0
- package/dist/src/ai/providers/gemini.d.ts +8 -0
- package/dist/src/ai/providers/ollama.d.ts +6 -0
- package/dist/src/ai/providers/openai.d.ts +7 -0
- package/dist/src/ai/providers/openaiCompatible.d.ts +30 -0
- package/dist/src/ai/providers/openaiResponses.d.ts +33 -0
- package/dist/src/ai/streamAI.d.ts +2 -0
- package/dist/src/ai/streamAIToSSE.d.ts +6 -0
- package/dist/src/constants.d.ts +60 -0
- package/dist/src/plugins/aiChat.d.ts +239 -0
- package/dist/types/ai.d.ts +4773 -0
- package/dist/types/anthropic.d.ts +20 -0
- package/dist/types/session.d.ts +16 -0
- package/dist/types/typeGuards.d.ts +3 -0
- package/package.json +59 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/ai/providers/anthropic.ts
|
|
3
|
+
var DEFAULT_BASE_URL = "https://api.anthropic.com";
|
|
4
|
+
var API_VERSION = "2023-06-01";
|
|
5
|
+
var MAX_TOKENS = 8192;
|
|
6
|
+
var EVENT_PREFIX_LENGTH = 7;
|
|
7
|
+
var DATA_PREFIX_LENGTH = 6;
|
|
8
|
+
var EMPTY_CHUNKS = [];
|
|
9
|
+
var isRecord = (val) => typeof val === "object" && val !== null;
|
|
10
|
+
var mapContentBlock = (block) => {
|
|
11
|
+
if (block.type === "thinking") {
|
|
12
|
+
return {
|
|
13
|
+
signature: block.signature,
|
|
14
|
+
thinking: block.thinking,
|
|
15
|
+
type: "thinking"
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
if (block.type === "image") {
|
|
19
|
+
return {
|
|
20
|
+
source: block.source,
|
|
21
|
+
type: "image"
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
if (block.type === "document") {
|
|
25
|
+
return {
|
|
26
|
+
source: block.source,
|
|
27
|
+
type: "document"
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if (block.type === "tool_result") {
|
|
31
|
+
return {
|
|
32
|
+
content: block.content,
|
|
33
|
+
tool_use_id: block.tool_use_id,
|
|
34
|
+
type: "tool_result"
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (block.type === "tool_use") {
|
|
38
|
+
return {
|
|
39
|
+
id: block.id,
|
|
40
|
+
input: block.input,
|
|
41
|
+
name: block.name,
|
|
42
|
+
type: "tool_use"
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return { text: block.content, type: "text" };
|
|
46
|
+
};
|
|
47
|
+
var mapMessage = (msg) => ({
|
|
48
|
+
content: typeof msg.content === "string" ? msg.content : msg.content.map(mapContentBlock),
|
|
49
|
+
role: msg.role === "system" ? "user" : msg.role
|
|
50
|
+
});
|
|
51
|
+
var mapToolDefinition = (tool) => ({
|
|
52
|
+
description: tool.description,
|
|
53
|
+
input_schema: tool.input_schema,
|
|
54
|
+
name: tool.name
|
|
55
|
+
});
|
|
56
|
+
var buildRequestBody = (params) => {
|
|
57
|
+
const messages = params.messages.filter((msg) => msg.role !== "system").map(mapMessage);
|
|
58
|
+
const body = {
|
|
59
|
+
max_tokens: MAX_TOKENS,
|
|
60
|
+
messages,
|
|
61
|
+
model: params.model,
|
|
62
|
+
stream: true
|
|
63
|
+
};
|
|
64
|
+
if (params.systemPrompt) {
|
|
65
|
+
body.system = params.systemPrompt;
|
|
66
|
+
}
|
|
67
|
+
if (params.tools && params.tools.length > 0) {
|
|
68
|
+
body.tools = params.tools.map(mapToolDefinition);
|
|
69
|
+
}
|
|
70
|
+
if (params.thinking) {
|
|
71
|
+
body.thinking = params.thinking;
|
|
72
|
+
body.max_tokens = Math.max(MAX_TOKENS, params.thinking.budget_tokens + MAX_TOKENS);
|
|
73
|
+
}
|
|
74
|
+
return body;
|
|
75
|
+
};
|
|
76
|
+
var classifyLine = (line) => {
|
|
77
|
+
if (line.startsWith("event: ")) {
|
|
78
|
+
return {
|
|
79
|
+
field: "event",
|
|
80
|
+
value: line.slice(EVENT_PREFIX_LENGTH)
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
if (line.startsWith("data: ")) {
|
|
84
|
+
return {
|
|
85
|
+
field: "data",
|
|
86
|
+
value: line.slice(DATA_PREFIX_LENGTH)
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return;
|
|
90
|
+
};
|
|
91
|
+
var applyClassified = (acc, classified) => {
|
|
92
|
+
if (!classified) {
|
|
93
|
+
return acc;
|
|
94
|
+
}
|
|
95
|
+
if (classified.field === "event") {
|
|
96
|
+
return { eventData: acc.eventData, eventType: classified.value };
|
|
97
|
+
}
|
|
98
|
+
return { eventData: classified.value, eventType: acc.eventType };
|
|
99
|
+
};
|
|
100
|
+
var parseEventLines = (event) => event.split(`
|
|
101
|
+
`).reduce((acc, line) => applyClassified(acc, classifyLine(line)), {
|
|
102
|
+
eventData: "",
|
|
103
|
+
eventType: ""
|
|
104
|
+
});
|
|
105
|
+
var safeParse = (text) => {
|
|
106
|
+
try {
|
|
107
|
+
const result = JSON.parse(text);
|
|
108
|
+
return result;
|
|
109
|
+
} catch {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
var tryParseJson = (text) => {
|
|
114
|
+
const result = safeParse(text);
|
|
115
|
+
if (isRecord(result)) {
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
return;
|
|
119
|
+
};
|
|
120
|
+
var getRecord = (obj, key) => {
|
|
121
|
+
const val = obj[key];
|
|
122
|
+
if (isRecord(val)) {
|
|
123
|
+
return val;
|
|
124
|
+
}
|
|
125
|
+
return;
|
|
126
|
+
};
|
|
127
|
+
var getString = (obj, key) => {
|
|
128
|
+
const val = obj[key];
|
|
129
|
+
if (typeof val === "string") {
|
|
130
|
+
return val;
|
|
131
|
+
}
|
|
132
|
+
return "";
|
|
133
|
+
};
|
|
134
|
+
var getNumber = (obj, key) => {
|
|
135
|
+
const val = obj[key];
|
|
136
|
+
if (typeof val === "number") {
|
|
137
|
+
return val;
|
|
138
|
+
}
|
|
139
|
+
return 0;
|
|
140
|
+
};
|
|
141
|
+
var handleContentBlockStart = (parsed, state) => {
|
|
142
|
+
const block = getRecord(parsed, "content_block");
|
|
143
|
+
if (block && block.type === "tool_use") {
|
|
144
|
+
state.currentToolId = getString(block, "id");
|
|
145
|
+
state.currentToolName = getString(block, "name");
|
|
146
|
+
state.toolInputJson = "";
|
|
147
|
+
state.isThinkingBlock = false;
|
|
148
|
+
} else if (block && block.type === "thinking") {
|
|
149
|
+
state.isThinkingBlock = true;
|
|
150
|
+
state.thinkingSignature = "";
|
|
151
|
+
} else {
|
|
152
|
+
state.isThinkingBlock = false;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
var handleContentBlockDelta = (parsed, state) => {
|
|
156
|
+
const delta = getRecord(parsed, "delta");
|
|
157
|
+
if (!delta) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (delta.type === "thinking_delta") {
|
|
161
|
+
return {
|
|
162
|
+
content: getString(delta, "thinking"),
|
|
163
|
+
type: "thinking"
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
if (delta.type === "text_delta") {
|
|
167
|
+
return {
|
|
168
|
+
content: getString(delta, "text"),
|
|
169
|
+
type: "text"
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
if (delta.type === "input_json_delta") {
|
|
173
|
+
state.toolInputJson += getString(delta, "partial_json");
|
|
174
|
+
}
|
|
175
|
+
if (delta.type === "signature_delta") {
|
|
176
|
+
state.thinkingSignature += getString(delta, "signature");
|
|
177
|
+
}
|
|
178
|
+
return;
|
|
179
|
+
};
|
|
180
|
+
var handleContentBlockStop = (state) => {
|
|
181
|
+
if (state.isThinkingBlock && state.thinkingSignature) {
|
|
182
|
+
state.isThinkingBlock = false;
|
|
183
|
+
const signature = state.thinkingSignature;
|
|
184
|
+
state.thinkingSignature = "";
|
|
185
|
+
return {
|
|
186
|
+
content: "",
|
|
187
|
+
signature,
|
|
188
|
+
type: "thinking"
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
if (!state.currentToolId) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const input = tryParseJson(state.toolInputJson) ?? state.toolInputJson;
|
|
195
|
+
const chunk = {
|
|
196
|
+
id: state.currentToolId,
|
|
197
|
+
input,
|
|
198
|
+
name: state.currentToolName,
|
|
199
|
+
type: "tool_use"
|
|
200
|
+
};
|
|
201
|
+
state.currentToolId = "";
|
|
202
|
+
state.currentToolName = "";
|
|
203
|
+
state.toolInputJson = "";
|
|
204
|
+
return chunk;
|
|
205
|
+
};
|
|
206
|
+
var extractUsage = (usageRecord, existingUsage) => {
|
|
207
|
+
if (!usageRecord) {
|
|
208
|
+
return existingUsage;
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
inputTokens: getNumber(usageRecord, "input_tokens") || existingUsage?.inputTokens || 0,
|
|
212
|
+
outputTokens: getNumber(usageRecord, "output_tokens") || existingUsage?.outputTokens || 0
|
|
213
|
+
};
|
|
214
|
+
};
|
|
215
|
+
var handleMessageDelta = (parsed, state) => {
|
|
216
|
+
const deltaUsage = getRecord(parsed, "usage");
|
|
217
|
+
state.usage = extractUsage(deltaUsage, state.usage);
|
|
218
|
+
};
|
|
219
|
+
var handleMessageStart = (parsed, state) => {
|
|
220
|
+
const message = getRecord(parsed, "message");
|
|
221
|
+
if (!message) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const startUsage = getRecord(message, "usage");
|
|
225
|
+
state.usage = extractUsage(startUsage, state.usage);
|
|
226
|
+
};
|
|
227
|
+
var handleError = (parsed) => {
|
|
228
|
+
const error = getRecord(parsed, "error");
|
|
229
|
+
const errorMessage = error ? getString(error, "message") : "";
|
|
230
|
+
throw new Error(errorMessage || "Anthropic API error");
|
|
231
|
+
};
|
|
232
|
+
var processEvent = (eventType, parsed, state) => {
|
|
233
|
+
switch (eventType) {
|
|
234
|
+
case "content_block_start": {
|
|
235
|
+
handleContentBlockStart(parsed, state);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
case "content_block_delta": {
|
|
239
|
+
return handleContentBlockDelta(parsed, state);
|
|
240
|
+
}
|
|
241
|
+
case "content_block_stop": {
|
|
242
|
+
return handleContentBlockStop(state);
|
|
243
|
+
}
|
|
244
|
+
case "message_delta": {
|
|
245
|
+
handleMessageDelta(parsed, state);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
case "message_start": {
|
|
249
|
+
handleMessageStart(parsed, state);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
case "message_stop": {
|
|
253
|
+
return { type: "done", usage: state.usage };
|
|
254
|
+
}
|
|
255
|
+
case "error": {
|
|
256
|
+
handleError(parsed);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
default: {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
var processSingleEvent = (event, state) => {
|
|
265
|
+
if (!event.trim()) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const { eventData, eventType } = parseEventLines(event);
|
|
269
|
+
if (!eventData) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
const parsed = tryParseJson(eventData);
|
|
273
|
+
if (!parsed) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
return processEvent(eventType, parsed, state);
|
|
277
|
+
};
|
|
278
|
+
var collectChunk = (event, state) => {
|
|
279
|
+
const chunk = processSingleEvent(event, state);
|
|
280
|
+
return chunk ? [chunk] : [];
|
|
281
|
+
};
|
|
282
|
+
var processBufferedEvents = (eventsText, state) => {
|
|
283
|
+
const events = eventsText.split(`
|
|
284
|
+
|
|
285
|
+
`);
|
|
286
|
+
state.buffer = events.pop() ?? "";
|
|
287
|
+
return events.flatMap((event) => collectChunk(event, state));
|
|
288
|
+
};
|
|
289
|
+
var readNextChunks = async (reader, decoder, state, signal) => {
|
|
290
|
+
if (signal?.aborted) {
|
|
291
|
+
return { chunks: EMPTY_CHUNKS, done: true };
|
|
292
|
+
}
|
|
293
|
+
const { done, value } = await reader.read();
|
|
294
|
+
if (done) {
|
|
295
|
+
return { chunks: EMPTY_CHUNKS, done: true };
|
|
296
|
+
}
|
|
297
|
+
const rawText = state.buffer + decoder.decode(value, { stream: true });
|
|
298
|
+
const chunks = processBufferedEvents(rawText, state);
|
|
299
|
+
return { chunks, done: false };
|
|
300
|
+
};
|
|
301
|
+
var findDoneChunk = (chunks) => chunks.findIndex((c) => c.type === "done");
|
|
302
|
+
var sseStreamLoop = async (reader, decoder, state, signal) => {
|
|
303
|
+
const result = await readNextChunks(reader, decoder, state, signal);
|
|
304
|
+
if (result.done) {
|
|
305
|
+
return { chunks: result.chunks, finished: true };
|
|
306
|
+
}
|
|
307
|
+
const doneIdx = findDoneChunk(result.chunks);
|
|
308
|
+
if (doneIdx >= 0) {
|
|
309
|
+
return { chunks: result.chunks.slice(0, doneIdx + 1), finished: true };
|
|
310
|
+
}
|
|
311
|
+
return { chunks: result.chunks, finished: false };
|
|
312
|
+
};
|
|
313
|
+
async function* streamChunks(reader, decoder, state, signal) {
|
|
314
|
+
let finished = false;
|
|
315
|
+
while (!finished) {
|
|
316
|
+
const result = await sseStreamLoop(reader, decoder, state, signal);
|
|
317
|
+
({ finished } = result);
|
|
318
|
+
yield* result.chunks;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
async function* parseSSEStream(body, signal) {
|
|
322
|
+
const reader = body.getReader();
|
|
323
|
+
const decoder = new TextDecoder;
|
|
324
|
+
const state = {
|
|
325
|
+
buffer: "",
|
|
326
|
+
currentToolId: "",
|
|
327
|
+
currentToolName: "",
|
|
328
|
+
isThinkingBlock: false,
|
|
329
|
+
thinkingSignature: "",
|
|
330
|
+
toolInputJson: "",
|
|
331
|
+
usage: undefined
|
|
332
|
+
};
|
|
333
|
+
try {
|
|
334
|
+
yield* streamChunks(reader, decoder, state, signal);
|
|
335
|
+
} finally {
|
|
336
|
+
reader.releaseLock();
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
var fetchAndStream = async function* (baseUrl, config, params) {
|
|
340
|
+
const body = buildRequestBody(params);
|
|
341
|
+
const response = await fetch(`${baseUrl}/v1/messages`, {
|
|
342
|
+
body: JSON.stringify(body),
|
|
343
|
+
headers: {
|
|
344
|
+
"anthropic-version": API_VERSION,
|
|
345
|
+
"Content-Type": "application/json",
|
|
346
|
+
"x-api-key": config.apiKey
|
|
347
|
+
},
|
|
348
|
+
method: "POST",
|
|
349
|
+
signal: params.signal
|
|
350
|
+
});
|
|
351
|
+
if (!response.ok) {
|
|
352
|
+
const errorText = await response.text();
|
|
353
|
+
throw new Error(`Anthropic API error ${response.status}: ${errorText}`);
|
|
354
|
+
}
|
|
355
|
+
if (!response.body) {
|
|
356
|
+
throw new Error("Anthropic API returned no response body");
|
|
357
|
+
}
|
|
358
|
+
yield* parseSSEStream(response.body, params.signal);
|
|
359
|
+
};
|
|
360
|
+
var anthropic = (config) => {
|
|
361
|
+
const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
|
|
362
|
+
return {
|
|
363
|
+
stream: (params) => fetchAndStream(baseUrl, config, params)
|
|
364
|
+
};
|
|
365
|
+
};
|
|
366
|
+
export {
|
|
367
|
+
anthropic
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
//# debugId=46FDFC941805E6B864756E2164756E21
|
|
371
|
+
//# sourceMappingURL=anthropic.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/ai/providers/anthropic.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type {\n\tAIChunk,\n\tAIProviderConfig,\n\tAIProviderContentBlock,\n\tAIProviderMessage,\n\tAIProviderStreamParams,\n\tAIProviderToolDefinition,\n\tAIUsage\n} from '../../../types/ai';\nimport type {\n\tAnthropicConfig,\n\tAnthropicMessage,\n\tAnthropicSSEState\n} from '../../../types/anthropic';\n\nconst DEFAULT_BASE_URL = 'https://api.anthropic.com';\nconst API_VERSION = '2023-06-01';\nconst MAX_TOKENS = 8192;\nconst EVENT_PREFIX_LENGTH = 7;\nconst DATA_PREFIX_LENGTH = 6;\n\nconst EMPTY_CHUNKS: AIChunk[] = [];\n\nconst isRecord = (val: unknown): val is Record<string, unknown> =>\n\ttypeof val === 'object' && val !== null;\n\nconst mapContentBlock = (block: AIProviderContentBlock) => {\n\tif (block.type === 'thinking') {\n\t\treturn {\n\t\t\tsignature: block.signature,\n\t\t\tthinking: block.thinking,\n\t\t\ttype: 'thinking'\n\t\t};\n\t}\n\n\tif (block.type === 'image') {\n\t\treturn {\n\t\t\tsource: block.source,\n\t\t\ttype: 'image'\n\t\t};\n\t}\n\n\tif (block.type === 'document') {\n\t\treturn {\n\t\t\tsource: block.source,\n\t\t\ttype: 'document'\n\t\t};\n\t}\n\n\tif (block.type === 'tool_result') {\n\t\treturn {\n\t\t\tcontent: block.content,\n\t\t\ttool_use_id: block.tool_use_id,\n\t\t\ttype: 'tool_result'\n\t\t};\n\t}\n\n\tif (block.type === 'tool_use') {\n\t\treturn {\n\t\t\tid: block.id,\n\t\t\tinput: block.input,\n\t\t\tname: block.name,\n\t\t\ttype: 'tool_use'\n\t\t};\n\t}\n\n\treturn { text: block.content, type: 'text' };\n};\n\nconst mapMessage = (msg: AIProviderMessage): AnthropicMessage => ({\n\tcontent:\n\t\ttypeof msg.content === 'string'\n\t\t\t? msg.content\n\t\t\t: msg.content.map(mapContentBlock),\n\trole: msg.role === 'system' ? 'user' : msg.role\n});\n\nconst mapToolDefinition = (tool: AIProviderToolDefinition) => ({\n\tdescription: tool.description,\n\tinput_schema: tool.input_schema,\n\tname: tool.name\n});\n\nconst buildRequestBody = (params: AIProviderStreamParams) => {\n\tconst messages: AnthropicMessage[] = params.messages\n\t\t.filter((msg) => msg.role !== 'system')\n\t\t.map(mapMessage);\n\n\tconst body: Record<string, unknown> = {\n\t\tmax_tokens: MAX_TOKENS,\n\t\tmessages,\n\t\tmodel: params.model,\n\t\tstream: true\n\t};\n\n\tif (params.systemPrompt) {\n\t\tbody.system = params.systemPrompt;\n\t}\n\n\tif (params.tools && params.tools.length > 0) {\n\t\tbody.tools = params.tools.map(mapToolDefinition);\n\t}\n\n\tif (params.thinking) {\n\t\tbody.thinking = params.thinking;\n\t\t// When thinking is enabled, max_tokens must be higher\n\t\tbody.max_tokens = Math.max(\n\t\t\tMAX_TOKENS,\n\t\t\tparams.thinking.budget_tokens + MAX_TOKENS\n\t\t);\n\t}\n\n\treturn body;\n};\n\nconst classifyLine = (line: string) => {\n\tif (line.startsWith('event: ')) {\n\t\treturn {\n\t\t\tfield: 'event' as const,\n\t\t\tvalue: line.slice(EVENT_PREFIX_LENGTH)\n\t\t};\n\t}\n\n\tif (line.startsWith('data: ')) {\n\t\treturn {\n\t\t\tfield: 'data' as const,\n\t\t\tvalue: line.slice(DATA_PREFIX_LENGTH)\n\t\t};\n\t}\n\n\treturn undefined;\n};\n\nconst applyClassified = (\n\tacc: { eventData: string; eventType: string },\n\tclassified: { field: 'event' | 'data'; value: string } | undefined\n) => {\n\tif (!classified) {\n\t\treturn acc;\n\t}\n\n\tif (classified.field === 'event') {\n\t\treturn { eventData: acc.eventData, eventType: classified.value };\n\t}\n\n\treturn { eventData: classified.value, eventType: acc.eventType };\n};\n\nconst parseEventLines = (event: string) =>\n\tevent\n\t\t.split('\\n')\n\t\t.reduce((acc, line) => applyClassified(acc, classifyLine(line)), {\n\t\t\teventData: '',\n\t\t\teventType: ''\n\t\t});\n\nconst safeParse = (text: string) => {\n\ttry {\n\t\tconst result: unknown = JSON.parse(text);\n\n\t\treturn result;\n\t} catch {\n\t\treturn undefined;\n\t}\n};\n\nconst tryParseJson = (text: string) => {\n\tconst result = safeParse(text);\n\n\tif (isRecord(result)) {\n\t\treturn result;\n\t}\n\n\treturn undefined;\n};\n\nconst getRecord = (obj: Record<string, unknown>, key: string) => {\n\tconst val = obj[key];\n\n\tif (isRecord(val)) {\n\t\treturn val;\n\t}\n\n\treturn undefined;\n};\n\nconst getString = (obj: Record<string, unknown>, key: string) => {\n\tconst val = obj[key];\n\n\tif (typeof val === 'string') {\n\t\treturn val;\n\t}\n\n\treturn '';\n};\n\nconst getNumber = (obj: Record<string, unknown>, key: string) => {\n\tconst val = obj[key];\n\n\tif (typeof val === 'number') {\n\t\treturn val;\n\t}\n\n\treturn 0;\n};\n\nconst handleContentBlockStart = (\n\tparsed: Record<string, unknown>,\n\tstate: AnthropicSSEState\n) => {\n\tconst block = getRecord(parsed, 'content_block');\n\n\tif (block && block.type === 'tool_use') {\n\t\tstate.currentToolId = getString(block, 'id');\n\t\tstate.currentToolName = getString(block, 'name');\n\t\tstate.toolInputJson = '';\n\t\tstate.isThinkingBlock = false;\n\t} else if (block && block.type === 'thinking') {\n\t\tstate.isThinkingBlock = true;\n\t\tstate.thinkingSignature = '';\n\t} else {\n\t\tstate.isThinkingBlock = false;\n\t}\n};\n\nconst handleContentBlockDelta = (\n\tparsed: Record<string, unknown>,\n\tstate: AnthropicSSEState\n) => {\n\tconst delta = getRecord(parsed, 'delta');\n\n\tif (!delta) {\n\t\treturn undefined;\n\t}\n\n\tif (delta.type === 'thinking_delta') {\n\t\treturn {\n\t\t\tcontent: getString(delta, 'thinking'),\n\t\t\ttype: 'thinking'\n\t\t} satisfies AIChunk;\n\t}\n\n\tif (delta.type === 'text_delta') {\n\t\treturn {\n\t\t\tcontent: getString(delta, 'text'),\n\t\t\ttype: 'text'\n\t\t} satisfies AIChunk;\n\t}\n\n\tif (delta.type === 'input_json_delta') {\n\t\tstate.toolInputJson += getString(delta, 'partial_json');\n\t}\n\n\tif (delta.type === 'signature_delta') {\n\t\tstate.thinkingSignature += getString(delta, 'signature');\n\t}\n\n\treturn undefined;\n};\n\nconst handleContentBlockStop = (state: AnthropicSSEState) => {\n\t// Emit thinking signature when thinking block completes\n\tif (state.isThinkingBlock && state.thinkingSignature) {\n\t\tstate.isThinkingBlock = false;\n\t\tconst signature = state.thinkingSignature;\n\t\tstate.thinkingSignature = '';\n\n\t\treturn {\n\t\t\tcontent: '',\n\t\t\tsignature,\n\t\t\ttype: 'thinking'\n\t\t} satisfies AIChunk;\n\t}\n\n\tif (!state.currentToolId) {\n\t\treturn undefined;\n\t}\n\n\tconst input = tryParseJson(state.toolInputJson) ?? state.toolInputJson;\n\n\tconst chunk: AIChunk = {\n\t\tid: state.currentToolId,\n\t\tinput,\n\t\tname: state.currentToolName,\n\t\ttype: 'tool_use'\n\t};\n\n\tstate.currentToolId = '';\n\tstate.currentToolName = '';\n\tstate.toolInputJson = '';\n\n\treturn chunk;\n};\n\nconst extractUsage = (\n\tusageRecord: Record<string, unknown> | undefined,\n\texistingUsage: AIUsage | undefined\n) => {\n\tif (!usageRecord) {\n\t\treturn existingUsage;\n\t}\n\n\treturn {\n\t\tinputTokens:\n\t\t\tgetNumber(usageRecord, 'input_tokens') ||\n\t\t\texistingUsage?.inputTokens ||\n\t\t\t0,\n\t\toutputTokens:\n\t\t\tgetNumber(usageRecord, 'output_tokens') ||\n\t\t\texistingUsage?.outputTokens ||\n\t\t\t0\n\t};\n};\n\nconst handleMessageDelta = (\n\tparsed: Record<string, unknown>,\n\tstate: AnthropicSSEState\n) => {\n\tconst deltaUsage = getRecord(parsed, 'usage');\n\tstate.usage = extractUsage(deltaUsage, state.usage);\n};\n\nconst handleMessageStart = (\n\tparsed: Record<string, unknown>,\n\tstate: AnthropicSSEState\n) => {\n\tconst message = getRecord(parsed, 'message');\n\n\tif (!message) {\n\t\treturn;\n\t}\n\n\tconst startUsage = getRecord(message, 'usage');\n\tstate.usage = extractUsage(startUsage, state.usage);\n};\n\nconst handleError = (parsed: Record<string, unknown>) => {\n\tconst error = getRecord(parsed, 'error');\n\tconst errorMessage = error ? getString(error, 'message') : '';\n\n\tthrow new Error(errorMessage || 'Anthropic API error');\n};\n\nconst processEvent = (\n\teventType: string,\n\tparsed: Record<string, unknown>,\n\tstate: AnthropicSSEState\n) => {\n\tswitch (eventType) {\n\t\tcase 'content_block_start': {\n\t\t\thandleContentBlockStart(parsed, state);\n\n\t\t\treturn undefined;\n\t\t}\n\n\t\tcase 'content_block_delta': {\n\t\t\treturn handleContentBlockDelta(parsed, state);\n\t\t}\n\n\t\tcase 'content_block_stop': {\n\t\t\treturn handleContentBlockStop(state);\n\t\t}\n\n\t\tcase 'message_delta': {\n\t\t\thandleMessageDelta(parsed, state);\n\n\t\t\treturn undefined;\n\t\t}\n\n\t\tcase 'message_start': {\n\t\t\thandleMessageStart(parsed, state);\n\n\t\t\treturn undefined;\n\t\t}\n\n\t\tcase 'message_stop': {\n\t\t\treturn { type: 'done' as const, usage: state.usage };\n\t\t}\n\n\t\tcase 'error': {\n\t\t\thandleError(parsed);\n\n\t\t\treturn undefined;\n\t\t}\n\n\t\tdefault: {\n\t\t\treturn undefined;\n\t\t}\n\t}\n};\n\nconst processSingleEvent = (event: string, state: AnthropicSSEState) => {\n\tif (!event.trim()) {\n\t\treturn undefined;\n\t}\n\n\tconst { eventData, eventType } = parseEventLines(event);\n\n\tif (!eventData) {\n\t\treturn undefined;\n\t}\n\n\tconst parsed = tryParseJson(eventData);\n\n\tif (!parsed) {\n\t\treturn undefined;\n\t}\n\n\treturn processEvent(eventType, parsed, state);\n};\n\nconst collectChunk = (event: string, state: AnthropicSSEState) => {\n\tconst chunk = processSingleEvent(event, state);\n\n\treturn chunk ? [chunk] : [];\n};\n\nconst processBufferedEvents = (\n\teventsText: string,\n\tstate: AnthropicSSEState\n) => {\n\tconst events = eventsText.split('\\n\\n');\n\tstate.buffer = events.pop() ?? '';\n\n\treturn events.flatMap((event) => collectChunk(event, state));\n};\n\nconst readNextChunks = async (\n\treader: ReadableStreamDefaultReader<Uint8Array>,\n\tdecoder: TextDecoder,\n\tstate: AnthropicSSEState,\n\tsignal?: AbortSignal\n) => {\n\tif (signal?.aborted) {\n\t\treturn { chunks: EMPTY_CHUNKS, done: true };\n\t}\n\n\tconst { done, value } = await reader.read();\n\n\tif (done) {\n\t\treturn { chunks: EMPTY_CHUNKS, done: true };\n\t}\n\n\tconst rawText = state.buffer + decoder.decode(value, { stream: true });\n\tconst chunks = processBufferedEvents(rawText, state);\n\n\treturn { chunks, done: false };\n};\n\nconst findDoneChunk = (chunks: AIChunk[]) =>\n\tchunks.findIndex((c) => c.type === 'done');\n\nconst sseStreamLoop = async (\n\treader: ReadableStreamDefaultReader<Uint8Array>,\n\tdecoder: TextDecoder,\n\tstate: AnthropicSSEState,\n\tsignal?: AbortSignal\n) => {\n\tconst result = await readNextChunks(reader, decoder, state, signal);\n\n\tif (result.done) {\n\t\treturn { chunks: result.chunks, finished: true };\n\t}\n\n\tconst doneIdx = findDoneChunk(result.chunks);\n\n\tif (doneIdx >= 0) {\n\t\treturn { chunks: result.chunks.slice(0, doneIdx + 1), finished: true };\n\t}\n\n\treturn { chunks: result.chunks, finished: false };\n};\n\n// eslint-disable-next-line func-style\nasync function* streamChunks(\n\treader: ReadableStreamDefaultReader<Uint8Array>,\n\tdecoder: TextDecoder,\n\tstate: AnthropicSSEState,\n\tsignal?: AbortSignal\n) {\n\tlet finished = false;\n\n\twhile (!finished) {\n\t\t// eslint-disable-next-line no-await-in-loop\n\t\tconst result = await sseStreamLoop(reader, decoder, state, signal);\n\t\t({ finished } = result);\n\t\tyield* result.chunks;\n\t}\n}\n\n// eslint-disable-next-line func-style\nasync function* parseSSEStream(\n\tbody: ReadableStream<Uint8Array>,\n\tsignal?: AbortSignal\n) {\n\tconst reader = body.getReader();\n\tconst decoder = new TextDecoder();\n\n\tconst state: AnthropicSSEState = {\n\t\tbuffer: '',\n\t\tcurrentToolId: '',\n\t\tcurrentToolName: '',\n\t\tisThinkingBlock: false,\n\t\tthinkingSignature: '',\n\t\ttoolInputJson: '',\n\t\tusage: undefined\n\t};\n\n\ttry {\n\t\tyield* streamChunks(reader, decoder, state, signal);\n\t} finally {\n\t\treader.releaseLock();\n\t}\n}\n\nconst fetchAndStream = async function* (\n\tbaseUrl: string,\n\tconfig: AnthropicConfig,\n\tparams: AIProviderStreamParams\n) {\n\tconst body = buildRequestBody(params);\n\n\tconst response = await fetch(`${baseUrl}/v1/messages`, {\n\t\tbody: JSON.stringify(body),\n\t\theaders: {\n\t\t\t'anthropic-version': API_VERSION,\n\t\t\t'Content-Type': 'application/json',\n\t\t\t'x-api-key': config.apiKey\n\t\t},\n\t\tmethod: 'POST',\n\t\tsignal: params.signal\n\t});\n\n\tif (!response.ok) {\n\t\tconst errorText = await response.text();\n\n\t\tthrow new Error(`Anthropic API error ${response.status}: ${errorText}`);\n\t}\n\n\tif (!response.body) {\n\t\tthrow new Error('Anthropic API returned no response body');\n\t}\n\n\tyield* parseSSEStream(response.body, params.signal);\n};\n\nexport const anthropic = (config: AnthropicConfig): AIProviderConfig => {\n\tconst baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n\n\treturn {\n\t\tstream: (params: AIProviderStreamParams) =>\n\t\t\tfetchAndStream(baseUrl, config, params)\n\t};\n};\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "46FDFC941805E6B864756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/ai/providers/gemini.ts
|
|
3
|
+
var DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com";
|
|
4
|
+
var SSE_DATA_PREFIX_LENGTH = 6;
|
|
5
|
+
var isRecord = (value) => typeof value === "object" && value !== null;
|
|
6
|
+
var isRecordArray = (value) => Array.isArray(value) && value.length > 0 && isRecord(value[0]);
|
|
7
|
+
var mapRole = (role) => {
|
|
8
|
+
if (role === "assistant" || role === "system")
|
|
9
|
+
return "model";
|
|
10
|
+
return "user";
|
|
11
|
+
};
|
|
12
|
+
var mapContentBlock = (block) => {
|
|
13
|
+
switch (block.type) {
|
|
14
|
+
case "text":
|
|
15
|
+
return { text: block.content };
|
|
16
|
+
case "image":
|
|
17
|
+
return {
|
|
18
|
+
inlineData: {
|
|
19
|
+
data: block.source.data,
|
|
20
|
+
mimeType: block.source.media_type
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
case "document":
|
|
24
|
+
return {
|
|
25
|
+
inlineData: {
|
|
26
|
+
data: block.source.data,
|
|
27
|
+
mimeType: block.source.media_type
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
case "tool_use":
|
|
31
|
+
return {
|
|
32
|
+
functionCall: {
|
|
33
|
+
args: typeof block.input === "string" ? JSON.parse(block.input) : block.input,
|
|
34
|
+
name: block.name
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
case "tool_result":
|
|
38
|
+
return {
|
|
39
|
+
functionResponse: {
|
|
40
|
+
name: block.tool_use_id,
|
|
41
|
+
response: { result: block.content }
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
default:
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
var convertMessageContent = (content) => {
|
|
49
|
+
if (typeof content === "string") {
|
|
50
|
+
return [{ text: content }];
|
|
51
|
+
}
|
|
52
|
+
return content.map(mapContentBlock).filter((mapped) => mapped !== null);
|
|
53
|
+
};
|
|
54
|
+
var hasFunctionResponse = (content) => content.some((block) => block.type === "tool_result");
|
|
55
|
+
var convertSingleMessage = (msg) => {
|
|
56
|
+
if (typeof msg.content !== "string" && hasFunctionResponse(msg.content)) {
|
|
57
|
+
return { parts: convertMessageContent(msg.content), role: "user" };
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
parts: convertMessageContent(msg.content),
|
|
61
|
+
role: mapRole(msg.role)
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
var convertMessages = (messages) => messages.map(convertSingleMessage);
|
|
65
|
+
var mapToolDefinitions = (tools) => tools.map((tool) => ({
|
|
66
|
+
description: tool.description,
|
|
67
|
+
name: tool.name,
|
|
68
|
+
parameters: tool.input_schema
|
|
69
|
+
}));
|
|
70
|
+
var buildRequestBody = (params, isImageModel) => {
|
|
71
|
+
const body = {
|
|
72
|
+
contents: convertMessages(params.messages)
|
|
73
|
+
};
|
|
74
|
+
if (isImageModel) {
|
|
75
|
+
body.generationConfig = {
|
|
76
|
+
responseModalities: ["TEXT", "IMAGE"]
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
if (params.systemPrompt) {
|
|
80
|
+
body.systemInstruction = {
|
|
81
|
+
parts: [{ text: params.systemPrompt }]
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
if (params.tools && params.tools.length > 0) {
|
|
85
|
+
body.tools = [
|
|
86
|
+
{ functionDeclarations: mapToolDefinitions(params.tools) }
|
|
87
|
+
];
|
|
88
|
+
}
|
|
89
|
+
return body;
|
|
90
|
+
};
|
|
91
|
+
var extractMimeFormat = (mimeType) => {
|
|
92
|
+
if (typeof mimeType !== "string") {
|
|
93
|
+
return "png";
|
|
94
|
+
}
|
|
95
|
+
if (mimeType.includes("jpeg") || mimeType.includes("jpg"))
|
|
96
|
+
return "jpeg";
|
|
97
|
+
if (mimeType.includes("webp"))
|
|
98
|
+
return "webp";
|
|
99
|
+
return "png";
|
|
100
|
+
};
|
|
101
|
+
var processTextPart = function* (part) {
|
|
102
|
+
if (typeof part.text === "string" && part.text) {
|
|
103
|
+
yield { content: part.text, type: "text" };
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
var processInlineDataPart = function* (inlineData) {
|
|
107
|
+
const data = typeof inlineData.data === "string" ? inlineData.data : "";
|
|
108
|
+
const { mimeType } = inlineData;
|
|
109
|
+
if (!data) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
yield {
|
|
113
|
+
data,
|
|
114
|
+
format: extractMimeFormat(mimeType),
|
|
115
|
+
isPartial: false,
|
|
116
|
+
type: "image"
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
var processFunctionCallPart = function* (functionCall) {
|
|
120
|
+
const name = typeof functionCall.name === "string" ? functionCall.name : "";
|
|
121
|
+
const args = functionCall.args ?? {};
|
|
122
|
+
yield {
|
|
123
|
+
id: `gemini-${name}-${Date.now()}`,
|
|
124
|
+
input: args,
|
|
125
|
+
name,
|
|
126
|
+
type: "tool_use"
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
var processPart = function* (part) {
|
|
130
|
+
if ("text" in part) {
|
|
131
|
+
yield* processTextPart(part);
|
|
132
|
+
}
|
|
133
|
+
if (isRecord(part.inlineData)) {
|
|
134
|
+
yield* processInlineDataPart(part.inlineData);
|
|
135
|
+
}
|
|
136
|
+
if (isRecord(part.functionCall)) {
|
|
137
|
+
yield* processFunctionCallPart(part.functionCall);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
var extractUsage = (parsed) => {
|
|
141
|
+
if (!isRecord(parsed.usageMetadata)) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const { usageMetadata } = parsed;
|
|
145
|
+
return {
|
|
146
|
+
inputTokens: typeof usageMetadata.promptTokenCount === "number" ? usageMetadata.promptTokenCount : 0,
|
|
147
|
+
outputTokens: typeof usageMetadata.candidatesTokenCount === "number" ? usageMetadata.candidatesTokenCount : 0
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
var processChunk = function* (parsed, state) {
|
|
151
|
+
const usage = extractUsage(parsed);
|
|
152
|
+
if (usage) {
|
|
153
|
+
state.usage = usage;
|
|
154
|
+
}
|
|
155
|
+
if (!isRecordArray(parsed.candidates)) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const [candidate] = parsed.candidates;
|
|
159
|
+
if (!candidate || !isRecord(candidate.content)) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const { content } = candidate;
|
|
163
|
+
if (!isRecordArray(content.parts)) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
for (const part of content.parts) {
|
|
167
|
+
yield* processPart(part);
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
var processSSELine = function* (line, state) {
|
|
171
|
+
const trimmed = line.trim();
|
|
172
|
+
if (!trimmed || !trimmed.startsWith("data: ")) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const data = trimmed.slice(SSE_DATA_PREFIX_LENGTH);
|
|
176
|
+
let parsed;
|
|
177
|
+
try {
|
|
178
|
+
parsed = JSON.parse(data);
|
|
179
|
+
} catch {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
yield* processChunk(parsed, state);
|
|
183
|
+
};
|
|
184
|
+
var processSSEBuffer = function* (lines, state) {
|
|
185
|
+
for (const line of lines) {
|
|
186
|
+
yield* processSSELine(line, state);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
var drainReader = async function* (reader, decoder, state, signal) {
|
|
190
|
+
let textBuffer = "";
|
|
191
|
+
for (let result = await reader.read();!result.done && !signal?.aborted; result = await reader.read()) {
|
|
192
|
+
textBuffer += decoder.decode(result.value, { stream: true });
|
|
193
|
+
const lines = textBuffer.split(`
|
|
194
|
+
`);
|
|
195
|
+
textBuffer = lines.pop() ?? "";
|
|
196
|
+
yield* processSSEBuffer(lines, state);
|
|
197
|
+
}
|
|
198
|
+
if (textBuffer.trim()) {
|
|
199
|
+
yield* processSSELine(textBuffer, state);
|
|
200
|
+
}
|
|
201
|
+
yield { type: "done", usage: state.usage };
|
|
202
|
+
};
|
|
203
|
+
var parseSSEStream = async function* (body, signal) {
|
|
204
|
+
const reader = body.getReader();
|
|
205
|
+
const decoder = new TextDecoder;
|
|
206
|
+
const state = {
|
|
207
|
+
buffer: "",
|
|
208
|
+
usage: undefined
|
|
209
|
+
};
|
|
210
|
+
try {
|
|
211
|
+
yield* drainReader(reader, decoder, state, signal);
|
|
212
|
+
} finally {
|
|
213
|
+
reader.releaseLock();
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
var fetchGeminiStream = async function* (baseUrl, apiKey, model, body, signal) {
|
|
217
|
+
const url = `${baseUrl}/v1beta/models/${model}:streamGenerateContent?alt=sse&key=${apiKey}`;
|
|
218
|
+
const response = await fetch(url, {
|
|
219
|
+
body: JSON.stringify(body),
|
|
220
|
+
headers: {
|
|
221
|
+
"Content-Type": "application/json"
|
|
222
|
+
},
|
|
223
|
+
method: "POST",
|
|
224
|
+
signal
|
|
225
|
+
});
|
|
226
|
+
if (!response.ok) {
|
|
227
|
+
const errorText = await response.text();
|
|
228
|
+
throw new Error(`Gemini API error ${response.status}: ${errorText}`);
|
|
229
|
+
}
|
|
230
|
+
if (!response.body) {
|
|
231
|
+
throw new Error("Gemini API returned no response body");
|
|
232
|
+
}
|
|
233
|
+
yield* parseSSEStream(response.body, signal);
|
|
234
|
+
};
|
|
235
|
+
var resolveImageModels = (raw) => {
|
|
236
|
+
if (!raw) {
|
|
237
|
+
return new Set;
|
|
238
|
+
}
|
|
239
|
+
if (raw instanceof Set) {
|
|
240
|
+
return raw;
|
|
241
|
+
}
|
|
242
|
+
return new Set(raw);
|
|
243
|
+
};
|
|
244
|
+
var gemini = (config) => {
|
|
245
|
+
const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
|
|
246
|
+
const imageModels = resolveImageModels(config.imageModels);
|
|
247
|
+
return {
|
|
248
|
+
stream: (params) => {
|
|
249
|
+
const isImageModel = imageModels.has(params.model);
|
|
250
|
+
const body = buildRequestBody(params, isImageModel);
|
|
251
|
+
return fetchGeminiStream(baseUrl, config.apiKey, params.model, body, params.signal);
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
};
|
|
255
|
+
export {
|
|
256
|
+
gemini
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
//# debugId=9DAEE9BA5DB0F89864756E2164756E21
|
|
260
|
+
//# sourceMappingURL=gemini.js.map
|