@linnlabs/linnkit 0.8.0 → 0.9.0

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.
@@ -42,7 +42,6 @@ var Logger = class {
42
42
  constructor(moduleName) {
43
43
  this.moduleName = moduleName;
44
44
  }
45
- moduleName;
46
45
  debug(message, data) {
47
46
  this.log(0 /* DEBUG */, "debug", message, data);
48
47
  }
@@ -1030,7 +1029,6 @@ var GraphExecutor = class {
1030
1029
  };
1031
1030
  this.telemetryPort = config.telemetryPort ?? noopTelemetry;
1032
1031
  }
1033
- checkpointer;
1034
1032
  nodes = /* @__PURE__ */ new Map();
1035
1033
  ephemeralLocals = /* @__PURE__ */ new Map();
1036
1034
  config;
@@ -4423,6 +4421,22 @@ var ToolNode = class {
4423
4421
  });
4424
4422
  }
4425
4423
  async run(state) {
4424
+ const events = [];
4425
+ while (true) {
4426
+ const result = await this.runNextPendingToolCall(state);
4427
+ if (Array.isArray(result.events) && result.events.length > 0) {
4428
+ events.push(...result.events);
4429
+ }
4430
+ if (result.kind === "route" && result.nextNodeId === "tool") {
4431
+ continue;
4432
+ }
4433
+ return {
4434
+ ...result,
4435
+ events
4436
+ };
4437
+ }
4438
+ }
4439
+ async runNextPendingToolCall(state) {
4426
4440
  const calls = state.local?.pendingToolCalls ?? [];
4427
4441
  const signalRaw = state.local?.signal;
4428
4442
  if (isAbortSignal(signalRaw) && signalRaw.aborted) {
@@ -4630,18 +4644,25 @@ var ToolNode = class {
4630
4644
  rawArguments: context.call.function?.arguments,
4631
4645
  parsedArguments: context.toolArgs
4632
4646
  });
4647
+ const remainingCalls = context.calls.slice(1);
4633
4648
  context.state.local = buildErrorLocalState({
4634
4649
  local: context.local,
4635
- remainingCalls: context.calls.slice(1),
4650
+ remainingCalls,
4636
4651
  conversationId: context.conversationId,
4637
4652
  turnId: context.turnId,
4638
4653
  runtimeEvents: context.bridge.getRuntimeEvents(),
4639
4654
  nextProtocolErrorCount: fuse.nextCount
4640
4655
  });
4641
- if (fuse.shouldFuse) {
4656
+ if (fuse.shouldFuse && remainingCalls.length === 0) {
4642
4657
  throw createToolProtocolFuseError(fuse.nextCount, context.exec.error);
4643
4658
  }
4644
- return { kind: "route", nextNodeId: "llm", events: context.bridge.getRuntimeEvents() };
4659
+ return {
4660
+ kind: "route",
4661
+ // 同一个 assistant.tool_calls batch 必须为每个 call 产出 tool_output。
4662
+ // 出错时也继续消费剩余 call,ToolNode.run 会在本节点内 drain 完 batch 再回 LLM。
4663
+ nextNodeId: remainingCalls.length > 0 ? "tool" : "llm",
4664
+ events: context.bridge.getRuntimeEvents()
4665
+ };
4645
4666
  }
4646
4667
  };
4647
4668
 
@@ -5378,18 +5399,18 @@ function createClassification(category, reason, suggestedDelay, extras) {
5378
5399
  }
5379
5400
  var ErrorClassifier = class {
5380
5401
  static classify(error, context) {
5381
- const isRecord23 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
5402
+ const isRecord24 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
5382
5403
  const baseMsg = (error.message || "").toLowerCase();
5383
5404
  const causeMsg = (() => {
5384
5405
  const cause = error.cause;
5385
5406
  if (typeof cause === "string") return cause.toLowerCase();
5386
5407
  if (cause instanceof Error) return (cause.message || "").toLowerCase();
5387
- if (isRecord23(cause) && typeof cause["message"] === "string") return String(cause["message"]).toLowerCase();
5408
+ if (isRecord24(cause) && typeof cause["message"] === "string") return String(cause["message"]).toLowerCase();
5388
5409
  return "";
5389
5410
  })();
5390
5411
  const causeCode = (() => {
5391
5412
  const cause = error.cause;
5392
- if (isRecord23(cause) && typeof cause["code"] === "string") return String(cause["code"]);
5413
+ if (isRecord24(cause) && typeof cause["code"] === "string") return String(cause["code"]);
5393
5414
  return "";
5394
5415
  })();
5395
5416
  const errorMessage = `${baseMsg} ${causeMsg}`.trim();
@@ -5881,6 +5902,9 @@ __export(llm_exports, {
5881
5902
  LLMPolicyEngine: () => LLMPolicyEngine,
5882
5903
  LlmCaller: () => LlmCaller,
5883
5904
  ModelResolver: () => ModelResolver,
5905
+ appendStreamingProviderReasoningDetails: () => appendStreamingProviderReasoningDetails,
5906
+ compactProviderReasoningDetails: () => compactProviderReasoningDetails,
5907
+ compactReasoningDetailsInValue: () => compactReasoningDetailsInValue,
5884
5908
  createDefaultTokenizerPort: () => createDefaultTokenizerPort,
5885
5909
  defaultPolicyEngine: () => defaultPolicyEngine
5886
5910
  });
@@ -6270,6 +6294,90 @@ function assertToolCallsHaveValidJsonArguments(toolCalls) {
6270
6294
  }
6271
6295
  }
6272
6296
 
6297
+ // src/runtime-kernel/llm/reasoning-details.ts
6298
+ var mergeableTextFields = ["reasoning_content"];
6299
+ function isRecord20(value) {
6300
+ return !!value && typeof value === "object" && !Array.isArray(value);
6301
+ }
6302
+ function findMergeableTextField(detail) {
6303
+ if (!isRecord20(detail)) return void 0;
6304
+ if (typeof detail.provider !== "string") return void 0;
6305
+ if (typeof detail.type !== "string") return void 0;
6306
+ for (const field of mergeableTextFields) {
6307
+ if (typeof detail[field] === "string") {
6308
+ return field;
6309
+ }
6310
+ }
6311
+ return void 0;
6312
+ }
6313
+ function hasOnlyStableTextDetailFields(detail, textField) {
6314
+ const allowedKeys = /* @__PURE__ */ new Set(["provider", "type", textField]);
6315
+ return Object.keys(detail).every((key) => allowedKeys.has(key));
6316
+ }
6317
+ function canMergeTextDetails(previous, incoming) {
6318
+ if (!isRecord20(previous) || !isRecord20(incoming)) return false;
6319
+ const previousField = findMergeableTextField(previous);
6320
+ const incomingField = findMergeableTextField(incoming);
6321
+ if (!previousField || previousField !== incomingField) return false;
6322
+ return previous.provider === incoming.provider && previous.type === incoming.type && hasOnlyStableTextDetailFields(previous, previousField) && hasOnlyStableTextDetailFields(incoming, incomingField);
6323
+ }
6324
+ function mergeStreamingText(previous, incoming) {
6325
+ if (!previous) return incoming;
6326
+ if (!incoming) return previous;
6327
+ if (incoming.startsWith(previous)) {
6328
+ return incoming;
6329
+ }
6330
+ if (previous.endsWith(incoming)) {
6331
+ return previous;
6332
+ }
6333
+ return `${previous}${incoming}`;
6334
+ }
6335
+ function appendStreamingProviderReasoningDetails(existing, incoming) {
6336
+ const next = [...existing];
6337
+ for (const detail of incoming) {
6338
+ const previous = next[next.length - 1];
6339
+ if (canMergeTextDetails(previous, detail)) {
6340
+ const textField = findMergeableTextField(previous);
6341
+ if (textField && isRecord20(detail)) {
6342
+ const mergedText = mergeStreamingText(String(previous[textField]), String(detail[textField]));
6343
+ if (mergedText === previous[textField]) {
6344
+ continue;
6345
+ }
6346
+ next[next.length - 1] = {
6347
+ ...previous,
6348
+ [textField]: mergedText
6349
+ };
6350
+ continue;
6351
+ }
6352
+ }
6353
+ next.push(detail);
6354
+ }
6355
+ return next;
6356
+ }
6357
+ function compactProviderReasoningDetails(reasoningDetails) {
6358
+ return appendStreamingProviderReasoningDetails([], reasoningDetails);
6359
+ }
6360
+ function compactReasoningDetailsInValue(value) {
6361
+ return compactValue(value);
6362
+ }
6363
+ function compactValue(value) {
6364
+ if (Array.isArray(value)) {
6365
+ return value.map((item) => compactValue(item));
6366
+ }
6367
+ if (!isRecord20(value)) {
6368
+ return value;
6369
+ }
6370
+ const compacted = {};
6371
+ for (const [key, childValue] of Object.entries(value)) {
6372
+ if (key === "reasoning_details" && Array.isArray(childValue)) {
6373
+ compacted[key] = compactProviderReasoningDetails(childValue);
6374
+ continue;
6375
+ }
6376
+ compacted[key] = compactValue(childValue);
6377
+ }
6378
+ return compacted;
6379
+ }
6380
+
6273
6381
  // src/runtime-kernel/llm/streaming-adapter.ts
6274
6382
  async function callLlmStream(params) {
6275
6383
  const {
@@ -6282,7 +6390,7 @@ async function callLlmStream(params) {
6282
6390
  } = params;
6283
6391
  let fullResponse = "";
6284
6392
  let streamError = null;
6285
- const reasoningDetails = [];
6393
+ let reasoningDetails = [];
6286
6394
  const streamAnswerId = generateMessageId();
6287
6395
  let streamChunkSeq = 0;
6288
6396
  let capturedUsage = void 0;
@@ -6352,13 +6460,21 @@ async function callLlmStream(params) {
6352
6460
  const reasoning = isRecord19(chunk) ? chunk["reasoning_details"] : void 0;
6353
6461
  if (reasoning !== void 0) {
6354
6462
  const newReasoningDetails = Array.isArray(reasoning) ? reasoning : [reasoning];
6355
- reasoningDetails.push(...newReasoningDetails);
6356
- eventHandler({
6357
- type: "provider_sidecar",
6358
- id: generateMessageId(),
6359
- timestamp: Date.now(),
6360
- reasoning_details: newReasoningDetails
6361
- });
6463
+ const previousReasoningDetails = reasoningDetails;
6464
+ const previousLength = previousReasoningDetails.length;
6465
+ const compactedReasoningDetails = appendStreamingProviderReasoningDetails(reasoningDetails, newReasoningDetails);
6466
+ reasoningDetails = compactedReasoningDetails;
6467
+ const previousLastChanged = previousLength > 0 && compactedReasoningDetails[previousLength - 1] !== previousReasoningDetails[previousLength - 1];
6468
+ const emitFromIndex = previousLastChanged ? previousLength - 1 : previousLength;
6469
+ const emittedReasoningDetails = compactedReasoningDetails.slice(Math.max(0, emitFromIndex));
6470
+ if (emittedReasoningDetails.length > 0) {
6471
+ eventHandler({
6472
+ type: "provider_sidecar",
6473
+ id: generateMessageId(),
6474
+ timestamp: Date.now(),
6475
+ reasoning_details: emittedReasoningDetails
6476
+ });
6477
+ }
6362
6478
  }
6363
6479
  if (chunk.tool_calls) {
6364
6480
  emitThoughtComplete(thoughtSegmenter.onBoundary());
@@ -6784,7 +6900,6 @@ var DefaultTokenizerPort = class {
6784
6900
  constructor(config = {}) {
6785
6901
  this.config = config;
6786
6902
  }
6787
- config;
6788
6903
  estimateText(text, _modelId) {
6789
6904
  return TokenCalculator.estimateTokens(text, {
6790
6905
  encoding: this.config.encoding,
@@ -6926,8 +7041,6 @@ var FinalAnswerCollector = class {
6926
7041
  this.conversationId = conversationId;
6927
7042
  this.turnId = turnId;
6928
7043
  }
6929
- conversationId;
6930
- turnId;
6931
7044
  answerId;
6932
7045
  chunks = [];
6933
7046
  chunkCount = 0;
@@ -6975,7 +7088,7 @@ var FinalAnswerCollector = class {
6975
7088
  };
6976
7089
 
6977
7090
  // src/runtime-kernel/child-runs/childRunTraceSink.ts
6978
- function isRecord20(v) {
7091
+ function isRecord21(v) {
6979
7092
  return !!v && typeof v === "object" && !Array.isArray(v);
6980
7093
  }
6981
7094
  function getString2(obj, key) {
@@ -7008,7 +7121,7 @@ function createChildRunTraceSink(params) {
7008
7121
  const { publisher, conversationId, turnId } = params;
7009
7122
  const finalAnswerCollector = new FinalAnswerCollector(conversationId, turnId);
7010
7123
  const sink = (evt) => {
7011
- if (!isRecord20(evt)) {
7124
+ if (!isRecord21(evt)) {
7012
7125
  return [];
7013
7126
  }
7014
7127
  const type = getString2(evt, "type");
@@ -7677,7 +7790,7 @@ function runRecordToMeta(record) {
7677
7790
  errorIfAny: record.errorIfAny ? { ...record.errorIfAny } : void 0
7678
7791
  };
7679
7792
  }
7680
- function isRecord21(value) {
7793
+ function isRecord22(value) {
7681
7794
  return typeof value === "object" && value !== null && !Array.isArray(value);
7682
7795
  }
7683
7796
  function readStringField(record, key) {
@@ -7698,7 +7811,7 @@ function getRunIdFromMetadata(event) {
7698
7811
  return snakeCaseRunId;
7699
7812
  }
7700
7813
  const runContext = metadata["run_context"];
7701
- if (isRecord21(runContext)) {
7814
+ if (isRecord22(runContext)) {
7702
7815
  return readStringField(runContext, "runId") ?? readStringField(runContext, "run_id");
7703
7816
  }
7704
7817
  return void 0;
@@ -7916,7 +8029,7 @@ function runMetaFromRecord(record) {
7916
8029
  }
7917
8030
 
7918
8031
  // src/runtime-kernel/run-supervisor/runSupervisor.ts
7919
- function isRecord22(value) {
8032
+ function isRecord23(value) {
7920
8033
  return typeof value === "object" && value !== null && !Array.isArray(value);
7921
8034
  }
7922
8035
  function readStringField2(record, key) {
@@ -7933,7 +8046,7 @@ function readRunIdFromRuntimeEvent(event) {
7933
8046
  return directRunId;
7934
8047
  }
7935
8048
  const runContext = metadata["run_context"];
7936
- if (!isRecord22(runContext)) {
8049
+ if (!isRecord23(runContext)) {
7937
8050
  return void 0;
7938
8051
  }
7939
8052
  return readStringField2(runContext, "runId") ?? readStringField2(runContext, "run_id");
@@ -7945,7 +8058,7 @@ function readAwaitingUserReason(event) {
7945
8058
  if (typeof event.prompt === "string" && event.prompt.trim().length > 0) {
7946
8059
  return event.prompt;
7947
8060
  }
7948
- if (isRecord22(event.form)) {
8061
+ if (isRecord23(event.form)) {
7949
8062
  const prompt = readStringField2(event.form, "prompt");
7950
8063
  if (prompt && prompt.trim().length > 0) {
7951
8064
  return prompt;
@@ -8594,11 +8707,14 @@ exports.ToolNode = ToolNode;
8594
8707
  exports.UserNode = UserNode;
8595
8708
  exports.WaitUserNode = WaitUserNode;
8596
8709
  exports.agentHasToolTrigger = agentHasToolTrigger;
8710
+ exports.appendStreamingProviderReasoningDetails = appendStreamingProviderReasoningDetails;
8597
8711
  exports.applySystemReminders = applySystemReminders;
8598
8712
  exports.audit = audit_exports;
8599
8713
  exports.budgetWarningTrigger = budgetWarningTrigger;
8600
8714
  exports.childRunTrace = child_run_trace_exports;
8601
8715
  exports.childRuns = child_runs_exports;
8716
+ exports.compactProviderReasoningDetails = compactProviderReasoningDetails;
8717
+ exports.compactReasoningDetailsInValue = compactReasoningDetailsInValue;
8602
8718
  exports.computeToolIdempotencyKey = computeToolIdempotencyKey;
8603
8719
  exports.consoleAudit = consoleAudit;
8604
8720
  exports.contextBudgetWarningTemplate = contextBudgetWarningTemplate;