braintrust 0.2.1 → 0.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.
package/dist/index.mjs CHANGED
@@ -6131,6 +6131,7 @@ async function loadPrompt({
6131
6131
  projectId,
6132
6132
  slug,
6133
6133
  version,
6134
+ environment,
6134
6135
  id,
6135
6136
  defaults,
6136
6137
  noTrace = false,
@@ -6141,6 +6142,11 @@ async function loadPrompt({
6141
6142
  forceLogin,
6142
6143
  state: stateArg
6143
6144
  }) {
6145
+ if (version && environment) {
6146
+ throw new Error(
6147
+ "Cannot specify both 'version' and 'environment' parameters. Please use only one (remove the other)."
6148
+ );
6149
+ }
6144
6150
  if (id) {
6145
6151
  } else if (isEmpty(projectName) && isEmpty(projectId)) {
6146
6152
  throw new Error("Must specify either projectName or projectId");
@@ -6158,7 +6164,10 @@ async function loadPrompt({
6158
6164
  forceLogin
6159
6165
  });
6160
6166
  if (id) {
6161
- response = await state.apiConn().get_json(`v1/prompt/${id}`, {});
6167
+ response = await state.apiConn().get_json(`v1/prompt/${id}`, {
6168
+ ...version && { version },
6169
+ ...environment && { environment }
6170
+ });
6162
6171
  if (response) {
6163
6172
  response = { objects: [response] };
6164
6173
  }
@@ -6167,10 +6176,14 @@ async function loadPrompt({
6167
6176
  project_name: projectName,
6168
6177
  project_id: projectId,
6169
6178
  slug,
6170
- version
6179
+ version,
6180
+ ...environment && { environment }
6171
6181
  });
6172
6182
  }
6173
6183
  } catch (e) {
6184
+ if (environment || version) {
6185
+ throw new Error(`Prompt not found with specified parameters: ${e}`);
6186
+ }
6174
6187
  console.warn("Failed to load prompt, attempting to fall back to cache:", e);
6175
6188
  let prompt2;
6176
6189
  if (id) {
@@ -6413,6 +6426,110 @@ function traced(callback, args) {
6413
6426
  })();
6414
6427
  }
6415
6428
  }
6429
+ function isGeneratorFunction(fn) {
6430
+ return Object.prototype.toString.call(fn) === "[object GeneratorFunction]";
6431
+ }
6432
+ function isAsyncGeneratorFunction(fn) {
6433
+ return Object.prototype.toString.call(fn) === "[object AsyncGeneratorFunction]";
6434
+ }
6435
+ function wrapTracedSyncGenerator(fn, spanArgs, noTraceIO) {
6436
+ const wrapper = function* (...fnArgs) {
6437
+ const span = startSpan(spanArgs);
6438
+ try {
6439
+ if (!noTraceIO) {
6440
+ span.log({ input: fnArgs });
6441
+ }
6442
+ const envValue = isomorph_default.getEnv("BRAINTRUST_MAX_GENERATOR_ITEMS");
6443
+ const maxItems = envValue !== void 0 ? Number(envValue) : 1e3;
6444
+ if (!noTraceIO && maxItems !== 0) {
6445
+ let collected = [];
6446
+ let truncated = false;
6447
+ const gen = generatorWithCurrent(span, fn.apply(this, fnArgs));
6448
+ try {
6449
+ for (const value of gen) {
6450
+ if (maxItems === -1 || !truncated && collected.length < maxItems) {
6451
+ collected.push(value);
6452
+ } else {
6453
+ truncated = true;
6454
+ collected = [];
6455
+ console.warn(
6456
+ `Generator output exceeded limit of ${maxItems} items, output not logged. Increase BRAINTRUST_MAX_GENERATOR_ITEMS or set to -1 to disable limit.`
6457
+ );
6458
+ }
6459
+ yield value;
6460
+ }
6461
+ if (!truncated) {
6462
+ span.log({ output: collected });
6463
+ }
6464
+ } catch (error2) {
6465
+ logError(span, error2);
6466
+ if (!truncated && collected.length > 0) {
6467
+ span.log({ output: collected });
6468
+ }
6469
+ throw error2;
6470
+ }
6471
+ } else {
6472
+ const gen = generatorWithCurrent(span, fn.apply(this, fnArgs));
6473
+ for (const value of gen) {
6474
+ yield value;
6475
+ }
6476
+ }
6477
+ } finally {
6478
+ span.end();
6479
+ }
6480
+ };
6481
+ Object.defineProperty(wrapper, "name", { value: fn.name });
6482
+ return wrapper;
6483
+ }
6484
+ function wrapTracedAsyncGenerator(fn, spanArgs, noTraceIO) {
6485
+ const wrapper = async function* (...fnArgs) {
6486
+ const span = startSpan(spanArgs);
6487
+ try {
6488
+ if (!noTraceIO) {
6489
+ span.log({ input: fnArgs });
6490
+ }
6491
+ const envValue = isomorph_default.getEnv("BRAINTRUST_MAX_GENERATOR_ITEMS");
6492
+ const maxItems = envValue !== void 0 ? Number(envValue) : 1e3;
6493
+ if (!noTraceIO && maxItems !== 0) {
6494
+ let collected = [];
6495
+ let truncated = false;
6496
+ const gen = asyncGeneratorWithCurrent(span, fn.apply(this, fnArgs));
6497
+ try {
6498
+ for await (const value of gen) {
6499
+ if (maxItems === -1 || !truncated && collected.length < maxItems) {
6500
+ collected.push(value);
6501
+ } else {
6502
+ truncated = true;
6503
+ collected = [];
6504
+ console.warn(
6505
+ `Generator output exceeded limit of ${maxItems} items, output not logged. Increase BRAINTRUST_MAX_GENERATOR_ITEMS or set to -1 to disable limit.`
6506
+ );
6507
+ }
6508
+ yield value;
6509
+ }
6510
+ if (!truncated) {
6511
+ span.log({ output: collected });
6512
+ }
6513
+ } catch (error2) {
6514
+ logError(span, error2);
6515
+ if (!truncated && collected.length > 0) {
6516
+ span.log({ output: collected });
6517
+ }
6518
+ throw error2;
6519
+ }
6520
+ } else {
6521
+ const gen = asyncGeneratorWithCurrent(span, fn.apply(this, fnArgs));
6522
+ for await (const value of gen) {
6523
+ yield value;
6524
+ }
6525
+ }
6526
+ } finally {
6527
+ span.end();
6528
+ }
6529
+ };
6530
+ Object.defineProperty(wrapper, "name", { value: fn.name });
6531
+ return wrapper;
6532
+ }
6416
6533
  function wrapTraced(fn, args) {
6417
6534
  const spanArgs = {
6418
6535
  name: fn.name,
@@ -6421,6 +6538,13 @@ function wrapTraced(fn, args) {
6421
6538
  };
6422
6539
  const hasExplicitInput = args && args.event && "input" in args.event && args.event.input !== void 0;
6423
6540
  const hasExplicitOutput = args && args.event && args.event.output !== void 0;
6541
+ const noTraceIO = args?.noTraceIO || hasExplicitInput || hasExplicitOutput;
6542
+ if (isGeneratorFunction(fn)) {
6543
+ return wrapTracedSyncGenerator(fn, spanArgs, !!noTraceIO);
6544
+ }
6545
+ if (isAsyncGeneratorFunction(fn)) {
6546
+ return wrapTracedAsyncGenerator(fn, spanArgs, !!noTraceIO);
6547
+ }
6424
6548
  if (args?.asyncFlush) {
6425
6549
  return (...fnArgs) => traced((span) => {
6426
6550
  if (!hasExplicitInput) {
@@ -6506,6 +6630,52 @@ function startSpanAndIsLogger(args) {
6506
6630
  function withCurrent(span, callback, state = void 0) {
6507
6631
  return (state ?? _globalState).currentSpan.run(span, () => callback(span));
6508
6632
  }
6633
+ function* generatorWithCurrent(span, gen, state = void 0) {
6634
+ let nextValue;
6635
+ while (true) {
6636
+ const result = withCurrent(
6637
+ span,
6638
+ () => {
6639
+ try {
6640
+ return gen.next(nextValue);
6641
+ } catch (e) {
6642
+ return { value: void 0, done: true, error: e };
6643
+ }
6644
+ },
6645
+ state
6646
+ );
6647
+ if ("error" in result) {
6648
+ throw result.error;
6649
+ }
6650
+ if (result.done) {
6651
+ return result.value;
6652
+ }
6653
+ nextValue = yield result.value;
6654
+ }
6655
+ }
6656
+ async function* asyncGeneratorWithCurrent(span, gen, state = void 0) {
6657
+ let nextValue;
6658
+ while (true) {
6659
+ const result = await withCurrent(
6660
+ span,
6661
+ async () => {
6662
+ try {
6663
+ return await gen.next(nextValue);
6664
+ } catch (e) {
6665
+ return { value: void 0, done: true, error: e };
6666
+ }
6667
+ },
6668
+ state
6669
+ );
6670
+ if ("error" in result) {
6671
+ throw result.error;
6672
+ }
6673
+ if (result.done) {
6674
+ return result.value;
6675
+ }
6676
+ nextValue = yield result.value;
6677
+ }
6678
+ }
6509
6679
  function withParent(parent, callback, state = void 0) {
6510
6680
  return (state ?? _globalState).currentParent.run(parent, () => callback());
6511
6681
  }
@@ -8002,7 +8172,9 @@ var _exportsForTestingOnly = {
8002
8172
  clearTestBackgroundLogger,
8003
8173
  simulateLoginForTests,
8004
8174
  simulateLogoutForTests,
8005
- setInitialTestState
8175
+ setInitialTestState,
8176
+ isGeneratorFunction,
8177
+ isAsyncGeneratorFunction
8006
8178
  };
8007
8179
 
8008
8180
  // src/node.ts
@@ -9799,7 +9971,7 @@ async function Eval(name, evaluator, reporterOrOpts) {
9799
9971
  const { data, baseExperiment: defaultBaseExperiment } = callEvaluatorData(
9800
9972
  evaluator.data
9801
9973
  );
9802
- const experiment = options.parent ? null : initExperiment2(evaluator.state, {
9974
+ const experiment = options.parent || options.noSendLogs ? null : initExperiment2(evaluator.state, {
9803
9975
  ...evaluator.projectId ? { projectId: evaluator.projectId } : { project: name },
9804
9976
  experiment: evaluator.experimentName,
9805
9977
  description: evaluator.description,
@@ -10611,6 +10783,8 @@ function responsesProxy(openai) {
10611
10783
  return responsesCreateProxy(target.create.bind(target));
10612
10784
  } else if (name === "stream") {
10613
10785
  return responsesStreamProxy(target.stream.bind(target));
10786
+ } else if (name === "parse") {
10787
+ return responsesParseProxy(target.parse.bind(target));
10614
10788
  }
10615
10789
  return Reflect.get(target, name, receiver);
10616
10790
  }
@@ -10655,6 +10829,36 @@ function parseEventFromResponseCreateResult(result) {
10655
10829
  metrics: parseMetricsFromUsage(result?.usage)
10656
10830
  };
10657
10831
  }
10832
+ function parseSpanFromResponseParseParams(params) {
10833
+ const input = [{ role: "user", content: params.input }];
10834
+ if (params.instructions) {
10835
+ input.push({ role: "system", content: params.instructions });
10836
+ }
10837
+ const spanArgs = {
10838
+ name: "openai.responses.parse",
10839
+ spanAttributes: {
10840
+ type: "llm"
10841
+ },
10842
+ event: {
10843
+ input,
10844
+ metadata: {
10845
+ ...filterFrom(params, ["input", "instructions"]),
10846
+ provider: "openai"
10847
+ }
10848
+ },
10849
+ startTime: getCurrentUnixTimestamp()
10850
+ };
10851
+ return {
10852
+ span: startSpan(spanArgs),
10853
+ start: spanArgs.startTime
10854
+ };
10855
+ }
10856
+ function parseEventFromResponseParseResult(result) {
10857
+ return {
10858
+ output: result?.output_parsed || result?.output_text || "",
10859
+ metrics: parseMetricsFromUsage(result?.usage)
10860
+ };
10861
+ }
10658
10862
  function traceResponseCreateStream(stream, timedSpan) {
10659
10863
  const span = timedSpan.span;
10660
10864
  let ttft = -1;
@@ -10729,6 +10933,16 @@ function responsesStreamProxy(target) {
10729
10933
  }
10730
10934
  });
10731
10935
  }
10936
+ function responsesParseProxy(target) {
10937
+ const hooks = {
10938
+ name: "openai.responses.parse",
10939
+ toSpanFunc: parseSpanFromResponseParseParams,
10940
+ resultToEventFunc: parseEventFromResponseParseResult,
10941
+ traceStreamFunc: traceResponseCreateStream
10942
+ // Reuse the same stream tracing
10943
+ };
10944
+ return proxyCreate(target, hooks);
10945
+ }
10732
10946
  var TOKEN_NAME_MAP = {
10733
10947
  input_tokens: "prompt_tokens",
10734
10948
  output_tokens: "completion_tokens",
@@ -11698,14 +11912,26 @@ function postProcessPrompt(prompt) {
11698
11912
  });
11699
11913
  }
11700
11914
  function postProcessOutput(text, toolCalls, finishReason) {
11701
- return {
11702
- index: 0,
11703
- message: {
11704
- role: "assistant",
11705
- content: text ?? (toolCalls ? toolCalls.length === 1 && toolCalls[0].toolName === "json" ? toolCalls[0].args : JSON.stringify(toolCalls) : "")
11706
- },
11707
- finish_reason: finishReason
11708
- };
11915
+ return [
11916
+ {
11917
+ index: 0,
11918
+ message: {
11919
+ role: "assistant",
11920
+ content: text ?? "",
11921
+ ...toolCalls && toolCalls.length > 0 ? {
11922
+ tool_calls: toolCalls.map((toolCall) => ({
11923
+ id: toolCall.toolCallId,
11924
+ function: {
11925
+ name: toolCall.toolName,
11926
+ arguments: toolCall.args
11927
+ },
11928
+ type: "function"
11929
+ }))
11930
+ } : {}
11931
+ },
11932
+ finish_reason: finishReason
11933
+ }
11934
+ ];
11709
11935
  }
11710
11936
 
11711
11937
  // src/wrappers/anthropic.ts
@@ -11972,7 +12198,13 @@ try {
11972
12198
  );
11973
12199
  OTEL_AVAILABLE = false;
11974
12200
  }
11975
- var FILTER_PREFIXES = ["gen_ai.", "braintrust.", "llm.", "ai."];
12201
+ var FILTER_PREFIXES = [
12202
+ "gen_ai.",
12203
+ "braintrust.",
12204
+ "llm.",
12205
+ "ai.",
12206
+ "traceloop."
12207
+ ];
11976
12208
  var AISpanProcessor = class _AISpanProcessor {
11977
12209
  static checkOtelAvailable() {
11978
12210
  if (!OTEL_AVAILABLE) {
@@ -12029,7 +12261,7 @@ var AISpanProcessor = class _AISpanProcessor {
12029
12261
  * Keep spans if:
12030
12262
  * 1. It's a root span (no parent)
12031
12263
  * 2. Custom filter returns true/false (if provided)
12032
- * 3. Span name starts with 'gen_ai.', 'braintrust.', 'llm.', or 'ai.'
12264
+ * 3. Span name starts with 'gen_ai.', 'braintrust.', 'llm.', 'ai.', or 'traceloop.'
12033
12265
  * 4. Any attribute name starts with those prefixes
12034
12266
  */
12035
12267
  shouldKeepFilteredSpan(span) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "braintrust",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "SDK for integrating Braintrust",
5
5
  "repository": {
6
6
  "type": "git",
@@ -92,7 +92,7 @@
92
92
  },
93
93
  "dependencies": {
94
94
  "@ai-sdk/provider": "^1.1.3",
95
- "@braintrust/core": "0.0.91",
95
+ "@braintrust/core": "0.0.93",
96
96
  "@next/env": "^14.2.3",
97
97
  "@vercel/functions": "^1.0.2",
98
98
  "argparse": "^2.0.1",