@assistant-ui/react-ai-sdk 1.3.10 → 1.3.11

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.
@@ -1 +1 @@
1
- {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../src/usage.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AACF,MAAM,WAAW,4BAA4B;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA8ID,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,4BAA4B,GAAG,SAAS,GAChD,gBAAgB,GAAG,SAAS,CAa9B;AACD,wBAAgB,mBAAmB,IAAI,gBAAgB,GAAG,SAAS,CAKlE"}
1
+ {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../src/usage.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,WAAW,4BAA4B;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAmGD,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,4BAA4B,GAAG,SAAS,GAChD,gBAAgB,GAAG,SAAS,CAa9B;AAED,wBAAgB,mBAAmB,IAAI,gBAAgB,GAAG,SAAS,CAKlE"}
package/dist/usage.js CHANGED
@@ -1,4 +1,11 @@
1
1
  import { useAuiState } from "@assistant-ui/react";
2
+ const USAGE_KEYS = [
3
+ "inputTokens",
4
+ "outputTokens",
5
+ "reasoningTokens",
6
+ "cachedInputTokens",
7
+ "totalTokens",
8
+ ];
2
9
  function asRecord(value) {
3
10
  if (!value || typeof value !== "object" || Array.isArray(value))
4
11
  return undefined;
@@ -10,71 +17,37 @@ function asPositiveTokenCount(value) {
10
17
  }
11
18
  return value;
12
19
  }
13
- function hasAnyUsageField(parsed) {
14
- return (parsed.inputTokens !== undefined ||
15
- parsed.outputTokens !== undefined ||
16
- parsed.reasoningTokens !== undefined ||
17
- parsed.cachedInputTokens !== undefined ||
18
- parsed.totalTokens !== undefined);
20
+ function computeTotalTokens(usage) {
21
+ if (usage.totalTokens !== undefined)
22
+ return usage.totalTokens;
23
+ if (usage.inputTokens !== undefined && usage.outputTokens !== undefined) {
24
+ return usage.inputTokens + usage.outputTokens;
25
+ }
26
+ return undefined;
19
27
  }
20
28
  function normalizeUsage(value) {
21
- const usage = asRecord(value);
22
- if (!usage)
29
+ const record = asRecord(value);
30
+ if (!record)
23
31
  return undefined;
24
- const inputTokens = asPositiveTokenCount(usage.inputTokens);
25
- const outputTokens = asPositiveTokenCount(usage.outputTokens);
26
- const reasoningTokens = asPositiveTokenCount(usage.reasoningTokens);
27
- const cachedInputTokens = asPositiveTokenCount(usage.cachedInputTokens);
28
- const totalTokens = asPositiveTokenCount(usage.totalTokens);
29
32
  const result = {};
30
- if (inputTokens !== undefined)
31
- result.inputTokens = inputTokens;
32
- if (outputTokens !== undefined)
33
- result.outputTokens = outputTokens;
34
- if (reasoningTokens !== undefined)
35
- result.reasoningTokens = reasoningTokens;
36
- if (cachedInputTokens !== undefined)
37
- result.cachedInputTokens = cachedInputTokens;
38
- if (totalTokens !== undefined)
39
- result.totalTokens = totalTokens;
40
- if (!hasAnyUsageField(result)) {
41
- return undefined;
33
+ let hasFields = false;
34
+ for (const key of USAGE_KEYS) {
35
+ const count = asPositiveTokenCount(record[key]);
36
+ if (count !== undefined) {
37
+ result[key] = count;
38
+ hasFields = true;
39
+ }
42
40
  }
43
- return result;
41
+ return hasFields ? result : undefined;
44
42
  }
45
- function buildUsageResult(parsed) {
46
- if (!hasAnyUsageField(parsed)) {
47
- return undefined;
48
- }
49
- const hasBothInputAndOutput = parsed.inputTokens !== undefined && parsed.outputTokens !== undefined;
50
- const totalTokens = parsed.totalTokens ??
51
- (hasBothInputAndOutput
52
- ? (parsed.inputTokens ?? 0) + (parsed.outputTokens ?? 0)
53
- : undefined);
54
- const result = {};
55
- if (totalTokens !== undefined)
56
- result.totalTokens = totalTokens;
57
- if (parsed.inputTokens !== undefined)
58
- result.inputTokens = parsed.inputTokens;
59
- if (parsed.outputTokens !== undefined)
60
- result.outputTokens = parsed.outputTokens;
61
- if (parsed.reasoningTokens !== undefined)
62
- result.reasoningTokens = parsed.reasoningTokens;
63
- if (parsed.cachedInputTokens !== undefined)
64
- result.cachedInputTokens = parsed.cachedInputTokens;
65
- return result;
43
+ function withComputedTotal(usage) {
44
+ const totalTokens = computeTotalTokens(usage);
45
+ return { ...usage, ...(totalTokens !== undefined && { totalTokens }) };
66
46
  }
67
47
  function usageFromSteps(value) {
68
48
  const steps = Array.isArray(value) ? value : [];
69
- let inputTokens = 0;
70
- let outputTokens = 0;
71
- let reasoningTokens = 0;
72
- let cachedInputTokens = 0;
73
- let totalTokens = 0;
74
- let hasInput = false;
75
- let hasOutput = false;
76
- let hasReasoning = false;
77
- let hasCachedInput = false;
49
+ const sums = {};
50
+ const present = {};
78
51
  let stepsWithUsage = 0;
79
52
  let stepsWithComputableTotal = 0;
80
53
  for (const step of steps) {
@@ -82,57 +55,33 @@ function usageFromSteps(value) {
82
55
  if (!usage)
83
56
  continue;
84
57
  stepsWithUsage++;
85
- const stepHasBothInputAndOutput = usage.inputTokens !== undefined && usage.outputTokens !== undefined;
86
- const stepTotal = usage.totalTokens ??
87
- (stepHasBothInputAndOutput
88
- ? (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0)
89
- : undefined);
58
+ const stepTotal = computeTotalTokens(usage);
90
59
  if (stepTotal !== undefined) {
91
- totalTokens += stepTotal;
60
+ sums.totalTokens = (sums.totalTokens ?? 0) + stepTotal;
92
61
  stepsWithComputableTotal++;
93
62
  }
94
- if (usage.inputTokens !== undefined) {
95
- inputTokens += usage.inputTokens;
96
- hasInput = true;
97
- }
98
- if (usage.outputTokens !== undefined) {
99
- outputTokens += usage.outputTokens;
100
- hasOutput = true;
101
- }
102
- if (usage.reasoningTokens !== undefined) {
103
- reasoningTokens += usage.reasoningTokens;
104
- hasReasoning = true;
105
- }
106
- if (usage.cachedInputTokens !== undefined) {
107
- cachedInputTokens += usage.cachedInputTokens;
108
- hasCachedInput = true;
63
+ for (const key of USAGE_KEYS) {
64
+ if (key === "totalTokens")
65
+ continue;
66
+ if (usage[key] !== undefined) {
67
+ sums[key] = (sums[key] ?? 0) + usage[key];
68
+ present[key] = true;
69
+ }
109
70
  }
110
71
  }
111
72
  if (stepsWithUsage === 0)
112
73
  return undefined;
113
- const parsed = {};
114
- if (hasInput)
115
- parsed.inputTokens = inputTokens;
116
- if (hasOutput)
117
- parsed.outputTokens = outputTokens;
118
- if (hasReasoning)
119
- parsed.reasoningTokens = reasoningTokens;
120
- if (hasCachedInput)
121
- parsed.cachedInputTokens = cachedInputTokens;
74
+ const result = {};
122
75
  if (stepsWithComputableTotal === stepsWithUsage) {
123
- parsed.totalTokens = totalTokens;
76
+ result.totalTokens = sums.totalTokens;
77
+ }
78
+ for (const key of USAGE_KEYS) {
79
+ if (key === "totalTokens")
80
+ continue;
81
+ if (present[key]) {
82
+ result[key] = sums[key];
83
+ }
124
84
  }
125
- const result = {};
126
- if (parsed.totalTokens !== undefined)
127
- result.totalTokens = parsed.totalTokens;
128
- if (parsed.inputTokens !== undefined)
129
- result.inputTokens = parsed.inputTokens;
130
- if (parsed.outputTokens !== undefined)
131
- result.outputTokens = parsed.outputTokens;
132
- if (parsed.reasoningTokens !== undefined)
133
- result.reasoningTokens = parsed.reasoningTokens;
134
- if (parsed.cachedInputTokens !== undefined)
135
- result.cachedInputTokens = parsed.cachedInputTokens;
136
85
  return result;
137
86
  }
138
87
  export function getThreadMessageTokenUsage(message) {
@@ -142,13 +91,11 @@ export function getThreadMessageTokenUsage(message) {
142
91
  if (!metadata)
143
92
  return undefined;
144
93
  const topLevelUsage = normalizeUsage(metadata.usage);
145
- if (topLevelUsage) {
146
- return buildUsageResult(topLevelUsage);
147
- }
94
+ if (topLevelUsage)
95
+ return withComputedTotal(topLevelUsage);
148
96
  const legacyUsage = normalizeUsage(asRecord(metadata.custom)?.usage);
149
- if (legacyUsage) {
150
- return buildUsageResult(legacyUsage);
151
- }
97
+ if (legacyUsage)
98
+ return withComputedTotal(legacyUsage);
152
99
  return usageFromSteps(metadata.steps);
153
100
  }
154
101
  export function useThreadTokenUsage() {
package/dist/usage.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"usage.js","sourceRoot":"","sources":["../src/usage.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"usage.js","sourceRoot":"","sources":["../src/usage.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/react-ai-sdk",
3
- "version": "1.3.10",
3
+ "version": "1.3.11",
4
4
  "description": "Vercel AI SDK adapter for assistant-ui",
5
5
  "keywords": [
6
6
  "ai-sdk",
@@ -31,12 +31,11 @@
31
31
  ],
32
32
  "sideEffects": false,
33
33
  "dependencies": {
34
- "@ai-sdk/react": "^3.0.100",
35
- "ai": "^6.0.98",
36
- "zod": "^4.3.6"
34
+ "@ai-sdk/react": "^3.0.113",
35
+ "ai": "^6.0.111"
37
36
  },
38
37
  "peerDependencies": {
39
- "@assistant-ui/react": "^0.12.14",
38
+ "@assistant-ui/react": "^0.12.15",
40
39
  "@types/react": "*",
41
40
  "assistant-cloud": "*",
42
41
  "react": "^18 || ^19"
@@ -57,7 +56,7 @@
57
56
  "jsdom": "^28.1.0",
58
57
  "react": "^19.2.4",
59
58
  "vitest": "^4.0.18",
60
- "@assistant-ui/react": "0.12.14",
59
+ "@assistant-ui/react": "0.12.15",
61
60
  "@assistant-ui/x-buildutils": "0.0.1",
62
61
  "assistant-stream": "0.3.4"
63
62
  },
@@ -194,4 +194,57 @@ describe("AISDKMessageConverter", () => {
194
194
  '{"type":"high_stock_model","limit":5,"filters":{"region":"us","sector":"tech"}}',
195
195
  );
196
196
  });
197
+
198
+ it("merges duplicate toolCallId across assistant snapshots", () => {
199
+ const metadata = {
200
+ toolArgsKeyOrderCache: new Map<string, Map<string, string[]>>(),
201
+ };
202
+
203
+ const converted = AISDKMessageConverter.toThreadMessages(
204
+ [
205
+ {
206
+ id: "a1",
207
+ role: "assistant",
208
+ parts: [
209
+ {
210
+ type: "tool-stocks",
211
+ toolCallId: "tc-order-1",
212
+ state: "input-streaming",
213
+ input: {
214
+ type: "high_stock_model",
215
+ limit: 5,
216
+ },
217
+ },
218
+ ],
219
+ } as any,
220
+ {
221
+ id: "a2",
222
+ role: "assistant",
223
+ parts: [
224
+ {
225
+ type: "tool-stocks",
226
+ toolCallId: "tc-order-1",
227
+ state: "input-available",
228
+ input: {
229
+ limit: 5,
230
+ type: "high_stock_model",
231
+ },
232
+ },
233
+ ],
234
+ } as any,
235
+ ],
236
+ false,
237
+ metadata,
238
+ );
239
+
240
+ const toolCalls = converted[0]?.content.filter(
241
+ (part): part is any => part.type === "tool-call",
242
+ );
243
+ expect(toolCalls).toHaveLength(1);
244
+ expect(toolCalls?.[0]?.toolCallId).toBe("tc-order-1");
245
+ expect(JSON.parse(toolCalls?.[0]?.argsText ?? "{}")).toEqual({
246
+ type: "high_stock_model",
247
+ limit: 5,
248
+ });
249
+ });
197
250
  });
package/src/usage.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { useAuiState } from "@assistant-ui/react";
2
+
2
3
  export type ThreadTokenUsage = {
3
4
  totalTokens?: number;
4
5
  inputTokens?: number;
@@ -6,167 +7,126 @@ export type ThreadTokenUsage = {
6
7
  reasoningTokens?: number;
7
8
  cachedInputTokens?: number;
8
9
  };
10
+
9
11
  export interface TokenUsageExtractableMessage {
10
12
  role?: string;
11
13
  metadata?: unknown;
12
14
  }
13
- // Internal types for parsing
15
+
14
16
  type UsageRecord = Record<string, unknown>;
17
+
18
+ const USAGE_KEYS = [
19
+ "inputTokens",
20
+ "outputTokens",
21
+ "reasoningTokens",
22
+ "cachedInputTokens",
23
+ "totalTokens",
24
+ ] as const satisfies (keyof ThreadTokenUsage)[];
25
+
15
26
  function asRecord(value: unknown): UsageRecord | undefined {
16
27
  if (!value || typeof value !== "object" || Array.isArray(value))
17
28
  return undefined;
18
29
  return value as UsageRecord;
19
30
  }
31
+
20
32
  function asPositiveTokenCount(value: unknown): number | undefined {
21
33
  if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
22
34
  return undefined;
23
35
  }
24
36
  return value;
25
37
  }
26
- // Internal type that makes everything optional for the intermediate parsing steps
27
- type ParsedUsage = {
28
- inputTokens?: number;
29
- outputTokens?: number;
30
- reasoningTokens?: number;
31
- cachedInputTokens?: number;
32
- totalTokens?: number;
33
- };
34
38
 
35
- function hasAnyUsageField(parsed: ParsedUsage): boolean {
36
- return (
37
- parsed.inputTokens !== undefined ||
38
- parsed.outputTokens !== undefined ||
39
- parsed.reasoningTokens !== undefined ||
40
- parsed.cachedInputTokens !== undefined ||
41
- parsed.totalTokens !== undefined
42
- );
39
+ function computeTotalTokens(usage: ThreadTokenUsage): number | undefined {
40
+ if (usage.totalTokens !== undefined) return usage.totalTokens;
41
+ if (usage.inputTokens !== undefined && usage.outputTokens !== undefined) {
42
+ return usage.inputTokens + usage.outputTokens;
43
+ }
44
+ return undefined;
43
45
  }
44
46
 
45
- function normalizeUsage(value: unknown): ParsedUsage | undefined {
46
- const usage = asRecord(value);
47
- if (!usage) return undefined;
48
- const inputTokens = asPositiveTokenCount(usage.inputTokens);
49
- const outputTokens = asPositiveTokenCount(usage.outputTokens);
50
- const reasoningTokens = asPositiveTokenCount(usage.reasoningTokens);
51
- const cachedInputTokens = asPositiveTokenCount(usage.cachedInputTokens);
52
- const totalTokens = asPositiveTokenCount(usage.totalTokens);
53
- const result: ParsedUsage = {};
54
- if (inputTokens !== undefined) result.inputTokens = inputTokens;
55
- if (outputTokens !== undefined) result.outputTokens = outputTokens;
56
- if (reasoningTokens !== undefined) result.reasoningTokens = reasoningTokens;
57
- if (cachedInputTokens !== undefined)
58
- result.cachedInputTokens = cachedInputTokens;
59
- if (totalTokens !== undefined) result.totalTokens = totalTokens;
60
-
61
- if (!hasAnyUsageField(result)) {
62
- return undefined;
63
- }
47
+ function normalizeUsage(value: unknown): ThreadTokenUsage | undefined {
48
+ const record = asRecord(value);
49
+ if (!record) return undefined;
64
50
 
65
- return result;
66
- }
67
- function buildUsageResult(parsed: ParsedUsage): ThreadTokenUsage | undefined {
68
- if (!hasAnyUsageField(parsed)) {
69
- return undefined;
70
- }
71
- const hasBothInputAndOutput =
72
- parsed.inputTokens !== undefined && parsed.outputTokens !== undefined;
73
- const totalTokens =
74
- parsed.totalTokens ??
75
- (hasBothInputAndOutput
76
- ? (parsed.inputTokens ?? 0) + (parsed.outputTokens ?? 0)
77
- : undefined);
78
51
  const result: ThreadTokenUsage = {};
79
- if (totalTokens !== undefined) result.totalTokens = totalTokens;
80
- if (parsed.inputTokens !== undefined) result.inputTokens = parsed.inputTokens;
81
- if (parsed.outputTokens !== undefined)
82
- result.outputTokens = parsed.outputTokens;
83
- if (parsed.reasoningTokens !== undefined)
84
- result.reasoningTokens = parsed.reasoningTokens;
85
- if (parsed.cachedInputTokens !== undefined)
86
- result.cachedInputTokens = parsed.cachedInputTokens;
87
- return result;
52
+ let hasFields = false;
53
+ for (const key of USAGE_KEYS) {
54
+ const count = asPositiveTokenCount(record[key]);
55
+ if (count !== undefined) {
56
+ result[key] = count;
57
+ hasFields = true;
58
+ }
59
+ }
60
+ return hasFields ? result : undefined;
61
+ }
62
+
63
+ function withComputedTotal(
64
+ usage: ThreadTokenUsage,
65
+ ): ThreadTokenUsage | undefined {
66
+ const totalTokens = computeTotalTokens(usage);
67
+ return { ...usage, ...(totalTokens !== undefined && { totalTokens }) };
88
68
  }
69
+
89
70
  function usageFromSteps(value: unknown): ThreadTokenUsage | undefined {
90
71
  const steps = Array.isArray(value) ? value : [];
91
- let inputTokens = 0;
92
- let outputTokens = 0;
93
- let reasoningTokens = 0;
94
- let cachedInputTokens = 0;
95
- let totalTokens = 0;
96
- let hasInput = false;
97
- let hasOutput = false;
98
- let hasReasoning = false;
99
- let hasCachedInput = false;
72
+
73
+ const sums: Record<string, number> = {};
74
+ const present: Record<string, boolean> = {};
100
75
  let stepsWithUsage = 0;
101
76
  let stepsWithComputableTotal = 0;
77
+
102
78
  for (const step of steps) {
103
79
  const usage = normalizeUsage(asRecord(step)?.usage);
104
80
  if (!usage) continue;
105
81
  stepsWithUsage++;
106
- const stepHasBothInputAndOutput =
107
- usage.inputTokens !== undefined && usage.outputTokens !== undefined;
108
- const stepTotal =
109
- usage.totalTokens ??
110
- (stepHasBothInputAndOutput
111
- ? (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0)
112
- : undefined);
82
+
83
+ const stepTotal = computeTotalTokens(usage);
113
84
  if (stepTotal !== undefined) {
114
- totalTokens += stepTotal;
85
+ sums.totalTokens = (sums.totalTokens ?? 0) + stepTotal;
115
86
  stepsWithComputableTotal++;
116
87
  }
117
- if (usage.inputTokens !== undefined) {
118
- inputTokens += usage.inputTokens;
119
- hasInput = true;
120
- }
121
- if (usage.outputTokens !== undefined) {
122
- outputTokens += usage.outputTokens;
123
- hasOutput = true;
124
- }
125
- if (usage.reasoningTokens !== undefined) {
126
- reasoningTokens += usage.reasoningTokens;
127
- hasReasoning = true;
128
- }
129
- if (usage.cachedInputTokens !== undefined) {
130
- cachedInputTokens += usage.cachedInputTokens;
131
- hasCachedInput = true;
88
+
89
+ for (const key of USAGE_KEYS) {
90
+ if (key === "totalTokens") continue;
91
+ if (usage[key] !== undefined) {
92
+ sums[key] = (sums[key] ?? 0) + usage[key];
93
+ present[key] = true;
94
+ }
132
95
  }
133
96
  }
97
+
134
98
  if (stepsWithUsage === 0) return undefined;
135
- const parsed: ParsedUsage = {};
136
- if (hasInput) parsed.inputTokens = inputTokens;
137
- if (hasOutput) parsed.outputTokens = outputTokens;
138
- if (hasReasoning) parsed.reasoningTokens = reasoningTokens;
139
- if (hasCachedInput) parsed.cachedInputTokens = cachedInputTokens;
99
+
100
+ const result: ThreadTokenUsage = {};
140
101
  if (stepsWithComputableTotal === stepsWithUsage) {
141
- parsed.totalTokens = totalTokens;
102
+ result.totalTokens = sums.totalTokens!;
103
+ }
104
+ for (const key of USAGE_KEYS) {
105
+ if (key === "totalTokens") continue;
106
+ if (present[key]) {
107
+ result[key] = sums[key]!;
108
+ }
142
109
  }
143
- const result: ThreadTokenUsage = {};
144
- if (parsed.totalTokens !== undefined) result.totalTokens = parsed.totalTokens;
145
- if (parsed.inputTokens !== undefined) result.inputTokens = parsed.inputTokens;
146
- if (parsed.outputTokens !== undefined)
147
- result.outputTokens = parsed.outputTokens;
148
- if (parsed.reasoningTokens !== undefined)
149
- result.reasoningTokens = parsed.reasoningTokens;
150
- if (parsed.cachedInputTokens !== undefined)
151
- result.cachedInputTokens = parsed.cachedInputTokens;
152
110
  return result;
153
111
  }
112
+
154
113
  export function getThreadMessageTokenUsage(
155
114
  message: TokenUsageExtractableMessage | undefined,
156
115
  ): ThreadTokenUsage | undefined {
157
116
  if (!message || message.role !== "assistant") return undefined;
117
+
158
118
  const metadata = asRecord(message.metadata);
159
119
  if (!metadata) return undefined;
120
+
160
121
  const topLevelUsage = normalizeUsage(metadata.usage);
161
- if (topLevelUsage) {
162
- return buildUsageResult(topLevelUsage);
163
- }
122
+ if (topLevelUsage) return withComputedTotal(topLevelUsage);
123
+
164
124
  const legacyUsage = normalizeUsage(asRecord(metadata.custom)?.usage);
165
- if (legacyUsage) {
166
- return buildUsageResult(legacyUsage);
167
- }
125
+ if (legacyUsage) return withComputedTotal(legacyUsage);
126
+
168
127
  return usageFromSteps(metadata.steps);
169
128
  }
129
+
170
130
  export function useThreadTokenUsage(): ThreadTokenUsage | undefined {
171
131
  const lastAssistant = useAuiState((s) =>
172
132
  s.thread.messages.findLast((m) => m.role === "assistant"),