@assistant-ui/ai-sdk 0.0.1

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 (167) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +32 -0
  3. package/dist/adapters/aiSDKFormatAdapter.d.ts +8 -0
  4. package/dist/adapters/aiSDKFormatAdapter.d.ts.map +1 -0
  5. package/dist/adapters/aiSDKFormatAdapter.js +26 -0
  6. package/dist/adapters/aiSDKFormatAdapter.js.map +1 -0
  7. package/dist/adapters/vercelAttachmentAdapter.d.ts +6 -0
  8. package/dist/adapters/vercelAttachmentAdapter.d.ts.map +1 -0
  9. package/dist/adapters/vercelAttachmentAdapter.js +37 -0
  10. package/dist/adapters/vercelAttachmentAdapter.js.map +1 -0
  11. package/dist/aiSDKExtras.d.ts +11 -0
  12. package/dist/aiSDKExtras.d.ts.map +1 -0
  13. package/dist/aiSDKExtras.js +7 -0
  14. package/dist/aiSDKExtras.js.map +1 -0
  15. package/dist/converters/convertMessage.d.ts +28 -0
  16. package/dist/converters/convertMessage.d.ts.map +1 -0
  17. package/dist/converters/convertMessage.js +249 -0
  18. package/dist/converters/convertMessage.js.map +1 -0
  19. package/dist/converters/modelContentEnvelope.d.ts +16 -0
  20. package/dist/converters/modelContentEnvelope.d.ts.map +1 -0
  21. package/dist/converters/modelContentEnvelope.js +22 -0
  22. package/dist/converters/modelContentEnvelope.js.map +1 -0
  23. package/dist/converters/toCreateMessage.d.ts +7 -0
  24. package/dist/converters/toCreateMessage.d.ts.map +1 -0
  25. package/dist/converters/toCreateMessage.js +58 -0
  26. package/dist/converters/toCreateMessage.js.map +1 -0
  27. package/dist/converters/toolOutputConversion.d.ts +29 -0
  28. package/dist/converters/toolOutputConversion.d.ts.map +1 -0
  29. package/dist/converters/toolOutputConversion.js +31 -0
  30. package/dist/converters/toolOutputConversion.js.map +1 -0
  31. package/dist/hooks.d.ts +19 -0
  32. package/dist/hooks.d.ts.map +1 -0
  33. package/dist/hooks.js +20 -0
  34. package/dist/hooks.js.map +1 -0
  35. package/dist/index.d.ts +14 -0
  36. package/dist/index.js +13 -0
  37. package/dist/index.native.d.ts +12 -0
  38. package/dist/index.native.js +11 -0
  39. package/dist/model-context/injectInteractableContext.d.ts +37 -0
  40. package/dist/model-context/injectInteractableContext.d.ts.map +1 -0
  41. package/dist/model-context/injectInteractableContext.js +52 -0
  42. package/dist/model-context/injectInteractableContext.js.map +1 -0
  43. package/dist/model-context/injectQuoteContext.d.ts +27 -0
  44. package/dist/model-context/injectQuoteContext.d.ts.map +1 -0
  45. package/dist/model-context/injectQuoteContext.js +51 -0
  46. package/dist/model-context/injectQuoteContext.js.map +1 -0
  47. package/dist/runtime/AISDKChat.d.ts +16 -0
  48. package/dist/runtime/AISDKChat.d.ts.map +1 -0
  49. package/dist/runtime/AISDKChat.js +69 -0
  50. package/dist/runtime/AISDKChat.js.map +1 -0
  51. package/dist/runtime/AISDKThreads.d.ts +45 -0
  52. package/dist/runtime/AISDKThreads.d.ts.map +1 -0
  53. package/dist/runtime/AISDKThreads.js +203 -0
  54. package/dist/runtime/AISDKThreads.js.map +1 -0
  55. package/dist/runtime/useAISDKRuntime.d.ts +49 -0
  56. package/dist/runtime/useAISDKRuntime.d.ts.map +1 -0
  57. package/dist/runtime/useAISDKRuntime.js +266 -0
  58. package/dist/runtime/useAISDKRuntime.js.map +1 -0
  59. package/dist/runtime/useChatRuntime.d.ts +13 -0
  60. package/dist/runtime/useChatRuntime.d.ts.map +1 -0
  61. package/dist/runtime/useChatRuntime.js +52 -0
  62. package/dist/runtime/useChatRuntime.js.map +1 -0
  63. package/dist/runtime/useChatThread.d.ts +77 -0
  64. package/dist/runtime/useChatThread.d.ts.map +1 -0
  65. package/dist/runtime/useChatThread.js +134 -0
  66. package/dist/runtime/useChatThread.js.map +1 -0
  67. package/dist/runtime/useExternalHistory.d.ts +11 -0
  68. package/dist/runtime/useExternalHistory.d.ts.map +1 -0
  69. package/dist/runtime/useExternalHistory.js +278 -0
  70. package/dist/runtime/useExternalHistory.js.map +1 -0
  71. package/dist/runtime/useResourceCleanup.d.ts +5 -0
  72. package/dist/runtime/useResourceCleanup.d.ts.map +1 -0
  73. package/dist/runtime/useResourceCleanup.js +47 -0
  74. package/dist/runtime/useResourceCleanup.js.map +1 -0
  75. package/dist/runtime/useStreamingTiming.d.ts +15 -0
  76. package/dist/runtime/useStreamingTiming.d.ts.map +1 -0
  77. package/dist/runtime/useStreamingTiming.js +38 -0
  78. package/dist/runtime/useStreamingTiming.js.map +1 -0
  79. package/dist/tools/frontendTools.d.ts +33 -0
  80. package/dist/tools/frontendTools.d.ts.map +1 -0
  81. package/dist/tools/frontendTools.js +33 -0
  82. package/dist/tools/frontendTools.js.map +1 -0
  83. package/dist/tools/generativeTools.d.ts +73 -0
  84. package/dist/tools/generativeTools.d.ts.map +1 -0
  85. package/dist/tools/generativeTools.js +245 -0
  86. package/dist/tools/generativeTools.js.map +1 -0
  87. package/dist/tools/mcp-stdio.node.d.ts +2 -0
  88. package/dist/tools/mcp-stdio.node.js +2 -0
  89. package/dist/tools/mcp-stdio.unsupported.d.ts +6 -0
  90. package/dist/tools/mcp-stdio.unsupported.d.ts.map +1 -0
  91. package/dist/tools/mcp-stdio.unsupported.js +11 -0
  92. package/dist/tools/mcp-stdio.unsupported.js.map +1 -0
  93. package/dist/transport/AssistantChatTransport.d.ts +23 -0
  94. package/dist/transport/AssistantChatTransport.d.ts.map +1 -0
  95. package/dist/transport/AssistantChatTransport.js +134 -0
  96. package/dist/transport/AssistantChatTransport.js.map +1 -0
  97. package/dist/transport/resumable.d.ts +38 -0
  98. package/dist/transport/resumable.d.ts.map +1 -0
  99. package/dist/transport/resumable.js +113 -0
  100. package/dist/transport/resumable.js.map +1 -0
  101. package/dist/usage.d.ts +19 -0
  102. package/dist/usage.d.ts.map +1 -0
  103. package/dist/usage.js +127 -0
  104. package/dist/usage.js.map +1 -0
  105. package/dist/utils/getVercelAIMessages.d.ts +7 -0
  106. package/dist/utils/getVercelAIMessages.d.ts.map +1 -0
  107. package/dist/utils/getVercelAIMessages.js +9 -0
  108. package/dist/utils/getVercelAIMessages.js.map +1 -0
  109. package/dist/utils/sliceMessagesUntil.d.ts +6 -0
  110. package/dist/utils/sliceMessagesUntil.d.ts.map +1 -0
  111. package/dist/utils/sliceMessagesUntil.js +12 -0
  112. package/dist/utils/sliceMessagesUntil.js.map +1 -0
  113. package/package.json +100 -0
  114. package/src/__tests__/standalone.e2e.ts +59 -0
  115. package/src/adapters/aiSDKFormatAdapter.ts +41 -0
  116. package/src/adapters/vercelAttachmentAdapter.test.ts +29 -0
  117. package/src/adapters/vercelAttachmentAdapter.ts +36 -0
  118. package/src/aiSDKExtras.ts +11 -0
  119. package/src/converters/convertMessage.test.ts +1130 -0
  120. package/src/converters/convertMessage.ts +457 -0
  121. package/src/converters/modelContentEnvelope.ts +41 -0
  122. package/src/converters/toCreateMessage.test.ts +596 -0
  123. package/src/converters/toCreateMessage.ts +99 -0
  124. package/src/converters/toolOutputConversion.ts +26 -0
  125. package/src/hooks.ts +23 -0
  126. package/src/index.native.ts +21 -0
  127. package/src/index.ts +29 -0
  128. package/src/model-context/injectInteractableContext.test.ts +167 -0
  129. package/src/model-context/injectInteractableContext.ts +61 -0
  130. package/src/model-context/injectQuoteContext.test.ts +33 -0
  131. package/src/model-context/injectQuoteContext.ts +62 -0
  132. package/src/runtime/AISDKChat.integration.test.tsx +47 -0
  133. package/src/runtime/AISDKChat.react-compiler.test.ts +49 -0
  134. package/src/runtime/AISDKChat.test.ts +193 -0
  135. package/src/runtime/AISDKChat.ts +47 -0
  136. package/src/runtime/AISDKThreads.cloud.test.ts +136 -0
  137. package/src/runtime/AISDKThreads.test.ts +548 -0
  138. package/src/runtime/AISDKThreads.ts +223 -0
  139. package/src/runtime/__tests__/controlled-transport.ts +43 -0
  140. package/src/runtime/useAISDKRuntime.approval.test.tsx +69 -0
  141. package/src/runtime/useAISDKRuntime.test.ts +1021 -0
  142. package/src/runtime/useAISDKRuntime.tool-output.test.tsx +113 -0
  143. package/src/runtime/useAISDKRuntime.ts +524 -0
  144. package/src/runtime/useChatOnTap.test.ts +58 -0
  145. package/src/runtime/useChatRuntime.integration.test.tsx +67 -0
  146. package/src/runtime/useChatRuntime.test.ts +522 -0
  147. package/src/runtime/useChatRuntime.ts +49 -0
  148. package/src/runtime/useChatThread.ts +286 -0
  149. package/src/runtime/useExternalHistory.test.ts +1289 -0
  150. package/src/runtime/useExternalHistory.ts +435 -0
  151. package/src/runtime/useResourceCleanup.ts +32 -0
  152. package/src/runtime/useStreamingTiming.ts +68 -0
  153. package/src/tools/frontendTools.test.ts +205 -0
  154. package/src/tools/frontendTools.ts +83 -0
  155. package/src/tools/generativeTools.test.ts +785 -0
  156. package/src/tools/generativeTools.ts +470 -0
  157. package/src/tools/mcp-stdio.node.ts +1 -0
  158. package/src/tools/mcp-stdio.unsupported.ts +12 -0
  159. package/src/transport/AssistantChatTransport.test.ts +300 -0
  160. package/src/transport/AssistantChatTransport.ts +204 -0
  161. package/src/transport/resumable.test.ts +262 -0
  162. package/src/transport/resumable.ts +161 -0
  163. package/src/usage.test.ts +186 -0
  164. package/src/usage.ts +174 -0
  165. package/src/utils/getVercelAIMessages.ts +11 -0
  166. package/src/utils/sliceMessagesUntil.test.ts +55 -0
  167. package/src/utils/sliceMessagesUntil.ts +20 -0
package/src/usage.ts ADDED
@@ -0,0 +1,174 @@
1
+ /// <reference types="@assistant-ui/core/react" />
2
+ import { useAuiState } from "@assistant-ui/store";
3
+
4
+ export type ThreadTokenUsage = {
5
+ totalTokens?: number;
6
+ inputTokens?: number;
7
+ outputTokens?: number;
8
+ reasoningTokens?: number;
9
+ cachedInputTokens?: number;
10
+ };
11
+
12
+ export interface TokenUsageExtractableMessage {
13
+ role?: string;
14
+ metadata?: unknown;
15
+ }
16
+
17
+ type UsageRecord = Record<string, unknown>;
18
+
19
+ const USAGE_KEYS = [
20
+ "inputTokens",
21
+ "outputTokens",
22
+ "reasoningTokens",
23
+ "cachedInputTokens",
24
+ "totalTokens",
25
+ ] as const satisfies (keyof ThreadTokenUsage)[];
26
+
27
+ function asRecord(value: unknown): UsageRecord | undefined {
28
+ if (!value || typeof value !== "object" || Array.isArray(value))
29
+ return undefined;
30
+ return value as UsageRecord;
31
+ }
32
+
33
+ function asPositiveTokenCount(value: unknown): number | undefined {
34
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
35
+ return undefined;
36
+ }
37
+ return value;
38
+ }
39
+
40
+ function computeTotalTokens(usage: ThreadTokenUsage): number | undefined {
41
+ if (usage.totalTokens !== undefined) return usage.totalTokens;
42
+ if (usage.inputTokens !== undefined && usage.outputTokens !== undefined) {
43
+ return usage.inputTokens + usage.outputTokens;
44
+ }
45
+ return undefined;
46
+ }
47
+
48
+ function normalizeUsage(value: unknown): ThreadTokenUsage | undefined {
49
+ const record = asRecord(value);
50
+ if (!record) return undefined;
51
+
52
+ const result: ThreadTokenUsage = {};
53
+ let hasFields = false;
54
+ for (const key of USAGE_KEYS) {
55
+ const count = asPositiveTokenCount(record[key]);
56
+ if (count !== undefined) {
57
+ result[key] = count;
58
+ hasFields = true;
59
+ }
60
+ }
61
+ // AI SDK v7 moved these under token detail objects; v6 kept them top-level.
62
+ if (result.reasoningTokens === undefined) {
63
+ const count = asPositiveTokenCount(
64
+ asRecord(record.outputTokenDetails)?.reasoningTokens,
65
+ );
66
+ if (count !== undefined) {
67
+ result.reasoningTokens = count;
68
+ hasFields = true;
69
+ }
70
+ }
71
+ if (result.cachedInputTokens === undefined) {
72
+ const count = asPositiveTokenCount(
73
+ asRecord(record.inputTokenDetails)?.cacheReadTokens,
74
+ );
75
+ if (count !== undefined) {
76
+ result.cachedInputTokens = count;
77
+ hasFields = true;
78
+ }
79
+ }
80
+ return hasFields ? result : undefined;
81
+ }
82
+
83
+ function withComputedTotal(
84
+ usage: ThreadTokenUsage,
85
+ ): ThreadTokenUsage | undefined {
86
+ const totalTokens = computeTotalTokens(usage);
87
+ return { ...usage, ...(totalTokens !== undefined && { totalTokens }) };
88
+ }
89
+
90
+ function usageFromSteps(value: unknown): ThreadTokenUsage | undefined {
91
+ const steps = Array.isArray(value) ? value : [];
92
+
93
+ const sums: Record<string, number> = {};
94
+ const present: Record<string, boolean> = {};
95
+ let stepsWithUsage = 0;
96
+ let stepsWithComputableTotal = 0;
97
+
98
+ for (const step of steps) {
99
+ const usage = normalizeUsage(asRecord(step)?.usage);
100
+ if (!usage) continue;
101
+ stepsWithUsage++;
102
+
103
+ const stepTotal = computeTotalTokens(usage);
104
+ if (stepTotal !== undefined) {
105
+ sums.totalTokens = (sums.totalTokens ?? 0) + stepTotal;
106
+ stepsWithComputableTotal++;
107
+ }
108
+
109
+ for (const key of USAGE_KEYS) {
110
+ if (key === "totalTokens") continue;
111
+ if (usage[key] !== undefined) {
112
+ sums[key] = (sums[key] ?? 0) + usage[key];
113
+ present[key] = true;
114
+ }
115
+ }
116
+ }
117
+
118
+ if (stepsWithUsage === 0) return undefined;
119
+
120
+ const result: ThreadTokenUsage = {};
121
+ if (stepsWithComputableTotal === stepsWithUsage) {
122
+ result.totalTokens = sums.totalTokens!;
123
+ }
124
+ for (const key of USAGE_KEYS) {
125
+ if (key === "totalTokens") continue;
126
+ if (present[key]) {
127
+ result[key] = sums[key]!;
128
+ }
129
+ }
130
+ return result;
131
+ }
132
+
133
+ export function getThreadMessageTokenUsage(
134
+ message: TokenUsageExtractableMessage | undefined,
135
+ ): ThreadTokenUsage | undefined {
136
+ if (!message || message.role !== "assistant") return undefined;
137
+
138
+ const metadata = asRecord(message.metadata);
139
+ if (!metadata) return undefined;
140
+
141
+ const topLevelUsage = normalizeUsage(metadata.usage);
142
+ if (topLevelUsage) return withComputedTotal(topLevelUsage);
143
+
144
+ const legacyUsage = normalizeUsage(asRecord(metadata.custom)?.usage);
145
+ if (legacyUsage) return withComputedTotal(legacyUsage);
146
+
147
+ return usageFromSteps(metadata.steps);
148
+ }
149
+
150
+ export function getLatestThreadTokenUsage(
151
+ messages: readonly TokenUsageExtractableMessage[] | undefined,
152
+ ): ThreadTokenUsage | undefined {
153
+ return getThreadMessageTokenUsage(findLatestMessageWithUsage(messages));
154
+ }
155
+
156
+ function findLatestMessageWithUsage(
157
+ messages: readonly TokenUsageExtractableMessage[] | undefined,
158
+ ): TokenUsageExtractableMessage | undefined {
159
+ if (!messages) return undefined;
160
+
161
+ for (let idx = messages.length - 1; idx >= 0; idx -= 1) {
162
+ const message = messages[idx];
163
+ if (getThreadMessageTokenUsage(message)) {
164
+ return message;
165
+ }
166
+ }
167
+
168
+ return undefined;
169
+ }
170
+
171
+ export function useThreadTokenUsage(): ThreadTokenUsage | undefined {
172
+ const msg = useAuiState((s) => findLatestMessageWithUsage(s.thread.messages));
173
+ return getThreadMessageTokenUsage(msg);
174
+ }
@@ -0,0 +1,11 @@
1
+ import {
2
+ getExternalStoreMessages,
3
+ type ThreadMessage,
4
+ } from "@assistant-ui/core";
5
+ import type { UIMessage } from "ai";
6
+
7
+ export const getVercelAIMessages = <UI_MESSAGE extends UIMessage = UIMessage>(
8
+ message: ThreadMessage,
9
+ ) => {
10
+ return getExternalStoreMessages(message) as UI_MESSAGE[];
11
+ };
@@ -0,0 +1,55 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import type { UIMessage } from "ai";
3
+ import { sliceMessagesUntil } from "./sliceMessagesUntil";
4
+
5
+ const msg = (id: string, role: UIMessage["role"]): UIMessage =>
6
+ ({ id, role, parts: [] }) as any;
7
+
8
+ describe("sliceMessagesUntil", () => {
9
+ it("returns an empty array when the message id is null", () => {
10
+ expect(
11
+ sliceMessagesUntil([msg("u1", "user"), msg("a1", "assistant")], null),
12
+ ).toEqual([]);
13
+ });
14
+
15
+ it("throws when the message id is not found", () => {
16
+ expect(() => sliceMessagesUntil([msg("u1", "user")], "missing")).toThrow(
17
+ 'sliceMessagesUntil: Message "missing" not found in AI SDK messages. This is likely an internal bug in assistant-ui.',
18
+ );
19
+ });
20
+
21
+ it("slices up to and including the target when no assistant follows", () => {
22
+ const messages = [
23
+ msg("u1", "user"),
24
+ msg("a1", "assistant"),
25
+ msg("u2", "user"),
26
+ ];
27
+ expect(sliceMessagesUntil(messages, "u2").map((m) => m.id)).toEqual([
28
+ "u1",
29
+ "a1",
30
+ "u2",
31
+ ]);
32
+ });
33
+
34
+ it("extends the slice to include trailing assistant messages", () => {
35
+ const messages = [
36
+ msg("u1", "user"),
37
+ msg("a1", "assistant"),
38
+ msg("a2", "assistant"),
39
+ msg("u2", "user"),
40
+ ];
41
+ expect(sliceMessagesUntil(messages, "u1").map((m) => m.id)).toEqual([
42
+ "u1",
43
+ "a1",
44
+ "a2",
45
+ ]);
46
+ });
47
+
48
+ it("returns the whole array when the target is the last message", () => {
49
+ const messages = [msg("u1", "user"), msg("a1", "assistant")];
50
+ expect(sliceMessagesUntil(messages, "a1").map((m) => m.id)).toEqual([
51
+ "u1",
52
+ "a1",
53
+ ]);
54
+ });
55
+ });
@@ -0,0 +1,20 @@
1
+ import type { UIMessage } from "ai";
2
+
3
+ export const sliceMessagesUntil = <UI_MESSAGE extends UIMessage = UIMessage>(
4
+ messages: UI_MESSAGE[],
5
+ messageId: string | null,
6
+ ) => {
7
+ if (messageId == null) return [];
8
+
9
+ let messageIdx = messages.findIndex((m) => m.id === messageId);
10
+ if (messageIdx === -1)
11
+ throw new Error(
12
+ `sliceMessagesUntil: Message "${messageId}" not found in AI SDK messages. This is likely an internal bug in assistant-ui.`,
13
+ );
14
+
15
+ while (messages[messageIdx + 1]?.role === "assistant") {
16
+ messageIdx++;
17
+ }
18
+
19
+ return messages.slice(0, messageIdx + 1);
20
+ };