netra-sdk 1.1.0-beta.1 → 1.2.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.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { __toESM, require_decamelize, require_camelcase, init_esm_node, __publicField, v7_default, __require } from './chunk-WMQRYJGR.js';
2
- import { createContextKey, propagation, trace, SpanKind, context, SpanStatusCode, ROOT_CONTEXT } from '@opentelemetry/api';
2
+ import { createContextKey, propagation, context, trace, SpanKind, SpanStatusCode, ROOT_CONTEXT } from '@opentelemetry/api';
3
3
  import { createRequire } from 'module';
4
4
  import { AsyncLocalStorage } from 'async_hooks';
5
5
  import { ConsoleSpanExporter } from '@opentelemetry/sdk-trace-base';
@@ -39,6 +39,37 @@ var Logger = class {
39
39
  }
40
40
  };
41
41
  Logger._debugMode = false;
42
+ function injectTraceContextHeaders(headers) {
43
+ const carrier = { ...headers };
44
+ try {
45
+ propagation.inject(context.active(), carrier);
46
+ } catch {
47
+ Logger.warn("netra: Failed to inject trace context headers");
48
+ return headers;
49
+ }
50
+ return carrier;
51
+ }
52
+ function extractContextFromHeaders(headers) {
53
+ const carrier = {};
54
+ for (const [key, value] of Object.entries(headers)) {
55
+ if (typeof value === "string") {
56
+ carrier[key.toLowerCase()] = value;
57
+ } else if (Array.isArray(value) && value.length > 0) {
58
+ carrier[key.toLowerCase()] = value[0];
59
+ }
60
+ }
61
+ return propagation.extract(context.active(), carrier);
62
+ }
63
+ function runWithExtractedContext(headers, fn) {
64
+ const extractedCtx = extractContextFromHeaders(headers);
65
+ return context.with(extractedCtx, fn);
66
+ }
67
+ function netraExpressMiddleware() {
68
+ return (req, _res, next) => {
69
+ const extractedCtx = extractContextFromHeaders(req.headers);
70
+ context.with(extractedCtx, next);
71
+ };
72
+ }
42
73
 
43
74
  // src/api/http-client.ts
44
75
  var NetraHttpClient = class {
@@ -112,7 +143,7 @@ var NetraHttpClient = class {
112
143
  );
113
144
  const response = await fetch(url.toString(), {
114
145
  method: "GET",
115
- headers: this.headers,
146
+ headers: injectTraceContextHeaders(this.headers),
116
147
  signal: controller.signal
117
148
  });
118
149
  clearTimeout(timeoutId);
@@ -141,7 +172,7 @@ var NetraHttpClient = class {
141
172
  );
142
173
  const response = await fetch(url.toString(), {
143
174
  method: "POST",
144
- headers: this.headers,
175
+ headers: injectTraceContextHeaders(this.headers),
145
176
  body: body ? JSON.stringify(body) : void 0,
146
177
  signal: controller.signal
147
178
  });
@@ -1694,13 +1725,13 @@ async function runSingleEvaluator(params) {
1694
1725
  if (config2) {
1695
1726
  expectedName = config2.name;
1696
1727
  }
1697
- const context14 = {
1728
+ const context16 = {
1698
1729
  input: itemInput,
1699
1730
  taskOutput,
1700
1731
  expectedOutput,
1701
1732
  metadata
1702
1733
  };
1703
- let result = evaluator.evaluate(context14);
1734
+ let result = evaluator.evaluate(context16);
1704
1735
  if (result instanceof Promise) {
1705
1736
  result = await result;
1706
1737
  }
@@ -2054,10 +2085,14 @@ var Evaluation = class {
2054
2085
  ctx.spanId = formatSpanId(spanContext.spanId);
2055
2086
  }
2056
2087
  ctx.sessionId = getSessionIdFromBaggage();
2057
- const { output, status } = await executeTask(task2, ctx.itemInput);
2088
+ const { output, status } = await span2.withActive(
2089
+ () => executeTask(task2, ctx.itemInput)
2090
+ );
2058
2091
  ctx.taskOutput = output;
2059
2092
  ctx.status = status;
2060
- ctx.testRunItemId = await this.postCompletedStatus(runId, ctx);
2093
+ ctx.testRunItemId = await span2.withActive(
2094
+ () => this.postCompletedStatus(runId, ctx)
2095
+ );
2061
2096
  if (evaluators && ctx.status === "completed") {
2062
2097
  const evalTask = this.runEvaluatorsForItem(runId, ctx, evaluators);
2063
2098
  bgEvalTasks.push(evalTask);
@@ -3671,6 +3706,10 @@ function setModelParams(span2, kwargs) {
3671
3706
  function buildInputMessages(kwargs, requestType) {
3672
3707
  const messages = [];
3673
3708
  if (requestType === "chat") {
3709
+ if (hasContent(kwargs.system)) {
3710
+ const systemContent = typeof kwargs.system === "string" ? kwargs.system : safeStringify(kwargs.system);
3711
+ messages.push({ role: "system", content: systemContent });
3712
+ }
3674
3713
  const rawMessages = kwargs.messages;
3675
3714
  if (!Array.isArray(rawMessages)) return messages;
3676
3715
  for (const msg of rawMessages) {
@@ -3995,7 +4034,7 @@ function anthropicWrapper(tracer, spanName, requestType) {
3995
4034
  return (async () => {
3996
4035
  try {
3997
4036
  const stream = await response;
3998
- return new AsyncStreamingWrapper(span2, stream, startTime, kwargs);
4037
+ return new AsyncStreamingWrapper(span2, stream, startTime, kwargs, spanContext);
3999
4038
  } catch (error) {
4000
4039
  Logger.error("netra.instrumentation.anthropic:", error);
4001
4040
  span2.setStatus({
@@ -4008,7 +4047,7 @@ function anthropicWrapper(tracer, spanName, requestType) {
4008
4047
  }
4009
4048
  })();
4010
4049
  } else {
4011
- return new AsyncStreamingWrapper(span2, response, startTime, kwargs);
4050
+ return new AsyncStreamingWrapper(span2, response, startTime, kwargs, spanContext);
4012
4051
  }
4013
4052
  } catch (error) {
4014
4053
  Logger.error("netra.instrumentation.anthropic:", error);
@@ -4137,7 +4176,7 @@ var LISTENER_REGISTRATION_METHODS = /* @__PURE__ */ new Set(["on", "once", "addL
4137
4176
  var COMPLETION_METHODS = /* @__PURE__ */ new Set(["finalMessage", "done", "finalText"]);
4138
4177
  var TRACKED_STREAM_EVENTS = /* @__PURE__ */ new Set(["message", "contentBlock", "text", "finalMessage"]);
4139
4178
  var MessageStreamWrapper = class {
4140
- constructor(span2, messageStream, startTime, requestKwargs) {
4179
+ constructor(span2, messageStream, startTime, requestKwargs, spanContext) {
4141
4180
  this.completeResponse = {
4142
4181
  content: [],
4143
4182
  model: "",
@@ -4147,6 +4186,7 @@ var MessageStreamWrapper = class {
4147
4186
  defineHidden(this, "messageStream", messageStream);
4148
4187
  defineHidden(this, "startTime", startTime);
4149
4188
  defineHidden(this, "requestKwargs", requestKwargs);
4189
+ defineHidden(this, "spanContext", spanContext || trace.setSpan(context.active(), span2));
4150
4190
  return new Proxy(this, {
4151
4191
  get(target, prop, receiver) {
4152
4192
  if (prop === "toJSON") {
@@ -4223,6 +4263,9 @@ var MessageStreamWrapper = class {
4223
4263
  throw err;
4224
4264
  }
4225
4265
  }
4266
+ getSpanContext() {
4267
+ return this.spanContext;
4268
+ }
4226
4269
  processEventData(eventType, data) {
4227
4270
  switch (eventType) {
4228
4271
  case "message":
@@ -4267,10 +4310,20 @@ var MessageStreamWrapper = class {
4267
4310
  if (!this.completeResponse.content) {
4268
4311
  this.completeResponse.content = [];
4269
4312
  }
4270
- this.completeResponse.content.push({
4271
- type: chunk.content_block.type,
4272
- text: ""
4273
- });
4313
+ const block = chunk.content_block;
4314
+ if (block.type === "tool_use") {
4315
+ this.completeResponse.content.push({
4316
+ type: "tool_use",
4317
+ id: block.id,
4318
+ name: block.name,
4319
+ input: ""
4320
+ });
4321
+ } else {
4322
+ this.completeResponse.content.push({
4323
+ type: block.type,
4324
+ text: ""
4325
+ });
4326
+ }
4274
4327
  break;
4275
4328
  }
4276
4329
  case "content_block_delta": {
@@ -4278,11 +4331,27 @@ var MessageStreamWrapper = class {
4278
4331
  this.completeResponse.content = [{ type: "text", text: "" }];
4279
4332
  }
4280
4333
  const lastBlock = this.completeResponse.content[this.completeResponse.content.length - 1];
4281
- if (lastBlock && chunk.delta?.text) {
4334
+ if (chunk.delta?.type === "input_json_delta" && lastBlock?.type === "tool_use") {
4335
+ lastBlock.input += chunk.delta.partial_json ?? "";
4336
+ } else if (lastBlock && chunk.delta?.text) {
4282
4337
  lastBlock.text += chunk.delta.text;
4283
4338
  }
4284
4339
  break;
4285
4340
  }
4341
+ case "content_block_stop": {
4342
+ const blocks = this.completeResponse.content;
4343
+ if (blocks && blocks.length > 0) {
4344
+ const finishedBlock = blocks[blocks.length - 1];
4345
+ if (finishedBlock?.type === "tool_use" && typeof finishedBlock.input === "string") {
4346
+ try {
4347
+ finishedBlock.input = JSON.parse(finishedBlock.input);
4348
+ } catch {
4349
+ Logger.warn("netra.instrumentation.anthropic: Failed to parse tool use input", finishedBlock.input);
4350
+ }
4351
+ }
4352
+ }
4353
+ break;
4354
+ }
4286
4355
  case "message_delta": {
4287
4356
  if (chunk.delta?.usage) {
4288
4357
  this.completeResponse.usage = {
@@ -4325,7 +4394,7 @@ var MessageStreamWrapper = class {
4325
4394
  }
4326
4395
  };
4327
4396
  var AsyncStreamingWrapper = class {
4328
- constructor(span2, response, startTime, requestKwargs) {
4397
+ constructor(span2, response, startTime, requestKwargs, spanContext) {
4329
4398
  this.iterator = null;
4330
4399
  this.completeResponse = {
4331
4400
  choices: [],
@@ -4335,6 +4404,7 @@ var AsyncStreamingWrapper = class {
4335
4404
  defineHidden(this, "response", response);
4336
4405
  defineHidden(this, "startTime", startTime);
4337
4406
  defineHidden(this, "requestKwargs", requestKwargs);
4407
+ defineHidden(this, "spanContext", spanContext || trace.setSpan(context.active(), span2));
4338
4408
  }
4339
4409
  toJSON() {
4340
4410
  return this.completeResponse;
@@ -4353,7 +4423,7 @@ var AsyncStreamingWrapper = class {
4353
4423
  throw new Error("Response is not iterable");
4354
4424
  }
4355
4425
  }
4356
- const result = await this.iterator.next();
4426
+ const result = await context.with(this.spanContext, () => this.iterator.next());
4357
4427
  if (result.done) {
4358
4428
  this.finalizeSpan(SpanStatusCode.OK);
4359
4429
  return result;
@@ -4379,10 +4449,20 @@ var AsyncStreamingWrapper = class {
4379
4449
  case "content_block_start": {
4380
4450
  const content = this.completeResponse.content || [];
4381
4451
  this.completeResponse.content = content;
4382
- content.push({
4383
- type: chunk.content_block.type,
4384
- text: ""
4385
- });
4452
+ const block = chunk.content_block;
4453
+ if (block.type === "tool_use") {
4454
+ content.push({
4455
+ type: "tool_use",
4456
+ id: block.id,
4457
+ name: block.name,
4458
+ input: ""
4459
+ });
4460
+ } else {
4461
+ content.push({
4462
+ type: block.type,
4463
+ text: ""
4464
+ });
4465
+ }
4386
4466
  break;
4387
4467
  }
4388
4468
  case "content_block_delta": {
@@ -4392,11 +4472,27 @@ var AsyncStreamingWrapper = class {
4392
4472
  this.completeResponse.content = content;
4393
4473
  }
4394
4474
  const lastBlock = content[content.length - 1];
4395
- if (lastBlock && chunk.delta?.text) {
4475
+ if (chunk.delta?.type === "input_json_delta" && lastBlock?.type === "tool_use") {
4476
+ lastBlock.input += chunk.delta.partial_json ?? "";
4477
+ } else if (lastBlock && chunk.delta?.text) {
4396
4478
  lastBlock.text += chunk.delta.text;
4397
4479
  }
4398
4480
  break;
4399
4481
  }
4482
+ case "content_block_stop": {
4483
+ const content = this.completeResponse.content || [];
4484
+ if (content.length > 0) {
4485
+ const finishedBlock = content[content.length - 1];
4486
+ if (finishedBlock?.type === "tool_use" && typeof finishedBlock.input === "string") {
4487
+ try {
4488
+ finishedBlock.input = JSON.parse(finishedBlock.input);
4489
+ } catch {
4490
+ Logger.warn("netra.instrumentation.anthropic: Failed to parse tool use input", finishedBlock.input);
4491
+ }
4492
+ }
4493
+ }
4494
+ break;
4495
+ }
4400
4496
  case "message_delta": {
4401
4497
  if (chunk.delta?.usage) {
4402
4498
  const currentUsage = this.completeResponse.usage || {};
@@ -4437,19 +4533,29 @@ var INSTRUMENTATION_NAME = "netra.instrumentation.anthropic";
4437
4533
  var INSTRUMENTS = ["anthropic >= 0.71.2"];
4438
4534
  var originalMethods = /* @__PURE__ */ new Map();
4439
4535
  var isInstrumented = false;
4440
- var AnthropicClass = null;
4536
+ var anthropicClasses = [];
4441
4537
  async function resolveAnthropicAsync() {
4442
- if (AnthropicClass) return AnthropicClass;
4538
+ if (anthropicClasses.length > 0) return anthropicClasses;
4443
4539
  try {
4444
4540
  const anthropicModule = await import('@anthropic-ai/sdk');
4445
- AnthropicClass = anthropicModule.Anthropic || anthropicModule.default || anthropicModule;
4446
- return AnthropicClass;
4541
+ anthropicClasses.push(anthropicModule.Anthropic || anthropicModule.default || anthropicModule);
4447
4542
  } catch {
4448
- return null;
4543
+ Logger.warn("Failed to resolve Anthropic ESM module");
4449
4544
  }
4545
+ try {
4546
+ const req = createRequire(import.meta.url);
4547
+ const mod = req("@anthropic-ai/sdk");
4548
+ const cjsClass = mod.Anthropic || mod.default || mod;
4549
+ if (!anthropicClasses.includes(cjsClass)) {
4550
+ anthropicClasses.push(cjsClass);
4551
+ }
4552
+ } catch {
4553
+ Logger.warn("Failed to resolve Anthropic CJS module");
4554
+ }
4555
+ return anthropicClasses;
4450
4556
  }
4451
4557
  function resolveAnthropic() {
4452
- return AnthropicClass;
4558
+ return anthropicClasses;
4453
4559
  }
4454
4560
  var NetraAnthropicInstrumentor = class {
4455
4561
  constructor() {
@@ -4463,8 +4569,8 @@ var NetraAnthropicInstrumentor = class {
4463
4569
  Logger.warn("Anthropic is already instrumented");
4464
4570
  return this;
4465
4571
  }
4466
- const Anthropic = await resolveAnthropicAsync();
4467
- if (!Anthropic) {
4572
+ const classes = await resolveAnthropicAsync();
4573
+ if (classes.length === 0) {
4468
4574
  return this;
4469
4575
  }
4470
4576
  try {
@@ -4481,9 +4587,11 @@ var NetraAnthropicInstrumentor = class {
4481
4587
  Logger.error(`Failed to initialize tracer: ${error}`);
4482
4588
  return this;
4483
4589
  }
4484
- this._instrumentMessages();
4485
- this._instrumentBetaMessages();
4486
- this._instrumentBatchMessages();
4590
+ classes.forEach((AnthropicSDK, index) => {
4591
+ this._instrumentMessages(AnthropicSDK, index);
4592
+ this._instrumentBetaMessages(AnthropicSDK, index);
4593
+ this._instrumentBatchMessages(AnthropicSDK, index);
4594
+ });
4487
4595
  isInstrumented = true;
4488
4596
  return this;
4489
4597
  }
@@ -4492,27 +4600,25 @@ var NetraAnthropicInstrumentor = class {
4492
4600
  Logger.warn("Anthropic is not instrumented");
4493
4601
  return;
4494
4602
  }
4495
- this._uninstrumentMessages();
4496
- this._uninstrumentBetaMessages();
4497
- this._uninstrumentBatchMessages();
4603
+ const classes = resolveAnthropic();
4604
+ classes.forEach((AnthropicSDK, index) => {
4605
+ this._uninstrumentMessages(AnthropicSDK, index);
4606
+ this._uninstrumentBetaMessages(AnthropicSDK, index);
4607
+ this._uninstrumentBatchMessages(AnthropicSDK, index);
4608
+ });
4498
4609
  originalMethods.clear();
4499
- AnthropicClass = null;
4610
+ anthropicClasses = [];
4500
4611
  isInstrumented = false;
4501
4612
  }
4502
4613
  isInstrumented() {
4503
4614
  return isInstrumented;
4504
4615
  }
4505
- _instrumentMessages() {
4616
+ _instrumentMessages(AnthropicSDK, index) {
4506
4617
  if (!this.tracer) {
4507
4618
  Logger.warn("Anthropic instrumentation: No tracer available");
4508
4619
  return;
4509
4620
  }
4510
4621
  try {
4511
- const AnthropicSDK = resolveAnthropic();
4512
- if (!AnthropicSDK) {
4513
- Logger.warn("Anthropic instrumentation: Anthropic SDK not available");
4514
- return;
4515
- }
4516
4622
  const MessagesClass = AnthropicSDK.Messages;
4517
4623
  if (!MessagesClass) {
4518
4624
  Logger.error(
@@ -4522,11 +4628,12 @@ var NetraAnthropicInstrumentor = class {
4522
4628
  }
4523
4629
  if (MessagesClass?.prototype?.stream) {
4524
4630
  const originalStream = MessagesClass.prototype.stream;
4525
- originalMethods.set("messages.stream", originalStream);
4631
+ originalMethods.set(`messages.stream-${index}`, originalStream);
4526
4632
  const tracer = this.tracer;
4527
4633
  MessagesClass.prototype.stream = function(...args) {
4528
4634
  const original = originalStream.bind(this);
4529
4635
  const kwargs = args[0] || {};
4636
+ const currentContext = context.active();
4530
4637
  const span2 = tracer.startSpan("anthropic.stream", {
4531
4638
  kind: SpanKind.CLIENT,
4532
4639
  attributes: {
@@ -4534,17 +4641,18 @@ var NetraAnthropicInstrumentor = class {
4534
4641
  "llm.streaming": true,
4535
4642
  "llm.operation": "stream"
4536
4643
  }
4537
- });
4644
+ }, currentContext);
4645
+ const spanContext = trace.setSpan(currentContext, span2);
4538
4646
  setRequestAttributes2(span2, kwargs, "chat");
4539
4647
  const startTime = Date.now();
4540
4648
  const instrumentedCreate = this.create;
4541
- const originalCreate = originalMethods.get("messages.create");
4649
+ const originalCreate = originalMethods.get(`messages.create-${index}`);
4542
4650
  if (originalCreate) {
4543
4651
  this.create = originalCreate;
4544
4652
  }
4545
4653
  try {
4546
- const messageStream = original(...args);
4547
- return new MessageStreamWrapper(span2, messageStream, startTime, kwargs);
4654
+ const messageStream = context.with(spanContext, () => original(...args));
4655
+ return new MessageStreamWrapper(span2, messageStream, startTime, kwargs, spanContext);
4548
4656
  } finally {
4549
4657
  if (originalCreate) {
4550
4658
  this.create = instrumentedCreate;
@@ -4554,7 +4662,7 @@ var NetraAnthropicInstrumentor = class {
4554
4662
  }
4555
4663
  if (MessagesClass?.prototype?.create) {
4556
4664
  const originalCreate = MessagesClass.prototype.create;
4557
- originalMethods.set("messages.create", originalCreate);
4665
+ originalMethods.set(`messages.create-${index}`, originalCreate);
4558
4666
  const tracer = this.tracer;
4559
4667
  const wrapper = chatWrapper(tracer);
4560
4668
  MessagesClass.prototype.create = function(...args) {
@@ -4572,17 +4680,12 @@ var NetraAnthropicInstrumentor = class {
4572
4680
  Logger.error(`Failed to instrument messages: ${error}`);
4573
4681
  }
4574
4682
  }
4575
- _instrumentBetaMessages() {
4683
+ _instrumentBetaMessages(AnthropicSDK, index) {
4576
4684
  if (!this.tracer) {
4577
4685
  Logger.warn("Anthropic instrumentation: No tracer available");
4578
4686
  return;
4579
4687
  }
4580
4688
  try {
4581
- const AnthropicSDK = resolveAnthropic();
4582
- if (!AnthropicSDK) {
4583
- Logger.warn("Anthropic instrumentation: Anthropic SDK not available");
4584
- return;
4585
- }
4586
4689
  const BetaMessagesClass = AnthropicSDK.Beta?.Messages;
4587
4690
  if (!BetaMessagesClass) {
4588
4691
  Logger.error(
@@ -4592,7 +4695,7 @@ var NetraAnthropicInstrumentor = class {
4592
4695
  }
4593
4696
  if (BetaMessagesClass?.prototype?.create) {
4594
4697
  const originalCreate = BetaMessagesClass.prototype.create;
4595
- originalMethods.set("beta.messages.create", originalCreate);
4698
+ originalMethods.set(`beta.messages.create-${index}`, originalCreate);
4596
4699
  const tracer = this.tracer;
4597
4700
  const wrapper = betaWrapper(tracer);
4598
4701
  BetaMessagesClass.prototype.create = function(...args) {
@@ -4610,17 +4713,12 @@ var NetraAnthropicInstrumentor = class {
4610
4713
  Logger.error(`Failed to instrument beta: ${error}`);
4611
4714
  }
4612
4715
  }
4613
- _instrumentBatchMessages() {
4716
+ _instrumentBatchMessages(AnthropicSDK, index) {
4614
4717
  if (!this.tracer) {
4615
4718
  Logger.warn("Anthropic instrumentation: No tracer available");
4616
4719
  return;
4617
4720
  }
4618
4721
  try {
4619
- const AnthropicSDK = resolveAnthropic();
4620
- if (!AnthropicSDK) {
4621
- Logger.warn("Anthropic instrumentation: Anthropic SDK not available");
4622
- return;
4623
- }
4624
4722
  const BatchMessageClass = AnthropicSDK.Messages?.Batches;
4625
4723
  if (!BatchMessageClass) {
4626
4724
  Logger.error(
@@ -4630,7 +4728,7 @@ var NetraAnthropicInstrumentor = class {
4630
4728
  }
4631
4729
  if (BatchMessageClass?.prototype?.create) {
4632
4730
  const originalCreate = BatchMessageClass.prototype.create;
4633
- originalMethods.set("batch.messages.create", originalCreate);
4731
+ originalMethods.set(`batch.messages.create-${index}`, originalCreate);
4634
4732
  const tracer = this.tracer;
4635
4733
  const wrapper = batchesWrapper(tracer);
4636
4734
  BatchMessageClass.prototype.create = function(...args) {
@@ -4648,25 +4746,25 @@ var NetraAnthropicInstrumentor = class {
4648
4746
  Logger.error(`Failed to instrument batches: ${error}`);
4649
4747
  }
4650
4748
  }
4651
- _uninstrumentMessages() {
4749
+ _uninstrumentMessages(AnthropicSDK, index) {
4652
4750
  try {
4653
- const AnthropicSDK = resolveAnthropic();
4654
- if (!AnthropicSDK) return;
4655
4751
  const MessagesClass = AnthropicSDK.Messages;
4656
- const originalCreate = originalMethods.get("messages.create");
4752
+ const originalCreate = originalMethods.get(`messages.create-${index}`);
4657
4753
  if (originalCreate && MessagesClass?.prototype) {
4658
4754
  MessagesClass.prototype.create = originalCreate;
4659
4755
  }
4756
+ const originalStream = originalMethods.get(`messages.stream-${index}`);
4757
+ if (originalStream && MessagesClass?.prototype) {
4758
+ MessagesClass.prototype.stream = originalStream;
4759
+ }
4660
4760
  } catch (error) {
4661
4761
  Logger.error(`Failed to uninstrument messages: ${error}`);
4662
4762
  }
4663
4763
  }
4664
- _uninstrumentBetaMessages() {
4764
+ _uninstrumentBetaMessages(AnthropicSDK, index) {
4665
4765
  try {
4666
- const AnthropicSDK = resolveAnthropic();
4667
- if (!AnthropicSDK) return;
4668
4766
  const BetaMessagesClass = AnthropicSDK.Beta?.Messages;
4669
- const originalCreate = originalMethods.get("beta.messages.create");
4767
+ const originalCreate = originalMethods.get(`beta.messages.create-${index}`);
4670
4768
  if (originalCreate && BetaMessagesClass?.prototype) {
4671
4769
  BetaMessagesClass.prototype.create = originalCreate;
4672
4770
  }
@@ -4674,12 +4772,10 @@ var NetraAnthropicInstrumentor = class {
4674
4772
  Logger.error(`Failed to uninstrument beta: ${error}`);
4675
4773
  }
4676
4774
  }
4677
- _uninstrumentBatchMessages() {
4775
+ _uninstrumentBatchMessages(AnthropicSDK, index) {
4678
4776
  try {
4679
- const AnthropicSDK = resolveAnthropic();
4680
- if (!AnthropicSDK) return;
4681
4777
  const BatchMessagesClass = AnthropicSDK.Messages?.Batches;
4682
- const originalCreate = originalMethods.get("batch.messages.create");
4778
+ const originalCreate = originalMethods.get(`batch.messages.create-${index}`);
4683
4779
  if (originalCreate && BatchMessagesClass?.prototype) {
4684
4780
  BatchMessagesClass.prototype.create = originalCreate;
4685
4781
  }
@@ -4701,6 +4797,16 @@ function safeStringify2(value) {
4701
4797
  return String(value);
4702
4798
  }
4703
4799
  }
4800
+ function extractModelName(model) {
4801
+ if (model && model.startsWith("models/")) {
4802
+ return model.replace(/^models\//, "");
4803
+ }
4804
+ return model;
4805
+ }
4806
+ function normalizeGenAIRole(raw) {
4807
+ const rawLower = raw.toLowerCase();
4808
+ return rawLower === "model" ? "assistant" : rawLower;
4809
+ }
4704
4810
  function setRequestAttributes3(span2, kwargs, requestType, args) {
4705
4811
  span2._netra_kwargs = kwargs;
4706
4812
  if (!span2.isRecording()) {
@@ -4818,6 +4924,26 @@ function _setPromptAttributes(span2, kwargs, args) {
4818
4924
  promptIndex++;
4819
4925
  }
4820
4926
  }
4927
+ if (kwargs.history && Array.isArray(kwargs.history)) {
4928
+ for (const turn of kwargs.history) {
4929
+ const role = normalizeGenAIRole(String(turn.role ?? "user"));
4930
+ const parts = turn.parts;
4931
+ if (Array.isArray(parts)) {
4932
+ const textContent = parts.filter((p) => p.text !== void 0).map((p) => String(p.text)).join("");
4933
+ if (textContent) {
4934
+ span2.setAttribute(
4935
+ `${SpanAttributes2.LLM_PROMPTS}.${promptIndex}.role`,
4936
+ role
4937
+ );
4938
+ span2.setAttribute(
4939
+ `${SpanAttributes2.LLM_PROMPTS}.${promptIndex}.content`,
4940
+ textContent
4941
+ );
4942
+ promptIndex++;
4943
+ }
4944
+ }
4945
+ }
4946
+ }
4821
4947
  if (!args) return;
4822
4948
  if (typeof args === "string") {
4823
4949
  span2.setAttribute(
@@ -5022,7 +5148,7 @@ function _setCompletionAttributes(span2, response) {
5022
5148
  const candidate = candidates[i];
5023
5149
  const content = candidate.content;
5024
5150
  if (!content) continue;
5025
- const role = String(content.role ?? "model");
5151
+ const role = normalizeGenAIRole(String(content.role ?? "model"));
5026
5152
  const parts = content.parts;
5027
5153
  if (!Array.isArray(parts)) continue;
5028
5154
  const textContent = parts.filter((p) => p.text !== void 0).map((p) => String(p.text)).join("");
@@ -5080,12 +5206,16 @@ function googleGenAIWrapper(tracer, spanName, requestType) {
5080
5206
  const modelInstance = this;
5081
5207
  const modelName = modelInstance.model;
5082
5208
  const systemInstruction = modelInstance.systemInstruction;
5209
+ const history = modelInstance.history;
5083
5210
  if (modelName) {
5084
- kwargs.model = modelName;
5211
+ kwargs.model = extractModelName(modelName);
5085
5212
  }
5086
5213
  if (systemInstruction) {
5087
5214
  kwargs.systemInstruction = systemInstruction;
5088
5215
  }
5216
+ if (history) {
5217
+ kwargs.history = history;
5218
+ }
5089
5219
  const currentContext = context.active();
5090
5220
  return tracer.startActiveSpan(
5091
5221
  spanName,
@@ -5163,8 +5293,10 @@ function googleGenAIStreamWrapper(tracer, spanName, requestType) {
5163
5293
  const modelInstance = this;
5164
5294
  const modelName = modelInstance.model;
5165
5295
  const systemInstruction = modelInstance.systemInstruction;
5166
- if (modelName) kwargs.model = modelName;
5296
+ const history = modelInstance.history;
5297
+ if (modelName) kwargs.model = extractModelName(modelName);
5167
5298
  if (systemInstruction) kwargs.systemInstruction = systemInstruction;
5299
+ if (history) kwargs.history = history;
5168
5300
  const currentContext = context.active();
5169
5301
  return tracer.startActiveSpan(
5170
5302
  spanName,
@@ -5303,25 +5435,75 @@ function googleGenAIStreamWrapper(tracer, spanName, requestType) {
5303
5435
  };
5304
5436
  };
5305
5437
  }
5438
+ function googleGenAIStartChatWrapper(tracer, spanName, requestType) {
5439
+ const sendMessageWrapperFn = googleGenAIWrapper(tracer, spanName, requestType);
5440
+ const sendMessageStreamWrapperFn = googleGenAIStreamWrapper(tracer, spanName, requestType);
5441
+ return function wrapper(original) {
5442
+ return function(...args) {
5443
+ const chatSession = original.apply(this, args);
5444
+ if (!chatSession) return chatSession;
5445
+ const modelInstance = this;
5446
+ const modelName = modelInstance.model;
5447
+ const systemInstruction = modelInstance.systemInstruction;
5448
+ const chatHistory = args[0]?.history;
5449
+ if (typeof chatSession.sendMessage === "function" && !chatSession.__netra_patched) {
5450
+ const originalSendMessage = chatSession.sendMessage.bind(chatSession);
5451
+ const wrappedSendMessage = sendMessageWrapperFn(originalSendMessage);
5452
+ chatSession.sendMessage = function(...sendArgs) {
5453
+ const ctx = this;
5454
+ if (modelName) ctx.model = modelName;
5455
+ if (systemInstruction) ctx.systemInstruction = systemInstruction;
5456
+ if (chatHistory) ctx.history = chatHistory;
5457
+ return wrappedSendMessage.apply(this, sendArgs);
5458
+ };
5459
+ if (typeof chatSession.sendMessageStream === "function") {
5460
+ const originalSendStream = chatSession.sendMessageStream.bind(chatSession);
5461
+ const wrappedSendStream = sendMessageStreamWrapperFn(originalSendStream);
5462
+ chatSession.sendMessageStream = function(...sendArgs) {
5463
+ const ctx = this;
5464
+ if (modelName) ctx.model = modelName;
5465
+ if (systemInstruction) ctx.systemInstruction = systemInstruction;
5466
+ if (chatHistory) ctx.history = chatHistory;
5467
+ return wrappedSendStream.apply(this, sendArgs);
5468
+ };
5469
+ }
5470
+ chatSession.__netra_patched = true;
5471
+ }
5472
+ return chatSession;
5473
+ };
5474
+ };
5475
+ }
5306
5476
  var chatWrapper2 = (tracer) => googleGenAIWrapper(tracer, CHAT_SPAN_NAME2, "chat");
5307
5477
  var embeddingsWrapper = (tracer) => googleGenAIWrapper(tracer, EMBEDDING_SPAN_NAME, "embedding");
5308
5478
  var chatStreamWrapper = (tracer) => googleGenAIStreamWrapper(tracer, CHAT_SPAN_NAME2, "chat");
5309
- var GenerativeModel = null;
5479
+ var startChatWrapper = (tracer) => googleGenAIStartChatWrapper(tracer, CHAT_SPAN_NAME2, "chat");
5310
5480
  var INSTRUMENTATION_NAME2 = "netra.instrumentation.google_genai";
5311
5481
  var INSTRUMENTS2 = ["@google/genai >= 0.24.1"];
5312
5482
  var isInstrumented2 = false;
5483
+ var generativeModelClasses = [];
5313
5484
  async function resolveGoogleGenerativeAIAsync() {
5314
- if (GenerativeModel) return GenerativeModel;
5485
+ if (generativeModelClasses.length > 0) return generativeModelClasses;
5315
5486
  try {
5316
5487
  const googleGenAIModule = await import('@google/generative-ai');
5317
- GenerativeModel = googleGenAIModule.GenerativeModel || googleGenAIModule.default?.GenerativeModel || googleGenAIModule.default || googleGenAIModule;
5318
- return GenerativeModel;
5488
+ const esmClass = googleGenAIModule.GenerativeModel || googleGenAIModule.default?.GenerativeModel || googleGenAIModule.default || googleGenAIModule;
5489
+ generativeModelClasses.push(esmClass);
5319
5490
  } catch {
5320
- return null;
5491
+ Logger.warn("Failed to resolve Google GenAI ESM module");
5492
+ }
5493
+ try {
5494
+ const req = createRequire(import.meta.url);
5495
+ const mod = req("@google/generative-ai");
5496
+ const cjsClass = mod.GenerativeModel || mod.default?.GenerativeModel || mod.default || mod;
5497
+ if (!generativeModelClasses.includes(cjsClass)) {
5498
+ generativeModelClasses.push(cjsClass);
5499
+ }
5500
+ } catch {
5501
+ Logger.warn("Failed to resolve Google GenAI CJS module");
5321
5502
  }
5503
+ return generativeModelClasses;
5322
5504
  }
5323
5505
  function resolveGoogleGenerativeAI() {
5324
- return GenerativeModel;
5506
+ return generativeModelClasses;
5325
5507
  }
5326
5508
  var NetraGoogleGenerativeAIInstrumentor = class {
5327
5509
  constructor() {
@@ -5335,16 +5517,15 @@ var NetraGoogleGenerativeAIInstrumentor = class {
5335
5517
  }
5336
5518
  /**
5337
5519
  * Instrument Google GenAI client methods (async version)
5338
- * Uses dynamic import() to ensure we get the same ES module instance
5339
- * that the application uses.
5520
+ * Tries both ESM and CJS resolution to cover dual-package setups.
5340
5521
  */
5341
5522
  async instrumentAsync(options = {}) {
5342
5523
  if (isInstrumented2) {
5343
5524
  Logger.warn("Google GenAI is already instrumented");
5344
5525
  return this;
5345
5526
  }
5346
- const model = await resolveGoogleGenerativeAIAsync();
5347
- if (!model) {
5527
+ const classes = await resolveGoogleGenerativeAIAsync();
5528
+ if (classes.length === 0) {
5348
5529
  return this;
5349
5530
  }
5350
5531
  try {
@@ -5354,7 +5535,7 @@ var NetraGoogleGenerativeAIInstrumentor = class {
5354
5535
  Logger.error(`Failed to initialize tracer: ${error}`);
5355
5536
  return this;
5356
5537
  }
5357
- this._instrumentGenerativeModel();
5538
+ classes.forEach((model) => this._instrumentGenerativeModel(model));
5358
5539
  isInstrumented2 = true;
5359
5540
  return this;
5360
5541
  }
@@ -5366,7 +5547,8 @@ var NetraGoogleGenerativeAIInstrumentor = class {
5366
5547
  Logger.warn("Google GenAI is already instrumented");
5367
5548
  return this;
5368
5549
  }
5369
- if (!resolveGoogleGenerativeAI()) {
5550
+ const classes = resolveGoogleGenerativeAI();
5551
+ if (classes.length === 0) {
5370
5552
  this.instrumentAsync(options).catch((e) => {
5371
5553
  Logger.error("Failed to instrument Google GenAI:", e);
5372
5554
  });
@@ -5379,7 +5561,7 @@ var NetraGoogleGenerativeAIInstrumentor = class {
5379
5561
  Logger.error(`Failed to initialize tracer: ${error}`);
5380
5562
  return this;
5381
5563
  }
5382
- this._instrumentGenerativeModel();
5564
+ classes.forEach((model) => this._instrumentGenerativeModel(model));
5383
5565
  isInstrumented2 = true;
5384
5566
  return this;
5385
5567
  }
@@ -5391,8 +5573,9 @@ var NetraGoogleGenerativeAIInstrumentor = class {
5391
5573
  Logger.warn("Google GenAI is not instrumented");
5392
5574
  return;
5393
5575
  }
5394
- this._uninstrumentGenerativeModel();
5395
- GenerativeModel = null;
5576
+ const classes = resolveGoogleGenerativeAI();
5577
+ classes.forEach((model) => this._uninstrumentGenerativeModel(model));
5578
+ generativeModelClasses = [];
5396
5579
  isInstrumented2 = false;
5397
5580
  }
5398
5581
  /**
@@ -5401,10 +5584,10 @@ var NetraGoogleGenerativeAIInstrumentor = class {
5401
5584
  isInstrumented() {
5402
5585
  return isInstrumented2;
5403
5586
  }
5404
- _instrumentGenerativeModel() {
5587
+ _instrumentGenerativeModel(GenerativeModel) {
5405
5588
  if (!this.tracer) return;
5406
5589
  try {
5407
- if (!GenerativeModel) {
5590
+ if (!GenerativeModel?.prototype) {
5408
5591
  Logger.error(
5409
5592
  "Failed to find Google GenAI GenerativeModel to instrument"
5410
5593
  );
@@ -5426,14 +5609,22 @@ var NetraGoogleGenerativeAIInstrumentor = class {
5426
5609
  "embedContent",
5427
5610
  embeddingsWrapper(tracer)
5428
5611
  );
5612
+ if (typeof GenerativeModel.prototype.startChat === "function") {
5613
+ shimmer.wrap(
5614
+ GenerativeModel.prototype,
5615
+ "startChat",
5616
+ startChatWrapper(tracer)
5617
+ );
5618
+ }
5429
5619
  } catch (error) {
5430
5620
  Logger.debug(
5431
5621
  `Google GenAI instrumentation: failed to instrument: ${error}`
5432
5622
  );
5433
5623
  }
5434
5624
  }
5435
- _uninstrumentGenerativeModel() {
5625
+ _uninstrumentGenerativeModel(GenerativeModel) {
5436
5626
  try {
5627
+ if (!GenerativeModel?.prototype) return;
5437
5628
  if (typeof GenerativeModel.prototype.generateContent === "function") {
5438
5629
  shimmer.unwrap(GenerativeModel.prototype, "generateContent");
5439
5630
  }
@@ -5443,6 +5634,9 @@ var NetraGoogleGenerativeAIInstrumentor = class {
5443
5634
  if (typeof GenerativeModel.prototype.embedContent === "function") {
5444
5635
  shimmer.unwrap(GenerativeModel.prototype, "embedContent");
5445
5636
  }
5637
+ if (typeof GenerativeModel.prototype.startChat === "function") {
5638
+ shimmer.unwrap(GenerativeModel.prototype, "startChat");
5639
+ }
5446
5640
  } catch (error) {
5447
5641
  Logger.debug(`Failed to uninstrument Google GenAI: ${error}`);
5448
5642
  }
@@ -5806,19 +6000,29 @@ var INSTRUMENTATION_NAME3 = "netra.instrumentation.groq";
5806
6000
  var INSTRUMENTS3 = ["groq-sdk >= 0.3.0"];
5807
6001
  var originalMethods2 = /* @__PURE__ */ new Map();
5808
6002
  var isInstrumented3 = false;
5809
- var GroqClass = null;
6003
+ var groqClasses = [];
5810
6004
  async function resolveGroqAsync() {
5811
- if (GroqClass) return GroqClass;
6005
+ if (groqClasses.length > 0) return groqClasses;
5812
6006
  try {
5813
6007
  const groqModule = await import('groq-sdk');
5814
- GroqClass = groqModule.Groq || groqModule.default || groqModule;
5815
- return GroqClass;
6008
+ groqClasses.push(groqModule.Groq || groqModule.default || groqModule);
5816
6009
  } catch {
5817
- return null;
6010
+ Logger.warn("Failed to resolve Groq ESM module");
5818
6011
  }
6012
+ try {
6013
+ const req = createRequire(import.meta.url);
6014
+ const mod = req("groq-sdk");
6015
+ const cjsClass = mod.Groq || mod.default || mod;
6016
+ if (!groqClasses.includes(cjsClass)) {
6017
+ groqClasses.push(cjsClass);
6018
+ }
6019
+ } catch {
6020
+ Logger.warn("Failed to resolve Groq CJS module");
6021
+ }
6022
+ return groqClasses;
5819
6023
  }
5820
6024
  function resolveGroq() {
5821
- return GroqClass;
6025
+ return groqClasses;
5822
6026
  }
5823
6027
  var NetraGroqInstrumentor = class {
5824
6028
  constructor() {
@@ -5829,16 +6033,15 @@ var NetraGroqInstrumentor = class {
5829
6033
  }
5830
6034
  /**
5831
6035
  * Instrument Groq client methods (async version)
5832
- * Uses dynamic import() to ensure we get the same ES module instance
5833
- * that the application uses.
6036
+ * Tries both ESM and CJS resolution to cover dual-package setups.
5834
6037
  */
5835
6038
  async instrumentAsync(options = {}) {
5836
6039
  if (isInstrumented3) {
5837
6040
  Logger.warn("Groq is already instrumented");
5838
6041
  return this;
5839
6042
  }
5840
- const Groq = await resolveGroqAsync();
5841
- if (!Groq) {
6043
+ const classes = await resolveGroqAsync();
6044
+ if (classes.length === 0) {
5842
6045
  return this;
5843
6046
  }
5844
6047
  try {
@@ -5855,21 +6058,23 @@ var NetraGroqInstrumentor = class {
5855
6058
  Logger.error(`Failed to initialize tracer: ${error}`);
5856
6059
  return this;
5857
6060
  }
5858
- this._instrumentChatCompletions(Groq);
6061
+ classes.forEach((Groq, index) => {
6062
+ this._instrumentChatCompletions(Groq, index);
6063
+ });
5859
6064
  isInstrumented3 = true;
5860
6065
  return this;
5861
6066
  }
5862
6067
  /**
5863
6068
  * Instrument Groq client methods (sync version - for backwards compatibility)
5864
- * Note: This uses a cached Groq class. Call instrumentAsync() for proper initialization.
6069
+ * Note: This uses cached Groq classes. Call instrumentAsync() for proper initialization.
5865
6070
  */
5866
6071
  instrument(options = {}) {
5867
6072
  if (isInstrumented3) {
5868
6073
  Logger.warn("Groq is already instrumented");
5869
6074
  return this;
5870
6075
  }
5871
- const Groq = resolveGroq();
5872
- if (!Groq) {
6076
+ const classes = resolveGroq();
6077
+ if (classes.length === 0) {
5873
6078
  this.instrumentAsync(options).catch((e) => {
5874
6079
  Logger.error("Failed to instrument Groq:", e);
5875
6080
  });
@@ -5889,7 +6094,9 @@ var NetraGroqInstrumentor = class {
5889
6094
  Logger.error(`Failed to initialize tracer: ${error}`);
5890
6095
  return this;
5891
6096
  }
5892
- this._instrumentChatCompletions(Groq);
6097
+ classes.forEach((Groq, index) => {
6098
+ this._instrumentChatCompletions(Groq, index);
6099
+ });
5893
6100
  isInstrumented3 = true;
5894
6101
  return this;
5895
6102
  }
@@ -5898,18 +6105,18 @@ var NetraGroqInstrumentor = class {
5898
6105
  Logger.warn("Groq is not instrumented");
5899
6106
  return;
5900
6107
  }
5901
- const Groq = resolveGroq();
5902
- if (Groq) {
5903
- this._uninstrumentChatCompletions(Groq);
5904
- }
6108
+ const classes = resolveGroq();
6109
+ classes.forEach((Groq, index) => {
6110
+ this._uninstrumentChatCompletions(Groq, index);
6111
+ });
5905
6112
  originalMethods2.clear();
5906
- GroqClass = null;
6113
+ groqClasses = [];
5907
6114
  isInstrumented3 = false;
5908
6115
  }
5909
6116
  isInstrumented() {
5910
6117
  return isInstrumented3;
5911
6118
  }
5912
- _instrumentChatCompletions(Groq) {
6119
+ _instrumentChatCompletions(Groq, index) {
5913
6120
  if (!this.tracer) {
5914
6121
  Logger.warn("Groq instrumentation: No tracer available");
5915
6122
  return;
@@ -5923,7 +6130,7 @@ var NetraGroqInstrumentor = class {
5923
6130
  return;
5924
6131
  }
5925
6132
  const originalCreate = CompletionsClass.prototype.create;
5926
- originalMethods2.set("chat.completions.create", originalCreate);
6133
+ originalMethods2.set(`chat.completions.create-${index}`, originalCreate);
5927
6134
  const tracer = this.tracer;
5928
6135
  const wrapper = chatWrapper3(tracer);
5929
6136
  CompletionsClass.prototype.create = function(...args) {
@@ -5938,10 +6145,10 @@ var NetraGroqInstrumentor = class {
5938
6145
  );
5939
6146
  }
5940
6147
  }
5941
- _uninstrumentChatCompletions(Groq) {
6148
+ _uninstrumentChatCompletions(Groq, index) {
5942
6149
  try {
5943
6150
  const CompletionsClass = Groq.Chat?.Completions;
5944
- const originalCreate = originalMethods2.get("chat.completions.create");
6151
+ const originalCreate = originalMethods2.get(`chat.completions.create-${index}`);
5945
6152
  if (originalCreate && CompletionsClass?.prototype) {
5946
6153
  CompletionsClass.prototype.create = originalCreate;
5947
6154
  }
@@ -7436,19 +7643,31 @@ var INSTRUMENTATION_NAME5 = "netra.instrumentation.mistral_ai";
7436
7643
  var INSTRUMENTS5 = ["@mistralai/mistralai >= 1.0.0"];
7437
7644
  var originalMethods4 = /* @__PURE__ */ new Map();
7438
7645
  var isInstrumented5 = false;
7439
- var MistralClass = null;
7646
+ var mistralClasses = [];
7440
7647
  async function resolveMistralAsync() {
7441
- if (MistralClass) return MistralClass;
7648
+ if (mistralClasses.length > 0) return mistralClasses;
7442
7649
  try {
7443
7650
  const mistralModule = await import('@mistralai/mistralai');
7444
- MistralClass = mistralModule.Mistral || mistralModule.default || mistralModule;
7445
- return MistralClass;
7651
+ mistralClasses.push(
7652
+ mistralModule.Mistral || mistralModule.default || mistralModule
7653
+ );
7446
7654
  } catch {
7447
- return null;
7655
+ Logger.warn("Failed to resolve MistralAI ESM module");
7656
+ }
7657
+ try {
7658
+ const req = createRequire(import.meta.url);
7659
+ const mod = req("@mistralai/mistralai");
7660
+ const cjsClass = mod.Mistral || mod.default || mod;
7661
+ if (!mistralClasses.includes(cjsClass)) {
7662
+ mistralClasses.push(cjsClass);
7663
+ }
7664
+ } catch {
7665
+ Logger.warn("Failed to resolve MistralAI CJS module");
7448
7666
  }
7667
+ return mistralClasses;
7449
7668
  }
7450
7669
  function resolveMistral() {
7451
- return MistralClass;
7670
+ return mistralClasses;
7452
7671
  }
7453
7672
  var NetraMistralAIInstrumentor = class {
7454
7673
  constructor() {
@@ -7463,16 +7682,15 @@ var NetraMistralAIInstrumentor = class {
7463
7682
  }
7464
7683
  /**
7465
7684
  * Instrument MistralAI client methods (async version)
7466
- * Uses dynamic import() to ensure we get the same ES module instance
7467
- * that the application uses.
7685
+ * Tries both ESM and CJS resolution to cover dual-package setups.
7468
7686
  */
7469
7687
  async instrumentAsync(options = {}) {
7470
7688
  if (isInstrumented5) {
7471
7689
  Logger.warn("MistralAI is already instrumented");
7472
7690
  return this;
7473
7691
  }
7474
- const Mistral = await resolveMistralAsync();
7475
- if (!Mistral) {
7692
+ const classes = await resolveMistralAsync();
7693
+ if (classes.length === 0) {
7476
7694
  return this;
7477
7695
  }
7478
7696
  try {
@@ -7489,11 +7707,16 @@ var NetraMistralAIInstrumentor = class {
7489
7707
  Logger.error(`Failed to initialize tracer: ${error}`);
7490
7708
  return this;
7491
7709
  }
7492
- const patchedChat = this._instrumentChat(Mistral);
7493
- const patchedEmbeddings = this._instrumentEmbeddings(Mistral);
7494
- const patchedFIM = this._instrumentFIM(Mistral);
7495
- const patchedAgents = this._instrumentAgents(Mistral);
7496
- const didPatch = patchedChat || patchedEmbeddings || patchedFIM || patchedAgents;
7710
+ let didPatch = false;
7711
+ classes.forEach((Mistral, index) => {
7712
+ const patchedChat = this._instrumentChat(Mistral, index);
7713
+ const patchedEmbeddings = this._instrumentEmbeddings(Mistral, index);
7714
+ const patchedFIM = this._instrumentFIM(Mistral, index);
7715
+ const patchedAgents = this._instrumentAgents(Mistral, index);
7716
+ if (patchedChat || patchedEmbeddings || patchedFIM || patchedAgents) {
7717
+ didPatch = true;
7718
+ }
7719
+ });
7497
7720
  if (!didPatch) {
7498
7721
  Logger.warn(
7499
7722
  "MistralAI instrumentation initialized but no methods were patched. Is '@mistralai/mistralai' installed and compatible?"
@@ -7505,15 +7728,15 @@ var NetraMistralAIInstrumentor = class {
7505
7728
  }
7506
7729
  /**
7507
7730
  * Instrument MistralAI client methods (sync version - for backwards compatibility)
7508
- * Note: This uses a cached Mistral class. Call instrumentAsync() for proper initialization.
7731
+ * Note: This uses cached Mistral classes. Call instrumentAsync() for proper initialization.
7509
7732
  */
7510
7733
  instrument(options = {}) {
7511
7734
  if (isInstrumented5) {
7512
7735
  Logger.warn("MistralAI is already instrumented");
7513
7736
  return this;
7514
7737
  }
7515
- const Mistral = resolveMistral();
7516
- if (!Mistral) {
7738
+ const classes = resolveMistral();
7739
+ if (classes.length === 0) {
7517
7740
  this.instrumentAsync(options).catch((e) => {
7518
7741
  Logger.error("Failed to instrument MistralAI:", e);
7519
7742
  });
@@ -7533,11 +7756,16 @@ var NetraMistralAIInstrumentor = class {
7533
7756
  Logger.error(`Failed to initialize tracer: ${error}`);
7534
7757
  return this;
7535
7758
  }
7536
- const patchedChat = this._instrumentChat(Mistral);
7537
- const patchedEmbeddings = this._instrumentEmbeddings(Mistral);
7538
- const patchedFIM = this._instrumentFIM(Mistral);
7539
- const patchedAgents = this._instrumentAgents(Mistral);
7540
- const didPatch = patchedChat || patchedEmbeddings || patchedFIM || patchedAgents;
7759
+ let didPatch = false;
7760
+ classes.forEach((Mistral, index) => {
7761
+ const patchedChat = this._instrumentChat(Mistral, index);
7762
+ const patchedEmbeddings = this._instrumentEmbeddings(Mistral, index);
7763
+ const patchedFIM = this._instrumentFIM(Mistral, index);
7764
+ const patchedAgents = this._instrumentAgents(Mistral, index);
7765
+ if (patchedChat || patchedEmbeddings || patchedFIM || patchedAgents) {
7766
+ didPatch = true;
7767
+ }
7768
+ });
7541
7769
  if (!didPatch) {
7542
7770
  Logger.warn(
7543
7771
  "MistralAI instrumentation initialized but no methods were patched. Is '@mistralai/mistralai' installed and compatible?"
@@ -7555,15 +7783,15 @@ var NetraMistralAIInstrumentor = class {
7555
7783
  Logger.warn("MistralAI is not instrumented");
7556
7784
  return;
7557
7785
  }
7558
- const Mistral = resolveMistral();
7559
- if (Mistral) {
7560
- this._uninstrumentChat(Mistral);
7561
- this._uninstrumentEmbeddings(Mistral);
7562
- this._uninstrumentFIM(Mistral);
7563
- this._uninstrumentAgents(Mistral);
7564
- }
7786
+ const classes = resolveMistral();
7787
+ classes.forEach((Mistral, index) => {
7788
+ this._uninstrumentChat(Mistral, index);
7789
+ this._uninstrumentEmbeddings(Mistral, index);
7790
+ this._uninstrumentFIM(Mistral, index);
7791
+ this._uninstrumentAgents(Mistral, index);
7792
+ });
7565
7793
  originalMethods4.clear();
7566
- MistralClass = null;
7794
+ mistralClasses = [];
7567
7795
  isInstrumented5 = false;
7568
7796
  }
7569
7797
  /**
@@ -7601,14 +7829,14 @@ var NetraMistralAIInstrumentor = class {
7601
7829
  this._ensureResourceCtors(Mistral);
7602
7830
  return this.resourceCtors[name] ?? null;
7603
7831
  }
7604
- _instrumentChat(Mistral) {
7832
+ _instrumentChat(Mistral, index) {
7605
7833
  if (!this.tracer) return false;
7606
7834
  try {
7607
7835
  const ChatClass = this._getCtor(Mistral, "chat");
7608
7836
  let didPatch = false;
7609
7837
  if (ChatClass?.prototype?.complete) {
7610
7838
  const originalComplete = ChatClass.prototype.complete;
7611
- originalMethods4.set("chat.complete", originalComplete);
7839
+ originalMethods4.set(`chat.complete-${index}`, originalComplete);
7612
7840
  const tracer = this.tracer;
7613
7841
  const wrapper = chatWrapper4(tracer);
7614
7842
  ChatClass.prototype.complete = function(...args) {
@@ -7625,7 +7853,7 @@ var NetraMistralAIInstrumentor = class {
7625
7853
  }
7626
7854
  if (ChatClass?.prototype?.stream) {
7627
7855
  const originalStream = ChatClass.prototype.stream;
7628
- originalMethods4.set("chat.stream", originalStream);
7856
+ originalMethods4.set(`chat.stream-${index}`, originalStream);
7629
7857
  const tracer = this.tracer;
7630
7858
  const wrapper = chatStreamWrapper2(tracer);
7631
7859
  ChatClass.prototype.stream = function(...args) {
@@ -7646,14 +7874,14 @@ var NetraMistralAIInstrumentor = class {
7646
7874
  return false;
7647
7875
  }
7648
7876
  }
7649
- _instrumentEmbeddings(Mistral) {
7877
+ _instrumentEmbeddings(Mistral, index) {
7650
7878
  if (!this.tracer) return false;
7651
7879
  try {
7652
7880
  const EmbeddingsClass = this._getCtor(Mistral, "embeddings");
7653
7881
  let didPatch = false;
7654
7882
  if (EmbeddingsClass?.prototype?.create) {
7655
7883
  const originalCreate = EmbeddingsClass.prototype.create;
7656
- originalMethods4.set("embeddings.create", originalCreate);
7884
+ originalMethods4.set(`embeddings.create-${index}`, originalCreate);
7657
7885
  const tracer = this.tracer;
7658
7886
  const wrapper = embeddingsWrapper2(tracer);
7659
7887
  EmbeddingsClass.prototype.create = function(...args) {
@@ -7674,14 +7902,14 @@ var NetraMistralAIInstrumentor = class {
7674
7902
  return false;
7675
7903
  }
7676
7904
  }
7677
- _instrumentFIM(Mistral) {
7905
+ _instrumentFIM(Mistral, index) {
7678
7906
  if (!this.tracer) return false;
7679
7907
  try {
7680
7908
  const FimClass = this._getCtor(Mistral, "fim");
7681
7909
  let didPatch = false;
7682
7910
  if (FimClass?.prototype?.complete) {
7683
7911
  const originalComplete = FimClass.prototype.complete;
7684
- originalMethods4.set("fim.complete", originalComplete);
7912
+ originalMethods4.set(`fim.complete-${index}`, originalComplete);
7685
7913
  const tracer = this.tracer;
7686
7914
  const wrapper = fimWrapper(tracer);
7687
7915
  FimClass.prototype.complete = function(...args) {
@@ -7698,7 +7926,7 @@ var NetraMistralAIInstrumentor = class {
7698
7926
  }
7699
7927
  if (FimClass?.prototype?.stream) {
7700
7928
  const originalStream = FimClass.prototype.stream;
7701
- originalMethods4.set("fim.stream", originalStream);
7929
+ originalMethods4.set(`fim.stream-${index}`, originalStream);
7702
7930
  const tracer = this.tracer;
7703
7931
  const wrapper = fimStreamWrapper(tracer);
7704
7932
  FimClass.prototype.stream = function(...args) {
@@ -7719,14 +7947,14 @@ var NetraMistralAIInstrumentor = class {
7719
7947
  return false;
7720
7948
  }
7721
7949
  }
7722
- _instrumentAgents(Mistral) {
7950
+ _instrumentAgents(Mistral, index) {
7723
7951
  if (!this.tracer) return false;
7724
7952
  try {
7725
7953
  const AgentsClass = this._getCtor(Mistral, "agents");
7726
7954
  let didPatch = false;
7727
7955
  if (AgentsClass?.prototype?.complete) {
7728
7956
  const originalComplete = AgentsClass.prototype.complete;
7729
- originalMethods4.set("agents.complete", originalComplete);
7957
+ originalMethods4.set(`agents.complete-${index}`, originalComplete);
7730
7958
  const tracer = this.tracer;
7731
7959
  const wrapper = agentsWrapper(tracer);
7732
7960
  AgentsClass.prototype.complete = function(...args) {
@@ -7743,7 +7971,7 @@ var NetraMistralAIInstrumentor = class {
7743
7971
  }
7744
7972
  if (AgentsClass?.prototype?.stream) {
7745
7973
  const originalStream = AgentsClass.prototype.stream;
7746
- originalMethods4.set("agents.stream", originalStream);
7974
+ originalMethods4.set(`agents.stream-${index}`, originalStream);
7747
7975
  const tracer = this.tracer;
7748
7976
  const wrapper = agentsStreamWrapper(tracer);
7749
7977
  AgentsClass.prototype.stream = function(...args) {
@@ -7764,14 +7992,14 @@ var NetraMistralAIInstrumentor = class {
7764
7992
  return false;
7765
7993
  }
7766
7994
  }
7767
- _uninstrumentChat(Mistral) {
7995
+ _uninstrumentChat(Mistral, index) {
7768
7996
  try {
7769
7997
  const ChatClass = this._getCtor(Mistral, "chat");
7770
- const originalComplete = originalMethods4.get("chat.complete");
7998
+ const originalComplete = originalMethods4.get(`chat.complete-${index}`);
7771
7999
  if (originalComplete && ChatClass?.prototype) {
7772
8000
  ChatClass.prototype.complete = originalComplete;
7773
8001
  }
7774
- const originalStream = originalMethods4.get("chat.stream");
8002
+ const originalStream = originalMethods4.get(`chat.stream-${index}`);
7775
8003
  if (originalStream && ChatClass?.prototype) {
7776
8004
  ChatClass.prototype.stream = originalStream;
7777
8005
  }
@@ -7779,10 +8007,10 @@ var NetraMistralAIInstrumentor = class {
7779
8007
  Logger.error(`Failed to uninstrument MistralAI chat: ${error}`);
7780
8008
  }
7781
8009
  }
7782
- _uninstrumentEmbeddings(Mistral) {
8010
+ _uninstrumentEmbeddings(Mistral, index) {
7783
8011
  try {
7784
8012
  const EmbeddingsClass = this._getCtor(Mistral, "embeddings");
7785
- const originalCreate = originalMethods4.get("embeddings.create");
8013
+ const originalCreate = originalMethods4.get(`embeddings.create-${index}`);
7786
8014
  if (originalCreate && EmbeddingsClass?.prototype) {
7787
8015
  EmbeddingsClass.prototype.create = originalCreate;
7788
8016
  }
@@ -7790,14 +8018,14 @@ var NetraMistralAIInstrumentor = class {
7790
8018
  Logger.error(`Failed to uninstrument MistralAI embeddings: ${error}`);
7791
8019
  }
7792
8020
  }
7793
- _uninstrumentFIM(Mistral) {
8021
+ _uninstrumentFIM(Mistral, index) {
7794
8022
  try {
7795
8023
  const FimClass = this._getCtor(Mistral, "fim");
7796
- const originalComplete = originalMethods4.get("fim.complete");
8024
+ const originalComplete = originalMethods4.get(`fim.complete-${index}`);
7797
8025
  if (originalComplete && FimClass?.prototype) {
7798
8026
  FimClass.prototype.complete = originalComplete;
7799
8027
  }
7800
- const originalStream = originalMethods4.get("fim.stream");
8028
+ const originalStream = originalMethods4.get(`fim.stream-${index}`);
7801
8029
  if (originalStream && FimClass?.prototype) {
7802
8030
  FimClass.prototype.stream = originalStream;
7803
8031
  }
@@ -7805,14 +8033,14 @@ var NetraMistralAIInstrumentor = class {
7805
8033
  Logger.error(`Failed to uninstrument MistralAI FIM: ${error}`);
7806
8034
  }
7807
8035
  }
7808
- _uninstrumentAgents(Mistral) {
8036
+ _uninstrumentAgents(Mistral, index) {
7809
8037
  try {
7810
8038
  const AgentsClass = this._getCtor(Mistral, "agents");
7811
- const originalComplete = originalMethods4.get("agents.complete");
8039
+ const originalComplete = originalMethods4.get(`agents.complete-${index}`);
7812
8040
  if (originalComplete && AgentsClass?.prototype) {
7813
8041
  AgentsClass.prototype.complete = originalComplete;
7814
8042
  }
7815
- const originalStream = originalMethods4.get("agents.stream");
8043
+ const originalStream = originalMethods4.get(`agents.stream-${index}`);
7816
8044
  if (originalStream && AgentsClass?.prototype) {
7817
8045
  AgentsClass.prototype.stream = originalStream;
7818
8046
  }
@@ -10065,14 +10293,14 @@ function initOpenTelemetryInstrumentations(config2, resolved, blockInstruments)
10065
10293
  netraHost = new URL(config2.otlpEndpoint).host;
10066
10294
  }
10067
10295
  } catch {
10068
- console.debug(`OTEL_NODE_EXCLUDED_URLS: malformed otlpEndpoint '${config2.otlpEndpoint}', skipping host-based exclusion`);
10296
+ Logger.debug(`OTEL_NODE_EXCLUDED_URLS: malformed otlpEndpoint '${config2.otlpEndpoint}', skipping host-based exclusion`);
10069
10297
  }
10070
10298
  const excludeRegexes = (process.env.OTEL_NODE_EXCLUDED_URLS || "").split(",").map((s) => s.trim()).filter(Boolean).map((pattern) => {
10071
10299
  try {
10072
10300
  return new RegExp(pattern);
10073
10301
  } catch {
10074
10302
  if (config2.debugMode) {
10075
- console.debug(
10303
+ Logger.debug(
10076
10304
  `Invalid OTEL_NODE_EXCLUDED_URLS pattern skipped: ${pattern}`
10077
10305
  );
10078
10306
  }
@@ -10114,11 +10342,11 @@ function initOpenTelemetryInstrumentations(config2, resolved, blockInstruments)
10114
10342
  });
10115
10343
  registerInstrumentations({ instrumentations: [_undiciInstrumentation] });
10116
10344
  if (config2.debugMode) {
10117
- console.debug("Undici/fetch instrumentation enabled");
10345
+ Logger.debug("Undici/fetch instrumentation enabled");
10118
10346
  }
10119
10347
  } catch (e) {
10120
10348
  if (config2.debugMode) {
10121
- console.debug("Undici/fetch instrumentation not available:", e);
10349
+ Logger.debug("Undici/fetch instrumentation not available:", e);
10122
10350
  }
10123
10351
  }
10124
10352
  }
@@ -10283,22 +10511,145 @@ async function uninstrumentAll() {
10283
10511
  if (_httpInstrumentation) {
10284
10512
  _httpInstrumentation.disable();
10285
10513
  _httpInstrumentation = null;
10286
- console.debug("HTTP instrumentation disabled");
10514
+ Logger.debug("HTTP instrumentation disabled");
10287
10515
  }
10288
10516
  } catch (e) {
10289
- console.debug("Failed to uninstrument HTTP:", e);
10517
+ Logger.debug("Failed to uninstrument HTTP:", e);
10290
10518
  }
10291
10519
  try {
10292
10520
  if (_undiciInstrumentation) {
10293
10521
  _undiciInstrumentation.disable();
10294
10522
  _undiciInstrumentation = null;
10295
- console.debug("Undici/fetch instrumentation disabled");
10523
+ Logger.debug("Undici/fetch instrumentation disabled");
10296
10524
  }
10297
10525
  } catch (e) {
10298
- console.debug("Failed to uninstrument undici:", e);
10526
+ Logger.debug("Failed to uninstrument undici:", e);
10299
10527
  }
10300
10528
  }
10529
+
10530
+ // src/simulation/task.ts
10531
+ var BaseTask = class {
10532
+ };
10533
+
10534
+ // src/simulation/utils.ts
10301
10535
  var LOG_PREFIX = "netra.simulation";
10536
+ var DEFAULT_FILE_DOWNLOAD_TIMEOUT_S = 30;
10537
+ var MAX_FILE_DOWNLOAD_WORKERS = 8;
10538
+ var _cachedFileDownloadTimeoutMs = null;
10539
+ function validateSimulationInputs(datasetId, task2) {
10540
+ if (!datasetId) {
10541
+ Logger.error(`${LOG_PREFIX}: dataset_id is required`);
10542
+ return false;
10543
+ }
10544
+ if (!(task2 instanceof BaseTask)) {
10545
+ Logger.error(`${LOG_PREFIX}: task must be a BaseTask instance`);
10546
+ return false;
10547
+ }
10548
+ return true;
10549
+ }
10550
+ function _getFileDownloadTimeout() {
10551
+ if (_cachedFileDownloadTimeoutMs !== null) {
10552
+ return _cachedFileDownloadTimeoutMs;
10553
+ }
10554
+ const envVal = process.env.NETRA_SIMULATION_FILE_DOWNLOAD_TIMEOUT;
10555
+ if (envVal) {
10556
+ const parsed = parseFloat(envVal);
10557
+ if (!isNaN(parsed) && parsed > 0) {
10558
+ _cachedFileDownloadTimeoutMs = parsed * 1e3;
10559
+ return _cachedFileDownloadTimeoutMs;
10560
+ }
10561
+ Logger.warn(
10562
+ `${LOG_PREFIX}: Invalid file download timeout '${envVal}', using default ${DEFAULT_FILE_DOWNLOAD_TIMEOUT_S}s`
10563
+ );
10564
+ }
10565
+ _cachedFileDownloadTimeoutMs = DEFAULT_FILE_DOWNLOAD_TIMEOUT_S * 1e3;
10566
+ return _cachedFileDownloadTimeoutMs;
10567
+ }
10568
+ async function _downloadSingleFile(fileData, timeoutMs, signal) {
10569
+ try {
10570
+ const response = await axios.get(fileData.downloadUrl, {
10571
+ responseType: "arraybuffer",
10572
+ timeout: timeoutMs,
10573
+ signal
10574
+ });
10575
+ const encoded = Buffer.from(response.data).toString("base64");
10576
+ return {
10577
+ fileName: fileData.fileName,
10578
+ contentType: fileData.contentType,
10579
+ description: fileData.description,
10580
+ data: encoded
10581
+ };
10582
+ } catch (error) {
10583
+ if (axios.isCancel(error)) {
10584
+ throw new Error(
10585
+ `Download of '${fileData.fileName}' was cancelled`
10586
+ );
10587
+ }
10588
+ const status = axios.isAxiosError(error) ? error.response?.status : void 0;
10589
+ const reason = status ? `HTTP ${status}` : error instanceof Error ? error.message : String(error);
10590
+ Logger.error(
10591
+ `${LOG_PREFIX}: Failed to download file '${fileData.fileName}': ${reason}`
10592
+ );
10593
+ throw new Error(
10594
+ `Failed to download file '${fileData.fileName}': ${reason}`
10595
+ );
10596
+ }
10597
+ }
10598
+ function parseFiles(rawFiles) {
10599
+ if (!rawFiles) {
10600
+ return [];
10601
+ }
10602
+ const parsed = [];
10603
+ for (const entry of rawFiles) {
10604
+ const fileName = entry.fileName || "";
10605
+ const downloadUrl = entry.downloadUrl || "";
10606
+ if (!fileName || !downloadUrl) {
10607
+ Logger.warn(
10608
+ `${LOG_PREFIX}: Skipping malformed file attachment (missing fileName or downloadUrl)`
10609
+ );
10610
+ continue;
10611
+ }
10612
+ parsed.push({
10613
+ fileName,
10614
+ contentType: entry.contentType || "",
10615
+ description: entry.description || void 0,
10616
+ downloadUrl
10617
+ });
10618
+ }
10619
+ return parsed;
10620
+ }
10621
+ async function processFiles(files) {
10622
+ if (!files || files.length === 0) {
10623
+ return null;
10624
+ }
10625
+ const timeoutMs = _getFileDownloadTimeout();
10626
+ const limit = pLimit(MAX_FILE_DOWNLOAD_WORKERS);
10627
+ const controller = new AbortController();
10628
+ const downloadPromises = files.map(
10629
+ (file) => limit(() => _downloadSingleFile(file, timeoutMs, controller.signal))
10630
+ );
10631
+ try {
10632
+ return await Promise.all(downloadPromises);
10633
+ } catch (error) {
10634
+ controller.abort();
10635
+ limit.clearQueue();
10636
+ throw error;
10637
+ }
10638
+ }
10639
+ async function executeTask2(task2, message, sessionId, rawFiles) {
10640
+ const processedFiles = rawFiles && rawFiles.length > 0 ? await processFiles(rawFiles) : null;
10641
+ const result = task2.run(message, sessionId, processedFiles);
10642
+ const resolvedResult = result instanceof Promise ? await result : result;
10643
+ if (typeof resolvedResult === "object" && resolvedResult !== null && "message" in resolvedResult && "sessionId" in resolvedResult) {
10644
+ return [resolvedResult.message, resolvedResult.sessionId];
10645
+ }
10646
+ throw new Error(
10647
+ `Task must return TaskResult, got ${typeof resolvedResult}`
10648
+ );
10649
+ }
10650
+
10651
+ // src/simulation/client.ts
10652
+ var LOG_PREFIX2 = "netra.simulation";
10302
10653
  var DEFAULT_TIMEOUT = 1e4;
10303
10654
  var SimulationHttpClient = class {
10304
10655
  constructor(config2) {
@@ -10311,20 +10662,29 @@ var SimulationHttpClient = class {
10311
10662
  _createClient(config2) {
10312
10663
  const endpoint = (config2.otlpEndpoint || "").trim();
10313
10664
  if (!endpoint) {
10314
- Logger.error(`${LOG_PREFIX}: NETRA_OTLP_ENDPOINT is required`);
10665
+ Logger.error(`${LOG_PREFIX2}: NETRA_OTLP_ENDPOINT is required`);
10315
10666
  return null;
10316
10667
  }
10317
10668
  const baseURL = this._resolveBaseUrl(endpoint);
10318
10669
  const headers = this._buildHeaders(config2);
10319
10670
  const timeout = this._getTimeout();
10320
10671
  try {
10321
- return axios.create({
10672
+ const instance = axios.create({
10322
10673
  baseURL,
10323
10674
  headers,
10324
10675
  timeout
10325
10676
  });
10677
+ instance.interceptors.request.use(
10678
+ (config3) => {
10679
+ const traceHeaders = injectTraceContextHeaders({});
10680
+ Object.assign(config3.headers, traceHeaders);
10681
+ return config3;
10682
+ },
10683
+ (error) => Promise.reject(error)
10684
+ );
10685
+ return instance;
10326
10686
  } catch (error) {
10327
- Logger.error(`${LOG_PREFIX}: Failed to create HTTP client:`, error);
10687
+ Logger.error(`${LOG_PREFIX2}: Failed to create HTTP client:`, error);
10328
10688
  return null;
10329
10689
  }
10330
10690
  }
@@ -10359,7 +10719,7 @@ var SimulationHttpClient = class {
10359
10719
  const timeout = parseFloat(timeoutStr);
10360
10720
  if (isNaN(timeout)) {
10361
10721
  Logger.warn(
10362
- `${LOG_PREFIX}: Invalid timeout '${timeoutStr}', using default ${DEFAULT_TIMEOUT}ms`
10722
+ `${LOG_PREFIX2}: Invalid timeout '${timeoutStr}', using default ${DEFAULT_TIMEOUT}ms`
10363
10723
  );
10364
10724
  return DEFAULT_TIMEOUT;
10365
10725
  }
@@ -10368,9 +10728,9 @@ var SimulationHttpClient = class {
10368
10728
  /**
10369
10729
  * Create a new simulation run for the specified dataset.
10370
10730
  */
10371
- async createRun(name, datasetId, context14) {
10731
+ async createRun(name, datasetId, context16) {
10372
10732
  if (!this.client) {
10373
- Logger.error(`${LOG_PREFIX}: Client not initialized`);
10733
+ Logger.error(`${LOG_PREFIX2}: Client not initialized`);
10374
10734
  return null;
10375
10735
  }
10376
10736
  try {
@@ -10378,14 +10738,14 @@ var SimulationHttpClient = class {
10378
10738
  const payload = {
10379
10739
  name,
10380
10740
  datasetId,
10381
- context: context14 || {}
10741
+ context: context16 || {}
10382
10742
  };
10383
10743
  const response = await this.client.post(url, payload);
10384
10744
  const data = response.data;
10385
10745
  const responseData = data.data || {};
10386
10746
  const userMessages = responseData.userMessages || [];
10387
10747
  if (userMessages.length === 0) {
10388
- Logger.warn(`${LOG_PREFIX}: No user messages returned from create_run`);
10748
+ Logger.warn(`${LOG_PREFIX2}: No user messages returned from create_run`);
10389
10749
  return null;
10390
10750
  }
10391
10751
  const runId = responseData.id || "";
@@ -10393,7 +10753,8 @@ var SimulationHttpClient = class {
10393
10753
  (msg) => ({
10394
10754
  runItemId: msg.testRunItemId || "",
10395
10755
  message: msg.userMessage || "",
10396
- turnId: msg.turnId || ""
10756
+ turnId: msg.turnId || "",
10757
+ files: parseFiles(msg.attachments)
10397
10758
  })
10398
10759
  );
10399
10760
  return {
@@ -10402,7 +10763,7 @@ var SimulationHttpClient = class {
10402
10763
  };
10403
10764
  } catch (error) {
10404
10765
  const errorMsg = this._extractErrorMessage(error);
10405
- Logger.error(`${LOG_PREFIX}: Failed to create simulation run:`, errorMsg);
10766
+ Logger.error(`${LOG_PREFIX2}: Failed to create simulation run:`, errorMsg);
10406
10767
  return null;
10407
10768
  }
10408
10769
  }
@@ -10411,7 +10772,7 @@ var SimulationHttpClient = class {
10411
10772
  */
10412
10773
  async triggerConversation(message, turnId, sessionId, traceId) {
10413
10774
  if (!this.client) {
10414
- Logger.error(`${LOG_PREFIX}: Client not initialized`);
10775
+ Logger.error(`${LOG_PREFIX2}: Client not initialized`);
10415
10776
  return null;
10416
10777
  }
10417
10778
  try {
@@ -10434,7 +10795,7 @@ var SimulationHttpClient = class {
10434
10795
  }
10435
10796
  const userMessages = responseData.userMessages || [];
10436
10797
  if (userMessages.length === 0) {
10437
- Logger.warn(`${LOG_PREFIX}: No user messages in continue response`);
10798
+ Logger.warn(`${LOG_PREFIX2}: No user messages in continue response`);
10438
10799
  return null;
10439
10800
  }
10440
10801
  const nextMsg = userMessages[0];
@@ -10442,11 +10803,12 @@ var SimulationHttpClient = class {
10442
10803
  decision,
10443
10804
  nextTurnId: nextMsg.turnId || "",
10444
10805
  nextUserMessage: nextMsg.userMessage || "",
10445
- nextRunItemId: nextMsg.testRunItemId || ""
10806
+ nextRunItemId: nextMsg.testRunItemId || "",
10807
+ nextFiles: parseFiles(nextMsg.attachments)
10446
10808
  };
10447
10809
  } catch (error) {
10448
10810
  const errorMsg = this._extractErrorMessage(error);
10449
- Logger.error(`${LOG_PREFIX}: Failed to trigger conversation:`, errorMsg);
10811
+ Logger.error(`${LOG_PREFIX2}: Failed to trigger conversation:`, errorMsg);
10450
10812
  return null;
10451
10813
  }
10452
10814
  }
@@ -10455,17 +10817,17 @@ var SimulationHttpClient = class {
10455
10817
  */
10456
10818
  async reportFailure(runId, runItemId, error) {
10457
10819
  if (!this.client) {
10458
- Logger.error(`${LOG_PREFIX}: Client not initialized`);
10820
+ Logger.error(`${LOG_PREFIX2}: Client not initialized`);
10459
10821
  return;
10460
10822
  }
10461
10823
  try {
10462
10824
  const url = `/evaluations/run/${runId}/item/${runItemId}/status`;
10463
10825
  const payload = { status: "failed", failureReason: error };
10464
10826
  await this.client.patch(url, payload);
10465
- Logger.info(`${LOG_PREFIX}: Reported failure - ${error}`);
10827
+ Logger.info(`${LOG_PREFIX2}: Reported failure - ${error}`);
10466
10828
  } catch (err) {
10467
10829
  const errorMsg = this._extractErrorMessage(err);
10468
- Logger.error(`${LOG_PREFIX}: Failed to report failure:`, errorMsg);
10830
+ Logger.error(`${LOG_PREFIX2}: Failed to report failure:`, errorMsg);
10469
10831
  }
10470
10832
  }
10471
10833
  /**
@@ -10474,7 +10836,7 @@ var SimulationHttpClient = class {
10474
10836
  async postRunStatus(runId, status) {
10475
10837
  if (!this.client) {
10476
10838
  Logger.error(
10477
- `${LOG_PREFIX}: Client not initialized; cannot post run status`
10839
+ `${LOG_PREFIX2}: Client not initialized; cannot post run status`
10478
10840
  );
10479
10841
  return { success: false };
10480
10842
  }
@@ -10484,14 +10846,14 @@ var SimulationHttpClient = class {
10484
10846
  const response = await this.client.post(url, payload);
10485
10847
  const data = response.data;
10486
10848
  if (data && typeof data === "object" && "data" in data) {
10487
- Logger.info(`${LOG_PREFIX}: Completed test run successfully`);
10849
+ Logger.info(`${LOG_PREFIX2}: Completed test run successfully`);
10488
10850
  return data.data || {};
10489
10851
  }
10490
10852
  return data;
10491
10853
  } catch (error) {
10492
10854
  const errorMsg = this._extractErrorMessage(error);
10493
10855
  Logger.error(
10494
- `${LOG_PREFIX}: Failed to post run status for run '${runId}':`,
10856
+ `${LOG_PREFIX2}: Failed to post run status for run '${runId}':`,
10495
10857
  errorMsg
10496
10858
  );
10497
10859
  return { success: false };
@@ -10515,34 +10877,6 @@ var SimulationHttpClient = class {
10515
10877
  }
10516
10878
  };
10517
10879
 
10518
- // src/simulation/task.ts
10519
- var BaseTask = class {
10520
- };
10521
-
10522
- // src/simulation/utils.ts
10523
- var LOG_PREFIX2 = "netra.simulation";
10524
- function validateSimulationInputs(datasetId, task2) {
10525
- if (!datasetId) {
10526
- Logger.error(`${LOG_PREFIX2}: dataset_id is required`);
10527
- return false;
10528
- }
10529
- if (!(task2 instanceof BaseTask)) {
10530
- Logger.error(`${LOG_PREFIX2}: task must be a BaseTask instance`);
10531
- return false;
10532
- }
10533
- return true;
10534
- }
10535
- async function executeTask2(task2, message, sessionId) {
10536
- const result = task2.run(message, sessionId);
10537
- const resolvedResult = result instanceof Promise ? await result : result;
10538
- if (typeof resolvedResult === "object" && resolvedResult !== null && "message" in resolvedResult && "sessionId" in resolvedResult) {
10539
- return [resolvedResult.message, resolvedResult.sessionId];
10540
- }
10541
- throw new Error(
10542
- `Task must return TaskResult, got ${typeof resolvedResult}`
10543
- );
10544
- }
10545
-
10546
10880
  // src/simulation/api.ts
10547
10881
  var LOG_PREFIX3 = "netra.simulation";
10548
10882
  var SPAN_NAME = "Netra.Simulation.TestRun";
@@ -10562,14 +10896,14 @@ var Simulation = class {
10562
10896
  name,
10563
10897
  datasetId,
10564
10898
  task: task2,
10565
- context: context14,
10899
+ context: context16,
10566
10900
  maxConcurrency = 5
10567
10901
  } = options;
10568
10902
  if (!validateSimulationInputs(datasetId, task2)) {
10569
10903
  return null;
10570
10904
  }
10571
10905
  const startTime = Date.now();
10572
- const runResult = await this._client.createRun(name, datasetId, context14);
10906
+ const runResult = await this._client.createRun(name, datasetId, context16);
10573
10907
  if (!runResult) {
10574
10908
  return null;
10575
10909
  }
@@ -10676,29 +11010,29 @@ var Simulation = class {
10676
11010
  * Execute a multi-turn conversation for a single simulation item.
10677
11011
  */
10678
11012
  async _executeConversation(runId, runItem, task2) {
10679
- const { runItemId, message: initialMessage, turnId: initialTurnId } = runItem;
11013
+ const { runItemId, message: initialMessage, turnId: initialTurnId, files: initialFiles } = runItem;
10680
11014
  let message = initialMessage;
10681
11015
  let turnId = initialTurnId;
10682
11016
  let sessionId = null;
11017
+ let rawFiles = initialFiles ?? [];
10683
11018
  while (true) {
11019
+ const span2 = new SpanWrapper(SPAN_NAME, {}, LOG_PREFIX3);
11020
+ span2.start();
10684
11021
  try {
10685
- const span2 = new SpanWrapper(SPAN_NAME, {}, LOG_PREFIX3);
10686
- span2.start();
10687
11022
  const traceId = span2.getCurrentSpan()?.spanContext().traceId ?? "";
10688
- const [responseMessage, taskSessionId] = await executeTask2(
10689
- task2,
10690
- message,
10691
- sessionId
11023
+ const [responseMessage, taskSessionId] = await span2.withActive(
11024
+ () => executeTask2(task2, message, sessionId, rawFiles)
10692
11025
  );
10693
11026
  if (taskSessionId) {
10694
11027
  sessionId = taskSessionId;
10695
11028
  }
10696
- span2.end();
10697
- const response = await this._client.triggerConversation(
10698
- responseMessage,
10699
- turnId,
10700
- sessionId || "",
10701
- traceId
11029
+ const response = await span2.withActive(
11030
+ () => this._client.triggerConversation(
11031
+ responseMessage,
11032
+ turnId,
11033
+ sessionId || "",
11034
+ traceId
11035
+ )
10702
11036
  );
10703
11037
  if (response === null) {
10704
11038
  const errorMsg = "Failed to get conversation response";
@@ -10721,6 +11055,7 @@ var Simulation = class {
10721
11055
  }
10722
11056
  message = response.nextUserMessage;
10723
11057
  turnId = response.nextTurnId;
11058
+ rawFiles = response.nextFiles || [];
10724
11059
  } catch (error) {
10725
11060
  const errorMsg = error instanceof Error ? error.message : String(error);
10726
11061
  Logger.error(
@@ -10733,6 +11068,8 @@ var Simulation = class {
10733
11068
  error: errorMsg,
10734
11069
  turnId
10735
11070
  };
11071
+ } finally {
11072
+ span2.end();
10736
11073
  }
10737
11074
  }
10738
11075
  }
@@ -11279,6 +11616,6 @@ Netra._metricsEnabled = false;
11279
11616
  Netra.withBlockedSpansLocal = withBlockedSpansLocal;
11280
11617
  var index_default = Netra;
11281
11618
 
11282
- export { Aggregation, BaseTask, ChartType, Config, ConversationType, DEFAULT_INSTRUMENTS, DEFAULT_INSTRUMENTS_FOR_ROOT, Dashboard, DimensionField, EntryStatus, Evaluation, FilterField, FilterType, FilteringSpanExporter, GroupBy, InstrumentationSpanProcessor, Measure, Netra, NetraAgentsTracingProcessor, NetraInstruments, NetraOpenAIAgentsInstrumentor, Operator, Prompts, RunEntryContext, RunStatus, Scope, ScrubbingSpanProcessor, SessionSpanProcessor, Simulation, SpanIOProcessor, SpanType, TrialAwareOTLPExporter, Usage, agent, index_default as default, metadataField, mistralAIInstrumentor, openaiAgentsInstrumentor, span, task, workflow };
11619
+ export { Aggregation, BaseTask, ChartType, Config, ConversationType, DEFAULT_INSTRUMENTS, DEFAULT_INSTRUMENTS_FOR_ROOT, Dashboard, DimensionField, EntryStatus, Evaluation, FilterField, FilterType, FilteringSpanExporter, GroupBy, InstrumentationSpanProcessor, Measure, Netra, NetraAgentsTracingProcessor, NetraInstruments, NetraOpenAIAgentsInstrumentor, Operator, Prompts, RunEntryContext, RunStatus, Scope, ScrubbingSpanProcessor, SessionSpanProcessor, Simulation, SpanIOProcessor, SpanType, TrialAwareOTLPExporter, Usage, agent, index_default as default, metadataField, mistralAIInstrumentor, netraExpressMiddleware, openaiAgentsInstrumentor, runWithExtractedContext, span, task, workflow };
11283
11620
  //# sourceMappingURL=index.js.map
11284
11621
  //# sourceMappingURL=index.js.map