assistant-ui 0.0.116 → 0.0.118

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 (127) hide show
  1. package/dist/codemods/utils/createTransformer.d.ts +13 -16
  2. package/dist/codemods/utils/createTransformer.d.ts.map +1 -1
  3. package/dist/codemods/utils/resolveBinding.d.ts +3 -0
  4. package/dist/codemods/utils/resolveBinding.d.ts.map +1 -0
  5. package/dist/codemods/utils/resolveBinding.js +78 -0
  6. package/dist/codemods/utils/resolveBinding.js.map +1 -0
  7. package/dist/codemods/v0-11/content-part-to-message-part.d.ts +1 -3
  8. package/dist/codemods/v0-11/content-part-to-message-part.d.ts.map +1 -1
  9. package/dist/codemods/v0-12/assistant-api-to-aui.d.ts +1 -3
  10. package/dist/codemods/v0-12/assistant-api-to-aui.d.ts.map +1 -1
  11. package/dist/codemods/v0-12/assistant-api-to-aui.js +51 -99
  12. package/dist/codemods/v0-12/assistant-api-to-aui.js.map +1 -1
  13. package/dist/codemods/v0-12/event-names-to-camelcase.d.ts +1 -3
  14. package/dist/codemods/v0-12/event-names-to-camelcase.d.ts.map +1 -1
  15. package/dist/codemods/v0-12/primitive-if-to-aui-if.d.ts +1 -3
  16. package/dist/codemods/v0-12/primitive-if-to-aui-if.d.ts.map +1 -1
  17. package/dist/codemods/v0-15/aui-accessor-calls-to-properties.d.ts +1 -3
  18. package/dist/codemods/v0-15/aui-accessor-calls-to-properties.d.ts.map +1 -1
  19. package/dist/codemods/v0-15/aui-accessor-calls-to-properties.js +33 -5
  20. package/dist/codemods/v0-15/aui-accessor-calls-to-properties.js.map +1 -1
  21. package/dist/codemods/v0-8/ui-package-split.d.ts +1 -3
  22. package/dist/codemods/v0-8/ui-package-split.d.ts.map +1 -1
  23. package/dist/codemods/v0-9/edge-package-split.d.ts +1 -3
  24. package/dist/codemods/v0-9/edge-package-split.d.ts.map +1 -1
  25. package/dist/commands/add.d.ts +13 -16
  26. package/dist/commands/add.d.ts.map +1 -1
  27. package/dist/commands/add.js +3 -3
  28. package/dist/commands/agent.d.ts +1 -4
  29. package/dist/commands/agent.d.ts.map +1 -1
  30. package/dist/commands/agent.js +16 -13
  31. package/dist/commands/agent.js.map +1 -1
  32. package/dist/commands/create.d.ts +43 -39
  33. package/dist/commands/create.d.ts.map +1 -1
  34. package/dist/commands/create.js +40 -26
  35. package/dist/commands/create.js.map +1 -1
  36. package/dist/commands/doctor.d.ts +17 -20
  37. package/dist/commands/doctor.d.ts.map +1 -1
  38. package/dist/commands/info.d.ts +4 -6
  39. package/dist/commands/info.d.ts.map +1 -1
  40. package/dist/commands/info.js +3 -3
  41. package/dist/commands/init.d.ts +8 -10
  42. package/dist/commands/init.d.ts.map +1 -1
  43. package/dist/commands/init.js +3 -4
  44. package/dist/commands/init.js.map +1 -1
  45. package/dist/commands/mcp.d.ts +1 -4
  46. package/dist/commands/mcp.d.ts.map +1 -1
  47. package/dist/commands/mcp.js +2 -2
  48. package/dist/commands/update.d.ts +1 -4
  49. package/dist/commands/update.d.ts.map +1 -1
  50. package/dist/commands/update.js +1 -1
  51. package/dist/commands/upgrade.d.ts +7 -10
  52. package/dist/commands/upgrade.d.ts.map +1 -1
  53. package/dist/index.d.ts +3 -1
  54. package/dist/index.d.ts.map +1 -0
  55. package/dist/lib/agent-skill.d.ts +11 -10
  56. package/dist/lib/agent-skill.d.ts.map +1 -1
  57. package/dist/lib/agent-skill.js +78 -2
  58. package/dist/lib/agent-skill.js.map +1 -1
  59. package/dist/lib/create-project.d.ts +33 -32
  60. package/dist/lib/create-project.d.ts.map +1 -1
  61. package/dist/lib/create-project.js +91 -5
  62. package/dist/lib/create-project.js.map +1 -1
  63. package/dist/lib/handle-cli-error.d.ts +1 -4
  64. package/dist/lib/handle-cli-error.d.ts.map +1 -1
  65. package/dist/lib/install-ai-sdk-lib.d.ts +1 -4
  66. package/dist/lib/install-ai-sdk-lib.d.ts.map +1 -1
  67. package/dist/lib/install-edge-lib.d.ts +1 -4
  68. package/dist/lib/install-edge-lib.d.ts.map +1 -1
  69. package/dist/lib/run-spawn.d.ts +17 -19
  70. package/dist/lib/run-spawn.d.ts.map +1 -1
  71. package/dist/lib/run-spawn.js +25 -10
  72. package/dist/lib/run-spawn.js.map +1 -1
  73. package/dist/lib/transform-options.d.ts +5 -8
  74. package/dist/lib/transform-options.d.ts.map +1 -1
  75. package/dist/lib/transform.d.ts +13 -16
  76. package/dist/lib/transform.d.ts.map +1 -1
  77. package/dist/lib/transform.js +19 -35
  78. package/dist/lib/transform.js.map +1 -1
  79. package/dist/lib/upgrade.d.ts +3 -6
  80. package/dist/lib/upgrade.d.ts.map +1 -1
  81. package/dist/lib/upgrade.js +6 -11
  82. package/dist/lib/upgrade.js.map +1 -1
  83. package/dist/lib/utils/config.d.ts +18 -21
  84. package/dist/lib/utils/config.d.ts.map +1 -1
  85. package/dist/lib/utils/file-scanner.d.ts +12 -15
  86. package/dist/lib/utils/file-scanner.d.ts.map +1 -1
  87. package/dist/lib/utils/logger.d.ts +7 -10
  88. package/dist/lib/utils/logger.d.ts.map +1 -1
  89. package/dist/lib/utils/package-installer.d.ts +7 -10
  90. package/dist/lib/utils/package-installer.d.ts.map +1 -1
  91. package/dist/lib/utils/package-json.d.ts +1 -4
  92. package/dist/lib/utils/package-json.d.ts.map +1 -1
  93. package/dist/lib/utils/package-manager.d.ts +9 -12
  94. package/dist/lib/utils/package-manager.d.ts.map +1 -1
  95. package/dist/lib/utils/package-manager.js +13 -2
  96. package/dist/lib/utils/package-manager.js.map +1 -1
  97. package/dist/lib/utils/registry.d.ts +5 -8
  98. package/dist/lib/utils/registry.d.ts.map +1 -1
  99. package/dist/lib/utils/workspace.d.ts +2 -5
  100. package/dist/lib/utils/workspace.d.ts.map +1 -1
  101. package/dist/program.d.ts +1 -4
  102. package/dist/program.d.ts.map +1 -1
  103. package/dist/program.js +6 -6
  104. package/dist/run.d.ts +1 -4
  105. package/dist/run.d.ts.map +1 -1
  106. package/package.json +8 -8
  107. package/src/codemods/utils/resolveBinding.test.ts +107 -0
  108. package/src/codemods/utils/resolveBinding.ts +130 -0
  109. package/src/codemods/v0-12/__tests__/assistant-api-to-aui.test.ts +256 -0
  110. package/src/codemods/v0-12/assistant-api-to-aui.ts +125 -225
  111. package/src/codemods/v0-15/__tests__/aui-accessor-calls-to-properties.test.ts +101 -0
  112. package/src/codemods/v0-15/aui-accessor-calls-to-properties.ts +60 -5
  113. package/src/commands/agent.ts +15 -22
  114. package/src/commands/create.ts +53 -20
  115. package/src/commands/init.ts +1 -2
  116. package/src/lib/agent-skill.ts +103 -1
  117. package/src/lib/create-project.ts +99 -4
  118. package/src/lib/run-spawn.test.ts +47 -0
  119. package/src/lib/run-spawn.ts +29 -17
  120. package/src/lib/transform-files.test.ts +71 -0
  121. package/src/lib/transform.test.ts +69 -1
  122. package/src/lib/transform.ts +23 -38
  123. package/src/lib/upgrade.ts +9 -13
  124. package/src/lib/utils/package-manager.ts +24 -1
  125. package/plugin/.claude-plugin/plugin.json +0 -8
  126. package/plugin/skills/assistant-ui/SKILL.md +0 -199
  127. package/plugin/skills/assistant-ui/shipables.json +0 -22
@@ -1,4 +1,8 @@
1
- import { describe, expect, it, vi } from "vitest";
1
+ import { describe, expect, it, onTestFinished, vi } from "vitest";
2
+ import { mkdtempSync, writeFileSync, rmSync, readFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { createRequire } from "node:module";
2
6
 
3
7
  const mocks = vi.hoisted(() => ({
4
8
  runSpawnCapture: vi.fn(),
@@ -13,6 +17,63 @@ import { transform } from "./transform";
13
17
  import { SpawnExitError } from "./run-spawn";
14
18
 
15
19
  describe("transform", () => {
20
+ it.each(["parse", "transform"])(
21
+ "rejects real jscodeshift %s failures",
22
+ async (kind) => {
23
+ const directory = mkdtempSync(join(tmpdir(), "aui-transform-errors-"));
24
+ onTestFinished(() => rmSync(directory, { recursive: true, force: true }));
25
+ const file = join(directory, "app.tsx");
26
+ const source =
27
+ kind === "parse"
28
+ ? 'import "@assistant-ui/react"; const broken = ;'
29
+ : 'import "@assistant-ui/react"; const value = 1;';
30
+ writeFileSync(file, source);
31
+ const codemod = join(directory, "transform.cjs");
32
+ writeFileSync(
33
+ codemod,
34
+ kind === "parse"
35
+ ? "module.exports = (file, api) => api.jscodeshift(file.source).toSource();"
36
+ : 'module.exports = () => { throw new Error("fixture transform failure"); };',
37
+ );
38
+ const actual =
39
+ await vi.importActual<typeof import("./run-spawn")>("./run-spawn");
40
+ const require = createRequire(import.meta.url);
41
+ mocks.runSpawnCapture.mockImplementationOnce(
42
+ (_command: string, args: string[]) => {
43
+ const runnerArgs = args.slice(1);
44
+ runnerArgs[runnerArgs.indexOf("-t") + 1] = codemod;
45
+ return actual.runSpawnCapture(process.execPath, [
46
+ require.resolve("jscodeshift/bin/jscodeshift.js"),
47
+ ...runnerArgs,
48
+ "--run-in-band",
49
+ ]);
50
+ },
51
+ );
52
+
53
+ const failure = transform(
54
+ "v0-12/assistant-api-to-aui",
55
+ directory,
56
+ {},
57
+ {
58
+ logStatus: false,
59
+ relevantFiles: [file],
60
+ },
61
+ );
62
+ await expect(failure).rejects.toBeInstanceOf(SpawnExitError);
63
+ await expect(failure).rejects.toThrow(
64
+ "Codemod 'v0-12/assistant-api-to-aui' failed",
65
+ );
66
+ await expect(failure).rejects.toThrow(
67
+ kind === "parse" ? "Transformation error" : "fixture transform failure",
68
+ );
69
+ await expect(failure).rejects.toMatchObject({
70
+ stdout: expect.stringContaining("Transformation error"),
71
+ });
72
+ expect(readFileSync(file, "utf8")).toBe(source);
73
+ },
74
+ 15_000,
75
+ );
76
+
16
77
  it("runs codemods asynchronously and reports progress", async () => {
17
78
  mocks.runSpawnCapture.mockResolvedValue({
18
79
  code: 0,
@@ -62,7 +123,14 @@ describe("transform", () => {
62
123
  );
63
124
 
64
125
  await expect(failure).rejects.toBeInstanceOf(SpawnExitError);
126
+ await expect(failure).rejects.toThrow(
127
+ "Codemod 'v0-8/ui-package-split' failed",
128
+ );
65
129
  await expect(failure).rejects.toThrow("SyntaxError: Broken input");
130
+ await expect(failure).rejects.toMatchObject({
131
+ stdout: "Processing file app.tsx\n",
132
+ stderr: "SyntaxError: Broken input\n",
133
+ });
66
134
  expect(onProgress).not.toHaveBeenCalled();
67
135
  });
68
136
 
@@ -7,19 +7,23 @@ import { sync as globSync } from "glob";
7
7
  import { runSpawnCapture, SpawnExitError, SpawnSignalError } from "./run-spawn";
8
8
 
9
9
  const log = debug("codemod:transform");
10
- const error = debug("codemod:transform:error");
11
10
 
12
11
  const __filename = fileURLToPath(import.meta.url);
13
12
  const __dirname = path.dirname(__filename);
14
13
 
15
14
  /**
16
- * Gets the list of files that need to be processed in the codebase
17
- * Only includes files that contain "assistant-ui" to optimize performance
15
+ * Gets relevant source files from an explicit file or directory target.
16
+ * Directory scans only include files containing "assistant-ui".
18
17
  */
19
- export function getRelevantFiles(cwd: string): string[] {
18
+ export function getRelevantFiles(source: string): string[] {
19
+ const target = path.resolve(source);
20
+ if (fs.statSync(target).isFile()) {
21
+ return /\.(js|jsx|ts|tsx)$/.test(target) ? [target] : [];
22
+ }
23
+
20
24
  const pattern = "**/*.{js,jsx,ts,tsx}";
21
25
  const files = globSync(pattern, {
22
- cwd,
26
+ cwd: target,
23
27
  ignore: [
24
28
  "**/node_modules/**",
25
29
  "**/dist/**",
@@ -27,26 +31,25 @@ export function getRelevantFiles(cwd: string): string[] {
27
31
  "**/*.min.js",
28
32
  "**/*.bundle.js",
29
33
  ],
30
- });
34
+ }).map((file) => path.join(target, file));
31
35
 
32
- // Filter files to only include those containing "assistant-ui"
33
36
  const relevantFiles = files.filter((file) => {
34
37
  try {
35
- const content = fs.readFileSync(path.join(cwd, file), "utf8");
38
+ const content = fs.readFileSync(file, "utf8");
36
39
  return content.includes("assistant-ui");
37
40
  } catch {
38
41
  return false;
39
42
  }
40
43
  });
41
44
 
42
- return relevantFiles.map((file) => path.join(cwd, file));
45
+ return relevantFiles;
43
46
  }
44
47
 
45
48
  /**
46
49
  * Counts the number of files that need to be processed
47
50
  */
48
- export function countFilesToProcess(cwd: string): number {
49
- return getRelevantFiles(cwd).length;
51
+ export function countFilesToProcess(source: string): number {
52
+ return getRelevantFiles(source).length;
50
53
  }
51
54
 
52
55
  function buildCommand(
@@ -62,6 +65,7 @@ function buildCommand(
62
65
  ...targetFiles,
63
66
  "--parser",
64
67
  "tsx",
68
+ "--fail-on-error",
65
69
  ];
66
70
 
67
71
  if (options.dry) {
@@ -89,23 +93,6 @@ export type TransformErrors = {
89
93
  summary: string;
90
94
  }[];
91
95
 
92
- function parseErrors(transform: string, output: string): TransformErrors {
93
- const errors: TransformErrors = [];
94
- const errorRegex = /ERR (.+) Transformation error/g;
95
- const syntaxErrorRegex = /SyntaxError: .+/g;
96
-
97
- for (const match of output.matchAll(errorRegex)) {
98
- const filename = match[1]!;
99
- const syntaxErrorMatch = syntaxErrorRegex.exec(output);
100
- if (syntaxErrorMatch) {
101
- const summary = syntaxErrorMatch[0];
102
- errors.push({ transform, filename, summary });
103
- }
104
- }
105
-
106
- return errors;
107
- }
108
-
109
96
  export async function transform(
110
97
  codemod: string,
111
98
  source: string,
@@ -138,7 +125,13 @@ export async function transform(
138
125
  throw new SpawnSignalError(result.signal, false);
139
126
  }
140
127
  if (result.code !== 0) {
141
- throw new SpawnExitError(result.code || 1, result.stderr);
128
+ const failure = new SpawnExitError(
129
+ result.code || 1,
130
+ result.stderr,
131
+ result.stdout,
132
+ );
133
+ failure.message = `Codemod '${codemod}' failed\n${failure.message}`;
134
+ throw failure;
142
135
  }
143
136
 
144
137
  const { stdout } = result;
@@ -148,13 +141,5 @@ export async function transform(
148
141
  options.onProgress(processedFiles);
149
142
  }
150
143
 
151
- const errors = parseErrors(codemod, stdout);
152
- if (options.logStatus && errors.length > 0) {
153
- errors.forEach(({ transform, filename, summary }) => {
154
- error(
155
- `Error applying codemod [codemod=${transform}, path=${filename}, summary=${summary}]`,
156
- );
157
- });
158
- }
159
- return errors;
144
+ return [];
160
145
  }
@@ -1,5 +1,5 @@
1
1
  import debug from "debug";
2
- import { transform, type TransformErrors, getRelevantFiles } from "./transform";
2
+ import { transform, getRelevantFiles } from "./transform";
3
3
  import type { TransformOptions } from "./transform-options";
4
4
  import { SingleBar, Presets } from "cli-progress";
5
5
  import installEdgeLib from "./install-edge-lib";
@@ -16,13 +16,12 @@ const bundle = [
16
16
  ];
17
17
 
18
18
  const log = debug("codemod:upgrade");
19
- const error = debug("codemod:upgrade:error");
20
19
 
21
20
  /**
22
21
  * Runs the upgrade cycle:
23
22
  * - Runs each codemod in the bundle.
24
23
  * - Displays progress using cli-progress.
25
- * - After codemods run, checks if any file now imports from the new packages and prompts for install.
24
+ * - Outside dry mode, checks for new package imports and prompts for install.
26
25
  */
27
26
  export async function upgrade(options: TransformOptions) {
28
27
  const cwd = process.cwd();
@@ -48,14 +47,13 @@ export async function upgrade(options: TransformOptions) {
48
47
  );
49
48
 
50
49
  bar.start(totalWork, 0, { status: "Starting..." });
51
- const allErrors: TransformErrors = [];
52
50
 
53
51
  try {
54
52
  for (const codemod of bundle) {
55
53
  bar.update(completedWork, { status: `Running ${codemod}...` });
56
54
 
57
55
  // Use a custom progress callback to update the progress bar
58
- const errors = await transform(codemod, cwd, options, {
56
+ await transform(codemod, cwd, options, {
59
57
  logStatus: false,
60
58
  onProgress: (processedFiles: number) => {
61
59
  completedWork = bundle.indexOf(codemod) * fileCount + processedFiles;
@@ -66,24 +64,22 @@ export async function upgrade(options: TransformOptions) {
66
64
  relevantFiles, // Pass the pre-computed relevant files
67
65
  });
68
66
 
69
- allErrors.push(...errors);
70
67
  completedWork = (bundle.indexOf(codemod) + 1) * fileCount;
71
68
  bar.update(completedWork, { status: `Completed ${codemod}` });
72
69
  }
73
70
 
74
- bar.update(totalWork, { status: "Checking dependencies..." });
71
+ bar.update(totalWork, {
72
+ status: options.dry ? "Preview complete" : "Checking dependencies...",
73
+ });
75
74
  } finally {
76
75
  bar.stop();
77
76
  }
78
77
 
79
- if (allErrors.length > 0) {
80
- log("Some codemods did not apply successfully to all files. Details:");
81
- allErrors.forEach(({ transform, filename, summary }) => {
82
- error(`codemod=${transform}, path=${filename}, summary=${summary}`);
83
- });
78
+ if (options.dry) {
79
+ logger.success("Dry run complete. No files were changed.");
80
+ return;
84
81
  }
85
82
 
86
- // After codemods run, check if files import from the new packages and prompt for install.
87
83
  logger.info("Checking for package dependencies...");
88
84
  await installEdgeLib();
89
85
  await installAiSdkLib();
@@ -9,13 +9,36 @@ export type PackageManagerName = "npm" | "pnpm" | "yarn" | "bun";
9
9
 
10
10
  export function askQuestion(query: string): Promise<string> {
11
11
  return new Promise((resolve) => {
12
+ // A stream only reaches EOF once, so a run that already consumed stdin gets
13
+ // no further `end` and an interface built over it would wait on a `close`
14
+ // that cannot arrive. An upgrade asks up to three questions.
15
+ if (process.stdin.readableEnded) {
16
+ resolve("");
17
+ return;
18
+ }
12
19
  const rl = readline.createInterface({
13
20
  input: process.stdin,
14
21
  output: process.stdout,
15
22
  });
16
- rl.question(query, (answer) => {
23
+ // stdin at EOF (a piped or CI run) emits `close` without a `line`, so the
24
+ // question callback alone would leave this pending forever and the process
25
+ // would exit 0 with the remaining work silently skipped. An empty answer is
26
+ // what pressing Enter sends, so EOF lands on the prompt's own default.
27
+ // A final answer with no trailing newline arrives as `line` instead of
28
+ // through the callback, and has to win over that default.
29
+ // Ctrl-C in raw mode is delivered as this event rather than as a signal,
30
+ // and without a listener readline answers it by closing — which the EOF
31
+ // default would then read as approval. A cancelled prompt declines.
32
+ let cancelled = false;
33
+ rl.on("SIGINT", () => {
34
+ cancelled = true;
17
35
  rl.close();
36
+ });
37
+ rl.on("line", resolve);
38
+ rl.on("close", () => resolve(cancelled ? "n" : ""));
39
+ rl.question(query, (answer) => {
18
40
  resolve(answer);
41
+ rl.close();
19
42
  });
20
43
  });
21
44
  }
@@ -1,8 +0,0 @@
1
- {
2
- "name": "assistant-ui",
3
- "description": "Build AI chat interfaces using assistant-ui components",
4
- "version": "0.1.0",
5
- "author": {
6
- "name": "assistant-ui"
7
- }
8
- }
@@ -1,199 +0,0 @@
1
- ---
2
- name: assistant-ui
3
- description: Add, configure, and integrate assistant-ui components in React apps. Use when developers ask to add a chat thread, set up a runtime, integrate with AI SDK, configure tools, or build AI chat interfaces with assistant-ui.
4
- ---
5
-
6
- # assistant-ui
7
-
8
- Use this skill to help users build AI chat interfaces with assistant-ui.
9
-
10
- ## Step 1: Check Project Setup
11
-
12
- Check if the project has `components.json` (shadcn config) and `@assistant-ui/react` installed.
13
-
14
- If assistant-ui is not yet set up, run:
15
-
16
- ```bash
17
- npx assistant-ui init --yes
18
- ```
19
-
20
- This initializes shadcn and installs the default assistant-ui chat components.
21
-
22
- ## Step 2: Add Components
23
-
24
- Install components via the shadcn registry:
25
-
26
- ```bash
27
- npx shadcn@latest add "https://r.assistant-ui.com/base/chat/b/ai-sdk-quick-start/json"
28
- ```
29
-
30
- For Radix-styled projects (`components.json` style not starting with `base-`), use `https://r.assistant-ui.com/chat/b/ai-sdk-quick-start/json` instead.
31
-
32
- Available component presets:
33
-
34
- | Preset | Registry URL |
35
- |--------|-------------|
36
- | AI SDK Quick Start | `https://r.assistant-ui.com/base/chat/b/ai-sdk-quick-start/json` |
37
-
38
- You can also add individual assistant-ui components with the assistant-ui CLI, which picks the right flavor from `components.json`:
39
-
40
- ```bash
41
- npx assistant-ui add thread
42
- npx assistant-ui add markdown-text
43
- ```
44
-
45
- To use the shadcn CLI directly instead, add the assistant-ui registry to `components.json`:
46
-
47
- ```json
48
- {
49
- "registries": {
50
- "@assistant-ui": "https://r.assistant-ui.com/styles/{style}/{name}.json"
51
- }
52
- }
53
- ```
54
-
55
- Then add components with the namespaced form:
56
-
57
- ```bash
58
- npx shadcn@latest add @assistant-ui/thread
59
- npx shadcn@latest add @assistant-ui/markdown-text
60
- ```
61
-
62
- ## Step 3: Runtime Setup
63
-
64
- assistant-ui requires a runtime. The most common setup uses AI SDK:
65
-
66
- ### AI SDK Runtime (recommended)
67
-
68
- Install the integration package:
69
- ```bash
70
- npm install @assistant-ui/ai-sdk
71
- ```
72
-
73
- Create a chat API route (Next.js App Router):
74
-
75
- ```ts
76
- // app/api/chat/route.ts
77
- import { openai } from "@ai-sdk/openai";
78
- import { streamText } from "ai";
79
-
80
- export const maxDuration = 30;
81
-
82
- export async function POST(req: Request) {
83
- const { messages, config } = await req.json();
84
-
85
- const result = streamText({
86
- model: openai("gpt-5.6-luna"),
87
- messages,
88
- ...config,
89
- });
90
-
91
- return result.toDataStreamResponse();
92
- }
93
- ```
94
-
95
- Create the assistant component:
96
-
97
- ```tsx
98
- "use client";
99
-
100
- import { AssistantRuntimeProvider } from "@assistant-ui/react";
101
- import { useChatRuntime } from "@assistant-ui/ai-sdk";
102
- import { Thread } from "@/components/assistant-ui/elements/thread.aui";
103
-
104
- export const Assistant = () => {
105
- const runtime = useChatRuntime({
106
- api: "/api/chat",
107
- });
108
-
109
- return (
110
- <AssistantRuntimeProvider runtime={runtime}>
111
- <Thread />
112
- </AssistantRuntimeProvider>
113
- );
114
- };
115
- ```
116
-
117
- ## Step 4: Tools (optional)
118
-
119
- To add tool calling support, define tools on the backend and render them on the frontend:
120
-
121
- ### Backend tool (AI SDK):
122
-
123
- ```ts
124
- import { streamText, tool } from "ai";
125
- import { z } from "zod";
126
-
127
- const result = streamText({
128
- model: openai("gpt-5.6-luna"),
129
- messages,
130
- tools: {
131
- get_weather: tool({
132
- description: "Get weather for a location",
133
- parameters: z.object({
134
- location: z.string(),
135
- }),
136
- execute: async ({ location }) => {
137
- return { temperature: 72, condition: "sunny", location };
138
- },
139
- }),
140
- },
141
- });
142
- ```
143
-
144
- ### Frontend tool UI:
145
-
146
- Create a toolkit with a renderer for the tool. The tool executes on the backend, so the entry uses `externalTool()` and only attaches UI:
147
-
148
- ```tsx
149
- // app/toolkit.tsx
150
- "use generative";
151
-
152
- import { defineToolkit, externalTool } from "@assistant-ui/react";
153
-
154
- export default defineToolkit({
155
- get_weather: {
156
- execute: externalTool(),
157
- render: ({ args, result }) => {
158
- return (
159
- <div>
160
- <p>Weather for {args?.location}</p>
161
- {result && <p>{result.temperature}F, {result.condition}</p>}
162
- </div>
163
- );
164
- },
165
- },
166
- });
167
- ```
168
-
169
- Register the toolkit in your assistant component:
170
-
171
- ```tsx
172
- import { AssistantRuntimeProvider, Tools, useAui } from "@assistant-ui/react";
173
- import toolkit from "@/app/toolkit";
174
-
175
- const aui = useAui({
176
- tools: Tools({ toolkit }),
177
- });
178
-
179
- <AssistantRuntimeProvider aui={aui} runtime={runtime}>
180
- <Thread />
181
- </AssistantRuntimeProvider>
182
- ```
183
-
184
- Do not use `makeAssistantToolUI`, `useAssistantToolUI`, `makeAssistantTool`, or `useAssistantTool`; they are deprecated in favor of toolkits.
185
-
186
- ## Key Packages
187
-
188
- | Package | Purpose |
189
- |---------|---------|
190
- | `@assistant-ui/react` | Core React components and primitives |
191
- | `@assistant-ui/ai-sdk` | Vercel AI SDK integration |
192
- | `@assistant-ui/react-markdown` | Markdown rendering |
193
- | `@assistant-ui/react-syntax-highlighter` | Code highlighting |
194
- | `@assistant-ui/ui` | Pre-built shadcn/ui component set |
195
- | `@assistant-ui/styles` | Pre-built CSS for non-Tailwind users |
196
-
197
- ## Environment Variables
198
-
199
- For OpenAI-based setups, ensure `OPENAI_API_KEY` is set in `.env.local`.
@@ -1,22 +0,0 @@
1
- {
2
- "version": "1.0.0",
3
- "keywords": [
4
- "assistant-ui",
5
- "chat",
6
- "ai",
7
- "chatbot",
8
- "react",
9
- "ai-sdk",
10
- "shadcn",
11
- "thread",
12
- "copilot",
13
- "assistant"
14
- ],
15
- "categories": ["frameworks", "ai-ml"],
16
- "author": {
17
- "name": "Yonom",
18
- "github": "Yonom"
19
- },
20
- "repository": "https://github.com/assistant-ui/assistant-ui",
21
- "homepage": "https://www.assistant-ui.com"
22
- }