190proof 1.0.76 → 1.0.77

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.mjs CHANGED
@@ -101,15 +101,42 @@ function isHeicImage(name, mime) {
101
101
 
102
102
  // index.ts
103
103
  import { GoogleGenAI } from "@google/genai";
104
- var { GoogleGenerativeAI } = __require("@google/generative-ai");
105
104
  var sharp = __require("sharp");
106
105
  var decode = __require("heic-decode");
106
+ async function withRetries(identifier, apiName, fn, options = {}) {
107
+ var _a;
108
+ const { retries = 5, baseDelayMs = 125, onError } = options;
109
+ logger_default.log(identifier, `Calling ${apiName} API with retries`);
110
+ let lastError;
111
+ for (let attempt = 0; attempt < retries; attempt++) {
112
+ try {
113
+ return await fn();
114
+ } catch (error3) {
115
+ lastError = error3;
116
+ if (onError) {
117
+ onError(error3, attempt);
118
+ } else {
119
+ logger_default.error(
120
+ identifier,
121
+ `Retry #${attempt} error: ${error3.message}`,
122
+ ((_a = error3.response) == null ? void 0 : _a.data) || error3
123
+ );
124
+ }
125
+ await timeout(baseDelayMs * attempt);
126
+ }
127
+ }
128
+ const error2 = new Error(
129
+ `Failed to call ${apiName} API after ${retries} attempts`
130
+ );
131
+ error2.cause = lastError;
132
+ throw error2;
133
+ }
107
134
  function parseStreamedResponse(identifier, paragraph, functionCallName, functionCallArgs, allowedFunctionNames) {
108
135
  let functionCall = null;
109
136
  if (functionCallName && functionCallArgs) {
110
137
  if (allowedFunctionNames && !allowedFunctionNames.has(functionCallName)) {
111
138
  throw new Error(
112
- "Stream error: received function call with unknown name: " + functionCallName
139
+ `Stream error: received function call with unknown name: ${functionCallName}`
113
140
  );
114
141
  }
115
142
  try {
@@ -120,7 +147,7 @@ function parseStreamedResponse(identifier, paragraph, functionCallName, function
120
147
  } catch (error2) {
121
148
  logger_default.error(
122
149
  identifier,
123
- "Error parsing functionCallArgs:",
150
+ "Error parsing function call arguments:",
124
151
  functionCallArgs
125
152
  );
126
153
  throw error2;
@@ -129,7 +156,7 @@ function parseStreamedResponse(identifier, paragraph, functionCallName, function
129
156
  if (!paragraph && !functionCall) {
130
157
  logger_default.error(
131
158
  identifier,
132
- "Stream error: received message without content or function_call, raw:",
159
+ "Stream error: received message without content or function_call:",
133
160
  JSON.stringify({ paragraph, functionCallName, functionCallArgs })
134
161
  );
135
162
  throw new Error(
@@ -143,328 +170,252 @@ function parseStreamedResponse(identifier, paragraph, functionCallName, function
143
170
  files: []
144
171
  };
145
172
  }
146
- async function callOpenAiWithRetries(identifier, openAiPayload, openAiConfig, retries = 5, chunkTimeoutMs = 15e3) {
147
- var _a, _b;
148
- logger_default.log(
149
- identifier,
150
- "Calling OpenAI API with retries:",
151
- openAiConfig == null ? void 0 : openAiConfig.service,
152
- openAiPayload.model
153
- );
154
- let errorObj;
155
- for (let i = 0; i <= retries; i++) {
156
- try {
157
- const timerId = `timer:${identifier}:${Date.now()}:callOpenAi:${openAiConfig == null ? void 0 : openAiConfig.service}-${openAiPayload.model}-${openAiConfig == null ? void 0 : openAiConfig.orgId}`;
158
- if (openAiPayload.model === "o1-mini" /* O1_MINI */ || openAiPayload.model === "o1-preview" /* O1_PREVIEW */) {
159
- return await callOpenAI(identifier, openAiPayload, openAiConfig);
160
- } else {
161
- return await callOpenAIStream(
162
- identifier,
163
- openAiPayload,
164
- openAiConfig,
165
- chunkTimeoutMs
166
- );
167
- }
168
- } catch (error2) {
169
- logger_default.error(
170
- identifier,
171
- `Retry #${i} error: ${error2.message}`,
172
- ((_a = error2.response) == null ? void 0 : _a.data) || error2.data || error2
173
- );
174
- const errorCode = (_b = error2.data) == null ? void 0 : _b.code;
175
- if (errorCode === "content_policy_violation") {
176
- logger_default.log(
177
- identifier,
178
- "Removing images due to content policy violation error"
179
- );
180
- openAiPayload.messages.forEach((message) => {
181
- if (Array.isArray(message.content)) {
182
- message.content = message.content.filter(
183
- (content) => content.type === "text"
184
- );
185
- }
186
- });
187
- }
188
- if (i >= 2 && (openAiConfig == null ? void 0 : openAiConfig.service) === "azure" && errorCode === "content_filter") {
189
- logger_default.log(
190
- identifier,
191
- "Switching to OpenAI service due to content filter error"
192
- );
193
- openAiConfig.service = "openai";
194
- }
195
- if (i === 3) {
196
- if ((openAiConfig == null ? void 0 : openAiConfig.service) === "azure") {
197
- logger_default.log(
198
- identifier,
199
- "Switching to OpenAI service due to Azure service error"
200
- );
201
- openAiConfig.service = "openai";
202
- }
203
- }
204
- if (i === 4) {
205
- if (openAiPayload.tools) {
206
- logger_default.log(
207
- identifier,
208
- "Switching to no tool choice due to persistent error"
209
- );
210
- openAiPayload.tool_choice = "none";
173
+ function truncatePayload(payload) {
174
+ return JSON.stringify(
175
+ {
176
+ ...payload,
177
+ messages: payload.messages.map((message) => {
178
+ const truncatedMessage = { ...message };
179
+ if (typeof truncatedMessage.content === "string") {
180
+ truncatedMessage.content = truncatedMessage.content.slice(0, 100);
181
+ } else if (Array.isArray(truncatedMessage.content)) {
182
+ truncatedMessage.content = truncatedMessage.content.map((block) => {
183
+ if (block.type === "image_url") {
184
+ return {
185
+ ...block,
186
+ image_url: { url: block.image_url.url.slice(0, 100) }
187
+ };
188
+ }
189
+ return block;
190
+ });
211
191
  }
192
+ return truncatedMessage;
193
+ })
194
+ },
195
+ null,
196
+ 2
197
+ );
198
+ }
199
+ async function getNormalizedBase64PNG(url, mime) {
200
+ const response = await axios.get(url, { responseType: "arraybuffer" });
201
+ let imageBuffer = Buffer.from(response.data);
202
+ let sharpOptions = {};
203
+ if (isHeicImage(url, mime)) {
204
+ const imageData = await decode({ buffer: imageBuffer });
205
+ imageBuffer = Buffer.from(imageData.data);
206
+ sharpOptions = {
207
+ raw: {
208
+ width: imageData.width,
209
+ height: imageData.height,
210
+ channels: 4
212
211
  }
213
- await timeout(250);
214
- }
212
+ };
215
213
  }
216
- logger_default.error(
217
- identifier,
218
- `Failed to call OpenAI API after ${retries} attempts. Please lookup OpenAI status for active issues.`,
219
- errorObj
220
- );
221
- throw new Error(
222
- `${identifier}: Failed to call OpenAI API after ${retries} attempts. Please lookup OpenAI status for active issues.`
223
- );
214
+ const resizedBuffer = await sharp(imageBuffer, sharpOptions).withMetadata().resize(1024, 1024, { fit: "inside", withoutEnlargement: true }).png().toBuffer();
215
+ return resizedBuffer.toString("base64");
224
216
  }
225
- async function callOpenAIStream(identifier, openAiPayload, openAiConfig, chunkTimeoutMs) {
226
- var _a, _b, _c, _d, _e, _f, _g;
227
- const functionNames = openAiPayload.tools ? new Set(openAiPayload.tools.map((fn) => fn.function.name)) : null;
228
- if (!openAiConfig) {
229
- openAiConfig = {
217
+ var ALLOWED_IMAGE_MIME_TYPES = [
218
+ "image/png",
219
+ "image/jpeg",
220
+ "image/gif",
221
+ "image/webp"
222
+ ];
223
+ function buildOpenAIRequestConfig(identifier, model, config) {
224
+ if (!config) {
225
+ config = {
230
226
  service: "openai",
231
227
  apiKey: process.env.OPENAI_API_KEY,
232
228
  baseUrl: ""
233
229
  };
234
230
  }
235
- let response;
236
- const controller = new AbortController();
237
- if (openAiConfig.service === "azure") {
238
- logger_default.log(identifier, "Using Azure OpenAI service", openAiPayload.model);
239
- const model = openAiPayload.model;
240
- if (!openAiConfig.modelConfigMap) {
231
+ if (config.service === "azure") {
232
+ logger_default.log(identifier, "Using Azure OpenAI service:", model);
233
+ if (!config.modelConfigMap) {
241
234
  throw new Error(
242
235
  "OpenAI config modelConfigMap is required when using Azure OpenAI service."
243
236
  );
244
237
  }
245
- const azureConfig = openAiConfig.modelConfigMap[model];
246
- let endpoint;
247
- if (azureConfig.endpoint) {
248
- endpoint = `${azureConfig.endpoint}/openai/deployments/${azureConfig.deployment}/chat/completions?api-version=${azureConfig.apiVersion}`;
249
- } else {
238
+ const azureConfig = config.modelConfigMap[model];
239
+ if (!(azureConfig == null ? void 0 : azureConfig.endpoint)) {
250
240
  throw new Error("Azure OpenAI endpoint is required in modelConfigMap.");
251
241
  }
252
- logger_default.log(identifier, "Using endpoint", endpoint);
253
- try {
254
- const stringifiedPayload = JSON.stringify({
255
- ...openAiPayload,
256
- stream: true
257
- });
258
- const parsedPayload = JSON.parse(stringifiedPayload);
259
- } catch (error2) {
260
- logger_default.error(
261
- identifier,
262
- "Stream error: Azure OpenAI JSON parsing error:",
263
- error2
264
- );
265
- }
266
- response = await fetch(endpoint, {
267
- method: "POST",
242
+ const endpoint = `${azureConfig.endpoint}/openai/deployments/${azureConfig.deployment}/chat/completions?api-version=${azureConfig.apiVersion}`;
243
+ logger_default.log(identifier, "Using endpoint:", endpoint);
244
+ return {
245
+ endpoint,
268
246
  headers: {
269
247
  "Content-Type": "application/json",
270
248
  "api-key": azureConfig.apiKey
271
- },
272
- body: JSON.stringify({
273
- ...openAiPayload,
274
- stream: true
275
- }),
276
- signal: controller.signal
277
- });
278
- } else {
279
- logger_default.log(identifier, "Using OpenAI service", openAiPayload.model);
280
- const endpoint = `https://api.openai.com/v1/chat/completions`;
281
- if (openAiConfig.orgId) {
282
- logger_default.log(identifier, "Using orgId", openAiConfig.orgId);
249
+ }
250
+ };
251
+ }
252
+ logger_default.log(identifier, "Using OpenAI service:", model);
253
+ if (config.orgId) {
254
+ logger_default.log(identifier, "Using orgId:", config.orgId);
255
+ }
256
+ return {
257
+ endpoint: "https://api.openai.com/v1/chat/completions",
258
+ headers: {
259
+ "Content-Type": "application/json",
260
+ Authorization: `Bearer ${config.apiKey}`,
261
+ ...config.orgId ? { "OpenAI-Organization": config.orgId } : {}
283
262
  }
284
- response = await fetch(endpoint, {
285
- method: "POST",
286
- headers: {
287
- "Content-Type": "application/json",
288
- Authorization: `Bearer ${openAiConfig.apiKey}`,
289
- ...openAiConfig.orgId ? { "OpenAI-Organization": openAiConfig.orgId } : {}
290
- },
291
- body: JSON.stringify({
292
- ...openAiPayload,
293
- stream: true
294
- }),
295
- signal: controller.signal
263
+ };
264
+ }
265
+ async function prepareOpenAIPayload(identifier, payload) {
266
+ var _a;
267
+ const preparedPayload = {
268
+ model: payload.model,
269
+ messages: [],
270
+ tools: (_a = payload.functions) == null ? void 0 : _a.map((fn) => ({
271
+ type: "function",
272
+ function: fn
273
+ })),
274
+ tool_choice: payload.function_call ? typeof payload.function_call === "string" ? payload.function_call : { type: "function", function: payload.function_call } : void 0
275
+ };
276
+ for (const message of payload.messages) {
277
+ const contentBlocks = [];
278
+ if (message.content) {
279
+ contentBlocks.push({ type: "text", text: message.content });
280
+ }
281
+ for (const file of message.files || []) {
282
+ if (ALLOWED_IMAGE_MIME_TYPES.includes(file.mimeType)) {
283
+ if (file.url) {
284
+ contentBlocks.push({
285
+ type: "image_url",
286
+ image_url: { url: file.url }
287
+ });
288
+ contentBlocks.push({ type: "text", text: `Image (${file.url})` });
289
+ } else if (file.data) {
290
+ contentBlocks.push({
291
+ type: "image_url",
292
+ image_url: { url: `data:${file.mimeType};base64,${file.data}` }
293
+ });
294
+ }
295
+ } else if (file.url) {
296
+ contentBlocks.push({
297
+ type: "text",
298
+ text: `File (${file.url})`
299
+ });
300
+ }
301
+ }
302
+ preparedPayload.messages.push({
303
+ role: message.role,
304
+ content: contentBlocks
296
305
  });
297
306
  }
298
- if (response.body) {
299
- let rawStreamedBody = "";
300
- let paragraph = "";
301
- let functionCallName = "";
302
- let functionCallArgs = "";
303
- const reader = response.body.getReader();
304
- let partialChunk = "";
305
- let abortTimeout = null;
306
- const startAbortTimeout = () => {
307
- abortTimeout && clearTimeout(abortTimeout);
308
- return setTimeout(() => {
309
- logger_default.error(identifier, `Stream timeout after ${chunkTimeoutMs}ms`);
310
- controller.abort();
311
- }, chunkTimeoutMs);
312
- };
313
- let chunkIndex = -1;
314
- while (true) {
315
- chunkIndex++;
316
- const abortTimeout2 = startAbortTimeout();
317
- const { done, value } = await reader.read();
318
- clearTimeout(abortTimeout2);
319
- if (done) {
320
- logger_default.error(
321
- identifier,
322
- `Stream ended prematurely after ${chunkIndex + 1} chunks`
307
+ return preparedPayload;
308
+ }
309
+ async function callOpenAIStream(id, openAiPayload, openAiConfig, chunkTimeoutMs) {
310
+ var _a, _b, _c, _d, _e, _f, _g, _h;
311
+ const functionNames = openAiPayload.tools ? new Set(openAiPayload.tools.map((fn) => fn.function.name)) : null;
312
+ const { endpoint, headers } = buildOpenAIRequestConfig(
313
+ id,
314
+ openAiPayload.model,
315
+ openAiConfig
316
+ );
317
+ const controller = new AbortController();
318
+ const response = await fetch(endpoint, {
319
+ method: "POST",
320
+ headers,
321
+ body: JSON.stringify({ ...openAiPayload, stream: true }),
322
+ signal: controller.signal
323
+ });
324
+ if (!response.body) {
325
+ throw new Error("Stream error: no response body");
326
+ }
327
+ let paragraph = "";
328
+ let functionCallName = "";
329
+ let functionCallArgs = "";
330
+ const reader = response.body.getReader();
331
+ let partialChunk = "";
332
+ let chunkIndex = -1;
333
+ const createAbortTimeout = () => setTimeout(() => {
334
+ logger_default.error(id, `Stream timeout after ${chunkTimeoutMs}ms`);
335
+ controller.abort();
336
+ }, chunkTimeoutMs);
337
+ while (true) {
338
+ chunkIndex++;
339
+ const abortTimeout = createAbortTimeout();
340
+ const { done, value } = await reader.read();
341
+ clearTimeout(abortTimeout);
342
+ if (done) {
343
+ logger_default.error(id, `Stream ended prematurely after ${chunkIndex + 1} chunks`);
344
+ throw new Error("Stream error: ended prematurely");
345
+ }
346
+ let chunk = new TextDecoder().decode(value);
347
+ if (partialChunk) {
348
+ chunk = partialChunk + chunk;
349
+ partialChunk = "";
350
+ }
351
+ const jsonStrings = chunk.split(/^data: /gm);
352
+ for (const jsonString of jsonStrings) {
353
+ if (!jsonString)
354
+ continue;
355
+ if (jsonString.includes("[DONE]")) {
356
+ return parseStreamedResponse(
357
+ id,
358
+ paragraph,
359
+ functionCallName,
360
+ functionCallArgs,
361
+ functionNames
323
362
  );
324
- throw new Error("Stream error: ended prematurely");
325
363
  }
326
- let chunk = new TextDecoder().decode(value);
327
- rawStreamedBody += chunk + "\n";
328
- if (partialChunk) {
329
- chunk = partialChunk + chunk;
330
- partialChunk = "";
364
+ let json;
365
+ try {
366
+ json = JSON.parse(jsonString.trim());
367
+ } catch (e) {
368
+ partialChunk = jsonString;
369
+ continue;
331
370
  }
332
- let jsonStrings = chunk.split(/^data: /gm);
333
- for (let jsonString of jsonStrings) {
334
- if (!jsonString) {
335
- continue;
336
- }
337
- if (jsonString.includes("[DONE]")) {
338
- try {
339
- return parseStreamedResponse(
340
- identifier,
341
- paragraph,
342
- functionCallName,
343
- functionCallArgs,
344
- functionNames
345
- );
346
- } catch (error2) {
347
- logger_default.error(identifier, "Stream error: parsing response");
348
- throw error2;
349
- }
371
+ if (!((_a = json.choices) == null ? void 0 : _a.length)) {
372
+ if (json.error) {
373
+ logger_default.error(id, "Stream error from OpenAI:", json.error);
374
+ const error2 = new Error("Stream error: OpenAI error");
375
+ error2.data = json.error;
376
+ error2.requestBody = truncatePayload(openAiPayload);
377
+ throw error2;
350
378
  }
351
- let json;
352
- try {
353
- json = JSON.parse(jsonString.trim());
354
- } catch (error2) {
355
- partialChunk = jsonString;
356
- continue;
357
- }
358
- if (!json.choices || !json.choices.length) {
359
- if (json.error) {
360
- logger_default.error(identifier, "Stream error: OpenAI error:", json.error);
361
- const error2 = new Error("Stream error: OpenAI error");
362
- error2.data = json.error;
363
- error2.requestBody = truncatePayload(openAiPayload);
364
- throw error2;
365
- }
366
- if (chunkIndex !== 0) {
367
- logger_default.error(identifier, "Stream error: no choices in JSON:", json);
368
- }
369
- continue;
370
- }
371
- const dToolCall = (_d = (_c = (_b = (_a = json.choices) == null ? void 0 : _a[0]) == null ? void 0 : _b.delta) == null ? void 0 : _c.tool_calls) == null ? void 0 : _d[0];
372
- if (dToolCall) {
373
- const toolCallIndex = dToolCall.index || 0;
374
- if (toolCallIndex === 0) {
375
- const dFn = dToolCall.function || {};
376
- if (dFn.name)
377
- functionCallName += dFn.name;
378
- if (dFn.arguments)
379
- functionCallArgs += dFn.arguments;
380
- }
381
- }
382
- const text = (_g = (_f = (_e = json.choices) == null ? void 0 : _e[0]) == null ? void 0 : _f.delta) == null ? void 0 : _g.content;
383
- if (text) {
384
- paragraph += text;
379
+ if (chunkIndex !== 0) {
380
+ logger_default.error(id, "Stream error: no choices in JSON:", json);
385
381
  }
382
+ continue;
386
383
  }
384
+ const toolCall = (_d = (_c = (_b = json.choices[0]) == null ? void 0 : _b.delta) == null ? void 0 : _c.tool_calls) == null ? void 0 : _d[0];
385
+ if ((toolCall == null ? void 0 : toolCall.index) === 0 || (toolCall == null ? void 0 : toolCall.index) === void 0) {
386
+ if ((_e = toolCall == null ? void 0 : toolCall.function) == null ? void 0 : _e.name)
387
+ functionCallName += toolCall.function.name;
388
+ if ((_f = toolCall == null ? void 0 : toolCall.function) == null ? void 0 : _f.arguments)
389
+ functionCallArgs += toolCall.function.arguments;
390
+ }
391
+ const text = (_h = (_g = json.choices[0]) == null ? void 0 : _g.delta) == null ? void 0 : _h.content;
392
+ if (text)
393
+ paragraph += text;
387
394
  }
388
- } else {
389
- throw new Error("Stream error: no response body");
390
395
  }
391
396
  }
392
- async function callOpenAI(identifier, openAiPayload, openAiConfig) {
393
- const functionNames = openAiPayload.tools ? new Set(openAiPayload.tools.map((fn) => fn.function.name)) : null;
394
- if (!openAiConfig) {
395
- openAiConfig = {
396
- service: "openai",
397
- apiKey: process.env.OPENAI_API_KEY,
398
- baseUrl: ""
399
- };
400
- }
401
- let response;
402
- if (openAiConfig.service === "azure") {
403
- logger_default.log(identifier, "Using Azure OpenAI service", openAiPayload.model);
404
- const model = openAiPayload.model;
405
- if (!openAiConfig.modelConfigMap) {
406
- throw new Error(
407
- "OpenAI config modelConfigMap is required when using Azure OpenAI service."
408
- );
409
- }
410
- const azureConfig = openAiConfig.modelConfigMap[model];
411
- let endpoint;
412
- if (azureConfig.endpoint) {
413
- endpoint = `${azureConfig.endpoint}/openai/deployments/${azureConfig.deployment}/chat/completions?api-version=${azureConfig.apiVersion}`;
414
- } else {
415
- throw new Error("Azure OpenAI endpoint is required in modelConfigMap.");
416
- }
417
- logger_default.log(identifier, "Using endpoint", endpoint);
418
- try {
419
- const stringifiedPayload = JSON.stringify({
420
- ...openAiPayload,
421
- stream: false
422
- });
423
- const parsedPayload = JSON.parse(stringifiedPayload);
424
- } catch (error2) {
425
- logger_default.error(identifier, "OpenAI JSON parsing error:", error2);
426
- throw error2;
427
- }
428
- response = await fetch(endpoint, {
429
- method: "POST",
430
- headers: {
431
- "Content-Type": "application/json",
432
- "api-key": azureConfig.apiKey
433
- },
434
- body: JSON.stringify({
435
- ...openAiPayload,
436
- stream: false
437
- })
438
- });
439
- } else {
440
- logger_default.log(identifier, "Using OpenAI service", openAiPayload.model);
441
- const endpoint = `https://api.openai.com/v1/chat/completions`;
442
- if (openAiConfig.orgId) {
443
- logger_default.log(identifier, "Using orgId", openAiConfig.orgId);
444
- }
445
- response = await fetch(endpoint, {
446
- method: "POST",
447
- headers: {
448
- "Content-Type": "application/json",
449
- Authorization: `Bearer ${openAiConfig.apiKey}`,
450
- ...openAiConfig.orgId ? { "OpenAI-Organization": openAiConfig.orgId } : {}
451
- },
452
- body: JSON.stringify({
453
- ...openAiPayload,
454
- stream: false
455
- })
456
- });
457
- }
397
+ async function callOpenAI(id, openAiPayload, openAiConfig) {
398
+ var _a;
399
+ const { endpoint, headers } = buildOpenAIRequestConfig(
400
+ id,
401
+ openAiPayload.model,
402
+ openAiConfig
403
+ );
404
+ const response = await fetch(endpoint, {
405
+ method: "POST",
406
+ headers,
407
+ body: JSON.stringify({ ...openAiPayload, stream: false })
408
+ });
458
409
  if (!response.ok) {
459
410
  const errorData = await response.json();
460
- logger_default.error(identifier, "OpenAI API error:", errorData);
411
+ logger_default.error(id, "OpenAI API error:", errorData);
461
412
  throw new Error(`OpenAI API Error: ${errorData.error.message}`);
462
413
  }
463
414
  const data = await response.json();
464
- if (!data.choices || !data.choices.length) {
415
+ if (!((_a = data.choices) == null ? void 0 : _a.length)) {
465
416
  if (data.error) {
466
- logger_default.error(identifier, "OpenAI error:", data.error);
467
- throw new Error("OpenAI error: " + data.error.message);
417
+ logger_default.error(id, "OpenAI error:", data.error);
418
+ throw new Error(`OpenAI error: ${data.error.message}`);
468
419
  }
469
420
  throw new Error("OpenAI error: No choices returned.");
470
421
  }
@@ -480,94 +431,167 @@ async function callOpenAI(identifier, openAiPayload, openAiConfig) {
480
431
  files: []
481
432
  };
482
433
  }
483
- function truncatePayload(payload) {
484
- return JSON.stringify(
434
+ async function callOpenAiWithRetries(id, openAiPayload, openAiConfig, retries = 5, chunkTimeoutMs = 15e3) {
435
+ logger_default.log(
436
+ id,
437
+ "Calling OpenAI API with retries:",
438
+ openAiConfig == null ? void 0 : openAiConfig.service,
439
+ openAiPayload.model
440
+ );
441
+ const useStreaming = openAiPayload.model !== "o1-mini" /* O1_MINI */ && openAiPayload.model !== "o1-preview" /* O1_PREVIEW */;
442
+ return withRetries(
443
+ id,
444
+ "OpenAI",
445
+ async () => {
446
+ if (useStreaming) {
447
+ return callOpenAIStream(id, openAiPayload, openAiConfig, chunkTimeoutMs);
448
+ } else {
449
+ return callOpenAI(id, openAiPayload, openAiConfig);
450
+ }
451
+ },
485
452
  {
486
- ...payload,
487
- messages: payload.messages.map((message) => {
488
- if (typeof message.content === "string") {
489
- message.content = message.content.slice(0, 100);
490
- } else if (Array.isArray(message.content)) {
491
- message.content = message.content.map((block) => {
492
- if (block.type === "image_url") {
493
- block.image_url.url = block.image_url.url.slice(0, 100);
453
+ retries,
454
+ baseDelayMs: 250,
455
+ onError: (error2, attempt) => {
456
+ var _a, _b;
457
+ logger_default.error(
458
+ id,
459
+ `Retry #${attempt} error: ${error2.message}`,
460
+ ((_a = error2.response) == null ? void 0 : _a.data) || error2.data || error2
461
+ );
462
+ if (((_b = error2.data) == null ? void 0 : _b.code) === "content_policy_violation") {
463
+ logger_default.log(id, "Removing images due to content policy violation");
464
+ openAiPayload.messages.forEach((message) => {
465
+ if (Array.isArray(message.content)) {
466
+ message.content = message.content.filter(
467
+ (content) => content.type === "text"
468
+ );
494
469
  }
495
- return block;
496
470
  });
497
471
  }
498
- return message;
499
- })
500
- },
501
- null,
502
- 2
472
+ }
473
+ }
503
474
  );
504
475
  }
505
- async function callAnthropicWithRetries(identifier, AiPayload, AiConfig, attempts = 5) {
506
- var _a, _b, _c, _d;
507
- logger_default.log(identifier, "Calling Anthropic API with retries");
508
- let lastResponse;
509
- for (let i = 0; i < attempts; i++) {
510
- try {
511
- lastResponse = await callAnthropic(identifier, AiPayload, AiConfig);
512
- return lastResponse;
513
- } catch (e) {
514
- logger_default.error(
515
- identifier,
516
- `Retry #${i} error: ${e.message}`,
517
- ((_a = e.response) == null ? void 0 : _a.data) || e
518
- );
519
- if (((_d = (_c = (_b = e.response) == null ? void 0 : _b.data) == null ? void 0 : _c.error) == null ? void 0 : _d.type) === "rate_limit_error") {
476
+ function jigAnthropicMessages(messages) {
477
+ var _a, _b;
478
+ let jiggedMessages = messages.slice();
479
+ if (((_a = jiggedMessages[0]) == null ? void 0 : _a.role) !== "user") {
480
+ jiggedMessages = [{ role: "user", content: "..." }, ...jiggedMessages];
481
+ }
482
+ jiggedMessages = jiggedMessages.reduce((acc, message) => {
483
+ if (acc.length === 0)
484
+ return [message];
485
+ const lastMessage = acc[acc.length - 1];
486
+ if (lastMessage.role === message.role) {
487
+ const lastContent = Array.isArray(lastMessage.content) ? lastMessage.content : [{ type: "text", text: lastMessage.content }];
488
+ const newContent = Array.isArray(message.content) ? message.content : [{ type: "text", text: message.content }];
489
+ lastMessage.content = [
490
+ ...lastContent,
491
+ { type: "text", text: "\n\n---\n\n" },
492
+ ...newContent
493
+ ];
494
+ return acc;
495
+ }
496
+ if (typeof message.content === "string") {
497
+ message.content = [{ type: "text", text: message.content }];
498
+ }
499
+ return [...acc, message];
500
+ }, []);
501
+ if (((_b = jiggedMessages[jiggedMessages.length - 1]) == null ? void 0 : _b.role) === "assistant") {
502
+ jiggedMessages.push({ role: "user", content: "..." });
503
+ }
504
+ return jiggedMessages;
505
+ }
506
+ async function prepareAnthropicPayload(_identifier, payload) {
507
+ const preparedPayload = {
508
+ model: payload.model,
509
+ messages: [],
510
+ functions: payload.functions,
511
+ temperature: payload.temperature
512
+ };
513
+ for (const message of payload.messages) {
514
+ if (message.role === "system") {
515
+ preparedPayload.system = message.content;
516
+ continue;
517
+ }
518
+ const contentBlocks = [];
519
+ if (message.content) {
520
+ contentBlocks.push({ type: "text", text: message.content });
521
+ }
522
+ for (const file of message.files || []) {
523
+ if (ALLOWED_IMAGE_MIME_TYPES.includes(file.mimeType)) {
524
+ if (file.url) {
525
+ contentBlocks.push({
526
+ type: "image",
527
+ source: {
528
+ type: "base64",
529
+ media_type: "image/png",
530
+ data: await getNormalizedBase64PNG(file.url, file.mimeType)
531
+ }
532
+ });
533
+ contentBlocks.push({ type: "text", text: `Image (${file.url})` });
534
+ } else if (file.data) {
535
+ contentBlocks.push({
536
+ type: "image",
537
+ source: {
538
+ type: "base64",
539
+ media_type: file.mimeType,
540
+ data: file.data
541
+ }
542
+ });
543
+ }
544
+ } else if (file.url) {
545
+ contentBlocks.push({
546
+ type: "text",
547
+ text: `File (${file.url})`
548
+ });
520
549
  }
521
- await timeout(125 * i);
522
550
  }
551
+ preparedPayload.messages.push({
552
+ role: message.role,
553
+ content: contentBlocks
554
+ });
523
555
  }
524
- const error2 = new Error(
525
- `Failed to call Anthropic API after ${attempts} attempts`
526
- );
527
- error2.response = lastResponse;
528
- throw error2;
556
+ return preparedPayload;
529
557
  }
530
- async function callAnthropic(identifier, AiPayload, AiConfig) {
531
- var _a, _b;
532
- const anthropicMessages = jigAnthropicMessages(AiPayload.messages);
558
+ async function callAnthropic(id, payload, config) {
559
+ var _a;
560
+ const anthropicMessages = jigAnthropicMessages(payload.messages);
561
+ const tools = (_a = payload.functions) == null ? void 0 : _a.map((f) => ({
562
+ ...f,
563
+ input_schema: f.parameters,
564
+ parameters: void 0
565
+ }));
533
566
  let data;
534
- let response;
535
- if ((AiConfig == null ? void 0 : AiConfig.service) === "bedrock") {
567
+ if ((config == null ? void 0 : config.service) === "bedrock") {
536
568
  const AWS_REGION = "us-east-1";
537
569
  const MODEL_ID = "anthropic.claude-3-haiku-20240307-v1:0";
538
570
  const client = new BedrockRuntimeClient({ region: AWS_REGION });
539
- const payload = {
571
+ const bedrockPayload = {
540
572
  anthropic_version: "bedrock-2023-05-31",
541
573
  max_tokens: 4096,
542
574
  messages: anthropicMessages,
543
- tools: (_a = AiPayload.functions) == null ? void 0 : _a.map((f) => ({
544
- ...f,
545
- input_schema: f.parameters,
546
- parameters: void 0
547
- }))
575
+ tools
548
576
  };
549
- const response2 = await client.send(
577
+ const response = await client.send(
550
578
  new InvokeModelCommand({
551
579
  contentType: "application/json",
552
- body: JSON.stringify(payload),
580
+ body: JSON.stringify(bedrockPayload),
553
581
  modelId: MODEL_ID
554
582
  })
555
583
  );
556
- const decodedResponseBody = new TextDecoder().decode(response2.body);
584
+ const decodedResponseBody = new TextDecoder().decode(response.body);
557
585
  data = JSON.parse(decodedResponseBody);
558
586
  } else {
559
- const response2 = await axios.post(
587
+ const response = await axios.post(
560
588
  "https://api.anthropic.com/v1/messages",
561
589
  {
562
- model: AiPayload.model,
590
+ model: payload.model,
563
591
  messages: anthropicMessages,
564
- tools: (_b = AiPayload.functions) == null ? void 0 : _b.map((f) => ({
565
- ...f,
566
- input_schema: f.parameters,
567
- parameters: void 0
568
- })),
569
- temperature: AiPayload.temperature,
570
- system: AiPayload.system,
592
+ tools,
593
+ temperature: payload.temperature,
594
+ system: payload.system,
571
595
  max_tokens: 4096
572
596
  },
573
597
  {
@@ -580,120 +604,64 @@ async function callAnthropic(identifier, AiPayload, AiConfig) {
580
604
  timeout: 6e4
581
605
  }
582
606
  );
583
- data = response2.data;
607
+ data = response.data;
584
608
  }
585
609
  const answers = data.content;
586
- if (!answers[0]) {
587
- logger_default.error(identifier, "Missing answer in Anthropic API:", data);
610
+ if (!(answers == null ? void 0 : answers[0])) {
611
+ logger_default.error(id, "Missing answer in Anthropic API response:", data);
588
612
  throw new Error("Missing answer in Anthropic API");
589
613
  }
590
614
  let textResponse = "";
591
- let functionCalls = [];
615
+ const functionCalls = [];
592
616
  for (const answer of answers) {
593
617
  if (!answer.type) {
594
- logger_default.error(identifier, "Missing answer type in Anthropic API:", data);
618
+ logger_default.error(id, "Missing answer type in Anthropic API response:", data);
595
619
  throw new Error("Missing answer type in Anthropic API");
596
620
  }
597
- let text = "";
598
621
  if (answer.type === "text") {
599
- text = answer.text.replace(/<thinking>.*?<\/thinking>/gs, "").replace(/<answer>|<\/answer>/gs, "").trim();
622
+ let text = answer.text.replace(/<thinking>.*?<\/thinking>/gs, "").replace(/<answer>|<\/answer>/gs, "").trim();
600
623
  if (!text) {
601
624
  text = answer.text.replace(
602
625
  /<thinking>|<\/thinking>|<answer>|<\/answer>/gs,
603
626
  ""
604
627
  );
605
- logger_default.log(
606
- identifier,
607
- "No text in answer, returning text within tags:",
608
- text
609
- );
628
+ logger_default.log(id, "No text in answer, returning text within tags:", text);
610
629
  }
611
- if (textResponse) {
612
- textResponse += `
630
+ textResponse = textResponse ? `${textResponse}
613
631
 
614
- ${text}`;
615
- } else {
616
- textResponse = text;
617
- }
632
+ ${text}` : text;
618
633
  } else if (answer.type === "tool_use") {
619
- const call = {
634
+ functionCalls.push({
620
635
  name: answer.name,
621
636
  arguments: answer.input
622
- };
623
- functionCalls.push(call);
637
+ });
624
638
  }
625
639
  }
626
640
  if (!textResponse && !functionCalls.length) {
627
- logger_default.error(
628
- identifier,
629
- "Missing text & fns in Anthropic API response:",
630
- data
631
- );
632
- throw new Error("Missing text & fns in Anthropic API response");
641
+ logger_default.error(id, "Missing text & functions in Anthropic API response:", data);
642
+ throw new Error("Missing text & functions in Anthropic API response");
633
643
  }
634
644
  return {
635
645
  role: "assistant",
636
646
  content: textResponse,
637
- function_call: functionCalls[0],
647
+ function_call: functionCalls[0] || null,
638
648
  files: []
639
649
  };
640
650
  }
641
- function jigAnthropicMessages(messages) {
642
- var _a, _b;
643
- let jiggedMessages = messages.slice();
644
- if (((_a = jiggedMessages[0]) == null ? void 0 : _a.role) !== "user") {
645
- jiggedMessages = [
646
- {
647
- role: "user",
648
- content: "..."
649
- },
650
- ...jiggedMessages
651
- ];
652
- }
653
- jiggedMessages = jiggedMessages.reduce((acc, message) => {
654
- if (acc.length === 0) {
655
- return [message];
656
- }
657
- const lastMessage = acc[acc.length - 1];
658
- if (lastMessage.role === message.role) {
659
- const lastContent = Array.isArray(lastMessage.content) ? lastMessage.content : [{ type: "text", text: lastMessage.content }];
660
- const newContent = Array.isArray(message.content) ? message.content : [{ type: "text", text: message.content }];
661
- lastMessage.content = [
662
- ...lastContent,
663
- { type: "text", text: "\n\n---\n\n" },
664
- ...newContent
665
- ];
666
- return acc;
667
- }
668
- if (typeof message.content === "string") {
669
- message.content = [{ type: "text", text: message.content }];
670
- }
671
- return [...acc, message];
672
- }, []);
673
- if (((_b = jiggedMessages[jiggedMessages.length - 1]) == null ? void 0 : _b.role) === "assistant") {
674
- jiggedMessages.push({
675
- role: "user",
676
- content: "..."
677
- });
678
- }
679
- return jiggedMessages;
651
+ async function callAnthropicWithRetries(id, payload, config, retries = 5) {
652
+ return withRetries(id, "Anthropic", () => callAnthropic(id, payload, config), {
653
+ retries
654
+ });
680
655
  }
681
656
  function jigGoogleMessages(messages) {
682
657
  var _a, _b;
683
658
  let jiggedMessages = messages.slice();
684
659
  if (((_a = jiggedMessages[0]) == null ? void 0 : _a.role) === "model") {
685
- jiggedMessages = [
686
- {
687
- role: "user",
688
- parts: [{ text: "..." }]
689
- },
690
- ...jiggedMessages
691
- ];
660
+ jiggedMessages = [{ role: "user", parts: [{ text: "..." }] }, ...jiggedMessages];
692
661
  }
693
662
  jiggedMessages = jiggedMessages.reduce((acc, message) => {
694
- if (acc.length === 0) {
663
+ if (acc.length === 0)
695
664
  return [message];
696
- }
697
665
  const lastMessage = acc[acc.length - 1];
698
666
  if (lastMessage.role === message.role) {
699
667
  lastMessage.parts = [...lastMessage.parts, ...message.parts];
@@ -702,15 +670,11 @@ function jigGoogleMessages(messages) {
702
670
  return [...acc, message];
703
671
  }, []);
704
672
  if (((_b = jiggedMessages[jiggedMessages.length - 1]) == null ? void 0 : _b.role) === "model") {
705
- jiggedMessages.push({
706
- role: "user",
707
- parts: [{ text: "..." }]
708
- });
673
+ jiggedMessages.push({ role: "user", parts: [{ text: "..." }] });
709
674
  }
710
675
  return jiggedMessages;
711
676
  }
712
- async function prepareGoogleAIPayload(payload) {
713
- var _a;
677
+ async function prepareGoogleAIPayload(_identifier, payload) {
714
678
  const preparedPayload = {
715
679
  model: payload.model,
716
680
  messages: [],
@@ -718,7 +682,6 @@ async function prepareGoogleAIPayload(payload) {
718
682
  functionDeclarations: payload.functions.map((fn) => ({
719
683
  name: fn.name,
720
684
  parameters: {
721
- // Google puts their description in the parameters object rather than in a top-level field
722
685
  description: fn.description,
723
686
  ...fn.parameters
724
687
  }
@@ -730,61 +693,47 @@ async function prepareGoogleAIPayload(payload) {
730
693
  preparedPayload.systemInstruction = message.content;
731
694
  continue;
732
695
  }
733
- const googleAIContentParts = [];
696
+ const parts = [];
734
697
  if (message.content) {
735
- googleAIContentParts.push({
736
- text: message.content
737
- });
698
+ parts.push({ text: message.content });
738
699
  }
739
700
  for (const file of message.files || []) {
740
- if (!((_a = file.mimeType) == null ? void 0 : _a.startsWith("image"))) {
741
- logger_default.warn(
742
- "payload",
743
- "Google AI API does not support non-image file types. Skipping file."
744
- );
745
- continue;
746
- }
747
- if (file.url) {
748
- googleAIContentParts.push({
749
- inlineData: {
750
- mimeType: "image/png",
751
- data: await getNormalizedBase64PNG(file.url, file.mimeType)
752
- }
753
- });
754
- googleAIContentParts.push({
755
- text: `Image URL: ${file.url}`
756
- });
757
- } else if (file.data) {
758
- if (!["image/png", "image/jpeg", "image/gif", "image/webp"].includes(
759
- file.mimeType
760
- )) {
761
- throw new Error(
762
- "Invalid image mimeType. Supported types are: image/png, image/jpeg, image/gif, image/webp"
763
- );
701
+ if (ALLOWED_IMAGE_MIME_TYPES.includes(file.mimeType)) {
702
+ if (file.url) {
703
+ parts.push({
704
+ inlineData: {
705
+ mimeType: "image/png",
706
+ data: await getNormalizedBase64PNG(file.url, file.mimeType)
707
+ }
708
+ });
709
+ parts.push({ text: `Image (${file.url})` });
710
+ } else if (file.data) {
711
+ parts.push({
712
+ inlineData: {
713
+ mimeType: file.mimeType,
714
+ data: file.data
715
+ }
716
+ });
764
717
  }
765
- googleAIContentParts.push({
766
- inlineData: {
767
- mimeType: file.mimeType,
768
- data: file.data
769
- }
718
+ } else if (file.url) {
719
+ parts.push({
720
+ text: `File (${file.url})`
770
721
  });
771
722
  }
772
723
  }
773
724
  preparedPayload.messages.push({
774
725
  role: message.role === "assistant" ? "model" : message.role,
775
- parts: googleAIContentParts
726
+ parts
776
727
  });
777
728
  }
778
729
  return preparedPayload;
779
730
  }
780
- async function callGoogleAI(identifier, payload) {
781
- var _a, _b, _c, _d;
731
+ async function callGoogleAI(id, payload) {
732
+ var _a, _b, _c, _d, _e, _f;
782
733
  const googleMessages = jigGoogleMessages(payload.messages);
783
734
  const history = googleMessages.slice(0, -1);
784
735
  const lastMessage = googleMessages.slice(-1)[0];
785
- const genAI = new GoogleGenAI({
786
- apiKey: process.env.GEMINI_API_KEY
787
- });
736
+ const genAI = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
788
737
  const chat = genAI.chats.create({
789
738
  model: payload.model,
790
739
  history,
@@ -794,296 +743,84 @@ async function callGoogleAI(identifier, payload) {
794
743
  systemInstruction: payload.systemInstruction
795
744
  }
796
745
  });
797
- const response = await chat.sendMessage({
798
- message: lastMessage.parts
799
- });
746
+ const response = await chat.sendMessage({ message: lastMessage.parts });
800
747
  let text = "";
801
748
  const files = [];
802
749
  for (const part of ((_c = (_b = (_a = response.candidates) == null ? void 0 : _a[0]) == null ? void 0 : _b.content) == null ? void 0 : _c.parts) || []) {
803
- if (part.text) {
750
+ if (part.text)
804
751
  text += part.text;
805
- }
806
- if (part.inlineData) {
807
- const imageData = part.inlineData.data;
808
- if (imageData) {
809
- files.push({
810
- mimeType: "image/png",
811
- data: imageData
812
- });
813
- }
752
+ if ((_d = part.inlineData) == null ? void 0 : _d.data) {
753
+ files.push({ mimeType: "image/png", data: part.inlineData.data });
814
754
  }
815
755
  }
816
- const functionCalls = response.functionCalls;
817
- const parsedFunctionCalls = functionCalls == null ? void 0 : functionCalls.map((fc) => {
756
+ const functionCalls = (_e = response.functionCalls) == null ? void 0 : _e.map((fc) => {
818
757
  var _a2, _b2;
819
758
  return {
820
759
  name: (_a2 = fc.name) != null ? _a2 : "",
821
760
  arguments: (_b2 = fc.args) != null ? _b2 : {}
822
761
  };
823
762
  });
824
- if (!text && !(parsedFunctionCalls == null ? void 0 : parsedFunctionCalls.length) && !files.length) {
825
- const candidate = (_d = response.candidates) == null ? void 0 : _d[0];
763
+ if (!text && !(functionCalls == null ? void 0 : functionCalls.length) && !files.length) {
764
+ const candidate = (_f = response.candidates) == null ? void 0 : _f[0];
826
765
  const finishReason = candidate == null ? void 0 : candidate.finishReason;
827
- const safetyRatings = candidate == null ? void 0 : candidate.safetyRatings;
828
- const usageMetadata = response.usageMetadata;
829
- const modelVersion = response.modelVersion;
830
- logger_default.error(
831
- identifier,
832
- "Missing text & fns in Google AI API response:",
833
- {
834
- finishReason,
835
- safetyRatings,
836
- usageMetadata,
837
- modelVersion,
838
- candidateContent: candidate == null ? void 0 : candidate.content,
839
- promptFeedback: response.promptFeedback,
840
- fullResponse: JSON.stringify(response)
841
- }
842
- );
843
- let errorMessage = "Missing text & fns in Google AI API response";
766
+ logger_default.error(id, "Missing text & functions in Google AI API response:", {
767
+ finishReason,
768
+ safetyRatings: candidate == null ? void 0 : candidate.safetyRatings,
769
+ usageMetadata: response.usageMetadata,
770
+ modelVersion: response.modelVersion,
771
+ candidateContent: candidate == null ? void 0 : candidate.content,
772
+ promptFeedback: response.promptFeedback
773
+ });
774
+ let errorMessage = "Missing text & functions in Google AI API response";
844
775
  if (finishReason) {
845
- errorMessage += `: finishReason=${finishReason}`;
846
- if (finishReason === "MALFORMED_FUNCTION_CALL") {
847
- errorMessage += " (Google could not generate valid function call arguments)";
848
- } else if (finishReason === "SAFETY") {
849
- errorMessage += " (blocked by safety filters)";
850
- } else if (finishReason === "RECITATION") {
851
- errorMessage += " (blocked due to recitation)";
852
- } else if (finishReason === "MAX_TOKENS") {
853
- errorMessage += " (response truncated due to max tokens)";
854
- }
776
+ const reasonDescriptions = {
777
+ MALFORMED_FUNCTION_CALL: "(Google could not generate valid function call arguments)",
778
+ SAFETY: "(blocked by safety filters)",
779
+ RECITATION: "(blocked due to recitation)",
780
+ MAX_TOKENS: "(response truncated due to max tokens)"
781
+ };
782
+ errorMessage += `: finishReason=${finishReason} ${reasonDescriptions[finishReason] || ""}`;
855
783
  }
856
784
  const error2 = new Error(errorMessage);
857
785
  error2.finishReason = finishReason;
858
- error2.safetyRatings = safetyRatings;
859
- error2.usageMetadata = usageMetadata;
860
- error2.modelVersion = modelVersion;
861
- error2.candidateContent = candidate == null ? void 0 : candidate.content;
862
- error2.promptFeedback = response.promptFeedback;
786
+ error2.safetyRatings = candidate == null ? void 0 : candidate.safetyRatings;
787
+ error2.usageMetadata = response.usageMetadata;
863
788
  throw error2;
864
789
  }
865
790
  return {
866
791
  role: "assistant",
867
792
  content: text || null,
868
793
  files,
869
- function_call: (parsedFunctionCalls == null ? void 0 : parsedFunctionCalls[0]) || null
794
+ function_call: (functionCalls == null ? void 0 : functionCalls[0]) || null
870
795
  };
871
796
  }
872
- async function callGoogleAIWithRetries(identifier, payload, retries = 5) {
873
- logger_default.log(identifier, "Calling Google AI API with retries");
874
- let lastError;
875
- for (let i = 0; i < retries; i++) {
876
- try {
877
- return await callGoogleAI(identifier, payload);
878
- } catch (e) {
879
- lastError = e;
797
+ async function callGoogleAIWithRetries(id, payload, retries = 5) {
798
+ return withRetries(id, "Google AI", () => callGoogleAI(id, payload), {
799
+ retries,
800
+ onError: (error2, attempt) => {
880
801
  const errorDetails = {
881
- message: e.message,
882
- finishReason: e.finishReason,
883
- modelVersion: e.modelVersion
802
+ message: error2.message,
803
+ finishReason: error2.finishReason,
804
+ modelVersion: error2.modelVersion
884
805
  };
885
- if (e.safetyRatings) {
886
- errorDetails.safetyRatings = e.safetyRatings;
887
- }
888
- if (e.usageMetadata) {
889
- errorDetails.usageMetadata = e.usageMetadata;
890
- }
891
- if (e.promptFeedback) {
892
- errorDetails.promptFeedback = e.promptFeedback;
893
- }
894
- if (e.status || e.statusText) {
895
- errorDetails.httpStatus = e.status;
896
- errorDetails.httpStatusText = e.statusText;
897
- }
898
- if (e.code) {
899
- errorDetails.errorCode = e.code;
900
- }
901
- if (e.details) {
902
- errorDetails.errorDetails = e.details;
903
- }
904
- logger_default.error(identifier, `Retry #${i} error: ${e.message}`, errorDetails);
905
- if (e.finishReason === "MALFORMED_FUNCTION_CALL" && i >= 3) {
906
- logger_default.log(
907
- identifier,
908
- "Removing tools due to persistent MALFORMED_FUNCTION_CALL errors"
909
- );
910
- payload.tools = void 0;
911
- }
912
- await timeout(125 * i);
806
+ if (error2.safetyRatings)
807
+ errorDetails.safetyRatings = error2.safetyRatings;
808
+ if (error2.usageMetadata)
809
+ errorDetails.usageMetadata = error2.usageMetadata;
810
+ if (error2.promptFeedback)
811
+ errorDetails.promptFeedback = error2.promptFeedback;
812
+ if (error2.status)
813
+ errorDetails.httpStatus = error2.status;
814
+ if (error2.code)
815
+ errorDetails.errorCode = error2.code;
816
+ if (error2.details)
817
+ errorDetails.errorDetails = error2.details;
818
+ logger_default.error(id, `Retry #${attempt} error: ${error2.message}`, errorDetails);
913
819
  }
914
- }
915
- const error2 = new Error(
916
- `Failed to call Google AI API after ${retries} attempts`
917
- );
918
- error2.cause = lastError;
919
- error2.finishReason = lastError == null ? void 0 : lastError.finishReason;
920
- error2.usageMetadata = lastError == null ? void 0 : lastError.usageMetadata;
921
- error2.safetyRatings = lastError == null ? void 0 : lastError.safetyRatings;
922
- throw error2;
923
- }
924
- async function callWithRetries(identifier, aiPayload, aiConfig, retries = 5, chunkTimeoutMs = 15e3) {
925
- const id = identifier;
926
- if (isAnthropicPayload(aiPayload)) {
927
- return await callAnthropicWithRetries(
928
- id,
929
- await prepareAnthropicPayload(aiPayload),
930
- aiConfig,
931
- retries
932
- );
933
- } else if (isOpenAiPayload(aiPayload)) {
934
- return await callOpenAiWithRetries(
935
- id,
936
- await prepareOpenAIPayload(aiPayload),
937
- aiConfig,
938
- retries,
939
- chunkTimeoutMs
940
- );
941
- } else if (isGroqPayload(aiPayload)) {
942
- return await callGroqWithRetries(id, await prepareGroqPayload(aiPayload));
943
- } else if (isGoogleAIPayload(aiPayload)) {
944
- return await callGoogleAIWithRetries(
945
- id,
946
- await prepareGoogleAIPayload(aiPayload),
947
- retries
948
- );
949
- } else {
950
- throw new Error("Invalid AI payload: Unknown model type.");
951
- }
952
- }
953
- function isAnthropicPayload(payload) {
954
- return Object.values(ClaudeModel).includes(payload.model);
955
- }
956
- async function prepareAnthropicPayload(payload) {
957
- var _a;
958
- const preparedPayload = {
959
- model: payload.model,
960
- messages: [],
961
- functions: payload.functions,
962
- temperature: payload.temperature
963
- };
964
- for (const message of payload.messages) {
965
- const anthropicContentBlocks = [];
966
- if (message.role === "system") {
967
- preparedPayload.system = message.content;
968
- continue;
969
- }
970
- if (message.content) {
971
- anthropicContentBlocks.push({
972
- type: "text",
973
- text: message.content
974
- });
975
- }
976
- for (const file of message.files || []) {
977
- if (!((_a = file.mimeType) == null ? void 0 : _a.startsWith("image"))) {
978
- logger_default.warn(
979
- "payload",
980
- "Anthropic API does not support non-image file types. Skipping file."
981
- );
982
- continue;
983
- }
984
- if (file.url) {
985
- anthropicContentBlocks.push({
986
- type: "image",
987
- source: {
988
- type: "base64",
989
- media_type: "image/png",
990
- data: await getNormalizedBase64PNG(file.url, file.mimeType)
991
- }
992
- });
993
- } else if (file.data) {
994
- if (!["image/png", "image/jpeg", "image/gif", "image/webp"].includes(
995
- file.mimeType
996
- )) {
997
- throw new Error(
998
- "Invalid image mimeType. Supported types are: image/png, image/jpeg, image/gif, image/webp"
999
- );
1000
- }
1001
- anthropicContentBlocks.push({
1002
- type: "image",
1003
- source: {
1004
- type: "base64",
1005
- media_type: file.mimeType,
1006
- data: file.data
1007
- }
1008
- });
1009
- }
1010
- }
1011
- preparedPayload.messages.push({
1012
- role: message.role,
1013
- content: anthropicContentBlocks
1014
- });
1015
- }
1016
- return preparedPayload;
1017
- }
1018
- function isOpenAiPayload(payload) {
1019
- return Object.values(GPTModel).includes(payload.model);
1020
- }
1021
- async function prepareOpenAIPayload(payload) {
1022
- var _a;
1023
- const preparedPayload = {
1024
- model: payload.model,
1025
- messages: [],
1026
- tools: (_a = payload.functions) == null ? void 0 : _a.map((fn) => ({
1027
- type: "function",
1028
- function: fn
1029
- })),
1030
- tool_choice: payload.function_call ? typeof payload.function_call === "string" ? payload.function_call : {
1031
- type: "function",
1032
- function: payload.function_call
1033
- } : void 0
1034
- };
1035
- for (const message of payload.messages) {
1036
- const openAIContentBlocks = [];
1037
- if (message.content) {
1038
- openAIContentBlocks.push({
1039
- type: "text",
1040
- text: message.content
1041
- });
1042
- }
1043
- const allowedFileMimeTypes = [
1044
- "image/png",
1045
- "image/jpeg",
1046
- "image/gif",
1047
- "image/webp"
1048
- ];
1049
- for (const file of message.files || []) {
1050
- if (allowedFileMimeTypes.includes(file.mimeType)) {
1051
- if (file.url) {
1052
- openAIContentBlocks.push({
1053
- type: "image_url",
1054
- image_url: {
1055
- url: file.url
1056
- }
1057
- });
1058
- openAIContentBlocks.push({
1059
- type: "text",
1060
- text: `Image URL: ${file.url}`
1061
- });
1062
- } else if (file.data) {
1063
- openAIContentBlocks.push({
1064
- type: "image_url",
1065
- image_url: {
1066
- url: `data:${file.mimeType};base64,${file.data}`
1067
- }
1068
- });
1069
- }
1070
- } else {
1071
- logger_default.warn(
1072
- "payload",
1073
- "Skipping file in message. File or image type not supported by OpenAI API:",
1074
- file.mimeType
1075
- );
1076
- }
1077
- }
1078
- preparedPayload.messages.push({
1079
- role: message.role,
1080
- content: openAIContentBlocks
1081
- });
1082
- }
1083
- return preparedPayload;
820
+ });
1084
821
  }
1085
- function isGroqPayload(payload) {
1086
- return Object.values(GroqModel).includes(payload.model);
822
+ function normalizeMessageContent(content) {
823
+ return Array.isArray(content) ? content.map((c) => c.type === "text" ? c.text : `[${c.type}]`).join("\n") : content;
1087
824
  }
1088
825
  function prepareGroqPayload(payload) {
1089
826
  var _a;
@@ -1097,20 +834,12 @@ function prepareGroqPayload(payload) {
1097
834
  type: "function",
1098
835
  function: fn
1099
836
  })),
1100
- tool_choice: payload.function_call ? typeof payload.function_call === "string" ? payload.function_call : {
1101
- type: "function",
1102
- function: payload.function_call
1103
- } : void 0,
837
+ tool_choice: payload.function_call ? typeof payload.function_call === "string" ? payload.function_call : { type: "function", function: payload.function_call } : void 0,
1104
838
  temperature: payload.temperature
1105
839
  };
1106
840
  }
1107
- function normalizeMessageContent(content) {
1108
- return Array.isArray(content) ? content.map((c) => c.type === "text" ? c.text : `[${c.type}]`).join("\n") : content;
1109
- }
1110
- function isGoogleAIPayload(payload) {
1111
- return Object.values(GeminiModel).includes(payload.model);
1112
- }
1113
- async function callGroq(identifier, payload) {
841
+ async function callGroq(id, payload) {
842
+ var _a, _b;
1114
843
  const response = await axios.post(
1115
844
  "https://api.groq.com/openai/v1/chat/completions",
1116
845
  payload,
@@ -1121,15 +850,13 @@ async function callGroq(identifier, payload) {
1121
850
  }
1122
851
  }
1123
852
  );
1124
- const data = response.data;
1125
- const answer = data.choices[0].message;
853
+ const answer = (_a = response.data.choices[0]) == null ? void 0 : _a.message;
1126
854
  if (!answer) {
1127
- logger_default.error(identifier, "Missing answer in Groq API:", data);
855
+ logger_default.error(id, "Missing answer in Groq API response:", response.data);
1128
856
  throw new Error("Missing answer in Groq API");
1129
857
  }
1130
- const textResponse = answer.content || null;
1131
858
  let functionCall = null;
1132
- if (answer.tool_calls && answer.tool_calls.length) {
859
+ if ((_b = answer.tool_calls) == null ? void 0 : _b.length) {
1133
860
  const toolCall = answer.tool_calls[0];
1134
861
  functionCall = {
1135
862
  name: toolCall.function.name,
@@ -1138,51 +865,55 @@ async function callGroq(identifier, payload) {
1138
865
  }
1139
866
  return {
1140
867
  role: "assistant",
1141
- content: textResponse,
868
+ content: answer.content || null,
1142
869
  function_call: functionCall,
1143
870
  files: []
1144
871
  };
1145
872
  }
1146
- async function callGroqWithRetries(identifier, payload, retries = 5) {
1147
- var _a;
1148
- logger_default.log(identifier, "Calling Groq API with retries");
1149
- let lastResponse;
1150
- for (let i = 0; i < retries; i++) {
1151
- try {
1152
- lastResponse = await callGroq(identifier, payload);
1153
- return lastResponse;
1154
- } catch (e) {
1155
- logger_default.error(
1156
- identifier,
1157
- `Retry #${i} error: ${e.message}`,
1158
- ((_a = e.response) == null ? void 0 : _a.data) || e
1159
- );
1160
- await timeout(125 * i);
1161
- }
1162
- }
1163
- const error2 = new Error(
1164
- `Failed to call Groq API after ${retries} attempts`
1165
- );
1166
- error2.response = lastResponse;
1167
- throw error2;
873
+ async function callGroqWithRetries(id, payload, retries = 5) {
874
+ return withRetries(id, "Groq", () => callGroq(id, payload), { retries });
1168
875
  }
1169
- async function getNormalizedBase64PNG(url, mime) {
1170
- const response = await axios.get(url, { responseType: "arraybuffer" });
1171
- let imageBuffer = Buffer.from(response.data);
1172
- let sharpOptions = {};
1173
- if (isHeicImage(url, mime)) {
1174
- const imageData = await decode({ buffer: imageBuffer });
1175
- imageBuffer = Buffer.from(imageData.data);
1176
- sharpOptions = {
1177
- raw: {
1178
- width: imageData.width,
1179
- height: imageData.height,
1180
- channels: 4
1181
- }
1182
- };
876
+ function isAnthropicPayload(payload) {
877
+ return Object.values(ClaudeModel).includes(payload.model);
878
+ }
879
+ function isOpenAiPayload(payload) {
880
+ return Object.values(GPTModel).includes(payload.model);
881
+ }
882
+ function isGroqPayload(payload) {
883
+ return Object.values(GroqModel).includes(payload.model);
884
+ }
885
+ function isGoogleAIPayload(payload) {
886
+ return Object.values(GeminiModel).includes(payload.model);
887
+ }
888
+ async function callWithRetries(id, aiPayload, aiConfig, retries = 5, chunkTimeoutMs = 15e3) {
889
+ if (isAnthropicPayload(aiPayload)) {
890
+ return callAnthropicWithRetries(
891
+ id,
892
+ await prepareAnthropicPayload(id, aiPayload),
893
+ aiConfig,
894
+ retries
895
+ );
1183
896
  }
1184
- const resizedBuffer = await sharp(imageBuffer, sharpOptions).withMetadata().resize(1024, 1024, { fit: "inside", withoutEnlargement: true }).png().toBuffer();
1185
- return resizedBuffer.toString("base64");
897
+ if (isOpenAiPayload(aiPayload)) {
898
+ return callOpenAiWithRetries(
899
+ id,
900
+ await prepareOpenAIPayload(id, aiPayload),
901
+ aiConfig,
902
+ retries,
903
+ chunkTimeoutMs
904
+ );
905
+ }
906
+ if (isGroqPayload(aiPayload)) {
907
+ return callGroqWithRetries(id, prepareGroqPayload(aiPayload), retries);
908
+ }
909
+ if (isGoogleAIPayload(aiPayload)) {
910
+ return callGoogleAIWithRetries(
911
+ id,
912
+ await prepareGoogleAIPayload(id, aiPayload),
913
+ retries
914
+ );
915
+ }
916
+ throw new Error("Invalid AI payload: Unknown model type.");
1186
917
  }
1187
918
  export {
1188
919
  ClaudeModel,