@threaded/ai 1.0.2 → 1.0.3

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