@outputai/llm 0.4.0 → 0.4.1-dev.06c2b50.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@outputai/llm",
3
- "version": "0.4.0",
3
+ "version": "0.4.1-dev.06c2b50.0",
4
4
  "description": "Framework abstraction to interact with LLM models",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -23,7 +23,7 @@
23
23
  "entities": "8.0.0",
24
24
  "gray-matter": "4.0.3",
25
25
  "liquidjs": "10.25.7",
26
- "@outputai/core": "0.4.0"
26
+ "@outputai/core": "0.4.1-dev.06c2b50.0"
27
27
  },
28
28
  "license": "Apache-2.0",
29
29
  "publishConfig": {
@@ -13,6 +13,8 @@ export const endTraceWithError = ( { traceId, error } ) => {
13
13
  export const endTraceWithSuccess = ( { traceId, modelId, response, cost, ...extra } ) => {
14
14
  const { totalUsage: usage, text: result, providerMetadata } = response;
15
15
  Tracing.addEventAttribute( { eventId: traceId, name: Tracing.Attribute.COST, value: cost } );
16
- Tracing.addEventEnd( { id: traceId, details: { result, usage, providerMetadata, ...extra } } );
16
+ Tracing.addEventAttribute( { eventId: traceId, name: Tracing.Attribute.TOKEN_USAGE, value: usage } );
17
+ Tracing.addEventEnd( { id: traceId, details: { result, providerMetadata, ...extra } } );
17
18
  emitEvent( 'cost:llm:request', { modelId, cost, usage } );
19
+ emitEvent( 'token_usage:llm:request', { modelId, usage } );
18
20
  };
@@ -1,4 +1,6 @@
1
1
  import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import buildTraceTree from '../../../core/src/tracing/tools/build_trace_tree.js';
3
+ import { EventAction } from '../../../core/src/tracing/trace_consts.js';
2
4
 
3
5
  vi.mock( '@outputai/core/sdk_activity_integration', () => ( {
4
6
  Tracing: {
@@ -7,7 +9,8 @@ vi.mock( '@outputai/core/sdk_activity_integration', () => ( {
7
9
  addEventAttribute: vi.fn(),
8
10
  addEventEnd: vi.fn(),
9
11
  Attribute: {
10
- COST: 'cost'
12
+ COST: 'cost',
13
+ TOKEN_USAGE: 'token_usage'
11
14
  }
12
15
  },
13
16
  emitEvent: vi.fn()
@@ -54,7 +57,7 @@ describe( 'trace utils', () => {
54
57
  } );
55
58
 
56
59
  describe( 'endTraceWithSuccess', () => {
57
- it( 'adds cost attribute, ends the trace with response fields and extra details, and emits cost:llm:request', () => {
60
+ it( 'adds cost + token_usage attributes, ends the trace without usage in output, and emits both cost and token_usage events', () => {
58
61
  const cost = { total: 0.01, components: [] };
59
62
  const usage = { inputTokens: 2, outputTokens: 3 };
60
63
  const response = {
@@ -76,20 +79,63 @@ describe( 'trace utils', () => {
76
79
  name: 'cost',
77
80
  value: cost
78
81
  } );
82
+ expect( tracing.addEventAttribute ).toHaveBeenCalledWith( {
83
+ eventId: 'trace-a',
84
+ name: 'token_usage',
85
+ value: usage
86
+ } );
79
87
  expect( tracing.addEventEnd ).toHaveBeenCalledWith( {
80
88
  id: 'trace-a',
81
89
  details: {
82
90
  result: 'hello',
83
- usage,
84
91
  providerMetadata: { provider: 'x' },
85
92
  sourcesFromTools: [ { url: 'https://u.test', title: '' } ]
86
93
  }
87
94
  } );
95
+ expect( tracing.addEventEnd.mock.calls[0][0].details ).not.toHaveProperty( 'usage' );
88
96
  expect( emitEvent ).toHaveBeenCalledWith( 'cost:llm:request', {
89
97
  modelId: 'my-model',
90
98
  cost,
91
99
  usage
92
100
  } );
101
+ expect( emitEvent ).toHaveBeenCalledWith( 'token_usage:llm:request', {
102
+ modelId: 'my-model',
103
+ usage
104
+ } );
105
+ } );
106
+
107
+ it( 'produces an llm trace node with attributes.token_usage and no output.usage when fed through buildTraceTree', () => {
108
+ const usage = { inputTokens: 12, outputTokens: 7, cachedInputTokens: 3, totalTokens: 22 };
109
+ const cost = { total: 0.0042, components: [ { name: 'input_tokens', value: 0.002 } ] };
110
+ const response = {
111
+ text: 'tree result',
112
+ totalUsage: usage,
113
+ providerMetadata: { provider: 'p' }
114
+ };
115
+
116
+ // Capture the calls the wrapper makes against Tracing/emit, and translate them into trace
117
+ // entries — what buildTraceTree consumes server-side to materialize the persisted trace JSON.
118
+ endTraceWithSuccess( { traceId: 'llm-1', modelId: 'm', response, cost } );
119
+
120
+ const entries = [
121
+ { kind: 'workflow', action: EventAction.START, name: 'wf', id: 'wf', parentId: undefined, details: {}, timestamp: 1 },
122
+ { kind: 'llm', action: EventAction.START, name: 'generateText', id: 'llm-1', parentId: 'wf', details: { prompt: 'p' }, timestamp: 10 }
123
+ ];
124
+ for ( const call of Tracing.addEventAttribute.mock.calls ) {
125
+ entries.push( { id: call[0].eventId, action: EventAction.ADD_ATTR, details: { name: call[0].name, value: call[0].value }, timestamp: 20 } );
126
+ }
127
+ const endDetails = Tracing.addEventEnd.mock.calls[0][0].details;
128
+ entries.push( { id: 'llm-1', action: EventAction.END, details: endDetails, timestamp: 30 } );
129
+ entries.push( { id: 'wf', action: EventAction.END, details: {}, timestamp: 40 } );
130
+
131
+ const tree = buildTraceTree( entries );
132
+ const llmNode = tree.children[0];
133
+
134
+ expect( llmNode.kind ).toBe( 'llm' );
135
+ expect( llmNode.attributes.token_usage ).toEqual( usage );
136
+ expect( llmNode.attributes.cost ).toEqual( cost );
137
+ expect( llmNode.output ).not.toHaveProperty( 'usage' );
138
+ expect( llmNode.output ).toMatchObject( { result: 'tree result', providerMetadata: { provider: 'p' } } );
93
139
  } );
94
140
  } );
95
141
  } );