@absolutejs/ai 0.0.5 → 0.0.6

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/ai/index.js CHANGED
@@ -1,5 +1,48 @@
1
1
  // @bun
2
+ // src/ai/providers/instrumentation.ts
3
+ var instrumentAIProvider = (provider, providerName) => ({
4
+ stream: (params) => {
5
+ if (!params.onUsage && !params.onSpan) {
6
+ return provider.stream(params);
7
+ }
8
+ return tapStream(provider.stream(params), params, providerName);
9
+ }
10
+ });
11
+ async function* tapStream(source, params, providerName) {
12
+ const startedAt = Date.now();
13
+ let lastUsage;
14
+ try {
15
+ for await (const chunk of source) {
16
+ if (chunk.type === "done" && chunk.usage) {
17
+ lastUsage = chunk.usage;
18
+ }
19
+ yield chunk;
20
+ }
21
+ } finally {
22
+ if (lastUsage && params.onUsage) {
23
+ try {
24
+ params.onUsage({
25
+ ...lastUsage,
26
+ model: params.model,
27
+ provider: providerName
28
+ });
29
+ } catch {}
30
+ }
31
+ if (params.onSpan) {
32
+ try {
33
+ params.onSpan({
34
+ durationMs: Date.now() - startedAt,
35
+ model: params.model,
36
+ provider: providerName,
37
+ usage: lastUsage
38
+ });
39
+ } catch {}
40
+ }
41
+ }
42
+ }
43
+
2
44
  // src/ai/providers/openai.ts
45
+ var h2IfHttps = (url) => url.startsWith("https://") ? { protocol: "http2" } : {};
3
46
  var DEFAULT_BASE_URL = "https://api.openai.com";
4
47
  var SSE_DATA_PREFIX_LENGTH = 6;
5
48
  var DONE_SENTINEL = "[DONE]";
@@ -112,6 +155,45 @@ var buildRequestBody = (params) => {
112
155
  };
113
156
  if (params.tools && params.tools.length > 0) {
114
157
  body.tools = mapToolDefinitions(params.tools);
158
+ if (params.toolChoice === "auto" || params.toolChoice === "none" || params.toolChoice === "required") {
159
+ body.tool_choice = params.toolChoice;
160
+ } else if (params.toolChoice && typeof params.toolChoice === "object") {
161
+ body.tool_choice = {
162
+ function: { name: params.toolChoice.name },
163
+ type: "function"
164
+ };
165
+ }
166
+ if (typeof params.parallelToolCalls === "boolean") {
167
+ body.parallel_tool_calls = params.parallelToolCalls;
168
+ }
169
+ }
170
+ if (typeof params.temperature === "number")
171
+ body.temperature = params.temperature;
172
+ if (typeof params.topP === "number")
173
+ body.top_p = params.topP;
174
+ if (typeof params.maxTokens === "number")
175
+ body.max_tokens = params.maxTokens;
176
+ if (params.stopSequences && params.stopSequences.length > 0)
177
+ body.stop = params.stopSequences;
178
+ if (typeof params.seed === "number")
179
+ body.seed = params.seed;
180
+ if (typeof params.frequencyPenalty === "number")
181
+ body.frequency_penalty = params.frequencyPenalty;
182
+ if (typeof params.presencePenalty === "number")
183
+ body.presence_penalty = params.presencePenalty;
184
+ if (params.responseFormat) {
185
+ if (params.responseFormat.type === "text" || params.responseFormat.type === "json_object") {
186
+ body.response_format = { type: params.responseFormat.type };
187
+ } else if (params.responseFormat.type === "json_schema") {
188
+ body.response_format = {
189
+ json_schema: {
190
+ name: params.responseFormat.name,
191
+ schema: params.responseFormat.schema,
192
+ strict: params.responseFormat.strict ?? true
193
+ },
194
+ type: "json_schema"
195
+ };
196
+ }
115
197
  }
116
198
  return body;
117
199
  };
@@ -287,7 +369,9 @@ var parseSSEStream = async function* (body, signal) {
287
369
  }
288
370
  };
289
371
  var fetchOpenAIStream = async function* (baseUrl, apiKey, body, signal) {
290
- const response = await fetch(`${baseUrl}/v1/chat/completions`, {
372
+ const target = `${baseUrl}/v1/chat/completions`;
373
+ const response = await fetch(target, {
374
+ ...h2IfHttps(target),
291
375
  body: JSON.stringify(body),
292
376
  headers: {
293
377
  Authorization: `Bearer ${apiKey}`,
@@ -307,12 +391,24 @@ var fetchOpenAIStream = async function* (baseUrl, apiKey, body, signal) {
307
391
  };
308
392
  var openai = (config) => {
309
393
  const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
310
- return {
394
+ if (!config.apiKey && !config.tokenSource) {
395
+ throw new Error("openai() requires either apiKey or tokenSource");
396
+ }
397
+ const resolveKey = async () => {
398
+ if (config.tokenSource) {
399
+ return await Promise.resolve(config.tokenSource());
400
+ }
401
+ return config.apiKey;
402
+ };
403
+ return instrumentAIProvider({
311
404
  stream: (params) => {
312
405
  const body = buildRequestBody(params);
313
- return fetchOpenAIStream(baseUrl, config.apiKey, body, params.signal);
406
+ return async function* () {
407
+ const apiKey = await resolveKey();
408
+ yield* fetchOpenAIStream(baseUrl, apiKey, body, params.signal);
409
+ }();
314
410
  }
315
- };
411
+ }, "openai");
316
412
  };
317
413
 
318
414
  // src/ai/providers/openaiCompatible.ts
@@ -340,13 +436,18 @@ var moonshot = (config) => openaiCompatible({
340
436
  apiKey: config.apiKey,
341
437
  baseUrl: "https://api.moonshot.ai"
342
438
  });
343
- var openaiCompatible = (config) => openai({ apiKey: config.apiKey, baseUrl: config.baseUrl });
439
+ var openaiCompatible = (config) => openai({
440
+ apiKey: config.apiKey,
441
+ baseUrl: config.baseUrl,
442
+ tokenSource: config.tokenSource
443
+ });
344
444
  var xai = (config) => openaiCompatible({
345
445
  apiKey: config.apiKey,
346
446
  baseUrl: "https://api.x.ai"
347
447
  });
348
448
 
349
449
  // src/ai/providers/openaiResponses.ts
450
+ var h2IfHttps2 = (url) => url.startsWith("https://") ? { protocol: "http2" } : {};
350
451
  var DEFAULT_BASE_URL2 = "https://api.openai.com";
351
452
  var EVENT_PREFIX_LENGTH = 7;
352
453
  var DATA_PREFIX_LENGTH = 6;
@@ -448,6 +549,45 @@ var buildRequestBody2 = (params, isImageModel) => {
448
549
  const tools = buildTools(params.tools, isImageModel);
449
550
  if (tools) {
450
551
  body.tools = tools;
552
+ if (params.toolChoice === "auto" || params.toolChoice === "none" || params.toolChoice === "required") {
553
+ body.tool_choice = params.toolChoice;
554
+ } else if (params.toolChoice && typeof params.toolChoice === "object") {
555
+ body.tool_choice = {
556
+ name: params.toolChoice.name,
557
+ type: "function"
558
+ };
559
+ }
560
+ if (typeof params.parallelToolCalls === "boolean") {
561
+ body.parallel_tool_calls = params.parallelToolCalls;
562
+ }
563
+ }
564
+ if (typeof params.temperature === "number")
565
+ body.temperature = params.temperature;
566
+ if (typeof params.topP === "number")
567
+ body.top_p = params.topP;
568
+ if (typeof params.maxTokens === "number")
569
+ body.max_output_tokens = params.maxTokens;
570
+ if (params.stopSequences && params.stopSequences.length > 0)
571
+ body.stop = params.stopSequences;
572
+ if (typeof params.seed === "number")
573
+ body.seed = params.seed;
574
+ if (typeof params.frequencyPenalty === "number")
575
+ body.frequency_penalty = params.frequencyPenalty;
576
+ if (typeof params.presencePenalty === "number")
577
+ body.presence_penalty = params.presencePenalty;
578
+ if (params.responseFormat) {
579
+ if (params.responseFormat.type === "text" || params.responseFormat.type === "json_object") {
580
+ body.text = { format: { type: params.responseFormat.type } };
581
+ } else if (params.responseFormat.type === "json_schema") {
582
+ body.text = {
583
+ format: {
584
+ name: params.responseFormat.name,
585
+ schema: params.responseFormat.schema,
586
+ strict: params.responseFormat.strict ?? true,
587
+ type: "json_schema"
588
+ }
589
+ };
590
+ }
451
591
  }
452
592
  if (params.thinking) {
453
593
  body.reasoning = {
@@ -682,7 +822,9 @@ var parseSSEStream2 = async function* (body, signal) {
682
822
  }
683
823
  };
684
824
  var fetchResponsesStream = async function* (baseUrl, apiKey, body, signal) {
685
- const response = await fetch(`${baseUrl}/v1/responses`, {
825
+ const target = `${baseUrl}/v1/responses`;
826
+ const response = await fetch(target, {
827
+ ...h2IfHttps2(target),
686
828
  body: JSON.stringify(body),
687
829
  headers: {
688
830
  Authorization: `Bearer ${apiKey}`,
@@ -712,16 +854,17 @@ var resolveImageModels = (imageModels) => {
712
854
  var openaiResponses = (config) => {
713
855
  const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL2;
714
856
  const imageModels = resolveImageModels(config.imageModels);
715
- return {
857
+ return instrumentAIProvider({
716
858
  stream: (params) => {
717
859
  const isImageModel = imageModels.has(params.model);
718
860
  const body = buildRequestBody2(params, isImageModel);
719
861
  return fetchResponsesStream(baseUrl, config.apiKey, body, params.signal);
720
862
  }
721
- };
863
+ }, "openai-responses");
722
864
  };
723
865
 
724
866
  // src/ai/providers/gemini.ts
867
+ var h2IfHttps3 = (url) => url.startsWith("https://") ? { protocol: "http2" } : {};
725
868
  var DEFAULT_BASE_URL3 = "https://generativelanguage.googleapis.com";
726
869
  var SSE_DATA_PREFIX_LENGTH2 = 6;
727
870
  var isRecord3 = (value) => typeof value === "object" && value !== null;
@@ -793,10 +936,29 @@ var buildRequestBody3 = (params, isImageModel) => {
793
936
  const body = {
794
937
  contents: convertMessages(params.messages)
795
938
  };
939
+ const generationConfig = {};
796
940
  if (isImageModel) {
797
- body.generationConfig = {
798
- responseModalities: ["TEXT", "IMAGE"]
799
- };
941
+ generationConfig.responseModalities = ["TEXT", "IMAGE"];
942
+ }
943
+ if (typeof params.temperature === "number")
944
+ generationConfig.temperature = params.temperature;
945
+ if (typeof params.topP === "number")
946
+ generationConfig.topP = params.topP;
947
+ if (typeof params.maxTokens === "number")
948
+ generationConfig.maxOutputTokens = params.maxTokens;
949
+ if (params.stopSequences && params.stopSequences.length > 0) {
950
+ generationConfig.stopSequences = params.stopSequences;
951
+ }
952
+ if (typeof params.seed === "number")
953
+ generationConfig.seed = params.seed;
954
+ if (params.responseFormat?.type === "json_object") {
955
+ generationConfig.responseMimeType = "application/json";
956
+ } else if (params.responseFormat?.type === "json_schema") {
957
+ generationConfig.responseMimeType = "application/json";
958
+ generationConfig.responseSchema = params.responseFormat.schema;
959
+ }
960
+ if (Object.keys(generationConfig).length > 0) {
961
+ body.generationConfig = generationConfig;
800
962
  }
801
963
  if (params.systemPrompt) {
802
964
  body.systemInstruction = {
@@ -805,6 +967,25 @@ var buildRequestBody3 = (params, isImageModel) => {
805
967
  }
806
968
  if (params.tools && params.tools.length > 0) {
807
969
  body.tools = [{ functionDeclarations: mapToolDefinitions2(params.tools) }];
970
+ if (params.toolChoice) {
971
+ const choice = params.toolChoice;
972
+ if (choice === "auto" || choice === "none") {
973
+ body.toolConfig = {
974
+ functionCallingConfig: { mode: choice === "auto" ? "AUTO" : "NONE" }
975
+ };
976
+ } else if (choice === "required") {
977
+ body.toolConfig = {
978
+ functionCallingConfig: { mode: "ANY" }
979
+ };
980
+ } else if (typeof choice === "object") {
981
+ body.toolConfig = {
982
+ functionCallingConfig: {
983
+ allowedFunctionNames: [choice.name],
984
+ mode: "ANY"
985
+ }
986
+ };
987
+ }
988
+ }
808
989
  }
809
990
  return body;
810
991
  };
@@ -936,6 +1117,7 @@ var parseSSEStream3 = async function* (body, signal) {
936
1117
  var fetchGeminiStream = async function* (baseUrl, apiKey, model, body, signal) {
937
1118
  const url = `${baseUrl}/v1beta/models/${model}:streamGenerateContent?alt=sse&key=${apiKey}`;
938
1119
  const response = await fetch(url, {
1120
+ ...h2IfHttps3(url),
939
1121
  body: JSON.stringify(body),
940
1122
  headers: {
941
1123
  "Content-Type": "application/json"
@@ -964,16 +1146,17 @@ var resolveImageModels2 = (raw) => {
964
1146
  var gemini = (config) => {
965
1147
  const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL3;
966
1148
  const imageModels = resolveImageModels2(config.imageModels);
967
- return {
1149
+ return instrumentAIProvider({
968
1150
  stream: (params) => {
969
1151
  const isImageModel = imageModels.has(params.model);
970
1152
  const body = buildRequestBody3(params, isImageModel);
971
1153
  return fetchGeminiStream(baseUrl, config.apiKey, params.model, body, params.signal);
972
1154
  }
973
- };
1155
+ }, "gemini");
974
1156
  };
975
1157
 
976
1158
  // src/ai/providers/anthropic.ts
1159
+ var h2IfHttps4 = (url) => url.startsWith("https://") ? { protocol: "http2" } : {};
977
1160
  var DEFAULT_BASE_URL4 = "https://api.anthropic.com";
978
1161
  var API_VERSION = "2023-06-01";
979
1162
  var MAX_TOKENS = 8192;
@@ -1040,10 +1223,26 @@ var buildRequestBody4 = (params) => {
1040
1223
  }
1041
1224
  if (params.tools && params.tools.length > 0) {
1042
1225
  body.tools = params.tools.map(mapToolDefinition2);
1226
+ if (params.toolChoice === "auto" || params.toolChoice === "none") {
1227
+ body.tool_choice = { type: params.toolChoice };
1228
+ } else if (params.toolChoice === "required") {
1229
+ body.tool_choice = { type: "any" };
1230
+ } else if (params.toolChoice && typeof params.toolChoice === "object") {
1231
+ body.tool_choice = { name: params.toolChoice.name, type: "tool" };
1232
+ }
1233
+ }
1234
+ if (typeof params.temperature === "number")
1235
+ body.temperature = params.temperature;
1236
+ if (typeof params.topP === "number")
1237
+ body.top_p = params.topP;
1238
+ if (typeof params.maxTokens === "number")
1239
+ body.max_tokens = params.maxTokens;
1240
+ if (params.stopSequences && params.stopSequences.length > 0) {
1241
+ body.stop_sequences = params.stopSequences;
1043
1242
  }
1044
1243
  if (params.thinking) {
1045
1244
  body.thinking = params.thinking;
1046
- body.max_tokens = Math.max(MAX_TOKENS, params.thinking.budget_tokens + MAX_TOKENS);
1245
+ body.max_tokens = Math.max(typeof body.max_tokens === "number" ? body.max_tokens : MAX_TOKENS, params.thinking.budget_tokens + MAX_TOKENS);
1047
1246
  }
1048
1247
  return body;
1049
1248
  };
@@ -1312,7 +1511,9 @@ async function* parseSSEStream4(body, signal) {
1312
1511
  }
1313
1512
  var fetchAndStream = async function* (baseUrl, config, params) {
1314
1513
  const body = buildRequestBody4(params);
1315
- const response = await fetch(`${baseUrl}/v1/messages`, {
1514
+ const target = `${baseUrl}/v1/messages`;
1515
+ const response = await fetch(target, {
1516
+ ...h2IfHttps4(target),
1316
1517
  body: JSON.stringify(body),
1317
1518
  headers: {
1318
1519
  "anthropic-version": API_VERSION,
@@ -1333,9 +1534,9 @@ var fetchAndStream = async function* (baseUrl, config, params) {
1333
1534
  };
1334
1535
  var anthropic = (config) => {
1335
1536
  const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL4;
1336
- return {
1537
+ return instrumentAIProvider({
1337
1538
  stream: (params) => fetchAndStream(baseUrl, config, params)
1338
- };
1539
+ }, "anthropic");
1339
1540
  };
1340
1541
 
1341
1542
  // src/ai/providers/ollama.ts
@@ -1390,6 +1591,29 @@ var buildRequestBody5 = (params) => {
1390
1591
  if (params.tools && params.tools.length > 0) {
1391
1592
  body.tools = mapToolDefinitions3(params.tools);
1392
1593
  }
1594
+ const options = {};
1595
+ if (typeof params.temperature === "number")
1596
+ options.temperature = params.temperature;
1597
+ if (typeof params.topP === "number")
1598
+ options.top_p = params.topP;
1599
+ if (typeof params.maxTokens === "number")
1600
+ options.num_predict = params.maxTokens;
1601
+ if (params.stopSequences && params.stopSequences.length > 0)
1602
+ options.stop = params.stopSequences;
1603
+ if (typeof params.seed === "number")
1604
+ options.seed = params.seed;
1605
+ if (typeof params.frequencyPenalty === "number")
1606
+ options.frequency_penalty = params.frequencyPenalty;
1607
+ if (typeof params.presencePenalty === "number")
1608
+ options.presence_penalty = params.presencePenalty;
1609
+ if (Object.keys(options).length > 0) {
1610
+ body.options = options;
1611
+ }
1612
+ if (params.responseFormat?.type === "json_object") {
1613
+ body.format = "json";
1614
+ } else if (params.responseFormat?.type === "json_schema") {
1615
+ body.format = params.responseFormat.schema;
1616
+ }
1393
1617
  return body;
1394
1618
  };
1395
1619
  var isRecord5 = (val) => val !== null && typeof val === "object" && !Array.isArray(val);
@@ -1548,9 +1772,9 @@ var fetchAndStream2 = async function* (baseUrl, params) {
1548
1772
  };
1549
1773
  var ollama = (config = {}) => {
1550
1774
  const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL5;
1551
- return {
1775
+ return instrumentAIProvider({
1552
1776
  stream: (params) => fetchAndStream2(baseUrl, params)
1553
- };
1777
+ }, "ollama");
1554
1778
  };
1555
1779
 
1556
1780
  // src/plugins/aiChat.ts
@@ -3308,5 +3532,5 @@ export {
3308
3532
  aiChat
3309
3533
  };
3310
3534
 
3311
- //# debugId=CBF762187998F30D64756E2164756E21
3535
+ //# debugId=6F73987BE935C99464756E2164756E21
3312
3536
  //# sourceMappingURL=index.js.map