@outputai/llm 0.11.1-next.da26845.0 → 0.12.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.
Files changed (47) hide show
  1. package/package.json +7 -3
  2. package/src/agent.js +7 -2
  3. package/src/generate.js +1 -0
  4. package/src/index.d.ts +14 -6
  5. package/src/utils/wrap.js +35 -2
  6. package/src/agent.spec.js +0 -595
  7. package/src/ai_provider.spec.js +0 -217
  8. package/src/ai_sdk_options.spec.js +0 -390
  9. package/src/fixtures/image_response_v7_google_vertex.js +0 -72
  10. package/src/fixtures/image_response_v7_openai.js +0 -77
  11. package/src/fixtures/models_api_light.json +0 -675
  12. package/src/fixtures/models_api_v7_subset.json +0 -259
  13. package/src/fixtures/stream_response_v7_anthropic.js +0 -389
  14. package/src/fixtures/stream_response_v7_google_vertex.js +0 -462
  15. package/src/fixtures/stream_response_v7_openai.js +0 -353
  16. package/src/fixtures/stream_response_v7_perplexity.js +0 -671
  17. package/src/fixtures/text_response_v7_anthropic.js +0 -359
  18. package/src/fixtures/text_response_v7_anthropic_cache.js +0 -719
  19. package/src/fixtures/text_response_v7_google_vertex.js +0 -435
  20. package/src/fixtures/text_response_v7_openai.js +0 -347
  21. package/src/fixtures/text_response_v7_perplexity.js +0 -764
  22. package/src/generate.spec.js +0 -525
  23. package/src/prompt/content.spec.js +0 -208
  24. package/src/prompt/interpolations.spec.js +0 -109
  25. package/src/prompt/loader.full.spec.js +0 -309
  26. package/src/prompt/loader.spec.js +0 -258
  27. package/src/prompt/markup/attributes.spec.js +0 -132
  28. package/src/prompt/markup/nodes.spec.js +0 -333
  29. package/src/prompt/markup/tokenizer.spec.js +0 -149
  30. package/src/prompt/markup/tokens.spec.js +0 -250
  31. package/src/prompt/validations.spec.js +0 -812
  32. package/src/utils/cost.full.spec.js +0 -272
  33. package/src/utils/cost.spec.js +0 -290
  34. package/src/utils/error_handler.spec.js +0 -230
  35. package/src/utils/file.spec.js +0 -89
  36. package/src/utils/image.spec.js +0 -20
  37. package/src/utils/legacy_cost_attribute.spec.js +0 -278
  38. package/src/utils/models.spec.js +0 -119
  39. package/src/utils/models_pricing.spec.js +0 -162
  40. package/src/utils/skills.spec.js +0 -168
  41. package/src/utils/sources.spec.js +0 -122
  42. package/src/utils/stream.spec.js +0 -55
  43. package/src/utils/tools.spec.js +0 -167
  44. package/src/utils/usage.full.spec.js +0 -215
  45. package/src/utils/usage.spec.js +0 -246
  46. package/src/utils/wrap.spec.js +0 -467
  47. package/src/validations.spec.js +0 -631
@@ -1,272 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from 'vitest';
2
- import { req1 as anthropicCacheSeed, req2 as anthropicCacheReadWrite } from '../fixtures/text_response_v7_anthropic_cache.js';
3
- import anthropicStream from '../fixtures/stream_response_v7_anthropic.js';
4
- import anthropicText from '../fixtures/text_response_v7_anthropic.js';
5
- import googleVertexImage from '../fixtures/image_response_v7_google_vertex.js';
6
- import googleVertexStream from '../fixtures/stream_response_v7_google_vertex.js';
7
- import googleVertexText from '../fixtures/text_response_v7_google_vertex.js';
8
- import openaiImage from '../fixtures/image_response_v7_openai.js';
9
- import openaiStream from '../fixtures/stream_response_v7_openai.js';
10
- import openaiText from '../fixtures/text_response_v7_openai.js';
11
- import perplexityStream from '../fixtures/stream_response_v7_perplexity.js';
12
- import perplexityText from '../fixtures/text_response_v7_perplexity.js';
13
- import pricingTable from '../fixtures/models_api_v7_subset.json' with { type: 'json' };
14
-
15
- const mockFetchModelsPricing = vi.hoisted( () => vi.fn() );
16
-
17
- vi.mock( './models_pricing.js', () => ( {
18
- fetchModelsPricing: ( ...args ) => mockFetchModelsPricing( ...args )
19
- } ) );
20
-
21
- import { LLMGenerationCost, LLMGenerationCostItem, calculateCosts } from './cost.js';
22
- import { LLMGenerationUsageItem, parseLLMUsage } from './usage.js';
23
-
24
- const INPUT = LLMGenerationUsageItem.Group.INPUT;
25
- const OUTPUT = LLMGenerationUsageItem.Group.OUTPUT;
26
- const OK = LLMGenerationCostItem.Status.OK;
27
- const FALLBACK = LLMGenerationCostItem.Status.FALLBACK;
28
- const MISSING = LLMGenerationCostItem.Status.MISSING;
29
-
30
- const models = new Map(
31
- Object.values( pricingTable ).flatMap( provider => Object.entries( provider.models ?? {} ).flatMap( ( [ modelId, model ] ) => (
32
- model.cost ? [ [ `${provider.id}/${modelId}`, model.cost ] ] : []
33
- ) ) )
34
- );
35
-
36
- const cases = [
37
- {
38
- name: 'OpenAI text',
39
- fixture: openaiText,
40
- providerId: 'openai',
41
- modelId: 'gpt-4.1-mini',
42
- input: 0.000414,
43
- output: 0.0006336,
44
- total: 0.0010476,
45
- status: LLMGenerationCost.Status.PRECISE,
46
- items: [
47
- [ INPUT, 'no_cache', 1035, 0.4, 0.000414, OK ],
48
- [ INPUT, 'cache_read', 0, 0.1, 0, OK ],
49
- [ INPUT, 'cache_write', 0, 0.4, 0, FALLBACK ],
50
- [ OUTPUT, 'text', 396, 1.6, 0.0006336, OK ],
51
- [ OUTPUT, 'reasoning', 0, 1.6, 0, FALLBACK ]
52
- ]
53
- },
54
- {
55
- name: 'OpenAI stream',
56
- fixture: openaiStream,
57
- providerId: 'openai',
58
- modelId: 'gpt-4.1-mini',
59
- input: 0.000414,
60
- output: 0.0006176,
61
- total: 0.0010316,
62
- status: LLMGenerationCost.Status.PRECISE,
63
- items: [
64
- [ INPUT, 'no_cache', 1035, 0.4, 0.000414, OK ],
65
- [ INPUT, 'cache_read', 0, 0.1, 0, OK ],
66
- [ INPUT, 'cache_write', 0, 0.4, 0, FALLBACK ],
67
- [ OUTPUT, 'text', 386, 1.6, 0.0006176, OK ],
68
- [ OUTPUT, 'reasoning', 0, 1.6, 0, FALLBACK ]
69
- ]
70
- },
71
- {
72
- name: 'OpenAI image',
73
- fixture: openaiImage,
74
- providerId: 'openai',
75
- modelId: 'gpt-image-1',
76
- input: null,
77
- output: null,
78
- total: null,
79
- status: LLMGenerationCost.Status.INCOMPLETE,
80
- items: [
81
- [ INPUT, null, 151, null, null, MISSING ],
82
- [ OUTPUT, null, 4160, null, null, MISSING ]
83
- ]
84
- },
85
- {
86
- name: 'Anthropic text',
87
- fixture: anthropicText,
88
- providerId: 'anthropic',
89
- modelId: 'claude-haiku-4-5',
90
- input: 0.001113,
91
- output: 0.004225,
92
- total: 0.005338,
93
- status: LLMGenerationCost.Status.PRECISE,
94
- items: [
95
- [ INPUT, 'no_cache', 1113, 1, 0.001113, OK ],
96
- [ INPUT, 'cache_read', 0, 0.1, 0, OK ],
97
- [ INPUT, 'cache_write', 0, 1.25, 0, OK ],
98
- [ OUTPUT, null, 845, 5, 0.004225, OK ]
99
- ]
100
- },
101
- {
102
- name: 'Anthropic stream',
103
- fixture: anthropicStream,
104
- providerId: 'anthropic',
105
- modelId: 'claude-haiku-4-5',
106
- input: 0.001113,
107
- output: 0.005235,
108
- total: 0.006348,
109
- status: LLMGenerationCost.Status.PRECISE,
110
- items: [
111
- [ INPUT, 'no_cache', 1113, 1, 0.001113, OK ],
112
- [ INPUT, 'cache_read', 0, 0.1, 0, OK ],
113
- [ INPUT, 'cache_write', 0, 1.25, 0, OK ],
114
- [ OUTPUT, null, 1047, 5, 0.005235, OK ]
115
- ]
116
- },
117
- {
118
- name: 'Anthropic cache seed',
119
- fixture: anthropicCacheSeed,
120
- providerId: 'anthropic',
121
- modelId: 'claude-haiku-4-5',
122
- input: 0.01876625,
123
- output: 0.000025,
124
- total: 0.01879125,
125
- status: LLMGenerationCost.Status.PRECISE,
126
- items: [
127
- [ INPUT, 'no_cache', 15, 1, 0.000015, OK ],
128
- [ INPUT, 'cache_read', 0, 0.1, 0, OK ],
129
- [ INPUT, 'cache_write', 15001, 1.25, 0.01875125, OK ],
130
- [ OUTPUT, null, 5, 5, 0.000025, OK ]
131
- ]
132
- },
133
- {
134
- name: 'Anthropic cache read and write',
135
- fixture: anthropicCacheReadWrite,
136
- providerId: 'anthropic',
137
- modelId: 'claude-haiku-4-5',
138
- input: 0.0065166,
139
- output: 0.00002,
140
- total: 0.0065366,
141
- status: LLMGenerationCost.Status.PRECISE,
142
- items: [
143
- [ INPUT, 'no_cache', 14, 1, 0.000014, OK ],
144
- [ INPUT, 'cache_read', 15001, 0.1, 0.0015001, OK ],
145
- [ INPUT, 'cache_write', 4002, 1.25, 0.0050025, OK ],
146
- [ OUTPUT, null, 4, 5, 0.00002, OK ]
147
- ]
148
- },
149
- {
150
- name: 'Google Vertex text',
151
- fixture: googleVertexText,
152
- providerId: 'google-vertex',
153
- modelId: 'gemini-2.5-flash',
154
- input: 0.0003096,
155
- output: 0.0034525,
156
- total: 0.0037621,
157
- status: LLMGenerationCost.Status.IMPRECISE,
158
- items: [
159
- [ INPUT, 'no_cache', 1032, 0.3, 0.0003096, OK ],
160
- [ INPUT, 'cache_read', 0, 0.03, 0, OK ],
161
- [ OUTPUT, 'text', 793, 2.5, 0.0019825, OK ],
162
- [ OUTPUT, 'reasoning', 588, 2.5, 0.00147, FALLBACK ]
163
- ]
164
- },
165
- {
166
- name: 'Google Vertex stream',
167
- fixture: googleVertexStream,
168
- providerId: 'google-vertex',
169
- modelId: 'gemini-2.5-flash',
170
- input: 0.0003096,
171
- output: 0.0033675,
172
- total: 0.0036771,
173
- status: LLMGenerationCost.Status.IMPRECISE,
174
- items: [
175
- [ INPUT, 'no_cache', 1032, 0.3, 0.0003096, OK ],
176
- [ INPUT, 'cache_read', 0, 0.03, 0, OK ],
177
- [ OUTPUT, 'text', 858, 2.5, 0.002145, OK ],
178
- [ OUTPUT, 'reasoning', 489, 2.5, 0.0012225, FALLBACK ]
179
- ]
180
- },
181
- {
182
- name: 'Google Vertex image',
183
- fixture: googleVertexImage,
184
- providerId: 'google-vertex',
185
- modelId: 'gemini-2.5-flash-image',
186
- input: 0.0000471,
187
- output: 0.0387,
188
- total: 0.0387471,
189
- status: LLMGenerationCost.Status.PRECISE,
190
- items: [
191
- [ INPUT, null, 157, 0.3, 0.0000471, OK ],
192
- [ OUTPUT, null, 1290, 30, 0.0387, OK ]
193
- ]
194
- },
195
- {
196
- name: 'Perplexity text',
197
- fixture: perplexityText,
198
- providerId: 'perplexity',
199
- modelId: 'sonar',
200
- input: 0.001025,
201
- output: 0.000437,
202
- total: 0.001462,
203
- status: LLMGenerationCost.Status.PRECISE,
204
- items: [
205
- [ INPUT, 'no_cache', 1025, 1, 0.001025, OK ],
206
- [ OUTPUT, 'text', 437, 1, 0.000437, OK ],
207
- [ OUTPUT, 'reasoning', 0, 1, 0, FALLBACK ]
208
- ]
209
- },
210
- {
211
- name: 'Perplexity stream',
212
- fixture: perplexityStream,
213
- providerId: 'perplexity',
214
- modelId: 'sonar',
215
- input: 0.001025,
216
- output: 0.000448,
217
- total: 0.001473,
218
- status: LLMGenerationCost.Status.PRECISE,
219
- items: [
220
- [ INPUT, 'no_cache', 1025, 1, 0.001025, OK ],
221
- [ OUTPUT, 'text', 448, 1, 0.000448, OK ],
222
- [ OUTPUT, 'reasoning', 0, 1, 0, FALLBACK ]
223
- ]
224
- }
225
- ];
226
-
227
- describe( 'calculateCosts with AI SDK response fixtures', () => {
228
- beforeEach( () => {
229
- mockFetchModelsPricing.mockReset();
230
- mockFetchModelsPricing.mockResolvedValue( models );
231
- } );
232
-
233
- it.each( cases )( 'calculates $name cost', async ( {
234
- fixture,
235
- providerId,
236
- modelId,
237
- input,
238
- output,
239
- total,
240
- status,
241
- items
242
- } ) => {
243
- const usage = parseLLMUsage( {
244
- prompt: {
245
- config: {
246
- provider: providerId,
247
- model: modelId
248
- }
249
- },
250
- usage: fixture.usage
251
- } );
252
- const result = await calculateCosts( usage );
253
-
254
- expect( JSON.parse( JSON.stringify( result ) ) ).toEqual( {
255
- type: LLMGenerationCost.TYPE,
256
- providerId,
257
- modelId,
258
- input,
259
- output,
260
- total,
261
- status,
262
- items: items.map( ( [ group, label, amount, ppm, itemTotal, itemStatus ] ) => ( {
263
- group,
264
- label,
265
- amount,
266
- ppm,
267
- total: itemTotal,
268
- status: itemStatus
269
- } ) )
270
- } );
271
- } );
272
- } );
@@ -1,290 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
-
3
- const mockFetchModelsPricing = vi.hoisted( () => vi.fn() );
4
-
5
- vi.mock( './models_pricing.js', () => ( {
6
- fetchModelsPricing: ( ...args ) => mockFetchModelsPricing( ...args )
7
- } ) );
8
-
9
- import { Logger } from '@outputai/core';
10
- import { LLMGenerationCost, LLMGenerationCostItem, calculateCosts } from './cost.js';
11
- import { LLMGenerationUsage, LLMGenerationUsageItem } from './usage.js';
12
-
13
- const INPUT = LLMGenerationUsageItem.Group.INPUT;
14
- const OUTPUT = LLMGenerationUsageItem.Group.OUTPUT;
15
- const MODEL_ID = 'test-model';
16
- const PROVIDER_ID = 'test-provider';
17
-
18
- const item = ( group, label, amount ) => new LLMGenerationUsageItem( group, label, amount );
19
- const usage = items => new LLMGenerationUsage( MODEL_ID, PROVIDER_ID, items );
20
- const pricing = value => new Map( [ [ `${PROVIDER_ID}/${MODEL_ID}`, value ] ] );
21
- const serialize = value => JSON.parse( JSON.stringify( value ) );
22
-
23
- describe( 'calculateCosts', () => {
24
- beforeEach( () => {
25
- mockFetchModelsPricing.mockReset();
26
- vi.spyOn( Logger, 'warn' ).mockImplementation( () => {} );
27
- } );
28
-
29
- afterEach( () => {
30
- vi.restoreAllMocks();
31
- } );
32
-
33
- it( 'returns null when model pricing cannot be fetched', async () => {
34
- mockFetchModelsPricing.mockResolvedValue( null );
35
-
36
- const result = await calculateCosts( usage( [
37
- item( INPUT, null, 100 ),
38
- item( OUTPUT, null, 50 )
39
- ] ) );
40
-
41
- expect( result ).toBeNull();
42
- expect( Logger.warn ).toHaveBeenCalledWith( 'Failed to fetch models pricing', { namespace: 'LLM' } );
43
- } );
44
-
45
- it( 'calculates precise aggregate input and output costs', async () => {
46
- mockFetchModelsPricing.mockResolvedValue( pricing( {
47
- input: 2,
48
- output: 10
49
- } ) );
50
-
51
- const result = await calculateCosts( usage( [
52
- item( INPUT, null, 1_000_000 ),
53
- item( OUTPUT, null, 500_000 )
54
- ] ) );
55
-
56
- expect( result ).toBeInstanceOf( LLMGenerationCost );
57
- expect( serialize( result ) ).toEqual( {
58
- type: LLMGenerationCost.TYPE,
59
- providerId: PROVIDER_ID,
60
- modelId: MODEL_ID,
61
- input: 2,
62
- output: 5,
63
- total: 7,
64
- status: LLMGenerationCost.Status.PRECISE,
65
- items: [
66
- {
67
- group: INPUT,
68
- label: null,
69
- amount: 1_000_000,
70
- ppm: 2,
71
- total: 2,
72
- status: LLMGenerationCostItem.Status.OK
73
- },
74
- {
75
- group: OUTPUT,
76
- label: null,
77
- amount: 500_000,
78
- ppm: 10,
79
- total: 5,
80
- status: LLMGenerationCostItem.Status.OK
81
- }
82
- ]
83
- } );
84
- } );
85
-
86
- it( 'uses specialized cache and reasoning prices when available', async () => {
87
- mockFetchModelsPricing.mockResolvedValue( pricing( {
88
- input: 4,
89
- cache_read: 1,
90
- cache_write: 5,
91
- output: 10,
92
- reasoning: 20
93
- } ) );
94
-
95
- const result = await calculateCosts( usage( [
96
- item( INPUT, 'no_cache', 500_000 ),
97
- item( INPUT, 'cache_read', 400_000 ),
98
- item( INPUT, 'cache_write', 100_000 ),
99
- item( OUTPUT, 'text', 200_000 ),
100
- item( OUTPUT, 'reasoning', 50_000 )
101
- ] ) );
102
-
103
- expect( result ).toMatchObject( {
104
- input: 2.9,
105
- output: 3,
106
- total: 5.9,
107
- status: LLMGenerationCost.Status.PRECISE
108
- } );
109
- expect( result.items ).toEqual( [
110
- new LLMGenerationCostItem( INPUT, 'no_cache', 500_000, 4, 2, LLMGenerationCostItem.Status.OK ),
111
- new LLMGenerationCostItem( INPUT, 'cache_read', 400_000, 1, 0.4, LLMGenerationCostItem.Status.OK ),
112
- new LLMGenerationCostItem( INPUT, 'cache_write', 100_000, 5, 0.5, LLMGenerationCostItem.Status.OK ),
113
- new LLMGenerationCostItem( OUTPUT, 'text', 200_000, 10, 2, LLMGenerationCostItem.Status.OK ),
114
- new LLMGenerationCostItem( OUTPUT, 'reasoning', 50_000, 20, 1, LLMGenerationCostItem.Status.OK )
115
- ] );
116
- } );
117
-
118
- it( 'falls back to aggregate prices for unpriced specialized items', async () => {
119
- mockFetchModelsPricing.mockResolvedValue( pricing( {
120
- input: 2,
121
- output: 10
122
- } ) );
123
-
124
- const result = await calculateCosts( usage( [
125
- item( INPUT, 'cache_read', 400_000 ),
126
- item( INPUT, 'cache_write', 100_000 ),
127
- item( OUTPUT, 'reasoning', 50_000 )
128
- ] ) );
129
-
130
- expect( result ).toMatchObject( {
131
- input: 1,
132
- output: 0.5,
133
- total: 1.5,
134
- status: LLMGenerationCost.Status.IMPRECISE
135
- } );
136
- expect( result.items.map( value => value.status ) ).toEqual( [
137
- LLMGenerationCostItem.Status.FALLBACK,
138
- LLMGenerationCostItem.Status.FALLBACK,
139
- LLMGenerationCostItem.Status.FALLBACK
140
- ] );
141
- } );
142
-
143
- it( 'marks cost incomplete while preserving totals for priced items', async () => {
144
- mockFetchModelsPricing.mockResolvedValue( pricing( {
145
- input: 2
146
- } ) );
147
-
148
- const result = await calculateCosts( usage( [
149
- item( INPUT, null, 100 ),
150
- item( OUTPUT, null, 50 )
151
- ] ) );
152
-
153
- expect( result ).toMatchObject( {
154
- input: 0.0002,
155
- output: null,
156
- total: 0.0002,
157
- status: LLMGenerationCost.Status.INCOMPLETE
158
- } );
159
- expect( result.items ).toEqual( [
160
- new LLMGenerationCostItem( INPUT, null, 100, 2, 0.0002, LLMGenerationCostItem.Status.OK ),
161
- new LLMGenerationCostItem( OUTPUT, null, 50, null, null, LLMGenerationCostItem.Status.MISSING )
162
- ] );
163
- } );
164
-
165
- it( 'returns an incomplete cost when the model has no pricing reference', async () => {
166
- mockFetchModelsPricing.mockResolvedValue( new Map() );
167
-
168
- const result = await calculateCosts( usage( [
169
- item( INPUT, null, 100 ),
170
- item( OUTPUT, null, 50 )
171
- ] ) );
172
-
173
- expect( result ).toMatchObject( {
174
- input: null,
175
- output: null,
176
- total: null,
177
- status: LLMGenerationCost.Status.INCOMPLETE
178
- } );
179
- expect( result.items.every( value => value.status === LLMGenerationCostItem.Status.MISSING ) ).toBe( true );
180
- expect( Logger.warn ).toHaveBeenCalledWith(
181
- 'Missing pricing reference for model',
182
- { namespace: 'LLM', modelId: MODEL_ID, providerId: PROVIDER_ID }
183
- );
184
- } );
185
-
186
- it( 'marks cost incomplete when usage is incomplete', async () => {
187
- mockFetchModelsPricing.mockResolvedValue( pricing( {
188
- input: 2,
189
- output: 10
190
- } ) );
191
-
192
- const result = await calculateCosts( usage( [
193
- item( INPUT, null, 100 )
194
- ] ) );
195
-
196
- expect( result ).toMatchObject( {
197
- input: 0.0002,
198
- output: null,
199
- total: 0.0002,
200
- status: LLMGenerationCost.Status.INCOMPLETE
201
- } );
202
- expect( result.items[0].status ).toBe( LLMGenerationCostItem.Status.OK );
203
- } );
204
-
205
- it( 'accepts zero-valued prices as precise', async () => {
206
- mockFetchModelsPricing.mockResolvedValue( pricing( {
207
- input: 0,
208
- output: 0
209
- } ) );
210
-
211
- const result = await calculateCosts( usage( [
212
- item( INPUT, null, 100 ),
213
- item( OUTPUT, null, 50 )
214
- ] ) );
215
-
216
- expect( result ).toMatchObject( {
217
- input: 0,
218
- output: 0,
219
- total: 0,
220
- status: LLMGenerationCost.Status.PRECISE
221
- } );
222
- expect( result.items.every( value => value.status === LLMGenerationCostItem.Status.OK ) ).toBe( true );
223
- } );
224
-
225
- it( 'uses decimal arithmetic for fractional prices', async () => {
226
- mockFetchModelsPricing.mockResolvedValue( pricing( {
227
- input: 0.1,
228
- output: 0.2
229
- } ) );
230
-
231
- const result = await calculateCosts( usage( [
232
- item( INPUT, null, 1 ),
233
- item( INPUT, null, 2 ),
234
- item( OUTPUT, null, 3 )
235
- ] ) );
236
-
237
- expect( result.input ).toBe( 0.0000003 );
238
- expect( result.output ).toBe( 0.0000006 );
239
- expect( result.total ).toBe( 0.0000009 );
240
- } );
241
- } );
242
-
243
- describe( 'LLMGenerationCost', () => {
244
- it.each( [
245
- {
246
- name: 'precise',
247
- usageStatus: LLMGenerationUsage.Status.COMPLETE,
248
- itemStatus: LLMGenerationCostItem.Status.OK,
249
- expected: LLMGenerationCost.Status.PRECISE
250
- },
251
- {
252
- name: 'imprecise',
253
- usageStatus: LLMGenerationUsage.Status.COMPLETE,
254
- itemStatus: LLMGenerationCostItem.Status.FALLBACK,
255
- expected: LLMGenerationCost.Status.IMPRECISE
256
- },
257
- {
258
- name: 'incomplete from missing pricing',
259
- usageStatus: LLMGenerationUsage.Status.COMPLETE,
260
- itemStatus: LLMGenerationCostItem.Status.MISSING,
261
- expected: LLMGenerationCost.Status.INCOMPLETE
262
- },
263
- {
264
- name: 'incomplete from usage',
265
- usageStatus: LLMGenerationUsage.Status.INCOMPLETE,
266
- itemStatus: LLMGenerationCostItem.Status.OK,
267
- expected: LLMGenerationCost.Status.INCOMPLETE
268
- },
269
- {
270
- name: 'precise with zero-value fallback pricing',
271
- usageStatus: LLMGenerationUsage.Status.COMPLETE,
272
- itemStatus: LLMGenerationCostItem.Status.FALLBACK,
273
- amount: 0,
274
- expected: LLMGenerationCost.Status.PRECISE
275
- },
276
- {
277
- name: 'precise with zero-value missing pricing',
278
- usageStatus: LLMGenerationUsage.Status.COMPLETE,
279
- itemStatus: LLMGenerationCostItem.Status.MISSING,
280
- amount: 0,
281
- expected: LLMGenerationCost.Status.PRECISE
282
- }
283
- ] )( 'sets $name status', ( { usageStatus, itemStatus, amount = 100, expected } ) => {
284
- const cost = new LLMGenerationCost( MODEL_ID, PROVIDER_ID, [
285
- new LLMGenerationCostItem( INPUT, null, amount, 2, 0.0002, itemStatus )
286
- ], usageStatus );
287
-
288
- expect( cost.status ).toBe( expected );
289
- } );
290
- } );