@threaded/ai 1.0.2 → 1.0.4

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.cjs CHANGED
@@ -25,7 +25,9 @@ __export(index_exports, {
25
25
  compose: () => compose,
26
26
  convertMCPSchemaToToolSchema: () => convertMCPSchemaToToolSchema,
27
27
  convertStandardSchemaToJsonSchema: () => convertStandardSchemaToJsonSchema,
28
+ convertStandardSchemaToSchemaProperties: () => convertStandardSchemaToSchemaProperties,
28
29
  createMCPTools: () => createMCPTools,
30
+ embed: () => embed,
29
31
  everyNMessages: () => everyNMessages,
30
32
  everyNTokens: () => everyNTokens,
31
33
  generateApprovalToken: () => generateApprovalToken,
@@ -39,6 +41,7 @@ __export(index_exports, {
39
41
  onApprovalRequested: () => onApprovalRequested,
40
42
  onApprovalResolved: () => onApprovalResolved,
41
43
  parseModelName: () => parseModelName,
44
+ rateLimited: () => rateLimited,
42
45
  removeApprovalListener: () => removeApprovalListener,
43
46
  requestApproval: () => requestApproval,
44
47
  resolveApproval: () => resolveApproval,
@@ -54,24 +57,16 @@ __export(index_exports, {
54
57
  module.exports = __toCommonJS(index_exports);
55
58
 
56
59
  // src/schema.ts
60
+ var import_zod = require("zod");
57
61
  var isStandardSchema = (schema) => {
58
62
  return schema && typeof schema === "object" && "~standard" in schema;
59
63
  };
60
64
  var convertStandardSchemaToJsonSchema = (standardSchema, name = "Schema") => {
61
- try {
62
- const zodModule = require("zod");
63
- if (zodModule && typeof zodModule.toJSONSchema === "function") {
64
- const jsonSchema = zodModule.toJSONSchema(standardSchema);
65
- return {
66
- name,
67
- schema: jsonSchema
68
- };
69
- }
70
- } catch (error) {
71
- }
72
- throw new Error(
73
- "Standard Schema conversion requires zod v4+ with toJSONSchema support. Please install zod@^4.0.0 or provide a JsonSchema object instead."
74
- );
65
+ const jsonSchema = import_zod.z.toJSONSchema(standardSchema);
66
+ return {
67
+ name,
68
+ schema: jsonSchema
69
+ };
75
70
  };
76
71
  var convertMCPSchemaToToolSchema = (mcpSchema) => {
77
72
  if (!mcpSchema?.properties) return {};
@@ -81,7 +76,8 @@ var convertMCPSchemaToToolSchema = (mcpSchema) => {
81
76
  result[key] = {
82
77
  type: prop.type || "string",
83
78
  description: prop.description || "",
84
- optional: !mcpSchema.required?.includes(key)
79
+ optional: !mcpSchema.required?.includes(key),
80
+ ...prop.enum && { enum: prop.enum }
85
81
  };
86
82
  }
87
83
  return result;
@@ -92,6 +88,10 @@ function normalizeSchema(schema, name) {
92
88
  }
93
89
  return schema;
94
90
  }
91
+ var convertStandardSchemaToSchemaProperties = (standardSchema) => {
92
+ const jsonSchema = import_zod.z.toJSONSchema(standardSchema);
93
+ return convertMCPSchemaToToolSchema(jsonSchema);
94
+ };
95
95
 
96
96
  // src/mcp.ts
97
97
  var createMCPTools = async (client) => {
@@ -130,9 +130,10 @@ var Inherit = /* @__PURE__ */ ((Inherit2) => {
130
130
 
131
131
  // src/utils.ts
132
132
  var toolConfigToToolDefinition = (tool) => {
133
+ const schema = isStandardSchema(tool.schema) ? convertStandardSchemaToSchemaProperties(tool.schema) : tool.schema;
133
134
  const properties = {};
134
135
  const required = [];
135
- for (const [key, prop] of Object.entries(tool.schema)) {
136
+ for (const [key, prop] of Object.entries(schema)) {
136
137
  properties[key] = convertSchemaProperty(prop);
137
138
  if (!prop.optional) {
138
139
  required.push(key);
@@ -198,6 +199,49 @@ var maxCalls = (toolConfig, maxCalls2) => ({
198
199
  _maxCalls: maxCalls2
199
200
  });
200
201
 
202
+ // src/embed.ts
203
+ var import_transformers = require("@huggingface/transformers");
204
+ var modelCache = /* @__PURE__ */ new Map();
205
+ var embed = async (model2, text, config) => {
206
+ if (model2.startsWith("openai/")) {
207
+ const modelName = model2.replace("openai/", "");
208
+ const apiKey = getKey("openai") || process.env.OPENAI_API_KEY;
209
+ if (!apiKey) {
210
+ throw new Error("OpenAI API key not found");
211
+ }
212
+ const body = {
213
+ model: modelName,
214
+ input: text
215
+ };
216
+ if (config?.dimensions) {
217
+ body.dimensions = config.dimensions;
218
+ }
219
+ const response = await fetch("https://api.openai.com/v1/embeddings", {
220
+ method: "POST",
221
+ headers: {
222
+ "Content-Type": "application/json",
223
+ Authorization: `Bearer ${apiKey}`
224
+ },
225
+ body: JSON.stringify(body)
226
+ });
227
+ if (!response.ok) {
228
+ const error = await response.text();
229
+ throw new Error(`OpenAI API error: ${error}`);
230
+ }
231
+ const data = await response.json();
232
+ return data.data[0].embedding;
233
+ }
234
+ if (!modelCache.has(model2)) {
235
+ const extractor2 = await (0, import_transformers.pipeline)("feature-extraction", model2, {
236
+ dtype: "fp32"
237
+ });
238
+ modelCache.set(model2, extractor2);
239
+ }
240
+ const extractor = modelCache.get(model2);
241
+ const result = await extractor(text, { pooling: "mean", normalize: true });
242
+ return Array.from(result.data);
243
+ };
244
+
201
245
  // src/providers/openai.ts
202
246
  var appendToolCalls = (toolCalls, tcchunklist) => {
203
247
  for (const tcchunk of tcchunklist) {
@@ -329,18 +373,66 @@ var handleOpenAIStream = async (response, ctx) => {
329
373
  };
330
374
 
331
375
  // src/providers/anthropic.ts
376
+ var convertToAnthropicFormat = (messages) => {
377
+ const result = [];
378
+ let i = 0;
379
+ while (i < messages.length) {
380
+ const msg = messages[i];
381
+ if (msg.role === "system") {
382
+ i++;
383
+ continue;
384
+ }
385
+ if (msg.role === "assistant") {
386
+ if (msg.tool_calls) {
387
+ result.push({
388
+ role: "assistant",
389
+ content: msg.tool_calls.map((tc) => ({
390
+ type: "tool_use",
391
+ id: tc.id,
392
+ name: tc.function.name,
393
+ input: JSON.parse(tc.function.arguments)
394
+ }))
395
+ });
396
+ } else {
397
+ result.push({
398
+ role: "assistant",
399
+ content: msg.content
400
+ });
401
+ }
402
+ i++;
403
+ } else if (msg.role === "tool") {
404
+ const toolResults = [];
405
+ while (i < messages.length && messages[i].role === "tool") {
406
+ const toolMsg = messages[i];
407
+ toolResults.push({
408
+ type: "tool_result",
409
+ tool_use_id: toolMsg.tool_call_id,
410
+ content: toolMsg.content
411
+ });
412
+ i++;
413
+ }
414
+ result.push({
415
+ role: "user",
416
+ content: toolResults
417
+ });
418
+ } else {
419
+ result.push(msg);
420
+ i++;
421
+ }
422
+ }
423
+ return result;
424
+ };
332
425
  var callAnthropic = async (config, ctx) => {
333
426
  const { model: model2, instructions, schema } = config;
334
427
  const apiKey = getKey("anthropic") || process.env.ANTHROPIC_API_KEY;
335
428
  if (!apiKey) {
336
429
  throw new Error("Anthropic API key not found");
337
430
  }
338
- const messages = [...ctx.history];
339
431
  let system = instructions;
340
- if (messages[0]?.role === "system") {
341
- system = messages[0].content;
342
- messages.shift();
432
+ if (ctx.history[0]?.role === "system") {
433
+ system = ctx.history[0].content;
343
434
  }
435
+ const messages = convertToAnthropicFormat(ctx.history);
344
436
  if (schema) {
345
437
  const schemaPrompt = `
346
438
 
@@ -442,10 +534,17 @@ var handleAnthropicStream = async (response, ctx) => {
442
534
  type: "function",
443
535
  function: {
444
536
  name: toolUse.name,
445
- arguments: JSON.stringify(toolUse.input)
446
- }
537
+ arguments: ""
538
+ },
539
+ index: parsed.index
447
540
  });
448
541
  }
542
+ if (parsed.type === "content_block_delta" && parsed.delta?.type === "input_json_delta") {
543
+ const toolCall = toolCalls.find((tc) => tc.index === parsed.index);
544
+ if (toolCall) {
545
+ toolCall.function.arguments += parsed.delta.partial_json;
546
+ }
547
+ }
449
548
  } catch (e) {
450
549
  }
451
550
  }
@@ -459,7 +558,7 @@ var handleAnthropicStream = async (response, ctx) => {
459
558
  content: fullContent
460
559
  };
461
560
  if (toolCalls.length > 0) {
462
- msg.tool_calls = toolCalls;
561
+ msg.tool_calls = toolCalls.map(({ index, ...tc }) => tc);
463
562
  }
464
563
  return {
465
564
  ...ctx,
@@ -681,7 +780,7 @@ var model = ({
681
780
  tools: []
682
781
  }
683
782
  ) : (
684
- // model()(/* few shot / history */);
783
+ // model()(/* few shot or history */);
685
784
  ctxOrMessage
686
785
  );
687
786
  const normalizedSchema = schema ? normalizeSchema(schema) : void 0;
@@ -712,27 +811,12 @@ var executeTools = async (ctx) => {
712
811
  approvalCallback,
713
812
  parallel = false,
714
813
  retryCount = 0,
715
- approvalId
814
+ approvalId,
815
+ executeOnApproval = false
716
816
  } = toolConfig;
717
- const approvalPromises = calls.map(async (call) => {
718
- if (requireApproval) {
719
- let approved;
720
- if (approvalCallback) {
721
- approved = await approvalCallback(call);
722
- } else {
723
- const response = await requestApproval(call, approvalId);
724
- approved = response.approved;
725
- }
726
- return { call, approved };
727
- } else {
728
- return { call, approved: true };
729
- }
730
- });
731
- const approvals = await Promise.all(approvalPromises);
732
817
  const updatedCounts = { ...ctx.toolCallCounts || {} };
733
- const runCall = async (call) => {
734
- const approval = approvals.find((a) => a.call.id === call.id);
735
- if (!approval?.approved) {
818
+ const runCall = async (call, approved) => {
819
+ if (!approved) {
736
820
  if (ctx.stream) {
737
821
  ctx.stream({
738
822
  type: "tool_error",
@@ -793,7 +877,51 @@ var executeTools = async (ctx) => {
793
877
  }
794
878
  return { call, result: { error } };
795
879
  };
796
- const results = parallel ? await Promise.all(calls.map(runCall)) : await runCallsSequentially(calls, runCall);
880
+ if (executeOnApproval && requireApproval) {
881
+ const resultPromises = calls.map(async (call) => {
882
+ let approved;
883
+ if (approvalCallback) {
884
+ approved = await approvalCallback(call);
885
+ } else {
886
+ const response = await requestApproval(call, approvalId);
887
+ approved = response.approved;
888
+ }
889
+ return runCall(call, approved);
890
+ });
891
+ const results2 = await Promise.all(resultPromises);
892
+ return {
893
+ ...ctx,
894
+ history: [
895
+ ...ctx.history,
896
+ ...results2.map(({ call, result }) => ({
897
+ role: "tool",
898
+ tool_call_id: call.id,
899
+ content: JSON.stringify(result)
900
+ }))
901
+ ],
902
+ toolCallCounts: updatedCounts
903
+ };
904
+ }
905
+ const approvalPromises = calls.map(async (call) => {
906
+ if (requireApproval) {
907
+ let approved;
908
+ if (approvalCallback) {
909
+ approved = await approvalCallback(call);
910
+ } else {
911
+ const response = await requestApproval(call, approvalId);
912
+ approved = response.approved;
913
+ }
914
+ return { call, approved };
915
+ } else {
916
+ return { call, approved: true };
917
+ }
918
+ });
919
+ const approvals = await Promise.all(approvalPromises);
920
+ const runCallWithApproval = async (call) => {
921
+ const approval = approvals.find((a) => a.call.id === call.id);
922
+ return runCall(call, approval?.approved ?? true);
923
+ };
924
+ const results = parallel ? await Promise.all(calls.map(runCallWithApproval)) : await runCallsSequentially(calls, runCallWithApproval);
797
925
  return {
798
926
  ...ctx,
799
927
  history: [
@@ -1122,6 +1250,59 @@ var scope = (config, ...steps) => {
1122
1250
  };
1123
1251
  };
1124
1252
  };
1253
+
1254
+ // src/utils/rateLimited.ts
1255
+ var rateLimited = (config) => (fn) => {
1256
+ const { rps, burst, concurrency } = config;
1257
+ let tokens = burst;
1258
+ let inFlight = 0;
1259
+ const queue = [];
1260
+ let intervalId = null;
1261
+ const refillTokens = () => {
1262
+ tokens = Math.min(tokens + 1, burst);
1263
+ processQueue();
1264
+ };
1265
+ const startInterval = () => {
1266
+ if (!intervalId) {
1267
+ intervalId = setInterval(refillTokens, 1e3 / rps);
1268
+ }
1269
+ };
1270
+ const stopInterval = () => {
1271
+ if (intervalId && queue.length === 0 && inFlight === 0) {
1272
+ clearInterval(intervalId);
1273
+ intervalId = null;
1274
+ }
1275
+ };
1276
+ const processQueue = () => {
1277
+ while (queue.length > 0 && tokens > 0 && inFlight < concurrency) {
1278
+ tokens--;
1279
+ inFlight++;
1280
+ const item = queue.shift();
1281
+ item.fn().then((result) => {
1282
+ inFlight--;
1283
+ item.resolve(result);
1284
+ processQueue();
1285
+ stopInterval();
1286
+ }).catch((error) => {
1287
+ inFlight--;
1288
+ item.reject(error);
1289
+ processQueue();
1290
+ stopInterval();
1291
+ });
1292
+ }
1293
+ };
1294
+ return (async (...args) => {
1295
+ return new Promise((resolve, reject) => {
1296
+ queue.push({
1297
+ fn: () => fn(...args),
1298
+ resolve,
1299
+ reject
1300
+ });
1301
+ startInterval();
1302
+ processQueue();
1303
+ });
1304
+ });
1305
+ };
1125
1306
  // Annotate the CommonJS export names for ESM import in node:
1126
1307
  0 && (module.exports = {
1127
1308
  Inherit,
@@ -1129,7 +1310,9 @@ var scope = (config, ...steps) => {
1129
1310
  compose,
1130
1311
  convertMCPSchemaToToolSchema,
1131
1312
  convertStandardSchemaToJsonSchema,
1313
+ convertStandardSchemaToSchemaProperties,
1132
1314
  createMCPTools,
1315
+ embed,
1133
1316
  everyNMessages,
1134
1317
  everyNTokens,
1135
1318
  generateApprovalToken,
@@ -1143,6 +1326,7 @@ var scope = (config, ...steps) => {
1143
1326
  onApprovalRequested,
1144
1327
  onApprovalResolved,
1145
1328
  parseModelName,
1329
+ rateLimited,
1146
1330
  removeApprovalListener,
1147
1331
  requestApproval,
1148
1332
  resolveApproval,