@noetic-tools/core 1.2.2 → 1.2.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.
@@ -2,7 +2,7 @@ import { frameworkCast, NoeticConfigError } from '@noetic-tools/types';
2
2
  import { z } from 'zod';
3
3
  import { convertTools, executeToolCall, extractOutputItems, extractSystemInstruction, extractUsage, itemsToInput, sanitizeToolNameForWire, } from '../adapters/openrouter.js';
4
4
  import { isFunctionCall } from '../interpreter/typeguards.js';
5
- import { GenAI, ToolAttr } from '../observability/genai-attributes.js';
5
+ import { GenAI, NoeticAttr, ToolAttr } from '../observability/genai-attributes.js';
6
6
  import { emitFrameworkEvent, getBroadcaster, shouldEmit } from '../runtime/broadcaster-utils.js';
7
7
  import { buildItemSchemaRegistry, createToolResultItem } from './model-schema.js';
8
8
  const MAX_TOOL_ROUNDS = 32;
@@ -230,17 +230,26 @@ class CallSpanCollector {
230
230
  harness;
231
231
  exporter;
232
232
  parent;
233
+ nodeId;
233
234
  spans = [];
234
- constructor(harness, exporter, parent) {
235
+ constructor(harness, exporter, parent, nodeId) {
235
236
  this.harness = harness;
236
237
  this.exporter = exporter;
237
238
  this.parent = parent;
239
+ this.nodeId = nodeId;
240
+ }
241
+ /** Link a span back to its declared workflow node, when the call carries one. */
242
+ stampNode(span) {
243
+ if (this.nodeId !== undefined) {
244
+ span.setAttribute(NoeticAttr.NODE_ID, this.nodeId);
245
+ }
238
246
  }
239
247
  /** Open a model-call span stamped with GenAI semantic-convention attributes. */
240
248
  startModelSpan(model) {
241
249
  const span = this.harness.createSpan('llm.call', this.parent);
242
250
  span.setAttribute(GenAI.SYSTEM, 'openrouter');
243
251
  span.setAttribute(GenAI.REQUEST_MODEL, model);
252
+ this.stampNode(span);
244
253
  this.spans.push(span);
245
254
  return span;
246
255
  }
@@ -258,6 +267,7 @@ class CallSpanCollector {
258
267
  const span = this.harness.createSpan('tool.call', parent);
259
268
  span.setAttribute(ToolAttr.NAME, toolName);
260
269
  span.setAttribute(ToolAttr.NEEDS_APPROVAL, needsApproval);
270
+ this.stampNode(span);
261
271
  span.end();
262
272
  this.spans.push(span);
263
273
  }
@@ -275,7 +285,7 @@ export class AgentHarnessModelCaller {
275
285
  this.opts = opts;
276
286
  }
277
287
  newSpanCollector(request) {
278
- return new CallSpanCollector(this.opts.harness, this.opts.traceExporter, request.parentSpan ?? request.ctx?.span ?? null);
288
+ return new CallSpanCollector(this.opts.harness, this.opts.traceExporter, request.parentSpan ?? request.ctx?.span ?? null, request.nodeId);
279
289
  }
280
290
  async callOverriddenModel(request) {
281
291
  if (!this.opts.callModelOverride) {
@@ -293,6 +293,7 @@ export async function executeLLM(step, input, ctx, layers) {
293
293
  ctx: baseCtx,
294
294
  layers,
295
295
  allowedToolNames,
296
+ nodeId: step.id,
296
297
  parentSpan: baseCtx.span,
297
298
  }
298
299
  : {
@@ -302,6 +303,7 @@ export async function executeLLM(step, input, ctx, layers) {
302
303
  params: step.params,
303
304
  outputSchema: step.output,
304
305
  emit: step.emit,
306
+ nodeId: step.id,
305
307
  parentSpan: baseCtx.span,
306
308
  };
307
309
  const response = await baseCtx.harness.callModel(request);
@@ -20,4 +20,6 @@ export declare const NoeticAttr: {
20
20
  readonly WORKFLOW_NODES: "noetic.workflow.nodes";
21
21
  /** JSON array of `{ from, to }` parent→child edges between declared nodes. */
22
22
  readonly WORKFLOW_EDGES: "noetic.workflow.edges";
23
+ /** Id of the declared workflow node an `llm.call`/`tool.call` span belongs to. */
24
+ readonly NODE_ID: "noetic.node.id";
23
25
  };
@@ -23,4 +23,6 @@ export const NoeticAttr = {
23
23
  WORKFLOW_NODES: 'noetic.workflow.nodes',
24
24
  /** JSON array of `{ from, to }` parent→child edges between declared nodes. */
25
25
  WORKFLOW_EDGES: 'noetic.workflow.edges',
26
+ /** Id of the declared workflow node an `llm.call`/`tool.call` span belongs to. */
27
+ NODE_ID: 'noetic.node.id',
26
28
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noetic-tools/core",
3
- "version": "1.2.2",
3
+ "version": "1.2.3",
4
4
  "type": "module",
5
5
  "description": "Step primitives, interpreter, runtime, and memory layer contract for Noetic agents.",
6
6
  "license": "Apache-2.0",
@@ -67,7 +67,7 @@
67
67
  },
68
68
  "dependencies": {
69
69
  "@noetic-tools/memory": "^1.0.1",
70
- "@noetic-tools/types": "^1.0.2",
70
+ "@noetic-tools/types": "^1.0.3",
71
71
  "@openrouter/agent": "^0.6.0",
72
72
  "zod": "^4.0.0"
73
73
  },