@assistant-ui/mcp-docs-server 0.1.31 → 0.1.33
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/.docs/organized/code-examples/waterfall.md +23 -25
- package/.docs/organized/code-examples/with-a2a.md +28 -28
- package/.docs/organized/code-examples/with-ag-ui.md +35 -30
- package/.docs/organized/code-examples/with-ai-sdk-v6.md +16 -14
- package/.docs/organized/code-examples/with-artifacts.md +44 -38
- package/.docs/organized/code-examples/with-assistant-transport.md +68 -56
- package/.docs/organized/code-examples/with-browser-extension.md +357 -0
- package/.docs/organized/code-examples/with-chain-of-thought.md +425 -90
- package/.docs/organized/code-examples/with-cloud-standalone.md +30 -29
- package/.docs/organized/code-examples/with-cloud.md +16 -14
- package/.docs/organized/code-examples/with-custom-thread-list.md +19 -17
- package/.docs/organized/code-examples/with-elevenlabs-conversational.md +29 -26
- package/.docs/organized/code-examples/with-elevenlabs-scribe.md +20 -18
- package/.docs/organized/code-examples/with-expo.md +37 -31
- package/.docs/organized/code-examples/with-external-store.md +13 -11
- package/.docs/organized/code-examples/with-ffmpeg.md +327 -290
- package/.docs/organized/code-examples/with-generative-ui.md +1195 -194
- package/.docs/organized/code-examples/with-google-adk.md +13 -11
- package/.docs/organized/code-examples/with-heat-graph.md +16 -14
- package/.docs/organized/code-examples/with-image-generation.md +456 -0
- package/.docs/organized/code-examples/with-interactables.md +322 -244
- package/.docs/organized/code-examples/with-langchain.md +18 -16
- package/.docs/organized/code-examples/with-langgraph.md +95 -88
- package/.docs/organized/code-examples/with-livekit.md +30 -27
- package/.docs/organized/code-examples/with-mcp.md +754 -0
- package/.docs/organized/code-examples/with-opencode.md +70 -70
- package/.docs/organized/code-examples/with-react-hook-form.md +24 -22
- package/.docs/organized/code-examples/with-react-ink.md +4 -4
- package/.docs/organized/code-examples/with-react-router.md +20 -18
- package/.docs/organized/code-examples/with-resumable-stream.md +661 -0
- package/.docs/organized/code-examples/with-store.md +22 -20
- package/.docs/organized/code-examples/with-tanstack.md +18 -19
- package/.docs/organized/code-examples/with-tap-runtime.md +31 -28
- package/.docs/raw/docs/(docs)/architecture.mdx +43 -2
- package/.docs/raw/docs/(docs)/cli.mdx +2 -2
- package/.docs/raw/docs/(docs)/copilots/model-context.mdx +34 -26
- package/.docs/raw/docs/(docs)/copilots/motivation.mdx +32 -26
- package/.docs/raw/docs/(docs)/index.mdx +2 -2
- package/.docs/raw/docs/(docs)/installation.mdx +2 -2
- package/.docs/raw/docs/(reference)/api-reference/adapters/attachments.mdx +26 -24
- package/.docs/raw/docs/(reference)/api-reference/adapters/feedback.mdx +20 -0
- package/.docs/raw/docs/(reference)/api-reference/adapters/index.mdx +20 -12
- package/.docs/raw/docs/(reference)/api-reference/adapters/model.mdx +44 -0
- package/.docs/raw/docs/(reference)/api-reference/adapters/persistence.mdx +41 -16
- package/.docs/raw/docs/(reference)/api-reference/adapters/runtime.mdx +12 -23
- package/.docs/raw/docs/(reference)/api-reference/adapters/suggestions.mdx +20 -0
- package/.docs/raw/docs/(reference)/api-reference/context-providers/assistant-runtime-provider.mdx +37 -8
- package/.docs/raw/docs/(reference)/api-reference/context-providers/index.mdx +11 -9
- package/.docs/raw/docs/(reference)/api-reference/context-providers/scoped-providers.mdx +64 -0
- package/.docs/raw/docs/(reference)/api-reference/external-store/index.mdx +22 -0
- package/.docs/raw/docs/(reference)/api-reference/external-store/message-conversion.mdx +52 -0
- package/.docs/raw/docs/(reference)/api-reference/external-store/runtime.mdx +46 -0
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/index.mdx +18 -0
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/rendering.mdx +33 -0
- package/.docs/raw/docs/(reference)/api-reference/generative-ui/spec.mdx +45 -0
- package/.docs/raw/docs/(reference)/api-reference/hooks/composer-triggers.mdx +98 -0
- package/.docs/raw/docs/(reference)/api-reference/hooks/index.mdx +18 -13
- package/.docs/raw/docs/(reference)/api-reference/hooks/model-context.mdx +31 -52
- package/.docs/raw/docs/(reference)/api-reference/hooks/primitives.mdx +640 -0
- package/.docs/raw/docs/(reference)/api-reference/hooks/runtimes.mdx +15 -28
- package/.docs/raw/docs/(reference)/api-reference/hooks/state.mdx +64 -18
- package/.docs/raw/docs/(reference)/api-reference/hooks/utilities.mdx +434 -0
- package/.docs/raw/docs/(reference)/api-reference/integrations/cloud-ai-sdk.mdx +24 -0
- package/.docs/raw/docs/(reference)/api-reference/integrations/index.mdx +11 -12
- package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +109 -0
- package/.docs/raw/docs/(reference)/api-reference/model-context/context.mdx +58 -0
- package/.docs/raw/docs/(reference)/api-reference/model-context/index.mdx +22 -0
- package/.docs/raw/docs/(reference)/api-reference/model-context/registry.mdx +20 -0
- package/.docs/raw/docs/(reference)/api-reference/overview.mdx +109 -132
- package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar-more.mdx +78 -221
- package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar.mdx +127 -242
- package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-if.mdx +39 -20
- package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-modal.mdx +66 -87
- package/.docs/raw/docs/(reference)/api-reference/primitives/attachment.mdx +50 -58
- package/.docs/raw/docs/(reference)/api-reference/primitives/branch-picker.mdx +80 -48
- package/.docs/raw/docs/(reference)/api-reference/primitives/chain-of-thought.mdx +67 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/composer.mdx +323 -461
- package/.docs/raw/docs/(reference)/api-reference/primitives/error.mdx +36 -43
- package/.docs/raw/docs/(reference)/api-reference/primitives/index.mdx +29 -24
- package/.docs/raw/docs/(reference)/api-reference/primitives/message-part.mdx +63 -245
- package/.docs/raw/docs/(reference)/api-reference/primitives/message.mdx +202 -588
- package/.docs/raw/docs/(reference)/api-reference/primitives/queue-item.mdx +65 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/selection-toolbar.mdx +35 -22
- package/.docs/raw/docs/(reference)/api-reference/primitives/suggestion.mdx +57 -140
- package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list-item-more.mdx +70 -161
- package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list-item.mdx +84 -108
- package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list.mdx +77 -96
- package/.docs/raw/docs/(reference)/api-reference/primitives/thread.mdx +173 -349
- package/.docs/raw/docs/(reference)/api-reference/runtimes/assistant-runtime.mdx +9 -21
- package/.docs/raw/docs/(reference)/api-reference/runtimes/attachment-runtime.mdx +10 -21
- package/.docs/raw/docs/(reference)/api-reference/runtimes/composer-runtime.mdx +15 -70
- package/.docs/raw/docs/(reference)/api-reference/runtimes/index.mdx +18 -13
- package/.docs/raw/docs/(reference)/api-reference/runtimes/message-part-runtime.mdx +25 -28
- package/.docs/raw/docs/(reference)/api-reference/runtimes/message-runtime.mdx +11 -63
- package/.docs/raw/docs/(reference)/api-reference/runtimes/queue-state.mdx +20 -0
- package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-item-runtime.mdx +11 -48
- package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-runtime.mdx +10 -47
- package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-runtime.mdx +18 -30
- package/.docs/raw/docs/(reference)/api-reference/tools/component-tools.mdx +85 -0
- package/.docs/raw/docs/(reference)/api-reference/tools/index.mdx +39 -0
- package/.docs/raw/docs/(reference)/api-reference/tools/rendering.mdx +127 -0
- package/.docs/raw/docs/(reference)/api-reference/tools/status.mdx +42 -0
- package/.docs/raw/docs/(reference)/api-reference/tools/toolkits.mdx +91 -0
- package/.docs/raw/docs/(reference)/api-reference/transport/assistant-transport.mdx +48 -0
- package/.docs/raw/docs/(reference)/api-reference/transport/frame.mdx +62 -0
- package/.docs/raw/docs/(reference)/api-reference/transport/index.mdx +22 -0
- package/.docs/raw/docs/(reference)/api-reference/utilities/index.mdx +19 -0
- package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +143 -0
- package/.docs/raw/docs/(reference)/api-reference/voice/index.mdx +22 -0
- package/.docs/raw/docs/(reference)/api-reference/voice/session.mdx +54 -0
- package/.docs/raw/docs/(reference)/api-reference/voice/speech-dictation.mdx +36 -0
- package/.docs/raw/docs/cloud/ai-sdk.mdx +1 -3
- package/.docs/raw/docs/cloud/index.mdx +2 -2
- package/.docs/raw/docs/guides/attachments.mdx +4 -4
- package/.docs/raw/docs/guides/branching.mdx +1 -1
- package/.docs/raw/docs/guides/chain-of-thought.mdx +12 -14
- package/.docs/raw/docs/guides/context-api.mdx +6 -5
- package/.docs/raw/docs/guides/dictation.mdx +2 -2
- package/.docs/raw/docs/guides/editing.mdx +1 -1
- package/.docs/raw/docs/guides/image-generation.mdx +74 -0
- package/.docs/raw/docs/guides/index.mdx +5 -14
- package/.docs/raw/docs/guides/latex.mdx +2 -2
- package/.docs/raw/docs/guides/mentions.mdx +6 -6
- package/.docs/raw/docs/guides/message-timing.mdx +38 -5
- package/.docs/raw/docs/guides/quoting.mdx +1 -1
- package/.docs/raw/docs/guides/resumable-stream-deployment.mdx +212 -0
- package/.docs/raw/docs/guides/resumable-stream-stores.mdx +152 -0
- package/.docs/raw/docs/guides/resumable-streams.mdx +210 -0
- package/.docs/raw/docs/guides/slash-commands.mdx +2 -2
- package/.docs/raw/docs/guides/speech.mdx +2 -2
- package/.docs/raw/docs/guides/suggestions.mdx +89 -5
- package/.docs/raw/docs/guides/voice.mdx +2 -2
- package/.docs/raw/docs/ink/adapters.mdx +37 -1
- package/.docs/raw/docs/ink/custom-backend.mdx +59 -8
- package/.docs/raw/docs/ink/hooks.mdx +98 -85
- package/.docs/raw/docs/ink/index.mdx +10 -11
- package/.docs/raw/docs/ink/migration.mdx +1 -1
- package/.docs/raw/docs/ink/primitives.mdx +350 -9
- package/.docs/raw/docs/integrations/auth/better-auth.mdx +1 -1
- package/.docs/raw/docs/integrations/auth/clerk.mdx +1 -1
- package/.docs/raw/docs/integrations/auth/next-auth.mdx +1 -1
- package/.docs/raw/docs/integrations/frameworks/ai-sdk.mdx +3 -3
- package/.docs/raw/docs/integrations/frameworks/cloudflare-agents/overview.mdx +282 -0
- package/.docs/raw/docs/integrations/frameworks/mastra/overview.mdx +2 -2
- package/.docs/raw/docs/integrations/frameworks/mastra/separate-server.mdx +1 -1
- package/.docs/raw/docs/integrations/gateways/index.mdx +9 -4
- package/.docs/raw/docs/integrations/index.mdx +17 -5
- package/.docs/raw/docs/integrations/observability/langfuse.mdx +8 -1
- package/.docs/raw/docs/integrations/observability/langsmith.mdx +8 -4
- package/.docs/raw/docs/integrations/persistence/custom-adapter.mdx +17 -2
- package/.docs/raw/docs/migrations/toolkit-tools.mdx +226 -0
- package/.docs/raw/docs/primitives/chain-of-thought.mdx +10 -16
- package/.docs/raw/docs/primitives/composer.mdx +53 -0
- package/.docs/raw/docs/primitives/index.mdx +2 -2
- package/.docs/raw/docs/primitives/message.mdx +9 -10
- package/.docs/raw/docs/primitives/suggestion.mdx +9 -0
- package/.docs/raw/docs/react-native/hooks.mdx +57 -82
- package/.docs/raw/docs/react-native/index.mdx +4 -4
- package/.docs/raw/docs/react-native/migration.mdx +1 -1
- package/.docs/raw/docs/react-native/primitives.mdx +2 -2
- package/.docs/raw/docs/runtimes/a2a/client-and-hooks.mdx +7 -4
- package/.docs/raw/docs/runtimes/a2a/overview.mdx +2 -2
- package/.docs/raw/docs/runtimes/ag-ui/overview.mdx +2 -2
- package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +32 -1
- package/.docs/raw/docs/runtimes/ai-sdk/overview.mdx +2 -2
- package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +7 -2
- package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +1 -1
- package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +129 -1
- package/.docs/raw/docs/runtimes/concepts/threads.mdx +7 -1
- package/.docs/raw/docs/runtimes/custom/data-stream.mdx +27 -2
- package/.docs/raw/docs/runtimes/custom/external-store.mdx +9 -1
- package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +18 -7
- package/.docs/raw/docs/runtimes/custom/overview.mdx +2 -2
- package/.docs/raw/docs/runtimes/google-adk/hooks.mdx +108 -38
- package/.docs/raw/docs/runtimes/google-adk/overview.mdx +2 -2
- package/.docs/raw/docs/runtimes/langchain.mdx +2 -2
- package/.docs/raw/docs/runtimes/langgraph/generative-ui.mdx +2 -2
- package/.docs/raw/docs/runtimes/langgraph/overview.mdx +2 -2
- package/.docs/raw/docs/runtimes/langgraph/streaming.mdx +1 -1
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +64 -50
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +98 -86
- package/.docs/raw/docs/runtimes/opencode/overview.mdx +2 -2
- package/.docs/raw/docs/runtimes/opencode/quickstart.mdx +1 -1
- package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +12 -5
- package/.docs/raw/docs/tools/backend.mdx +136 -0
- package/.docs/raw/docs/tools/defining-tools.mdx +413 -0
- package/.docs/raw/docs/tools/dynamic-tools.mdx +110 -0
- package/.docs/raw/docs/tools/generative-ui.mdx +214 -0
- package/.docs/raw/docs/tools/index.mdx +76 -0
- package/.docs/raw/docs/{guides → tools}/interactables.mdx +3 -3
- package/.docs/raw/docs/tools/mcp-apps.mdx +231 -0
- package/.docs/raw/docs/{integrations/tools → tools}/mcp.mdx +77 -50
- package/.docs/raw/docs/{guides → tools}/multi-agent.mdx +19 -21
- package/.docs/raw/docs/tools/tool-ui.mdx +967 -0
- package/.docs/raw/docs/tools/user-managed-mcp.mdx +337 -0
- package/.docs/raw/docs/ui/composer-trigger-popover.mdx +11 -1
- package/.docs/raw/docs/ui/directive-text.mdx +3 -3
- package/.docs/raw/docs/ui/mcp-config.mdx +102 -0
- package/.docs/raw/docs/ui/model-selector.mdx +8 -8
- package/.docs/raw/docs/ui/part-grouping.mdx +84 -50
- package/.docs/raw/docs/ui/reasoning.mdx +4 -5
- package/.docs/raw/docs/ui/sources.mdx +17 -0
- package/.docs/raw/docs/ui/streamdown.mdx +34 -2
- package/.docs/raw/docs/ui/thread-list.mdx +2 -2
- package/.docs/raw/docs/ui/thread.mdx +2 -2
- package/.docs/raw/docs/ui/tool-group.mdx +5 -6
- package/.docs/raw/docs/utilities/heat-graph.mdx +1 -1
- package/README.md +14 -72
- package/dist/constants.d.ts +12 -9
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +13 -9
- package/dist/constants.js.map +1 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -24
- package/dist/index.js.map +1 -1
- package/dist/prepare-docs/code-examples.d.ts +4 -1
- package/dist/prepare-docs/code-examples.d.ts.map +1 -1
- package/dist/prepare-docs/code-examples.js +109 -121
- package/dist/prepare-docs/code-examples.js.map +1 -1
- package/dist/prepare-docs/copy-raw.d.ts +4 -1
- package/dist/prepare-docs/copy-raw.d.ts.map +1 -1
- package/dist/prepare-docs/copy-raw.js +45 -42
- package/dist/prepare-docs/copy-raw.js.map +1 -1
- package/dist/prepare-docs/prepare.d.ts +1 -2
- package/dist/prepare-docs/prepare.js +17 -17
- package/dist/prepare-docs/prepare.js.map +1 -1
- package/dist/stdio.d.ts +1 -3
- package/dist/stdio.js +6 -3
- package/dist/stdio.js.map +1 -1
- package/dist/tools/docs.d.ts +20 -15
- package/dist/tools/docs.d.ts.map +1 -1
- package/dist/tools/docs.js +140 -161
- package/dist/tools/docs.js.map +1 -1
- package/dist/tools/examples.d.ts +20 -15
- package/dist/tools/examples.d.ts.map +1 -1
- package/dist/tools/examples.js +74 -86
- package/dist/tools/examples.js.map +1 -1
- package/dist/tools/tests/test-setup.d.ts +5 -2
- package/dist/tools/tests/test-setup.d.ts.map +1 -1
- package/dist/tools/tests/test-setup.js +21 -28
- package/dist/tools/tests/test-setup.js.map +1 -1
- package/dist/utils/logger.d.ts +8 -5
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +17 -17
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/mcp-format.d.ts +8 -5
- package/dist/utils/mcp-format.d.ts.map +1 -1
- package/dist/utils/mcp-format.js +9 -9
- package/dist/utils/mcp-format.js.map +1 -1
- package/dist/utils/mdx.d.ts +8 -6
- package/dist/utils/mdx.d.ts.map +1 -1
- package/dist/utils/mdx.js +22 -22
- package/dist/utils/mdx.js.map +1 -1
- package/dist/utils/paths.d.ts +9 -6
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +66 -76
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/security.d.ts +4 -1
- package/dist/utils/security.d.ts.map +1 -1
- package/dist/utils/security.js +19 -40
- package/dist/utils/security.js.map +1 -1
- package/package.json +5 -5
- package/.docs/organized/code-examples/with-parent-id-grouping.md +0 -596
- package/.docs/raw/docs/(docs)/copilots/make-assistant-tool-ui.mdx +0 -151
- package/.docs/raw/docs/(docs)/copilots/make-assistant-tool.mdx +0 -230
- package/.docs/raw/docs/(reference)/api-reference/adapters/feedback-speech.mdx +0 -41
- package/.docs/raw/docs/(reference)/api-reference/context-providers/text-message-part-provider.mdx +0 -40
- package/.docs/raw/docs/(reference)/api-reference/integrations/react-data-stream.mdx +0 -260
- package/.docs/raw/docs/(reference)/api-reference/integrations/react-hook-form.mdx +0 -103
- package/.docs/raw/docs/(reference)/api-reference/integrations/vercel-ai-sdk.mdx +0 -254
- package/.docs/raw/docs/guides/tool-ui.mdx +0 -858
- package/.docs/raw/docs/guides/tools.mdx +0 -675
- package/dist/prepare-docs/prepare.d.ts.map +0 -1
- package/dist/stdio.d.ts.map +0 -1
- /package/.docs/raw/docs/{(reference)/migrations → migrations}/deprecation-policy.mdx +0 -0
- /package/.docs/raw/docs/{(reference) → migrations}/react-compatibility.mdx +0 -0
- /package/.docs/raw/docs/{(reference)/migrations → migrations}/react-langgraph-v0-7.mdx +0 -0
- /package/.docs/raw/docs/{(reference)/migrations → migrations}/v0-11.mdx +0 -0
- /package/.docs/raw/docs/{(reference)/migrations → migrations}/v0-12.mdx +0 -0
- /package/.docs/raw/docs/{(reference)/migrations → migrations}/v0-14.mdx +0 -0
|
@@ -3,46 +3,132 @@
|
|
|
3
3
|
## app/api/chat/route.ts
|
|
4
4
|
|
|
5
5
|
```typescript
|
|
6
|
-
import {
|
|
6
|
+
import { createOpenAI } from "@ai-sdk/openai";
|
|
7
7
|
import {
|
|
8
8
|
streamText,
|
|
9
9
|
convertToModelMessages,
|
|
10
10
|
tool,
|
|
11
11
|
stepCountIs,
|
|
12
12
|
zodSchema,
|
|
13
|
+
createUIMessageStream,
|
|
14
|
+
JsonToSseTransformStream,
|
|
13
15
|
} from "ai";
|
|
14
|
-
import type { UIMessage } from "ai";
|
|
16
|
+
import type { UIMessage, UIMessageStreamWriter } from "ai";
|
|
15
17
|
import { frontendTools } from "@assistant-ui/react-ai-sdk";
|
|
16
18
|
import { z } from "zod";
|
|
17
19
|
|
|
18
20
|
export const maxDuration = 30;
|
|
19
21
|
|
|
22
|
+
type SearchResult = { id: string; url: string; title: string };
|
|
23
|
+
|
|
24
|
+
const searchSources = (query: string): SearchResult[] => {
|
|
25
|
+
const q = query.toLowerCase();
|
|
26
|
+
if (q.includes("fibonacci")) {
|
|
27
|
+
return [
|
|
28
|
+
{
|
|
29
|
+
id: "src-fib-1",
|
|
30
|
+
url: "https://en.wikipedia.org/wiki/Fibonacci_number",
|
|
31
|
+
title: "Fibonacci number (Wikipedia)",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: "src-fib-2",
|
|
35
|
+
url: "https://oeis.org/A000045",
|
|
36
|
+
title: "OEIS A000045: Fibonacci numbers",
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
}
|
|
40
|
+
if (
|
|
41
|
+
q.includes("renewable") ||
|
|
42
|
+
q.includes("energy") ||
|
|
43
|
+
q.includes("climate")
|
|
44
|
+
) {
|
|
45
|
+
return [
|
|
46
|
+
{
|
|
47
|
+
id: "src-iea",
|
|
48
|
+
url: "https://www.iea.org/reports/renewables-2024",
|
|
49
|
+
title: "IEA Renewables 2024",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: "src-irena",
|
|
53
|
+
url: "https://www.irena.org/Publications",
|
|
54
|
+
title: "IRENA Publications",
|
|
55
|
+
},
|
|
56
|
+
];
|
|
57
|
+
}
|
|
58
|
+
return [
|
|
59
|
+
{
|
|
60
|
+
id: "src-wiki",
|
|
61
|
+
url: `https://en.wikipedia.org/wiki/Special:Search?search=${encodeURIComponent(query)}`,
|
|
62
|
+
title: "Wikipedia search results",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: "src-web",
|
|
66
|
+
url: `https://duckduckgo.com/?q=${encodeURIComponent(query)}`,
|
|
67
|
+
title: `Web results for: ${query}`,
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
};
|
|
71
|
+
|
|
20
72
|
export async function POST(req: Request) {
|
|
21
73
|
const {
|
|
22
74
|
messages,
|
|
23
75
|
tools,
|
|
24
76
|
}: { messages: UIMessage[]; tools: Record<string, any> } = await req.json();
|
|
25
77
|
|
|
78
|
+
const uiStream = createUIMessageStream({
|
|
79
|
+
execute: async ({ writer }) => {
|
|
80
|
+
if (!process.env.OPENAI_API_KEY) {
|
|
81
|
+
await streamFallback(writer, messages);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
await streamModel(writer, messages, tools);
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
return new Response(
|
|
89
|
+
uiStream
|
|
90
|
+
.pipeThrough(new JsonToSseTransformStream())
|
|
91
|
+
.pipeThrough(new TextEncoderStream()),
|
|
92
|
+
{
|
|
93
|
+
headers: {
|
|
94
|
+
"Content-Type": "text/event-stream",
|
|
95
|
+
"Cache-Control": "no-cache",
|
|
96
|
+
Connection: "keep-alive",
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function streamModel(
|
|
103
|
+
writer: UIMessageStreamWriter,
|
|
104
|
+
messages: UIMessage[],
|
|
105
|
+
frontendToolDefs: Record<string, any>,
|
|
106
|
+
) {
|
|
107
|
+
const toolNameByCall = new Map<string, string>();
|
|
108
|
+
const openai = createOpenAI({
|
|
109
|
+
apiKey: process.env.OPENAI_API_KEY!,
|
|
110
|
+
...(process.env.OPENAI_BASE_URL && {
|
|
111
|
+
baseURL: process.env.OPENAI_BASE_URL,
|
|
112
|
+
}),
|
|
113
|
+
});
|
|
114
|
+
|
|
26
115
|
const result = streamText({
|
|
27
|
-
//
|
|
116
|
+
// Reasoning model so the chain-of-thought group has real content.
|
|
28
117
|
model: openai("gpt-5.4-mini"),
|
|
29
118
|
messages: await convertToModelMessages(messages),
|
|
30
119
|
stopWhen: stepCountIs(10),
|
|
31
120
|
tools: {
|
|
32
|
-
|
|
33
|
-
...frontendTools(tools),
|
|
34
|
-
|
|
35
|
-
// Backend-only tools
|
|
121
|
+
...frontendTools(frontendToolDefs),
|
|
36
122
|
get_current_weather: tool({
|
|
37
123
|
description: "Get the current weather for a city",
|
|
38
|
-
inputSchema: zodSchema(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
},
|
|
124
|
+
inputSchema: zodSchema(z.object({ city: z.string() })),
|
|
125
|
+
execute: async ({ city }) => `The weather in ${city} is sunny, 72°F`,
|
|
126
|
+
}),
|
|
127
|
+
search_web: tool({
|
|
128
|
+
description:
|
|
129
|
+
"Search the web for citations on a topic. Returns a list of source URLs and titles the assistant should consult.",
|
|
130
|
+
inputSchema: zodSchema(z.object({ query: z.string() })),
|
|
131
|
+
execute: async ({ query }) => ({ sources: searchSources(query) }),
|
|
46
132
|
}),
|
|
47
133
|
},
|
|
48
134
|
providerOptions: {
|
|
@@ -53,9 +139,106 @@ export async function POST(req: Request) {
|
|
|
53
139
|
},
|
|
54
140
|
});
|
|
55
141
|
|
|
56
|
-
|
|
142
|
+
// Forward model chunks, then synthesize `source-url` parts from each
|
|
143
|
+
// `search_web` tool output so the UI renders them as `source` parts. OpenAI
|
|
144
|
+
// does not natively emit `source-url` chunks without web search, so the route
|
|
145
|
+
// produces them deterministically from the tool result.
|
|
146
|
+
for await (const chunk of result.toUIMessageStream({ sendReasoning: true })) {
|
|
147
|
+
writer.write(chunk);
|
|
148
|
+
|
|
149
|
+
if (chunk.type === "tool-input-available") {
|
|
150
|
+
toolNameByCall.set(chunk.toolCallId, chunk.toolName);
|
|
151
|
+
}
|
|
152
|
+
if (
|
|
153
|
+
chunk.type === "tool-output-available" &&
|
|
154
|
+
toolNameByCall.get(chunk.toolCallId) === "search_web"
|
|
155
|
+
) {
|
|
156
|
+
const output = chunk.output as { sources?: SearchResult[] };
|
|
157
|
+
for (const source of output.sources ?? []) {
|
|
158
|
+
writer.write({
|
|
159
|
+
type: "source-url",
|
|
160
|
+
sourceId: `${source.id}-${crypto.randomUUID()}`,
|
|
161
|
+
url: source.url,
|
|
162
|
+
title: source.title,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function streamFallback(
|
|
170
|
+
writer: UIMessageStreamWriter,
|
|
171
|
+
messages: UIMessage[],
|
|
172
|
+
) {
|
|
173
|
+
const lastUserText =
|
|
174
|
+
messages
|
|
175
|
+
.filter((m) => m.role === "user")
|
|
176
|
+
.at(-1)
|
|
177
|
+
?.parts.flatMap((p) => (p.type === "text" ? [p.text] : []))
|
|
178
|
+
.join(" ") ?? "your question";
|
|
179
|
+
|
|
180
|
+
const messageId = `msg-${crypto.randomUUID()}`;
|
|
181
|
+
const reasoningId = `r-${crypto.randomUUID()}`;
|
|
182
|
+
const textId = `t-${crypto.randomUUID()}`;
|
|
183
|
+
const toolCallId = `call-${crypto.randomUUID()}`;
|
|
184
|
+
const sources = searchSources(lastUserText);
|
|
185
|
+
|
|
186
|
+
writer.write({ type: "start", messageId });
|
|
187
|
+
writer.write({ type: "start-step" });
|
|
188
|
+
|
|
189
|
+
writer.write({ type: "reasoning-start", id: reasoningId });
|
|
190
|
+
const reasoning =
|
|
191
|
+
"OPENAI_API_KEY is not set, so this response is generated deterministically by the route. " +
|
|
192
|
+
"A real run emits the same chunk shapes from the model: a reasoning step, " +
|
|
193
|
+
"a search_web tool call, source-url parts derived from the tool output, " +
|
|
194
|
+
"and a final answer that summarises the sources.";
|
|
195
|
+
for (const word of reasoning.match(/\S+\s*|\s+/g) ?? [reasoning]) {
|
|
196
|
+
writer.write({ type: "reasoning-delta", id: reasoningId, delta: word });
|
|
197
|
+
await sleep(15);
|
|
198
|
+
}
|
|
199
|
+
writer.write({ type: "reasoning-end", id: reasoningId });
|
|
200
|
+
|
|
201
|
+
writer.write({
|
|
202
|
+
type: "tool-input-available",
|
|
203
|
+
toolCallId,
|
|
204
|
+
toolName: "search_web",
|
|
205
|
+
input: { query: lastUserText },
|
|
206
|
+
});
|
|
207
|
+
await sleep(150);
|
|
208
|
+
writer.write({
|
|
209
|
+
type: "tool-output-available",
|
|
210
|
+
toolCallId,
|
|
211
|
+
output: { sources },
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
for (const source of sources) {
|
|
215
|
+
writer.write({
|
|
216
|
+
type: "source-url",
|
|
217
|
+
sourceId: `${source.id}-${crypto.randomUUID()}`,
|
|
218
|
+
url: source.url,
|
|
219
|
+
title: source.title,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
writer.write({ type: "text-start", id: textId });
|
|
224
|
+
const answer =
|
|
225
|
+
`\n\nMock answer for: "${lastUserText}". ` +
|
|
226
|
+
"The badges above are structured `source` message parts emitted as " +
|
|
227
|
+
"`source-url` chunks, not URLs scraped from text. Set `OPENAI_API_KEY` " +
|
|
228
|
+
"to switch to a real model run.";
|
|
229
|
+
for (const word of answer.match(/\S+\s*|\s+/g) ?? [answer]) {
|
|
230
|
+
writer.write({ type: "text-delta", id: textId, delta: word });
|
|
231
|
+
await sleep(15);
|
|
232
|
+
}
|
|
233
|
+
writer.write({ type: "text-end", id: textId });
|
|
234
|
+
|
|
235
|
+
writer.write({ type: "finish-step" });
|
|
236
|
+
writer.write({ type: "finish" });
|
|
57
237
|
}
|
|
58
238
|
|
|
239
|
+
const sleep = (ms: number) =>
|
|
240
|
+
new Promise<void>((resolve) => setTimeout(resolve, ms));
|
|
241
|
+
|
|
59
242
|
```
|
|
60
243
|
|
|
61
244
|
## app/globals.css
|
|
@@ -194,7 +377,8 @@ import "./globals.css";
|
|
|
194
377
|
|
|
195
378
|
export const metadata: Metadata = {
|
|
196
379
|
title: "Chain of Thought Example",
|
|
197
|
-
description:
|
|
380
|
+
description:
|
|
381
|
+
"Reasoning, tool calls, and source citations rendered as grouped message parts.",
|
|
198
382
|
};
|
|
199
383
|
|
|
200
384
|
export default function RootLayout({
|
|
@@ -218,13 +402,16 @@ export default function RootLayout({
|
|
|
218
402
|
|
|
219
403
|
import { type FC, type PropsWithChildren, useState } from "react";
|
|
220
404
|
import { MarkdownText } from "@/components/assistant-ui/markdown-text";
|
|
405
|
+
import { Sources } from "@/components/assistant-ui/sources";
|
|
221
406
|
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
|
|
222
407
|
import { Button } from "@/components/ui/button";
|
|
223
408
|
import {
|
|
224
|
-
AuiIf,
|
|
225
409
|
ComposerPrimitive,
|
|
410
|
+
groupPartByType,
|
|
226
411
|
MessagePrimitive,
|
|
412
|
+
SuggestionPrimitive,
|
|
227
413
|
ThreadPrimitive,
|
|
414
|
+
useThread,
|
|
228
415
|
} from "@assistant-ui/react";
|
|
229
416
|
import {
|
|
230
417
|
ArrowDownIcon,
|
|
@@ -237,10 +424,14 @@ import {
|
|
|
237
424
|
export const MyThread: FC = () => {
|
|
238
425
|
return (
|
|
239
426
|
<ThreadPrimitive.Root
|
|
240
|
-
className="flex h-full flex-col
|
|
427
|
+
className="bg-background flex h-full flex-col"
|
|
241
428
|
style={{ ["--thread-max-width" as string]: "44rem" }}
|
|
242
429
|
>
|
|
243
430
|
<ThreadPrimitive.Viewport className="flex flex-1 flex-col overflow-y-scroll scroll-smooth px-4 pt-8">
|
|
431
|
+
<ThreadPrimitive.Empty>
|
|
432
|
+
<ThreadWelcome />
|
|
433
|
+
</ThreadPrimitive.Empty>
|
|
434
|
+
|
|
244
435
|
<ThreadPrimitive.Messages>
|
|
245
436
|
{({ message }) => {
|
|
246
437
|
if (message.role === "user") return <UserMessage />;
|
|
@@ -269,11 +460,43 @@ const Text: FC<{ text: string }> = ({ text }) => {
|
|
|
269
460
|
return <p>{text}</p>;
|
|
270
461
|
};
|
|
271
462
|
|
|
463
|
+
const ThreadWelcome: FC = () => {
|
|
464
|
+
return (
|
|
465
|
+
<div className="mx-auto flex w-full max-w-(--thread-max-width) grow flex-col justify-center gap-6">
|
|
466
|
+
<div>
|
|
467
|
+
<h1 className="text-2xl font-semibold">
|
|
468
|
+
What should we think through?
|
|
469
|
+
</h1>
|
|
470
|
+
</div>
|
|
471
|
+
<div className="flex flex-wrap gap-2">
|
|
472
|
+
<ThreadPrimitive.Suggestions>
|
|
473
|
+
{() => <ThreadSuggestionItem />}
|
|
474
|
+
</ThreadPrimitive.Suggestions>
|
|
475
|
+
</div>
|
|
476
|
+
</div>
|
|
477
|
+
);
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
const ThreadSuggestionItem: FC = () => {
|
|
481
|
+
return (
|
|
482
|
+
<SuggestionPrimitive.Trigger send asChild>
|
|
483
|
+
<Button
|
|
484
|
+
type="button"
|
|
485
|
+
variant="ghost"
|
|
486
|
+
className="bg-background hover:bg-muted h-auto w-full flex-col items-start justify-start gap-1 rounded-2xl border px-4 py-3 text-start text-sm sm:w-[calc(50%-0.25rem)]"
|
|
487
|
+
>
|
|
488
|
+
<SuggestionPrimitive.Title className="font-medium" />
|
|
489
|
+
<SuggestionPrimitive.Description className="text-muted-foreground empty:hidden" />
|
|
490
|
+
</Button>
|
|
491
|
+
</SuggestionPrimitive.Trigger>
|
|
492
|
+
);
|
|
493
|
+
};
|
|
494
|
+
|
|
272
495
|
const UserMessage: FC = () => {
|
|
273
496
|
return (
|
|
274
497
|
<MessagePrimitive.Root className="mx-auto w-full max-w-(--thread-max-width) py-3">
|
|
275
498
|
<div className="flex justify-end">
|
|
276
|
-
<div className="max-w-[80%] rounded-2xl
|
|
499
|
+
<div className="bg-primary text-primary-foreground max-w-[80%] rounded-2xl px-4 py-2">
|
|
277
500
|
<MessagePrimitive.Parts>
|
|
278
501
|
{({ part }) => {
|
|
279
502
|
if (part.type === "text") return <Text {...part} />;
|
|
@@ -291,13 +514,11 @@ const AssistantMessage: FC = () => {
|
|
|
291
514
|
<MessagePrimitive.Root className="mx-auto w-full max-w-(--thread-max-width) py-3">
|
|
292
515
|
<div className="flex flex-col gap-2 px-2 leading-relaxed">
|
|
293
516
|
<MessagePrimitive.GroupedParts
|
|
294
|
-
groupBy={(
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
return null;
|
|
300
|
-
}}
|
|
517
|
+
groupBy={groupPartByType({
|
|
518
|
+
reasoning: ["group-chainOfThought", "group-reasoning"],
|
|
519
|
+
"tool-call": ["group-chainOfThought", "group-tool"],
|
|
520
|
+
source: ["group-sources"],
|
|
521
|
+
})}
|
|
301
522
|
>
|
|
302
523
|
{({ part, children }) => {
|
|
303
524
|
switch (part.type) {
|
|
@@ -309,12 +530,16 @@ const AssistantMessage: FC = () => {
|
|
|
309
530
|
return (
|
|
310
531
|
<PartLayout label="Taking action">{children}</PartLayout>
|
|
311
532
|
);
|
|
533
|
+
case "group-sources":
|
|
534
|
+
return <SourcesLayout>{children}</SourcesLayout>;
|
|
312
535
|
case "text":
|
|
313
536
|
return <MarkdownText />;
|
|
314
537
|
case "reasoning":
|
|
315
538
|
return <Reasoning {...part} />;
|
|
316
539
|
case "tool-call":
|
|
317
540
|
return <ToolCall {...part} />;
|
|
541
|
+
case "source":
|
|
542
|
+
return <Sources {...part} />;
|
|
318
543
|
default:
|
|
319
544
|
return null;
|
|
320
545
|
}
|
|
@@ -332,7 +557,7 @@ const ChainOfThoughtGroup: FC<PropsWithChildren> = ({ children }) => {
|
|
|
332
557
|
<div className="my-2 rounded-lg border">
|
|
333
558
|
<button
|
|
334
559
|
type="button"
|
|
335
|
-
className="flex w-full cursor-pointer items-center gap-2 px-4 py-2
|
|
560
|
+
className="hover:bg-muted/50 flex w-full cursor-pointer items-center gap-2 px-4 py-2 text-sm font-medium"
|
|
336
561
|
onClick={() => setOpen((o) => !o)}
|
|
337
562
|
>
|
|
338
563
|
{open ? (
|
|
@@ -347,6 +572,15 @@ const ChainOfThoughtGroup: FC<PropsWithChildren> = ({ children }) => {
|
|
|
347
572
|
);
|
|
348
573
|
};
|
|
349
574
|
|
|
575
|
+
const SourcesLayout: FC<PropsWithChildren> = ({ children }) => {
|
|
576
|
+
return (
|
|
577
|
+
<div className="flex flex-wrap items-center gap-1.5">
|
|
578
|
+
<span className="text-muted-foreground mr-1 text-xs">Sources</span>
|
|
579
|
+
{children}
|
|
580
|
+
</div>
|
|
581
|
+
);
|
|
582
|
+
};
|
|
583
|
+
|
|
350
584
|
const PartLayout: FC<PropsWithChildren<{ label: string }>> = ({
|
|
351
585
|
children,
|
|
352
586
|
label,
|
|
@@ -357,7 +591,7 @@ const PartLayout: FC<PropsWithChildren<{ label: string }>> = ({
|
|
|
357
591
|
<div className="border-t">
|
|
358
592
|
<button
|
|
359
593
|
type="button"
|
|
360
|
-
className="flex w-full cursor-pointer items-center gap-2 px-4 py-1.5 text-
|
|
594
|
+
className="text-muted-foreground hover:bg-muted/50 flex w-full cursor-pointer items-center gap-2 px-4 py-1.5 text-xs"
|
|
361
595
|
onClick={() => setOpen((o) => !o)}
|
|
362
596
|
>
|
|
363
597
|
{open ? (
|
|
@@ -374,7 +608,7 @@ const PartLayout: FC<PropsWithChildren<{ label: string }>> = ({
|
|
|
374
608
|
|
|
375
609
|
const Reasoning: FC<{ text: string }> = ({ text }) => {
|
|
376
610
|
return (
|
|
377
|
-
<p className="
|
|
611
|
+
<p className="text-muted-foreground px-4 py-2 text-sm whitespace-pre-wrap italic">
|
|
378
612
|
{text}
|
|
379
613
|
</p>
|
|
380
614
|
);
|
|
@@ -398,16 +632,18 @@ const ToolCall: FC<{ toolName: string; status: { type: string } }> = ({
|
|
|
398
632
|
};
|
|
399
633
|
|
|
400
634
|
const Composer: FC = () => {
|
|
635
|
+
const isRunning = useThread((state) => state.isRunning);
|
|
636
|
+
|
|
401
637
|
return (
|
|
402
|
-
<ComposerPrimitive.Root className="
|
|
638
|
+
<ComposerPrimitive.Root className="border-input bg-background has-[textarea:focus-visible]:border-ring has-[textarea:focus-visible]:ring-ring/20 flex w-full flex-col rounded-2xl border px-1 pt-2 transition-shadow outline-none has-[textarea:focus-visible]:ring-2">
|
|
403
639
|
<ComposerPrimitive.Input
|
|
404
640
|
placeholder="Send a message..."
|
|
405
|
-
className="mb-1 max-h-32 min-h-14 w-full resize-none bg-transparent px-4 pt-2 pb-3 text-sm outline-none
|
|
641
|
+
className="placeholder:text-muted-foreground mb-1 max-h-32 min-h-14 w-full resize-none bg-transparent px-4 pt-2 pb-3 text-sm outline-none"
|
|
406
642
|
rows={1}
|
|
407
643
|
autoFocus
|
|
408
644
|
/>
|
|
409
645
|
<div className="relative mx-2 mb-2 flex items-center justify-end">
|
|
410
|
-
|
|
646
|
+
{!isRunning ? (
|
|
411
647
|
<ComposerPrimitive.Send asChild>
|
|
412
648
|
<TooltipIconButton
|
|
413
649
|
tooltip="Send"
|
|
@@ -419,8 +655,7 @@ const Composer: FC = () => {
|
|
|
419
655
|
<ArrowUpIcon className="size-4" />
|
|
420
656
|
</TooltipIconButton>
|
|
421
657
|
</ComposerPrimitive.Send>
|
|
422
|
-
|
|
423
|
-
<AuiIf condition={(s) => s.thread.isRunning}>
|
|
658
|
+
) : (
|
|
424
659
|
<ComposerPrimitive.Cancel asChild>
|
|
425
660
|
<Button
|
|
426
661
|
type="button"
|
|
@@ -431,7 +666,7 @@ const Composer: FC = () => {
|
|
|
431
666
|
<SquareIcon className="size-3 fill-current" />
|
|
432
667
|
</Button>
|
|
433
668
|
</ComposerPrimitive.Cancel>
|
|
434
|
-
|
|
669
|
+
)}
|
|
435
670
|
</div>
|
|
436
671
|
</ComposerPrimitive.Root>
|
|
437
672
|
);
|
|
@@ -446,7 +681,8 @@ const Composer: FC = () => {
|
|
|
446
681
|
|
|
447
682
|
import {
|
|
448
683
|
AssistantRuntimeProvider,
|
|
449
|
-
|
|
684
|
+
Tools,
|
|
685
|
+
type Toolkit,
|
|
450
686
|
useAui,
|
|
451
687
|
AuiProvider,
|
|
452
688
|
Suggestions,
|
|
@@ -456,53 +692,56 @@ import { z } from "zod";
|
|
|
456
692
|
import { MyThread } from "./MyThread";
|
|
457
693
|
import { lastAssistantMessageIsCompleteWithApprovalResponses } from "ai";
|
|
458
694
|
|
|
459
|
-
const
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
695
|
+
const toolkit = {
|
|
696
|
+
execute_js: {
|
|
697
|
+
type: "frontend",
|
|
698
|
+
description: "Execute JavaScript code and return the result",
|
|
699
|
+
parameters: z.object({
|
|
700
|
+
code: z.string().describe("The JavaScript code to execute"),
|
|
701
|
+
}),
|
|
702
|
+
execute: async ({ code }) => {
|
|
703
|
+
try {
|
|
704
|
+
const result = eval(code);
|
|
705
|
+
return { success: true, result: String(result) };
|
|
706
|
+
} catch (e) {
|
|
707
|
+
return { success: false, error: String(e) };
|
|
708
|
+
}
|
|
709
|
+
},
|
|
710
|
+
render: ({ args, result, status }) => (
|
|
711
|
+
<div className="bg-muted/30 my-2 rounded-lg border p-4 text-sm">
|
|
712
|
+
<p className="mb-1 font-semibold">execute_js</p>
|
|
713
|
+
<pre className="bg-background rounded p-2 font-mono text-xs whitespace-pre-wrap">
|
|
714
|
+
{args.code}
|
|
715
|
+
</pre>
|
|
716
|
+
{status.type !== "running" && result && (
|
|
717
|
+
<div className="mt-2 border-t pt-2">
|
|
718
|
+
<p className="text-muted-foreground font-semibold">
|
|
719
|
+
{result.success ? "Result:" : "Error:"}
|
|
720
|
+
</p>
|
|
721
|
+
<pre className="font-mono text-xs whitespace-pre-wrap">
|
|
722
|
+
{result.success ? result.result : result.error}
|
|
723
|
+
</pre>
|
|
724
|
+
</div>
|
|
725
|
+
)}
|
|
726
|
+
</div>
|
|
727
|
+
),
|
|
473
728
|
},
|
|
474
|
-
|
|
475
|
-
<div className="my-2 rounded-lg border bg-muted/30 p-4 text-sm">
|
|
476
|
-
<p className="mb-1 font-semibold">execute_js</p>
|
|
477
|
-
<pre className="whitespace-pre-wrap rounded bg-background p-2 font-mono text-xs">
|
|
478
|
-
{args.code}
|
|
479
|
-
</pre>
|
|
480
|
-
{status.type !== "running" && result && (
|
|
481
|
-
<div className="mt-2 border-t pt-2">
|
|
482
|
-
<p className="font-semibold text-muted-foreground">
|
|
483
|
-
{result.success ? "Result:" : "Error:"}
|
|
484
|
-
</p>
|
|
485
|
-
<pre className="whitespace-pre-wrap font-mono text-xs">
|
|
486
|
-
{result.success ? result.result : result.error}
|
|
487
|
-
</pre>
|
|
488
|
-
</div>
|
|
489
|
-
)}
|
|
490
|
-
</div>
|
|
491
|
-
),
|
|
492
|
-
});
|
|
729
|
+
} satisfies Toolkit;
|
|
493
730
|
|
|
494
731
|
function MyThreadWithSuggestions() {
|
|
495
732
|
const aui = useAui({
|
|
496
733
|
suggestions: Suggestions([
|
|
497
734
|
{
|
|
498
735
|
title: "Calculate Fibonacci(20)",
|
|
499
|
-
label: "
|
|
500
|
-
prompt:
|
|
736
|
+
label: "with chain of thought",
|
|
737
|
+
prompt:
|
|
738
|
+
"Calculate the 20th Fibonacci number using JavaScript and show your reasoning.",
|
|
501
739
|
},
|
|
502
740
|
{
|
|
503
|
-
title: "
|
|
504
|
-
label: "show
|
|
505
|
-
prompt:
|
|
741
|
+
title: "Research with citations",
|
|
742
|
+
label: "show sources",
|
|
743
|
+
prompt:
|
|
744
|
+
"Research recent developments in renewable energy and cite your sources.",
|
|
506
745
|
},
|
|
507
746
|
]),
|
|
508
747
|
});
|
|
@@ -517,10 +756,12 @@ export default function Home() {
|
|
|
517
756
|
const runtime = useChatRuntime({
|
|
518
757
|
sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses,
|
|
519
758
|
});
|
|
759
|
+
const aui = useAui({
|
|
760
|
+
tools: Tools({ toolkit }),
|
|
761
|
+
});
|
|
520
762
|
|
|
521
763
|
return (
|
|
522
|
-
<AssistantRuntimeProvider runtime={runtime}>
|
|
523
|
-
<ExecuteJsTool />
|
|
764
|
+
<AssistantRuntimeProvider aui={aui} runtime={runtime}>
|
|
524
765
|
<div className="h-full">
|
|
525
766
|
<MyThreadWithSuggestions />
|
|
526
767
|
</div>
|
|
@@ -563,12 +804,13 @@ export default function Home() {
|
|
|
563
804
|
## next.config.js
|
|
564
805
|
|
|
565
806
|
```javascript
|
|
807
|
+
import { withAui } from "@assistant-ui/next";
|
|
566
808
|
/** @type {import('next').NextConfig} */
|
|
567
809
|
const nextConfig = {
|
|
568
810
|
transpilePackages: ["@assistant-ui/react", "@assistant-ui/react-ai-sdk"],
|
|
569
811
|
};
|
|
570
812
|
|
|
571
|
-
export default nextConfig;
|
|
813
|
+
export default withAui(nextConfig);
|
|
572
814
|
|
|
573
815
|
```
|
|
574
816
|
|
|
@@ -586,29 +828,30 @@ export default nextConfig;
|
|
|
586
828
|
"start": "next start"
|
|
587
829
|
},
|
|
588
830
|
"dependencies": {
|
|
589
|
-
"@ai-sdk/openai": "^3.0.
|
|
831
|
+
"@ai-sdk/openai": "^3.0.67",
|
|
590
832
|
"@assistant-ui/react": "workspace:*",
|
|
591
833
|
"@assistant-ui/react-ai-sdk": "workspace:*",
|
|
592
834
|
"@assistant-ui/react-markdown": "workspace:*",
|
|
593
835
|
"@assistant-ui/ui": "workspace:*",
|
|
594
|
-
"ai": "^6.0.
|
|
836
|
+
"ai": "^6.0.193",
|
|
595
837
|
"class-variance-authority": "^0.7.1",
|
|
596
838
|
"clsx": "^2.1.1",
|
|
597
|
-
"lucide-react": "^1.
|
|
598
|
-
"next": "^16.2.
|
|
599
|
-
"react": "^19.2.
|
|
600
|
-
"react-dom": "^19.2.
|
|
601
|
-
"tailwind-merge": "^3.
|
|
839
|
+
"lucide-react": "^1.17.0",
|
|
840
|
+
"next": "^16.2.6",
|
|
841
|
+
"react": "^19.2.6",
|
|
842
|
+
"react-dom": "^19.2.6",
|
|
843
|
+
"tailwind-merge": "^3.6.0",
|
|
602
844
|
"zod": "^4.4.3"
|
|
603
845
|
},
|
|
604
846
|
"devDependencies": {
|
|
847
|
+
"@assistant-ui/next": "workspace:*",
|
|
605
848
|
"@assistant-ui/x-buildutils": "workspace:*",
|
|
606
|
-
"@tailwindcss/postcss": "^4.
|
|
607
|
-
"@types/node": "^25.
|
|
608
|
-
"@types/react": "^19.2.
|
|
849
|
+
"@tailwindcss/postcss": "^4.3.0",
|
|
850
|
+
"@types/node": "^25.9.1",
|
|
851
|
+
"@types/react": "^19.2.15",
|
|
609
852
|
"@types/react-dom": "^19.2.3",
|
|
610
|
-
"postcss": "^8.5.
|
|
611
|
-
"tailwindcss": "^4.
|
|
853
|
+
"postcss": "^8.5.15",
|
|
854
|
+
"tailwindcss": "^4.3.0",
|
|
612
855
|
"tw-animate-css": "^1.4.0",
|
|
613
856
|
"typescript": "^6.0.3"
|
|
614
857
|
}
|
|
@@ -616,6 +859,97 @@ export default nextConfig;
|
|
|
616
859
|
|
|
617
860
|
```
|
|
618
861
|
|
|
862
|
+
## README.md
|
|
863
|
+
|
|
864
|
+
```markdown
|
|
865
|
+
# Chain of Thought Example
|
|
866
|
+
|
|
867
|
+
A real chat flow that renders reasoning, tool calls, and source citations as grouped message parts. Demonstrates `MessagePrimitive.GroupedParts` with a nested `groupBy` path, plus structured `source` parts emitted from a search tool.
|
|
868
|
+
|
|
869
|
+
## Quick start
|
|
870
|
+
|
|
871
|
+
### Using the CLI
|
|
872
|
+
|
|
873
|
+
```bash
|
|
874
|
+
npx assistant-ui@latest create my-app --example with-chain-of-thought
|
|
875
|
+
cd my-app
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
### Environment variables
|
|
879
|
+
|
|
880
|
+
`OPENAI_API_KEY` is optional. With the key set the route runs a real reasoning model and a `search_web` tool; without it the route streams a deterministic mock response with the same chunk shapes, so the UI is fully demonstrable offline.
|
|
881
|
+
|
|
882
|
+
Create `.env.local`:
|
|
883
|
+
|
|
884
|
+
```
|
|
885
|
+
OPENAI_API_KEY=sk-...
|
|
886
|
+
```
|
|
887
|
+
|
|
888
|
+
### Run
|
|
889
|
+
|
|
890
|
+
```bash
|
|
891
|
+
npm install
|
|
892
|
+
npm run dev
|
|
893
|
+
```
|
|
894
|
+
|
|
895
|
+
Open [http://localhost:3000](http://localhost:3000).
|
|
896
|
+
|
|
897
|
+
## What the example demonstrates
|
|
898
|
+
|
|
899
|
+
| Concern | Where it lives |
|
|
900
|
+
|---|---|
|
|
901
|
+
| Real chat flow with `useChatRuntime` | `app/page.tsx` |
|
|
902
|
+
| Reasoning, tool calls, source citations streamed as message parts | `app/api/chat/route.ts` |
|
|
903
|
+
| Nested adjacency-based grouping with `MessagePrimitive.GroupedParts` | `app/MyThread.tsx` |
|
|
904
|
+
| `Sources` component rendering `source` parts | `app/MyThread.tsx` |
|
|
905
|
+
| Fallback path that runs without an API key | `app/api/chat/route.ts` (`streamFallback`) |
|
|
906
|
+
|
|
907
|
+
## How parts are grouped
|
|
908
|
+
|
|
909
|
+
The thread groups three part types under one collapsible "Thinking" section, then renders source badges in a row below it. The grouping is configured on `MessagePrimitive.GroupedParts`:
|
|
910
|
+
|
|
911
|
+
```tsx
|
|
912
|
+
<MessagePrimitive.GroupedParts
|
|
913
|
+
groupBy={(part) => {
|
|
914
|
+
if (part.type === "reasoning") return ["group-chainOfThought", "group-reasoning"];
|
|
915
|
+
if (part.type === "tool-call") return ["group-chainOfThought", "group-tool"];
|
|
916
|
+
if (part.type === "source") return ["group-chainOfThought", "group-sources"];
|
|
917
|
+
return null;
|
|
918
|
+
}}
|
|
919
|
+
>
|
|
920
|
+
```
|
|
921
|
+
|
|
922
|
+
Each part is mapped to a nested group key path. Adjacent parts that share a path are coalesced and rendered through the matching `group-*` case in the render function. `null` leaves a part ungrouped.
|
|
923
|
+
|
|
924
|
+
This is the canonical pattern. For non-adjacent clustering (parts that share an identifier but appear at different positions in the message), see [`/docs/ui/part-grouping`](https://www.assistant-ui.com/docs/ui/part-grouping).
|
|
925
|
+
|
|
926
|
+
## How sources are emitted
|
|
927
|
+
|
|
928
|
+
The route emits structured `source-url` chunks; `@assistant-ui/react-ai-sdk` converts them to `source` message parts that the `Sources` component renders as badges. They are not URLs scraped from assistant text.
|
|
929
|
+
|
|
930
|
+
```ts
|
|
931
|
+
writer.write({
|
|
932
|
+
type: "source-url",
|
|
933
|
+
sourceId: "src-1",
|
|
934
|
+
url: "https://example.com/report",
|
|
935
|
+
title: "Example report",
|
|
936
|
+
});
|
|
937
|
+
```
|
|
938
|
+
|
|
939
|
+
In the real path the `search_web` tool returns sources in its output, and the route writes one `source-url` chunk per result immediately after the `tool-output-available` chunk. In the fallback path the same shape is written directly. The UI rendering is identical in both modes.
|
|
940
|
+
|
|
941
|
+
## Why these features live together
|
|
942
|
+
|
|
943
|
+
Chain-of-thought, tool calls, and citation sources are the parts of an assistant turn a user typically wants to inspect together. Grouping them under one collapsible keeps the message tidy by default and reveals the full reasoning trail on demand.
|
|
944
|
+
|
|
945
|
+
## Related documentation
|
|
946
|
+
|
|
947
|
+
- [Message Part Grouping](https://www.assistant-ui.com/docs/ui/part-grouping)
|
|
948
|
+
- [Sources component](https://www.assistant-ui.com/docs/ui/sources)
|
|
949
|
+
- [AI SDK v6 runtime](https://www.assistant-ui.com/docs/runtimes/ai-sdk/v6)
|
|
950
|
+
|
|
951
|
+
```
|
|
952
|
+
|
|
619
953
|
## tsconfig.json
|
|
620
954
|
|
|
621
955
|
```json
|
|
@@ -631,6 +965,7 @@ export default nextConfig;
|
|
|
631
965
|
"@/lib/utils": ["../../packages/ui/src/lib/utils"],
|
|
632
966
|
"@assistant-ui/ui/*": ["../../packages/ui/src/*"],
|
|
633
967
|
"@assistant-ui/*": ["../../packages/*/src"],
|
|
968
|
+
"@assistant-ui/core/*": ["../../packages/core/src/*"],
|
|
634
969
|
"@assistant-ui/react/*": ["../../packages/react/src/*"],
|
|
635
970
|
"@assistant-ui/tap/*": ["../../packages/tap/src/*"],
|
|
636
971
|
"assistant-stream": ["../../packages/assistant-stream/src"]
|