-
Notifications
You must be signed in to change notification settings - Fork 574
[Package] @thirdweb-dev/ai-sdk-provider #7922
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[Package] @thirdweb-dev/ai-sdk-provider #7922
Conversation
🦋 Changeset detectedLatest commit: 06efc26 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds a new @thirdweb-dev/ai-sdk-provider package (provider, tools, types, build configs), integrates an AI chat UI and streaming API into the playground, adds multiple UI components, updates EIP-1193 connect behavior to short-circuit when already connected, bumps shiki, and adds a changeset. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant UI as Playground UI (ChatContainer)
participant API as Next.js API /api/chat
participant Provider as ThirdwebProvider
participant TWAPI as Thirdweb API
User->>UI: Submit prompt (messages + sessionId)
UI->>API: POST /api/chat (stream: true)
API->>Provider: provider.chat(...).doStream(messages, context)
Provider->>TWAPI: POST /ai/chat (stream: true)
TWAPI-->>Provider: SSE stream (init, delta, action, done)
Provider-->>API: Stream parts (text / reasoning / tool-call / finish, response-metadata)
API-->>UI: Stream UI-formatted parts (includes session_id on finish)
UI-->>User: Render text, reasoning, and tool prompts
sequenceDiagram
autonumber
actor User
participant UI as ChatContainer
participant Wallet as Wallet/Signer
participant API as /api/chat
UI-->>UI: Receive tool-call (e.g., sign_transaction)
UI->>Wallet: prepareTransaction / sign/send
Wallet-->>UI: tx hash or error
UI->>API: addToolResult(tool="sign_transaction", output={transaction_hash}) + follow-up message (sessionId)
API-->>UI: Stream continued response
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (1)
✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
c237ff2
to
5ac1076
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 17
🧹 Nitpick comments (35)
package.json (1)
65-65
: Ai-SDK-Provider Dev Script Verified & Playground:Build Update
- The
packages/ai-sdk-provider/package.json
does define adev
script (tsc … --watch
), so yourturbo run dev --filter=…/packages/ai-sdk-provider
filter is effective.- Currently, the root
playground:build
script only runsIf you’d like to build the AI SDK provider alongside the app, consider updating it to:turbo run build --filter=./apps/playground-web"playground:build": "turbo run build --filter=./apps/playground-web \ + --filter=./packages/ai-sdk-provider"
apps/playground-web/src/app/ai/components/ChatPageContent.tsx (2)
794-799
: Guard presence text access to avoid undefined render.When the presence message exists but
texts
is empty (initial state), this rendersundefined
.Apply:
- {message.type === "presence" && ( + {message.type === "presence" && ( <div className="text-muted-foreground text-sm italic flex items-center gap-2"> - {message.texts[message.texts.length - 1]} + {message.texts?.length + ? message.texts[message.texts.length - 1] + : "Processing..."} </div> )}
601-613
: Prefer a stable key; avoid stringifying the whole message object.
key={
${index}-${message}}
stringifies the object to "[object Object]" and pairs with the index. Use a stable id (e.g.,request_id
) with index fallback to reduce re-mounts.Example:
- key={`${index}-${message}`} + key={`msg-${"request_id" in message && message.request_id ? message.request_id : index}`}apps/playground-web/src/components/code/code-example.tsx (1)
58-69
: Export looks good; add explicit return type and className support for overrides.Matches repo conventions for exported UI helpers and improves reuse.
Apply:
-export function TabName(props: { - name: string; - icon: React.FC<{ className: string }>; -}) { - return ( - <div className="flex items-center gap-2 border-b p-4 text-muted-foreground text-sm"> +export function TabName(props: { + name: string; + icon: React.FC<{ className: string }>; + className?: string; +}): JSX.Element { + return ( + <div className={`flex items-center gap-2 border-b p-4 text-muted-foreground text-sm ${props.className ?? ""}`}> <props.icon className="size-4" /> {props.name} </div> ); }packages/thirdweb/src/adapters/eip1193/to-eip1193.ts (1)
133-136
: Avoid console.error in library code.Prefer routing through an internal logger or removing the log to prevent noisy consumer consoles.
apps/playground-web/src/components/loader.tsx (1)
86-96
: Improve a11y for the spinner wrapper.Expose progress semantics to AT.
- <div + <div className={cn( 'inline-flex animate-spin items-center justify-center', className )} + role="status" + aria-live="polite" + aria-busy="true" {...props} >apps/playground-web/src/components/conversation.tsx (3)
13-19
: Announce new messages to screen readers.Add live-region attributes to the log container.
<StickToBottom className={cn('relative flex-1 overflow-y-auto', className)} initial="smooth" resize="smooth" role="log" + aria-live="polite" + aria-relevant="additions text" {...props} />
47-59
: Add an accessible label to the scroll button.Improve discoverability for AT users.
<Button className={cn( 'absolute bottom-4 left-[50%] translate-x-[-50%] rounded-full', className )} onClick={handleScrollToBottom} size="icon" type="button" variant="outline" + aria-label="Scroll to bottom" {...props} > - <ArrowDownIcon className="size-4" /> + <ArrowDownIcon className="size-4" aria-hidden="true" /> </Button>
12-20
: Add explicit return types for exported components.Matches repo TS guidelines for explicit returns.
-export const Conversation = ({ className, ...props }: ConversationProps) => ( +export const Conversation = ({ className, ...props }: ConversationProps): JSX.Element => ( ... ); -export const ConversationContent = ({ className, ...props }: ConversationContentProps) => ( +export const ConversationContent = ({ className, ...props }: ConversationContentProps): JSX.Element => ( ... ); -export const ConversationScrollButton = ({ className, ...props }: ConversationScrollButtonProps) => { +export const ConversationScrollButton = ({ className, ...props }: ConversationScrollButtonProps): JSX.Element | null => { ... }Also applies to: 26-31, 35-62
apps/playground-web/src/components/message.tsx (3)
36-41
: Remove stray Tailwind class "is-user:dark".This token doesn’t map to a valid variant and has no effect.
- 'group-[.is-assistant]:bg-secondary group-[.is-assistant]:text-foreground', - 'is-user:dark', + 'group-[.is-assistant]:bg-secondary group-[.is-assistant]:text-foreground',
10-12
: Clarify allowed roles forfrom
.If only 'user' | 'assistant' are expected, narrow the type to avoid unexpected layout for other roles.
-export type MessageProps = HTMLAttributes<HTMLDivElement> & { - from: UIMessage['role']; -}; +type ChatRole = Extract<UIMessage['role'], 'user' | 'assistant'>; +export type MessageProps = HTMLAttributes<HTMLDivElement> & { from: ChatRole };
58-61
: Improve avatar a11y and fallback initials; drop redundant margins.
- Use name-based alt when available; empty alt when decorative.
- Compute initials from up to two words and uppercase.
-export const MessageAvatar = ({ - src, - name, - className, - ...props -}: MessageAvatarProps) => ( - <Avatar className={cn('size-8 ring-1 ring-border', className)} {...props}> - <AvatarImage alt="" className="mt-0 mb-0" src={src} /> - <AvatarFallback>{name?.slice(0, 2) || 'ME'}</AvatarFallback> - </Avatar> -); +export const MessageAvatar = ({ + src, + name, + className, + ...props +}: MessageAvatarProps) => { + const initials = + name + ?.trim() + .split(/\s+/) + .slice(0, 2) + .map((p) => p[0]?.toUpperCase()) + .join('') || 'ME'; + return ( + <Avatar className={cn('size-8 ring-1 ring-border', className)} {...props}> + <AvatarImage alt={name ? `${name} avatar` : ''} src={src} /> + <AvatarFallback>{initials}</AvatarFallback> + </Avatar> + ); +};apps/playground-web/src/components/reasoning.tsx (3)
79-91
: Comment says auto-open on start, but code only auto-closes. Implement auto-open or update the comment.If intended, auto-open when streaming starts.
-// Auto-open when streaming starts, auto-close when streaming ends (once only) +// Auto-open when streaming starts, auto-close when streaming ends (once only) useEffect(() => { - if (defaultOpen && !isStreaming && isOpen && !hasAutoClosedRef) { + // auto-open on start + if (isStreaming && !isOpen) { + setIsOpen(true); + } + // auto-close once after finish + if (defaultOpen && !isStreaming && isOpen && !hasAutoClosedRef) { // Add a small delay before closing to allow user to see the content const timer = setTimeout(() => { setIsOpen(false); - setHasAutoClosedRef(true); + setHasAutoClosedRef(true); }, AUTO_CLOSE_DELAY); return () => clearTimeout(timer); } }, [isStreaming, isOpen, defaultOpen, setIsOpen, hasAutoClosedRef]);
64-66
:hasAutoClosedRef
is state, not a ref; useuseRef
for true “once” semantics without re-renders.Less churn and clearer naming.
-const [hasAutoClosedRef, setHasAutoClosedRef] = useState(false); +const hasAutoClosedRef = useRef(false); ... - if (defaultOpen && !isStreaming && isOpen && !hasAutoClosedRef) { + if (defaultOpen && !isStreaming && isOpen && !hasAutoClosedRef.current) { const timer = setTimeout(() => { setIsOpen(false); - setHasAutoClosedRef(true); + hasAutoClosedRef.current = true; }, AUTO_CLOSE_DELAY);Also applies to: 81-86
59-63
: Controlledduration
may not update when parent controls it.When
duration
is controlled via props,setDuration
won’t change the value locally. Ensure the parent updates the prop on stream end, or guardsetDuration
behind uncontrolled mode.-} ); +} ); ... -} else if (startTime !== null) { - setDuration(Math.round((Date.now() - startTime) / MS_IN_S)); +} else if (startTime !== null) { + // only update when uncontrolled + if (durationProp === undefined) { + setDuration(Math.round((Date.now() - startTime) / MS_IN_S)); + }Also applies to: 67-77
packages/ai-sdk-provider/tsconfig.json (2)
9-9
: Remove empty "exclude" to avoid surprising defaults later.An empty array overrides tsconfig’s default excludes. Since "include" already scopes to "src" and "test", drop "exclude" or set explicit dirs if needed.
Apply:
- "exclude": [],
1-13
: Consider moving "extends" to the top for readability.Order is not semantically significant, but keeping "extends" first matches common tsconfig conventions.
Optional tidy-up:
-{ - // This configuration is used for local development and type checking. - "compilerOptions": { +{ + "extends": "./tsconfig.base.json", + // This configuration is used for local development and type checking. + "compilerOptions": { "baseUrl": ".", "paths": { "~test/*": ["./test/src/*"] } }, - "exclude": [], - // This configuration is used for local development and type checking. - "extends": "./tsconfig.base.json", "include": ["src", "test"] }apps/playground-web/package.json (1)
4-4
: Ensure heavy dependencies are lazily loaded in the client bundleWe ran the import-pattern check and confirmed that none of these heavy modules are currently loaded via dynamic import/next/dynamic. To keep the initial payload lean, please refactor all top-level static imports of the following packages to use dynamic loading:
• apps/playground-web/src/app/ai/ai-sdk/page.tsx (lines 119–121)
–import { useChat } from '@ai-sdk/react'
–import { DefaultChatTransport } from 'ai'
• apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (lines 3–5)
–import { useChat } from '@ai-sdk/react'
–import { DefaultChatTransport } from 'ai'
• apps/playground-web/src/components/ui/markdown-renderer.tsx (line 3–4)
–import ReactMarkdown from 'react-markdown'
–import remarkGfm from 'remark-gfm'
• apps/playground-web/src/components/code/code.tsx (line 1)
–import type { BundledLanguage } from 'shiki'
• apps/playground-web/src/components/code/code-example.tsx (line 3)
–import type { BundledLanguage } from 'shiki'
• apps/playground-web/src/components/code/code.client.tsx (line 2)
–import type { BundledLanguage } from 'shiki'
• apps/playground-web/src/components/code/getCodeHtml.tsx (line 3)
–import { codeToHtml } from 'shiki'
• apps/playground-web/src/components/response.tsx (line 5)
–import { Streamdown } from 'streamdown'
• apps/playground-web/src/components/conversation.tsx (line 8)
–import { StickToBottom, useStickToBottomContext } from 'use-stick-to-bottom'
Example refactor using Next.js dynamic import:
import dynamic from 'next/dynamic'; const ReactMarkdown = dynamic(() => import('react-markdown'), { ssr: false });Or for named exports:
const { useChat } = dynamic( () => import('@ai-sdk/react').then(mod => ({ default: mod.useChat })), { ssr: false }, );Please update these imports to dynamic loading to reduce the client-side bundle size.
packages/ai-sdk-provider/tsconfig.build.json (1)
1-16
: Add outDir and declarations to avoid emitting into src and to ship types.Without outDir, tsc emits alongside sources. Consider generating declarations for consumers.
Proposed:
{ "compilerOptions": { "moduleResolution": "NodeNext", "rootDir": "./src", - "sourceMap": true + "sourceMap": true, + "outDir": "./dist", + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": false },Confirm this matches your build pipeline (tsup/rollup may already handle this).
packages/ai-sdk-provider/tsconfig.base.json (1)
21-24
: Consider matching target to lib (ES2022) for consistency.You use lib ES2022 but target ES2021. Aligning target can avoid downlevel quirks and matches the Error.cause comment.
- "lib": [ - "ES2022", - "DOM" - ], + "lib": ["ES2022", "DOM"], @@ - "target": "ES2021", + "target": "ES2022",Also applies to: 41-41
apps/playground-web/src/app/api/chat/route.ts (4)
1-1
: Add server-only guard at top of server route.Prevents accidental client bundling; aligns with repo convention for server code.
+import "server-only"; import { convertToModelMessages, streamText, UIMessage } from "ai";
8-11
: Avoid relying on NEXT_PUBLIC_ var for server-only baseURL; validate secrets.
- Using a public env var to drive a server baseURL is brittle; the provider has sane defaults.
- Fail fast if THIRDWEB_SECRET_KEY is missing to avoid runtime surprises.
-const thirdweb = createThirdweb({ - baseURL: `https://${process.env.NEXT_PUBLIC_API_URL}`, - secretKey: process.env.THIRDWEB_SECRET_KEY, -}); +if (!process.env.THIRDWEB_SECRET_KEY) { + throw new Error("THIRDWEB_SECRET_KEY is required for the chat API route"); +} +const thirdweb = createThirdweb({ + secretKey: process.env.THIRDWEB_SECRET_KEY, +});
13-17
: Validate request body with zod before use.Hardens the endpoint and avoids undefined access when upstream changes occur.
-export async function POST(req: Request) { - const body = await req.json(); - const { messages, sessionId }: { messages: UIMessage[]; sessionId: string } = - body; +export async function POST(req: Request) { + const Body = z.object({ + messages: z.array(z.any()), + sessionId: z.string().optional().default(""), + }); + const { messages, sessionId } = Body.parse(await req.json());
20-24
: Don’t hardcode chain/from in production-facing examples.Make these configurable via env or request to avoid confusing outputs across networks/wallets.
- context: { - session_id: sessionId, - chain_ids: [8453], - from: "0x2247d5d238d0f9d37184d8332aE0289d1aD9991b", - }, + context: { + session_id: sessionId, + // consider reading chain_ids/from from env or request body + chain_ids: [8453], + from: "0x2247d5d238d0f9d37184d8332aE0289d1aD9991b", + },packages/ai-sdk-provider/src/types.ts (1)
8-12
: Optional: extract metadata type for reuse.Improves reuse across client/server without importing UIMessage everywhere.
export type ThirdwebMessageMetadata = { session_id: string }; export type ThirdwebAiMessage = UIMessage<ThirdwebMessageMetadata, UIDataTypes, { /* … */ }>;apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (3)
153-159
: Replace inline styles with Tailwind per guidelines.Playground uses Tailwind only; avoid inline styles.
- <TransactionButton - style={{ - width: "100%", - }} + <TransactionButton + className="w-full"
132-146
: Guard invalid tx defaults to avoid runtime errors.Empty “to” with “0x” data will fail on many chains. Disable/early-return until input is provided; optionally validate address.
const transactionData: { chain_id: number; to: string; data: `0x${string}`; value: bigint; } = useMemo(() => { return { chain_id: input?.chain_id || 8453, to: input?.to || "", data: (input?.data as `0x${string}`) || "0x", value: input?.value ? BigInt(input.value) : BigInt(0), }; }, [input]); + const canSend = + (transactionData.to && transactionData.to.length > 0) || + transactionData.data !== "0x";And apply:
- <TransactionButton + <TransactionButton + disabled={!canSend}
168-176
: Return tool error on failed send (optional).Improves chat continuity when user cancels or tx fails.
onTransactionSent={(transaction) => { addToolResult({ tool: "sign_transaction", toolCallId, output: { transaction_hash: transaction.transactionHash, }, }); }} + onError={(err) => { + // You could also emit a user-facing message here. + console.error("transaction error", err); + }}apps/playground-web/src/app/ai/ai-sdk/page.tsx (1)
60-101
: Keep server sample minimal and consistent with route.Consider mirroring the actual route (no baseURL; add maxDuration; optional tools) so copy-paste works out of the box.
packages/ai-sdk-provider/package.json (1)
45-55
: Build scripts look good; minor DX improvement.Add a “prepublishOnly” to ensure clean builds on publish.
"scripts": { "dev": "tsc --project ./tsconfig.build.json --module nodenext --moduleResolution nodenext --outDir ./dist/esm --watch", "build": "pnpm clean && pnpm build:cjs && pnpm build:esm && pnpm build:types", + "prepublishOnly": "pnpm build", "build:cjs": "tsc --project ./tsconfig.build.json --module commonjs --outDir ./dist/cjs --verbatimModuleSyntax false && printf '{\"type\":\"commonjs\"}' > ./dist/cjs/package.json", "build:esm": "tsc --project ./tsconfig.build.json --module es2020 --outDir ./dist/esm && printf '{\"type\": \"module\",\"sideEffects\":false}' > ./dist/esm/package.json", "build:types": "tsc --project ./tsconfig.build.json --module esnext --declarationDir ./dist/types --emitDeclarationOnly --declaration --declarationMap", "clean": "rimraf dist", "fix": "biome check ./src --fix", "format": "biome format ./src --write", "lint": "biome check ./src" },
packages/ai-sdk-provider/src/provider.ts (2)
397-415
: Don’t mutate reasoning text (remove auto “. ”).Emit exactly what the server sends.
- delta: `${parsed.data}. `, // TODO (ai): this should be in the backends + delta: String(parsed.data),
513-515
: Noisy logging in production.Gate the warn behind a debug flag or env check.
- console.warn("Failed to parse SSE data:", data, e); + if (process.env.NODE_ENV !== "production") { + console.warn("Failed to parse SSE data:", data, e); + }apps/playground-web/src/components/prompt-input.tsx (3)
165-173
: A11y: icon-only submit needs an accessible label.Add
aria-label
/title
when no children are provided.<Button className={cn('gap-1.5 rounded-lg', className)} size={size} type="submit" variant={variant} + aria-label={children ? undefined : 'Send message'} + title={children ? undefined : 'Send'} {...props} >
34-37
: Remove unusedminHeight
/maxHeight
props or wire them via Tailwind tokens.They’re declared but unused; also guideline says “Tailwind only – no inline styles.”
-export type PromptInputTextareaProps = ComponentProps<typeof Textarea> & { - minHeight?: number; - maxHeight?: number; -}; +export type PromptInputTextareaProps = ComponentProps<typeof Textarea>; @@ export const PromptInputTextarea = ({ onChange, className, placeholder = 'What would you like to know?', - minHeight = 48, - maxHeight = 164, ...props }: PromptInputTextareaProps) => {Also applies to: 39-46
24-32
: Optional: forward refs for better composition.Wrap primitives with
forwardRef
to allow parent focus/measure.Also applies to: 68-85, 117-140, 146-175
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (27)
.changeset/many-pants-tease.md
(1 hunks)apps/playground-web/package.json
(3 hunks)apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
(1 hunks)apps/playground-web/src/app/ai/ai-sdk/page.tsx
(1 hunks)apps/playground-web/src/app/ai/components/ChatPageContent.tsx
(1 hunks)apps/playground-web/src/app/api/chat/route.ts
(1 hunks)apps/playground-web/src/app/navLinks.ts
(1 hunks)apps/playground-web/src/components/code/code-example.tsx
(1 hunks)apps/playground-web/src/components/conversation.tsx
(1 hunks)apps/playground-web/src/components/loader.tsx
(1 hunks)apps/playground-web/src/components/message.tsx
(1 hunks)apps/playground-web/src/components/prompt-input.tsx
(1 hunks)apps/playground-web/src/components/reasoning.tsx
(1 hunks)apps/playground-web/src/components/response.tsx
(1 hunks)apps/playground-web/src/components/ui/avatar.tsx
(1 hunks)package.json
(1 hunks)packages/ai-sdk-provider/README.md
(1 hunks)packages/ai-sdk-provider/biome.json
(1 hunks)packages/ai-sdk-provider/package.json
(1 hunks)packages/ai-sdk-provider/src/exports/thirdweb.ts
(1 hunks)packages/ai-sdk-provider/src/provider.ts
(1 hunks)packages/ai-sdk-provider/src/tools.ts
(1 hunks)packages/ai-sdk-provider/src/types.ts
(1 hunks)packages/ai-sdk-provider/tsconfig.base.json
(1 hunks)packages/ai-sdk-provider/tsconfig.build.json
(1 hunks)packages/ai-sdk-provider/tsconfig.json
(1 hunks)packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}
: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/types
or localtypes.ts
barrels
Prefer type aliases over interface except for nominal shapes
Avoidany
andunknown
unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial
,Pick
, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
Files:
apps/playground-web/src/components/reasoning.tsx
apps/playground-web/src/components/conversation.tsx
apps/playground-web/src/components/response.tsx
apps/playground-web/src/components/ui/avatar.tsx
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
packages/ai-sdk-provider/src/types.ts
apps/playground-web/src/app/api/chat/route.ts
packages/ai-sdk-provider/src/tools.ts
apps/playground-web/src/components/code/code-example.tsx
apps/playground-web/src/components/message.tsx
apps/playground-web/src/app/navLinks.ts
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
packages/ai-sdk-provider/src/provider.ts
apps/playground-web/src/app/ai/ai-sdk/page.tsx
apps/playground-web/src/components/loader.tsx
apps/playground-web/src/components/prompt-input.tsx
packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
packages/ai-sdk-provider/src/exports/thirdweb.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
apps/playground-web/src/components/reasoning.tsx
apps/playground-web/src/components/conversation.tsx
apps/playground-web/src/components/response.tsx
apps/playground-web/src/components/ui/avatar.tsx
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
packages/ai-sdk-provider/src/types.ts
apps/playground-web/src/app/api/chat/route.ts
packages/ai-sdk-provider/src/tools.ts
apps/playground-web/src/components/code/code-example.tsx
apps/playground-web/src/components/message.tsx
apps/playground-web/src/app/navLinks.ts
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
packages/ai-sdk-provider/src/provider.ts
apps/playground-web/src/app/ai/ai-sdk/page.tsx
apps/playground-web/src/components/loader.tsx
apps/playground-web/src/components/prompt-input.tsx
packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
packages/ai-sdk-provider/src/exports/thirdweb.ts
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}
: Import UI primitives from@/components/ui/*
(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLink
for internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()
from@/lib/utils
for conditional class logic
Use design system tokens (e.g.,bg-card
,border-border
,text-muted-foreground
)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()
to retrieve JWT from cookies on server side
UseAuthorization: Bearer
header – never embed tokens in URLs
Return typed results (e.g.,Project[]
,User[]
) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query
)
Use descriptive, stablequeryKeys
for React Query cache hits
ConfigurestaleTime
/cacheTime
in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-js
in server components
Files:
apps/playground-web/src/components/reasoning.tsx
apps/playground-web/src/components/conversation.tsx
apps/playground-web/src/components/response.tsx
apps/playground-web/src/components/ui/avatar.tsx
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
apps/playground-web/src/app/api/chat/route.ts
apps/playground-web/src/components/code/code-example.tsx
apps/playground-web/src/components/message.tsx
apps/playground-web/src/app/navLinks.ts
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
apps/playground-web/src/app/ai/ai-sdk/page.tsx
apps/playground-web/src/components/loader.tsx
apps/playground-web/src/components/prompt-input.tsx
package.json
📄 CodeRabbit inference engine (CLAUDE.md)
Track bundle budgets via
package.json#size-limit
Files:
package.json
🧠 Learnings (23)
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to **/*.{ts,tsx} : Re-use shared types from `@/types` or local `types.ts` barrels
Applied to files:
packages/ai-sdk-provider/tsconfig.json
packages/ai-sdk-provider/tsconfig.base.json
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to biome.json : Biome is the primary linter/formatter; rules are defined in `biome.json`
Applied to files:
packages/ai-sdk-provider/biome.json
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to packages/thirdweb/src/wallets/** : EIP-1193, EIP-5792, EIP-7702 standard support in wallet modules
Applied to files:
.changeset/many-pants-tease.md
packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to packages/thirdweb/src/wallets/** : Support for in-app wallets (social/email login)
Applied to files:
.changeset/many-pants-tease.md
packages/ai-sdk-provider/README.md
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to packages/thirdweb/src/wallets/** : Smart wallets with account abstraction
Applied to files:
.changeset/many-pants-tease.md
packages/ai-sdk-provider/README.md
packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/components/*.client.tsx : Client components must start with `'use client';` before imports.
Applied to files:
apps/playground-web/src/components/response.tsx
apps/playground-web/src/components/ui/avatar.tsx
📚 Learning: 2025-07-31T16:17:42.753Z
Learnt from: MananTank
PR: thirdweb-dev/js#7768
File: apps/playground-web/src/app/navLinks.ts:1-1
Timestamp: 2025-07-31T16:17:42.753Z
Learning: Configuration files that import and reference React components (like icon components from lucide-react) need the "use client" directive, even if they primarily export static data, because the referenced components need to be executed in a client context when used by other client components.
Applied to files:
apps/playground-web/src/components/response.tsx
apps/playground-web/src/components/ui/avatar.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Components that listen to user events, animations or live updates.
Applied to files:
apps/playground-web/src/components/response.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Import UI primitives from `@/components/ui/*` (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
Applied to files:
apps/playground-web/src/components/ui/avatar.tsx
apps/playground-web/src/components/prompt-input.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to src/exports/react.native.ts : React Native specific exports are in `src/exports/react.native.ts`
Applied to files:
apps/playground-web/src/components/ui/avatar.tsx
apps/playground-web/src/components/code/code-example.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Interactive UI that relies on hooks (`useState`, `useEffect`, React Query, wallet hooks).
Applied to files:
apps/playground-web/src/components/ui/avatar.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{tsx} : Expose `className` prop on root element of components for overrides
Applied to files:
apps/playground-web/src/components/ui/avatar.tsx
apps/playground-web/src/components/code/code-example.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to packages/wagmi-adapter/** : Wagmi ecosystem integration is in `packages/wagmi-adapter/`
Applied to files:
packages/ai-sdk-provider/README.md
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to packages/thirdweb/src/wallets/** : Unified `Wallet` and `Account` interfaces in wallet architecture
Applied to files:
packages/ai-sdk-provider/README.md
packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Build all packages using `pnpm build` and specific packages with dependencies using `turbo run build --filter=./packages/*`
Applied to files:
package.json
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Monorepo is managed with Turborepo and pnpm workspaces
Applied to files:
package.json
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Accept a typed `props` object and export a named function (`export function MyComponent()`).
Applied to files:
apps/playground-web/src/components/code/code-example.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Use `NavLink` for internal navigation with automatic active states in dashboard and playground apps
Applied to files:
apps/playground-web/src/app/navLinks.ts
📚 Learning: 2025-08-07T17:24:31.965Z
Learnt from: MananTank
PR: thirdweb-dev/js#7812
File: apps/dashboard/src/app/(app)/team/~/~project/[[...paths]]/page.tsx:1-11
Timestamp: 2025-08-07T17:24:31.965Z
Learning: In Next.js App Router, page components (page.tsx files) are server components by default and do not require the "server-only" import directive. The "server-only" directive is primarily used for utility functions, API helpers, and data access modules that should never be included in the client bundle.
Applied to files:
apps/playground-web/src/app/ai/ai-sdk/page.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Prefer composable primitives over custom markup: `Button`, `Input`, `Select`, `Tabs`, `Card`, `Sidebar`, `Separator`, `Badge`.
Applied to files:
apps/playground-web/src/components/prompt-input.tsx
📚 Learning: 2025-06-03T23:44:40.243Z
Learnt from: joaquim-verges
PR: thirdweb-dev/js#7268
File: packages/thirdweb/src/wallets/in-app/core/wallet/in-app-core.ts:210-216
Timestamp: 2025-06-03T23:44:40.243Z
Learning: EIP7702 wallets do not need special handling for switching chains, unlike EIP4337 wallets which require reconnection when switching chains. In the switchChain method condition, EIP7702 should be intentionally excluded from the reconnection logic.
Applied to files:
packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to packages/thirdweb/src/exports/** : Export everything via `exports/` directory, grouped by feature in the SDK public API
Applied to files:
packages/ai-sdk-provider/src/exports/thirdweb.ts
📚 Learning: 2025-06-17T18:30:52.976Z
Learnt from: MananTank
PR: thirdweb-dev/js#7356
File: apps/nebula/src/app/not-found.tsx:1-1
Timestamp: 2025-06-17T18:30:52.976Z
Learning: In the thirdweb/js project, the React namespace is available for type annotations (like React.FC) without needing to explicitly import React. This is project-specific configuration that differs from typical TypeScript/React setups.
Applied to files:
packages/ai-sdk-provider/src/exports/thirdweb.ts
🧬 Code graph analysis (7)
apps/playground-web/src/components/reasoning.tsx (1)
apps/playground-web/src/components/response.tsx (1)
Response
(9-20)
packages/ai-sdk-provider/src/types.ts (2)
packages/ai-sdk-provider/src/exports/thirdweb.ts (1)
ThirdwebAiMessage
(2-2)packages/ai-sdk-provider/src/tools.ts (3)
SignTransactionInput
(65-67)MonitorTransactionInput
(69-71)SignSwapInput
(68-68)
apps/playground-web/src/app/api/chat/route.ts (1)
packages/ai-sdk-provider/src/provider.ts (1)
createThirdweb
(561-563)
packages/ai-sdk-provider/src/tools.ts (1)
packages/nebula/src/client/types.gen.ts (4)
AgentActionSignTransactionData
(460-481)AgentActionSignSwapDataToken
(390-415)AgentActionSignSwapDataIntent
(352-385)AgentActionSignSwapData
(338-347)
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (3)
packages/ai-sdk-provider/src/types.ts (1)
ThirdwebAiMessage
(8-34)apps/playground-web/src/components/prompt-input.tsx (3)
PromptInput
(24-32)PromptInputTextarea
(39-85)PromptInputSubmit
(146-175)apps/playground-web/src/lib/client.ts (1)
THIRDWEB_CLIENT
(20-43)
packages/ai-sdk-provider/src/provider.ts (1)
packages/ai-sdk-provider/src/tools.ts (1)
thirdwebTools
(44-63)
apps/playground-web/src/app/ai/ai-sdk/page.tsx (4)
packages/ai-sdk-provider/src/provider.ts (1)
ThirdwebProvider
(530-558)apps/playground-web/src/components/blocks/APIHeader.tsx (1)
PageLayout
(49-70)apps/playground-web/src/components/code/code-example.tsx (2)
TabName
(58-68)CodeExample
(17-56)apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (1)
ChatContainer
(28-119)
🔇 Additional comments (15)
packages/thirdweb/src/adapters/eip1193/to-eip1193.ts (1)
123-126
: Good: short-circuit when already connected.Returning the cached address immediately avoids unnecessary UI prompts.
.changeset/many-pants-tease.md (1)
5-5
: Changelog reads well.Clear, scoped patch note.
apps/playground-web/src/components/ui/avatar.tsx (1)
1-50
: Solid Radix wrappers with proper ref forwarding and className merge. LGTM.apps/playground-web/src/app/navLinks.ts (2)
19-26
: Sidebar label changes look good."Live Demo" label and added "AI SDK" link align with the new pages.
23-26
: Route/ai/ai-sdk
confirmed presentThe file
apps/playground-web/src/app/ai/ai-sdk/page.tsx
exists and defines the page content for the AI SDK route, ensuring the sidebar link won’t be broken.apps/playground-web/package.json (1)
1-51
: Deps set looks consistent for new AI SDK features.Workspace ranges and versions align with Next 15/React 19. No immediate issues spotted.
packages/ai-sdk-provider/src/exports/thirdweb.ts (1)
1-2
: ESM-friendly re-exports with .js extensions LGTM.Pattern matches NodeNext + verbatimModuleSyntax best practices. Ensure tsconfig.build uses NodeNext resolution (see separate comment).
packages/ai-sdk-provider/src/types.ts (1)
8-34
: Type surface looks solid and idiomatic.Good use of UIMessage generics and type-only imports; aligns with tools’ IO.
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (1)
149-152
: Great UX: gated signing behind connection.ConnectButton fallback is correct for guarded actions.
apps/playground-web/src/app/ai/ai-sdk/page.tsx (1)
26-41
: LGTM: Server page composition and examples are clear.Matches Playground patterns; no need for “server-only” per team learning on page files.
packages/ai-sdk-provider/package.json (1)
31-35
: Verify Zod v4 compatibility before widening peerDep
We checked npm dist-tags for Zod and saw thatlatest
is at 4.1.3 whilenext
points to a 3.25.x beta, meaning v4 is officially released but we have no evidence your CI actually tests against it. Without explicit tests, consumers installing a v4 release could encounter breaking changes.• Your peerDeps currently allow
"zod": "^3.25.75 || ^4"
• No.github/workflows/*.yml
references to a Zod version matrix were found—there’s no CI guard covering both majors.
• Please either
- Pin to
^3
until you’ve verified code against v4, or- Expand your test matrix to install & run against both Zod 3 (e.g. 3.25.75) and Zod 4 (e.g. 4.1.3) in CI
packages/ai-sdk-provider/src/provider.ts (3)
125-140
: Verify Thirdweb /ai/chat Support forrole: "tool"
I’m not certain whether Thirdweb’s
/ai/chat
endpoint accepts messages withrole: "tool"
. Please check the official Thirdweb AI Chat API documentation. Ifrole: "tool"
is unsupported, convert tool results into an assistant message instead.Locations to update:
- packages/ai-sdk-provider/src/provider.ts lines 125–140
Suggested diff:
case "tool": { - return { - role: message.role, - content: toolContent, - }; + return { + // Thirdweb may not accept `role: "tool"`. Use "assistant" for tool output. + role: "assistant", + content: toolContent, + }; }
345-376
: Confirmed: Thirdweb SSE “delta” events usev
for text chunks
According to the official thirdweb streaming API docs, eachdelta
event’sdata
payload is a JSON object containing a singlev
property with the next piece of text. No alternate field names (e.g.delta
ordata
) are used for text chunks in the/ai/chat
stream (portal.thirdweb.com)
160-165
: It looks like ripgrep didn’t pick up any definition forLanguageModelV2CallOptions
or occurrences ofmodelId
in the provider package, and we can’t confirm the correct request field purely from this code search. To be sure, please verify against the Thirdweb AI Chat API spec or SDK type definitions:
- Does the
LanguageModelV2CallOptions
type include amodelId
(ormodel_id
) field?- When performing a POST to
/ai/chat
, is the model parameter named"model"
or"model_id"
?If you have access to the API docs or to the types in the core SDK (e.g.
@thirdweb-dev/sdk
or similar package), please confirm the exact field name and update this snippet accordingly:- const body = { - messages, - stream: false, - context: this.settings.context, - }; + const body = { + model: this.modelId, // replace "model" with "model_id" if that’s the correct field + messages, + stream: false, + context: this.settings.context, + };Until we can verify the exact field name, I recommend manually checking the API spec or the originating type definitions to avoid breaking the contract.
apps/playground-web/src/components/prompt-input.tsx (1)
156-163
: Action Required: VerifyChatStatus
Values and Align Status ChecksPlease confirm that the string literals used in the
if
/else
chain match all possibleChatStatus
values exported by theai
package. Any mismatch could lead to unreachable branches or missing UI states.• File:
apps/playground-web/src/components/prompt-input.tsx
• Lines: 156–163Consider using a
switch (status)
with adefault
branch (or exhaustiveness check) to ensure you cover everyChatStatus
.
5ac1076
to
1c89599
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 8
♻️ Duplicate comments (12)
apps/playground-web/src/components/loader.tsx (1)
12-12
: Remove inline style; follow Tailwind-only rule.Inline styles are disallowed in apps/playground-web. Rely on inherited text color instead.
- style={{ color: "currentcolor" }} + className="text-inherit"apps/dashboard/package.json (1)
63-63
: Shiki v3 upgrade here as well — mirror the verification.Same risks as Nebula; reuse the verification steps from the nebula comment to catch API/import mismatches at build time.
apps/playground-web/src/app/ai/components/ChatPageContent.tsx (1)
35-36
: Client component importing server module breaks RSC boundaries — move promptNebula to a client wrapper.This file is a Client Component ("use client"). Importing from ../server/chat at runtime can cause Next.js to error or bundle server code client-side. Import only types from server/types and use a browser-safe client wrapper.
Apply:
-import { type NebulaContext, promptNebula } from "../server/chat"; -import type { NebulaUserMessage } from "../server/types"; +import type { NebulaContext, NebulaUserMessage } from "../server/types"; +import { promptNebula } from "../client/chat";Add the wrapper (new file):
// apps/playground-web/src/app/ai/client/chat.ts "use client"; import type { NebulaContext, NebulaUserMessage } from "../server/types"; type StreamHandler = (ev: any) => void; export async function promptNebula(params: { abortController: AbortController; context: NebulaContext | undefined; message: NebulaUserMessage; handleStream: StreamHandler; }) { const { abortController, context, message, handleStream } = params; const res = await fetch("/api/chat", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ message, context }), signal: abortController.signal, }); if (!res.ok || !res.body) throw new Error(`Request failed: ${res.status}`); const reader = res.body.getReader(); const decoder = new TextDecoder(); let buf = ""; while (true) { const { done, value } = await reader.read(); if (done) break; buf += decoder.decode(value, { stream: true }); let idx: number; while ((idx = buf.indexOf("\n\n")) !== -1) { const chunk = buf.slice(0, idx).trim(); buf = buf.slice(idx + 2); if (!chunk) continue; const dataLine = chunk.split("\n").find((l) => l.startsWith("data:")); if (!dataLine) continue; try { handleStream(JSON.parse(dataLine.replace(/^data:\s*/, ""))); } catch { // ignore malformed chunks } } } }packages/ai-sdk-provider/tsconfig.base.json (1)
14-15
: importHelpers requires tslib at runtimeWith importHelpers: true, emitted JS imports from "tslib". Ensure packages/ai-sdk-provider/package.json lists tslib under dependencies to avoid runtime module-not-found.
"dependencies": { + "tslib": "^2.7.0", /* existing deps */ }
packages/ai-sdk-provider/src/tools.ts (3)
5-11
: Schema key should be chainId (camelCase), not chain_idMatches generated Nebula types and prevents parsing mismatches.
const AgentActionSignTransactionData = z.object({ - chain_id: z.number(), + chainId: z.number(), function: z.string().nullable().optional(), to: z.string(), value: z.string(), data: z.string(), });
2-2
: Invalid zod import pathImport from "zod", not "zod/v4".
-import { z } from "zod/v4"; +import { z } from "zod";
13-22
: Swap intent fields should be camelCaseAlign with generated types (originChainId, destinationChainId, etc.).
const AgentActionSignSwapDataIntent = z.object({ - origin_chain_id: z.number(), - origin_token_address: z.string(), - destination_chain_id: z.number(), - destination_token_address: z.string(), + originChainId: z.number(), + originTokenAddress: z.string(), + destinationChainId: z.number(), + destinationTokenAddress: z.string(), amount: z.string(), sender: z.string(), receiver: z.string(), - max_steps: z.number(), + maxSteps: z.number(), });packages/ai-sdk-provider/src/provider.ts (4)
119-121
: Guard lastUserMessage to avoid [undefined]Prevent sending
[undefined]
when no user/tool message exists.- const messages = this.settings.context?.session_id - ? [lastUserMessage] - : allMessages; + const messages = + this.settings.context?.session_id && lastUserMessage + ? [lastUserMessage] + : allMessages;
196-200
: Advertise SSE in streaming requestAdd Accept: text/event-stream for doStream.
- headers: this.getHeaders(), + headers: { ...this.getHeaders(), Accept: "text/event-stream" },
428-435
: Don’t stringify tool-call inputEmit the object so the AI SDK can validate against Tool.parameters.
- input: JSON.stringify(input), + input,
62-67
: Flatten system message content to stringThirdweb API likely expects a string, not an array of parts.
- case "system": - return { - role: message.role, - content: message.content, - }; + case "system": { + const content = message.content + .map((p) => (p.type === "text" ? p.text : "")) + .join(""); + return { role: "system", content }; + }apps/playground-web/src/components/response.tsx (1)
22-24
: Remove brittle custom memo comparator; use named component with explicit return type.Comparator ignores prop changes like
components
,remarkPlugins
, etc., causing stale renders. Prefer default shallow compare and add explicit return type per guidelines.-export const Response = memo( - ({ className, ...props }: ResponseProps) => ( +export const Response = memo(function ResponseComponent( + { className, ...props }: ResponseProps, +): JSX.Element { <Streamdown components={{ /* ...unchanged... */ }} remarkPlugins={[remarkGfm]} className={cn( "size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0", className, )} {...props} /> - ), - (prevProps, nextProps) => prevProps.children === nextProps.children, -); +});Also applies to: 158-160
🧹 Nitpick comments (14)
apps/playground-web/src/components/loader.tsx (3)
1-3
: Clarify RSC boundary: make this a Client Component (likely usage) or mark server-only.This spinner is commonly used in interactive client UI. If that’s the intent, add "use client"; otherwise add
import "server-only";
. Please confirm.+'use client'; import type { HTMLAttributes } from "react"; import { cn } from "@/lib/utils";
1-1
: Add explicit return types per repo TypeScript guidelines.Keep arrows if you prefer, but annotate returns explicitly.
-import type { HTMLAttributes } from "react"; +import type { HTMLAttributes, JSX } from "react";-const LoaderIcon = ({ size = 16 }: LoaderIconProps) => ( +const LoaderIcon = ({ size = 16 }: LoaderIconProps): JSX.Element => (-export const Loader = ({ className, size = 16, ...props }: LoaderProps) => ( +export const Loader = ({ className, size = 16, ...props }: LoaderProps): JSX.Element => (Also applies to: 8-8, 86-86
17-18
: Avoid duplicate IDs from clipPath; it’s unnecessary here.The rectangular clipPath doesn’t change rendering and risks ID collisions with multiple loaders on the page. Remove it.
- <g clipPath="url(#clip0_2393_1490)"> + {/* clipPath removed */} <path d="M8 0V4" stroke="currentColor" strokeWidth="1.5" /> ... - </g> - <defs> - <clipPath id="clip0_2393_1490"> - <rect fill="white" height="16" width="16" /> - </clipPath> - </defs>Also applies to: 73-79
apps/playground-web/package.json (1)
4-4
: Track budgets for new heavy deps & lazy-load in client components.
- Add size-limit scripts and budgets to apps/playground-web/package.json (include @ai-sdk/react, ai, streamdown, shiki) per repo guidelines
- Refactor client components (e.g. chat-container.tsx, response.tsx, code-example.tsx, code.client.tsx) to lazy-load these heavy libs via dynamic imports
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (5)
189-201
: Replace inline styles with Tailwind per app guidelinesUse className="w-full" instead of style width.
- <TransactionButton - style={{ - width: "100%", - }} + <TransactionButton + className="w-full"Applies also to the swap button at Lines 263-266.
107-107
: Remove stray console.logLeftover debug log in the render path.
- console.log("---sign_swap", part);
52-55
: sessionId vs session_id naming driftYou set sessionId in the request body, while server/provider types use session_id. Align keys end-to-end or translate in the API route to avoid dropped sessions.
- body: { - sessionId, - }, + body: { + session_id: sessionId, + },Ensure the API route forwards context.session_id accordingly.
Also applies to: 210-214, 284-288
173-179
: Be resilient to pending schema rename (chainId vs chain_id)Downstream tools.ts is moving to camelCase (chainId). Support both to avoid breaking the UI during transition.
- chain_id: input?.chain_id || 8453, + chain_id: (input as any)?.chain_id ?? (input as any)?.chainId ?? 8453, - to: input?.to || "", + to: (input as any)?.to || "", - data: (input?.data as `0x${string}`) || "0x", + data: ((input as any)?.data as `0x${string}`) || "0x", - value: input?.value ? BigInt(input.value) : BigInt(0), + value: + (input as any)?.value !== undefined + ? BigInt((input as any).value) + : BigInt(0),And similarly for the swap transaction: read from input.transaction.chain_id or .chainId.
Also applies to: 245-253
186-221
: Validate required fields before preparing transactionDisable the button or show an error if
to
is empty or value parsing fails to avoid runtime errors in prepareTransaction.- <TransactionButton + <TransactionButton className="w-full" - transaction={() => - prepareTransaction({ + disabled={!transactionData.to} + transaction={() => { + // basic guard + if (!transactionData.to) { + throw new Error("Missing 'to' address"); + } + return prepareTransaction({ client: THIRDWEB_CLIENT, chain: defineChain(transactionData.chain_id), to: transactionData.to, data: transactionData.data, value: transactionData.value, - }) - } + }); + }}Repeat for the swap button block.
Also applies to: 260-295
packages/ai-sdk-provider/src/types.ts (1)
31-50
: Context keys use snake_case; document mapping at the API boundarySince app code often uses camelCase (e.g., sessionId), note the expected snake_case here or add a small adapter in provider to reduce consumer errors.
packages/ai-sdk-provider/src/provider.ts (1)
129-137
: Consider addingAccept: application/json
to non-streaming and JSON parse errorsMinor hardening; optional.
- headers: this.getHeaders(), + headers: { ...this.getHeaders(), Accept: "application/json" },And keep the SSE Accept only in doStream.
Also applies to: 193-201
apps/playground-web/src/components/response.tsx (3)
103-107
: Preserve caller-provided classes on lists and table cells.Current literals override incoming
className
. Merge withcn()
.<ol - {...cleanedProps(props)} - className="mb-4 list-outside list-decimal pl-5 [&_ol_li:first-of-type]:mt-1.5 [&_ul_li:first-of-type]:mt-1.5" + {...cleanedProps(props)} + className={cn( + "mb-4 list-outside list-decimal pl-5 [&_ol_li:first-of-type]:mt-1.5 [&_ul_li:first-of-type]:mt-1.5", + props.className, + )} />- <TableCell {...cleanedProps(props)} className="text-left" /> + <TableCell + {...cleanedProps(props)} + className={cn("text-left", props.className)} + />- <TableHead - {...cleanedProps(props)} - className="text-left text-foreground" - > + <TableHead + {...cleanedProps(props)} + className={cn("text-left text-foreground", props.className)} + > {c} </TableHead><ul - {...cleanedProps(props)} - className="mb-4 list-outside list-disc pl-5 [&_ol_li:first-of-type]:mt-1.5 [&_ul_li:first-of-type]:mt-1.5" + {...cleanedProps(props)} + className={cn( + "mb-4 list-outside list-disc pl-5 [&_ol_li:first-of-type]:mt-1.5 [&_ul_li:first-of-type]:mt-1.5", + props.className, + )} />Also applies to: 128-130, 132-139, 144-147
151-157
: Consider merging user-provided plugins/components instead of overwriting.Spreading
{...props}
last means caller can fully replacecomponents
/remarkPlugins
. If the intent is “defaults + user additions,” destructurecomponents
/remarkPlugins
from props and merge, e.g.,components={{ ...defaults, ...(userComponents ?? {}) }}
andremarkPlugins={[remarkGfm, ...(userPlugins ?? [])]}
.If you want, I can push a patch that safely merges defaults and preserves user overrides precedence.
162-162
: displayName becomes redundant with a named memoized function.If you adopt the named
memo(function ResponseComponent …)
,Response.displayName
is optional. Remove for brevity.-Response.displayName = "Response";
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (30)
.changeset/many-pants-tease.md
(1 hunks)apps/dashboard/package.json
(1 hunks)apps/nebula/package.json
(1 hunks)apps/playground-web/package.json
(3 hunks)apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
(1 hunks)apps/playground-web/src/app/ai/ai-sdk/page.tsx
(1 hunks)apps/playground-web/src/app/ai/components/ChatPageContent.tsx
(1 hunks)apps/playground-web/src/app/api/chat/route.ts
(1 hunks)apps/playground-web/src/app/navLinks.ts
(1 hunks)apps/playground-web/src/components/code/code-example.tsx
(1 hunks)apps/playground-web/src/components/conversation.tsx
(1 hunks)apps/playground-web/src/components/loader.tsx
(1 hunks)apps/playground-web/src/components/message.tsx
(1 hunks)apps/playground-web/src/components/prompt-input.tsx
(1 hunks)apps/playground-web/src/components/reasoning.tsx
(1 hunks)apps/playground-web/src/components/response.tsx
(1 hunks)apps/playground-web/src/components/ui/avatar.tsx
(1 hunks)apps/portal/package.json
(1 hunks)package.json
(1 hunks)packages/ai-sdk-provider/README.md
(1 hunks)packages/ai-sdk-provider/biome.json
(1 hunks)packages/ai-sdk-provider/package.json
(1 hunks)packages/ai-sdk-provider/src/exports/thirdweb.ts
(1 hunks)packages/ai-sdk-provider/src/provider.ts
(1 hunks)packages/ai-sdk-provider/src/tools.ts
(1 hunks)packages/ai-sdk-provider/src/types.ts
(1 hunks)packages/ai-sdk-provider/tsconfig.base.json
(1 hunks)packages/ai-sdk-provider/tsconfig.build.json
(1 hunks)packages/ai-sdk-provider/tsconfig.json
(1 hunks)packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- apps/portal/package.json
- .changeset/many-pants-tease.md
🚧 Files skipped from review as they are similar to previous changes (16)
- packages/ai-sdk-provider/README.md
- packages/ai-sdk-provider/tsconfig.build.json
- packages/ai-sdk-provider/tsconfig.json
- packages/ai-sdk-provider/src/exports/thirdweb.ts
- apps/playground-web/src/app/navLinks.ts
- apps/playground-web/src/components/message.tsx
- apps/playground-web/src/app/api/chat/route.ts
- apps/playground-web/src/components/ui/avatar.tsx
- packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
- packages/ai-sdk-provider/package.json
- apps/playground-web/src/components/code/code-example.tsx
- apps/playground-web/src/components/conversation.tsx
- apps/playground-web/src/app/ai/ai-sdk/page.tsx
- apps/playground-web/src/components/reasoning.tsx
- packages/ai-sdk-provider/biome.json
- apps/playground-web/src/components/prompt-input.tsx
🧰 Additional context used
📓 Path-based instructions (4)
package.json
📄 CodeRabbit inference engine (CLAUDE.md)
Track bundle budgets via
package.json#size-limit
Files:
package.json
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}
: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/types
or localtypes.ts
barrels
Prefer type aliases over interface except for nominal shapes
Avoidany
andunknown
unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial
,Pick
, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
Files:
apps/playground-web/src/components/loader.tsx
packages/ai-sdk-provider/src/provider.ts
packages/ai-sdk-provider/src/types.ts
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
apps/playground-web/src/components/response.tsx
packages/ai-sdk-provider/src/tools.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
apps/playground-web/src/components/loader.tsx
packages/ai-sdk-provider/src/provider.ts
packages/ai-sdk-provider/src/types.ts
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
apps/playground-web/src/components/response.tsx
packages/ai-sdk-provider/src/tools.ts
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}
: Import UI primitives from@/components/ui/*
(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLink
for internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()
from@/lib/utils
for conditional class logic
Use design system tokens (e.g.,bg-card
,border-border
,text-muted-foreground
)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()
to retrieve JWT from cookies on server side
UseAuthorization: Bearer
header – never embed tokens in URLs
Return typed results (e.g.,Project[]
,User[]
) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query
)
Use descriptive, stablequeryKeys
for React Query cache hits
ConfigurestaleTime
/cacheTime
in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-js
in server components
Files:
apps/playground-web/src/components/loader.tsx
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
apps/playground-web/src/components/response.tsx
🧠 Learnings (13)
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Build all packages using `pnpm build` and specific packages with dependencies using `turbo run build --filter=./packages/*`
Applied to files:
package.json
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Monorepo is managed with Turborepo and pnpm workspaces
Applied to files:
package.json
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Never hard-code colors – always go through Tailwind variables.
Applied to files:
apps/playground-web/src/components/loader.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to **/*.{ts,tsx} : Re-use shared types from `@/types` or local `types.ts` barrels
Applied to files:
packages/ai-sdk-provider/tsconfig.base.json
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Server Components (Node edge): Start files with `import "server-only";`
Applied to files:
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Client Components (browser): Begin files with `'use client';`
Applied to files:
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/api/**/*.{ts,tsx} : Prefix files with `import "server-only";` so they never end up in the client bundle.
Applied to files:
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/components/*.client.tsx : Client components must start with `'use client';` before imports.
Applied to files:
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{ts,tsx} : Client-side only: never import `posthog-js` in server components.
Applied to files:
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
📚 Learning: 2025-08-07T17:24:31.965Z
Learnt from: MananTank
PR: thirdweb-dev/js#7812
File: apps/dashboard/src/app/(app)/team/~/~project/[[...paths]]/page.tsx:1-11
Timestamp: 2025-08-07T17:24:31.965Z
Learning: In Next.js App Router, page components (page.tsx files) are server components by default and do not require the "server-only" import directive. The "server-only" directive is primarily used for utility functions, API helpers, and data access modules that should never be included in the client bundle.
Applied to files:
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{ts,tsx} : Export default async functions without `'use client';` – they run on the Node edge.
Applied to files:
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
📚 Learning: 2025-07-31T16:17:42.753Z
Learnt from: MananTank
PR: thirdweb-dev/js#7768
File: apps/playground-web/src/app/navLinks.ts:1-1
Timestamp: 2025-07-31T16:17:42.753Z
Learning: Configuration files that import and reference React components (like icon components from lucide-react) need the "use client" directive, even if they primarily export static data, because the referenced components need to be executed in a client context when used by other client components.
Applied to files:
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Never import `posthog-js` in server components
Applied to files:
apps/playground-web/src/app/ai/components/ChatPageContent.tsx
🧬 Code graph analysis (5)
packages/ai-sdk-provider/src/provider.ts (2)
packages/ai-sdk-provider/src/types.ts (3)
ThirdwebConfig
(12-29)ThirdwebSettings
(31-50)DEFAULT_BASE_URL
(10-10)packages/ai-sdk-provider/src/tools.ts (1)
createTools
(51-75)
packages/ai-sdk-provider/src/types.ts (2)
packages/ai-sdk-provider/src/exports/thirdweb.ts (1)
ThirdwebAiMessage
(2-2)packages/ai-sdk-provider/src/tools.ts (5)
SignTransactionInput
(77-79)TransactionOutput
(34-36)SignSwapInput
(80-80)MonitorTransactionInput
(81-83)MonitorTransactionOutput
(47-49)
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (2)
packages/ai-sdk-provider/src/types.ts (1)
ThirdwebAiMessage
(52-71)apps/playground-web/src/lib/client.ts (1)
THIRDWEB_CLIENT
(20-43)
apps/playground-web/src/components/response.tsx (2)
apps/portal/src/components/code/code.client.tsx (1)
CodeClient
(27-54)apps/portal/src/components/ui/table.tsx (5)
TableContainer
(150-150)TableBody
(144-144)TableCell
(148-148)TableHead
(146-146)TableHeader
(143-143)
packages/ai-sdk-provider/src/tools.ts (2)
packages/nebula/src/client/types.gen.ts (3)
AgentActionSignTransactionData
(460-481)AgentActionSignSwapDataIntent
(352-385)AgentActionSignSwapData
(338-347)packages/ai-sdk-provider/src/types.ts (1)
ThirdwebConfig
(12-29)
🔇 Additional comments (10)
apps/playground-web/src/components/loader.tsx (1)
82-85
: Props typing looks good.Nice: type alias over interface, extends HTMLAttributes, and a focused
size?: number
.apps/nebula/package.json (1)
35-35
: Verify Shiki v3 API usage and buildDouble-check that the
codeToHtml
import ingetCodeHtml.tsx
and theBundledLanguage
type incode.client.tsx
still resolve under Shiki 3’s ESM-only bundles (entry points, wasm assets), then runpnpm install && pnpm --filter nebula-app build
to confirm no bundler/runtime errors.package.json (1)
65-65
: LGTM: include ai-sdk-provider in playground dev filter.This ensures local edits to the new package are picked up by the playground.
packages/ai-sdk-provider/tsconfig.base.json (2)
21-24
: Confirm DOM lib usage is intentionalIncluding "DOM" widens globals in a server-leaning package. If only needed for fetch typing, consider relying on undici types or a minimal fetch type to avoid accidental browser globals bleed.
25-29
: NodeNext + verbatimModuleSyntax: ensure package.json is ESMWith "module": "NodeNext" and "verbatimModuleSyntax": true, verify package.json has "type": "module" and emitted paths end with .js in TS imports (you already do). Prevent CJS/ESM interop issues.
Also applies to: 41-44
packages/ai-sdk-provider/src/types.ts (2)
10-29
: Config surface looks goodDEFAULT_BASE_URL and ThirdwebConfig fields are clear; no issues found.
52-71
: ThirdwebAiMessage tool payload types align with the UITypes match the client usage; keep them in sync with tools.ts schemas after the pending fixes.
packages/ai-sdk-provider/src/tools.ts (1)
77-84
: Exported input types must reflect the updated schemasAfter the schema fixes, these inferred types will match the UI’s expectations.
apps/playground-web/src/components/response.tsx (2)
164-170
: Helper looks good as-is.Clear prop scrubbing for
node
, typed correctly.
26-33
: Render a secure native anchor and preserve incoming classes.Use
<a>
(not Lucide’s icon), addrel="noopener noreferrer"
for target-blank links, and mergeprops.className
.- a: (props) => ( - <Link - href={props.href ?? "#"} - target="_blank" - {...cleanedProps(props)} - className="mt-4 underline decoration-muted-foreground/50 decoration-dotted underline-offset-[5px] hover:text-foreground hover:decoration-foreground hover:decoration-solid" - /> - ), + a: (props) => ( + <a + href={props.href ?? "#"} + target="_blank" + rel="noopener noreferrer" + {...cleanedProps(props)} + className={cn( + "mt-4 underline decoration-muted-foreground/50 decoration-dotted underline-offset-[5px] hover:text-foreground hover:decoration-foreground hover:decoration-solid", + props.className, + )} + /> + ),⛔ Skipped due to learnings
Learnt from: jnsdls PR: thirdweb-dev/js#7365 File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ProjectFTUX.tsx:16-17 Timestamp: 2025-06-18T04:30:04.326Z Learning: Next.js Link component fully supports both internal and external URLs and works appropriately with all standard anchor attributes including target="_blank", rel="noopener noreferrer", etc. Using Link for external URLs is completely appropriate and recommended.
Learnt from: MananTank PR: thirdweb-dev/js#7812 File: apps/dashboard/src/app/(app)/(dashboard)/published-contract/components/token-banner.tsx:48-60 Timestamp: 2025-08-07T20:43:21.864Z Learning: In the TokenBanner component at apps/dashboard/src/app/(app)/(dashboard)/published-contract/components/token-banner.tsx, the Link components use target="_blank" with internal application routes (starting with "/") to open pages in new tabs within the same application. These internal links do not require rel="noopener noreferrer" security attributes, which are only needed for external URLs.
Learnt from: jnsdls PR: thirdweb-dev/js#7365 File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ProjectFTUX.tsx:16-17 Timestamp: 2025-06-18T04:27:16.172Z Learning: Next.js Link component supports external URLs without throwing errors. When used with absolute URLs (like https://...), it behaves like a regular anchor tag without client-side routing, but does not cause runtime crashes or errors as previously believed.
if (currentEvent === "init") { | ||
const parsed = JSON.parse(data); | ||
if (parsed.session_id) { | ||
controller.enqueue({ | ||
type: "response-metadata", | ||
id: parsed.session_id || "", | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Send response metadata as metadata, not id
Expose session_id via response-metadata.metadata so clients can consume it.
- if (parsed.session_id) {
- controller.enqueue({
- type: "response-metadata",
- id: parsed.session_id || "",
- });
- }
+ if (parsed.session_id) {
+ controller.enqueue({
+ type: "response-metadata",
+ metadata: { session_id: parsed.session_id },
+ });
+ }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
if (currentEvent === "init") { | |
const parsed = JSON.parse(data); | |
if (parsed.session_id) { | |
controller.enqueue({ | |
type: "response-metadata", | |
id: parsed.session_id || "", | |
}); | |
} | |
if (currentEvent === "init") { | |
const parsed = JSON.parse(data); | |
if (parsed.session_id) { | |
controller.enqueue({ | |
type: "response-metadata", | |
metadata: { session_id: parsed.session_id }, | |
}); | |
} |
🤖 Prompt for AI Agents
In packages/ai-sdk-provider/src/provider.ts around lines 351 to 358, the code
currently enqueues response-metadata with session_id set as id; change the shape
so session_id is exposed under a metadata property instead. Specifically, when
parsed.session_id exists, enqueue an object like { type: "response-metadata",
metadata: { session_id: parsed.session_id } } (remove or leave id empty as
appropriate for downstream consumers) so clients can read session_id from
response-metadata.metadata.
switch (parsed.type) { | ||
case "sign_transaction": | ||
toolName = "sign_transaction"; | ||
input = { | ||
chain_id: parsed.data.chain_id, | ||
to: parsed.data.to, | ||
value: parsed.data.value, | ||
data: parsed.data.data, | ||
function: parsed.data.function, | ||
}; | ||
break; | ||
|
||
case "sign_swap": | ||
toolName = "sign_swap"; | ||
input = { | ||
action: parsed.data.action, | ||
from: parsed.data.from, | ||
to: parsed.data.to, | ||
intent: parsed.data.intent, | ||
transaction: parsed.data.transaction, | ||
}; | ||
break; | ||
|
||
case "monitor_transaction": | ||
toolName = "monitor_transaction"; | ||
input = { | ||
transactionId: parsed.data.transaction_id, | ||
}; | ||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Align tool input shapes and keys (camelCase + monitor_transaction)
- sign_transaction: use chainId (camelCase) to match tools schema.
- monitor_transaction: key is transaction_id per schema.
- case "sign_transaction":
+ case "sign_transaction":
toolName = "sign_transaction";
input = {
- chain_id: parsed.data.chain_id,
+ chainId: parsed.data.chainId ?? parsed.data.chain_id,
to: parsed.data.to,
value: parsed.data.value,
data: parsed.data.data,
function: parsed.data.function,
};
break;
...
- case "monitor_transaction":
+ case "monitor_transaction":
toolName = "monitor_transaction";
input = {
- transactionId: parsed.data.transaction_id,
+ transaction_id: parsed.data.transaction_id,
};
break;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
switch (parsed.type) { | |
case "sign_transaction": | |
toolName = "sign_transaction"; | |
input = { | |
chain_id: parsed.data.chain_id, | |
to: parsed.data.to, | |
value: parsed.data.value, | |
data: parsed.data.data, | |
function: parsed.data.function, | |
}; | |
break; | |
case "sign_swap": | |
toolName = "sign_swap"; | |
input = { | |
action: parsed.data.action, | |
from: parsed.data.from, | |
to: parsed.data.to, | |
intent: parsed.data.intent, | |
transaction: parsed.data.transaction, | |
}; | |
break; | |
case "monitor_transaction": | |
toolName = "monitor_transaction"; | |
input = { | |
transactionId: parsed.data.transaction_id, | |
}; | |
break; | |
switch (parsed.type) { | |
case "sign_transaction": | |
toolName = "sign_transaction"; | |
input = { | |
chainId: parsed.data.chainId ?? parsed.data.chain_id, | |
to: parsed.data.to, | |
value: parsed.data.value, | |
data: parsed.data.data, | |
function: parsed.data.function, | |
}; | |
break; | |
case "sign_swap": | |
toolName = "sign_swap"; | |
input = { | |
action: parsed.data.action, | |
from: parsed.data.from, | |
to: parsed.data.to, | |
intent: parsed.data.intent, | |
transaction: parsed.data.transaction, | |
}; | |
break; | |
case "monitor_transaction": | |
toolName = "monitor_transaction"; | |
input = { | |
transaction_id: parsed.data.transaction_id, | |
}; | |
break; |
🤖 Prompt for AI Agents
In packages/ai-sdk-provider/src/provider.ts around lines 391 to 419, the tool
input shapes don't match the expected schema: for the "sign_transaction" case
change chain_id -> chainId (use camelCase) and ensure the other keys follow the
tool schema naming, and for the "monitor_transaction" case change transactionId
-> transaction_id (use the schema's snake_case key); update the input object
keys accordingly so they exactly match the tools' expected field names.
1c89599
to
63d12c9
Compare
63d12c9
to
3a95998
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (14)
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (3)
184-186
: Guard against invalid tx payload to avoid runtime errors.Don’t call
prepareTransaction
with emptyto
/data
/value
.if (!account) { return <ConnectButton client={THIRDWEB_CLIENT} />; } + const isValidTx = + Boolean(transactionData.to) || + transactionData.data !== "0x" || + transactionData.value > 0n; + if (!isValidTx) { + return ( + <div className="py-4 text-sm text-muted-foreground"> + Waiting for transaction details… + </div> + ); + } + return ( <div className="py-4"> <TransactionButton - style={{ - width: "100%", - }} + className="w-full" transaction={() => prepareTransaction({ client: THIRDWEB_CLIENT, chain: defineChain(transactionData.chain_id), to: transactionData.to, data: transactionData.data, value: transactionData.value, }) } onTransactionSent={(transaction) => { addToolResult({ tool: "sign_transaction", toolCallId, output: { transaction_hash: transaction.transactionHash, chain_id: transaction.chain.id, }, }); sendMessage(undefined, { body: { sessionId, }, }); }} + onError={(err) => { + console.error("Transaction failed:", err); + sendMessage({ text: "Transaction signing failed." }, { body: { sessionId } }); + }} > Sign Transaction </TransactionButton> </div> );Also applies to: 188-223
258-260
: Mirror tx validation and add error handling for swap-signing path.Keep both signing paths consistent and resilient.
if (!account) { return <ConnectButton client={THIRDWEB_CLIENT} />; } + const isValidTx = + Boolean(transactionData.to) || + transactionData.data !== "0x" || + transactionData.value > 0n; + if (!isValidTx) { + return ( + <div className="py-4 text-sm text-muted-foreground"> + Waiting for swap transaction details… + </div> + ); + } + return ( <div className="py-4"> - <TransactionButton - style={{ - width: "100%", - }} + <TransactionButton + className="w-full" transaction={() => prepareTransaction({ client: THIRDWEB_CLIENT, chain: defineChain(transactionData.chain_id), to: transactionData.to, data: transactionData.data, value: transactionData.value, }) } onTransactionSent={(transaction) => { addToolResult({ tool: "sign_swap", toolCallId, output: { transaction_hash: transaction.transactionHash, chain_id: transaction.chain.id, }, }); sendMessage(undefined, { body: { sessionId, }, }); }} + onError={(err) => { + console.error("Swap transaction failed:", err); + sendMessage({ text: "Swap signing failed." }, { body: { sessionId } }); + }} > Sign swap </TransactionButton> </div> );Also applies to: 262-295
31-32
: Potential secret leakage: don’t import a client that may embedsecretKey
in the browser.Move secrets to server-only modules; use a client-safe public client on the web.
-import { THIRDWEB_CLIENT } from "../../../../lib/client"; +// Use a client-safe client that never embeds secretKey +import { THIRDWEB_PUBLIC_CLIENT as THIRDWEB_CLIENT } from "@/lib/public-client";Create
apps/playground-web/src/lib/public-client.ts
:"use client"; import { createThirdwebClient } from "thirdweb"; export const THIRDWEB_PUBLIC_CLIENT = createThirdwebClient({ clientId: process.env.NEXT_PUBLIC_THIRDWEB_CLIENT_ID as string, });packages/ai-sdk-provider/src/provider.ts (6)
193-201
: Advertise SSE support viaAccept: text/event-stream
.Some backends rely on this header for streaming.
- headers: this.getHeaders(), + headers: { ...this.getHeaders(), Accept: "text/event-stream" },
62-67
: Flattensystem
message content to a string.thirdweb API likely expects a string, not parts.
- case "system": - return { - role: message.role, - content: message.content, - }; + case "system": { + const content = Array.isArray(message.content) + ? message.content + .map((p) => (p.type === "text" ? p.text : "")) + .join("") + : String(message.content ?? ""); + return { role: "system", content }; + }
115-121
: GuardlastUserMessage
to avoid[undefined]
.Only prefer the last user message when it exists.
- const messages = this.settings.context?.session_id - ? [lastUserMessage] - : allMessages; + const messages = + this.settings.context?.session_id && lastUserMessage + ? [lastUserMessage] + : allMessages;
179-185
: Apply the samelastUserMessage
guard in streaming path.Keep behavior consistent across non-stream and stream.
- const messages = this.settings.context?.session_id - ? [lastUserMessage] - : allMessages; + const messages = + this.settings.context?.session_id && lastUserMessage + ? [lastUserMessage] + : allMessages;
351-358
: Send response metadata in themetadata
field, not asid
.This matches the AI SDK stream contract and lets clients read
session_id
.- if (parsed.session_id) { - controller.enqueue({ - type: "response-metadata", - id: parsed.session_id || "", - }); - } + if (parsed.session_id) { + controller.enqueue({ + type: "response-metadata", + metadata: { session_id: parsed.session_id }, + }); + }
391-419
: Tool-call mapping: fix input shapes and avoid stringifyinginput
.
- Use camelCase
chainId
(fallback to snake case if present).- Use
transaction_id
for monitor.- Pass
input
as an object (not JSON string).switch (parsed.type) { case "sign_transaction": toolName = "sign_transaction"; input = { - chain_id: parsed.data.chain_id, + chainId: parsed.data.chainId ?? parsed.data.chain_id, to: parsed.data.to, value: parsed.data.value, data: parsed.data.data, function: parsed.data.function, }; break; case "sign_swap": toolName = "sign_swap"; input = { action: parsed.data.action, from: parsed.data.from, to: parsed.data.to, intent: parsed.data.intent, transaction: parsed.data.transaction, }; break; case "monitor_transaction": toolName = "monitor_transaction"; input = { - transactionId: parsed.data.transaction_id, + transaction_id: parsed.data.transaction_id, }; break; ... // Emit as tool call controller.enqueue({ type: "tool-call", toolCallId, toolName, - input: JSON.stringify(input), + input, providerExecuted: false, });Also applies to: 428-435
packages/ai-sdk-provider/src/tools.ts (5)
2-2
: Fix zod import path.
zod/v4
will fail; import fromzod
.-import { z } from "zod/v4"; +import { z } from "zod";
5-11
: Align tx schema with generated types: usechainId
(camelCase).Matches
packages/nebula/*/types.gen.ts
.const AgentActionSignTransactionData = z.object({ - chain_id: z.number(), + chainId: z.number(), function: z.string().nullable().optional(), to: z.string(), value: z.string(), data: z.string(), });
13-22
: Swap intent fields should be camelCase;max_steps
→maxSteps
.Mirror Nebula types.
const AgentActionSignSwapDataIntent = z.object({ - origin_chain_id: z.number(), - origin_token_address: z.string(), - destination_chain_id: z.number(), - destination_token_address: z.string(), + originChainId: z.number(), + originTokenAddress: z.string(), + destinationChainId: z.number(), + destinationTokenAddress: z.string(), amount: z.string(), sender: z.string(), receiver: z.string(), - max_steps: z.number(), + maxSteps: z.number(), });
24-28
: Include token shapes and allow nullableaction
in swap schema.Nebula includes
from
/to
token objects;action
can be null.+const AgentActionSignSwapDataToken = z.object({ + address: z.string(), + chain_id: z.number(), // token keeps snake_case per generated types + amount: z.string(), + symbol: z.string(), + decimals: z.number(), + price: z.number().nullable(), +}); + const AgentActionSignSwapData = z.object({ - action: z.string(), + action: z.string().nullable(), intent: AgentActionSignSwapDataIntent, + from: AgentActionSignSwapDataToken, + to: AgentActionSignSwapDataToken, transaction: AgentActionSignTransactionData, });
51-75
: Use AI SDK v4Tool
shape:parameters
(dropoutputSchema
).This will type-check and validate inputs at runtime.
-export function createTools(_config: ThirdwebConfig): Record<string, Tool> { - return { - sign_transaction: { +export function createTools(_config: ThirdwebConfig) { + return { + sign_transaction: { id: "thirdweb.sign_transaction" as const, name: "sign_transaction", description: "Sign a transaction", - inputSchema: AgentActionSignTransactionData, - outputSchema: AgentActionTransactionOutputData, - } as const, - sign_swap: { + parameters: AgentActionSignTransactionData, + }, + sign_swap: { id: "thirdweb.sign_swap" as const, name: "sign_swap", description: "Sign a swap transaction", - inputSchema: AgentActionSignSwapData, - outputSchema: AgentActionTransactionOutputData, - } as const, - monitor_transaction: { + parameters: AgentActionSignSwapData, + }, + monitor_transaction: { id: "thirdweb.monitor_transaction" as const, name: "monitor_transaction", description: "Monitor a transaction", - inputSchema: AgentActionMonitorTransactionData, - outputSchema: AgentActionMonitorTransactionOutputData, - } as const, - } as const; + parameters: AgentActionMonitorTransactionData, + }, + } as const satisfies Record<string, Tool>; }
🧹 Nitpick comments (5)
apps/playground-web/src/app/ai/ai-sdk/page.tsx (2)
5-6
: Prefer path aliases over deep relatives for consistency.Use
@/
paths to match repo conventions.-import { PageLayout } from "../../../components/blocks/APIHeader"; -import { createMetadata } from "../../../lib/metadata"; +import { PageLayout } from "@/components/blocks/APIHeader"; +import { createMetadata } from "@/lib/metadata";
25-40
: Add explicit return types for components.Keeps with TS style guide and improves DX.
-export default function Page() { +export default function Page(): React.ReactElement { return ( ... ); }Apply similarly to
ServerCodeExample
andChatExample
.-function ServerCodeExample() { +function ServerCodeExample(): React.ReactElement {-function ChatExample() { +function ChatExample(): React.ReactElement {apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (3)
30-30
: Use path alias for local imports.Minor consistency fix.
-import { Loader } from "../../../../components/loader"; +import { Loader } from "@/components/loader";
107-111
: Remove leftover debug log.Avoid shipping console noise.
- console.log("---sign_swap", part);
191-201
: Use Tailwind instead of inline styles.Project guideline: Tailwind only.
- <TransactionButton - style={{ - width: "100%", - }} + <TransactionButton + className="w-full"
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (29)
.changeset/many-pants-tease.md
(1 hunks)apps/dashboard/package.json
(1 hunks)apps/nebula/package.json
(1 hunks)apps/playground-web/package.json
(3 hunks)apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
(1 hunks)apps/playground-web/src/app/ai/ai-sdk/page.tsx
(1 hunks)apps/playground-web/src/app/api/chat/route.ts
(1 hunks)apps/playground-web/src/app/navLinks.ts
(1 hunks)apps/playground-web/src/components/code/code-example.tsx
(1 hunks)apps/playground-web/src/components/conversation.tsx
(1 hunks)apps/playground-web/src/components/loader.tsx
(1 hunks)apps/playground-web/src/components/message.tsx
(1 hunks)apps/playground-web/src/components/prompt-input.tsx
(1 hunks)apps/playground-web/src/components/reasoning.tsx
(1 hunks)apps/playground-web/src/components/response.tsx
(1 hunks)apps/playground-web/src/components/ui/avatar.tsx
(1 hunks)apps/portal/package.json
(1 hunks)package.json
(1 hunks)packages/ai-sdk-provider/README.md
(1 hunks)packages/ai-sdk-provider/biome.json
(1 hunks)packages/ai-sdk-provider/package.json
(1 hunks)packages/ai-sdk-provider/src/exports/thirdweb.ts
(1 hunks)packages/ai-sdk-provider/src/provider.ts
(1 hunks)packages/ai-sdk-provider/src/tools.ts
(1 hunks)packages/ai-sdk-provider/src/types.ts
(1 hunks)packages/ai-sdk-provider/tsconfig.base.json
(1 hunks)packages/ai-sdk-provider/tsconfig.build.json
(1 hunks)packages/ai-sdk-provider/tsconfig.json
(1 hunks)packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- apps/portal/package.json
- .changeset/many-pants-tease.md
🚧 Files skipped from review as they are similar to previous changes (22)
- apps/nebula/package.json
- apps/playground-web/src/components/conversation.tsx
- apps/playground-web/src/components/ui/avatar.tsx
- packages/ai-sdk-provider/README.md
- packages/ai-sdk-provider/src/types.ts
- apps/playground-web/src/app/navLinks.ts
- packages/ai-sdk-provider/tsconfig.build.json
- apps/playground-web/src/components/loader.tsx
- packages/ai-sdk-provider/tsconfig.json
- packages/ai-sdk-provider/tsconfig.base.json
- apps/playground-web/src/app/api/chat/route.ts
- apps/playground-web/src/components/response.tsx
- apps/playground-web/package.json
- packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
- apps/dashboard/package.json
- apps/playground-web/src/components/reasoning.tsx
- apps/playground-web/src/components/code/code-example.tsx
- packages/ai-sdk-provider/package.json
- apps/playground-web/src/components/message.tsx
- packages/ai-sdk-provider/biome.json
- package.json
- apps/playground-web/src/components/prompt-input.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}
: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/types
or localtypes.ts
barrels
Prefer type aliases over interface except for nominal shapes
Avoidany
andunknown
unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial
,Pick
, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
Files:
packages/ai-sdk-provider/src/exports/thirdweb.ts
apps/playground-web/src/app/ai/ai-sdk/page.tsx
packages/ai-sdk-provider/src/provider.ts
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
packages/ai-sdk-provider/src/tools.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
packages/ai-sdk-provider/src/exports/thirdweb.ts
apps/playground-web/src/app/ai/ai-sdk/page.tsx
packages/ai-sdk-provider/src/provider.ts
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
packages/ai-sdk-provider/src/tools.ts
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}
: Import UI primitives from@/components/ui/*
(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLink
for internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()
from@/lib/utils
for conditional class logic
Use design system tokens (e.g.,bg-card
,border-border
,text-muted-foreground
)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()
to retrieve JWT from cookies on server side
UseAuthorization: Bearer
header – never embed tokens in URLs
Return typed results (e.g.,Project[]
,User[]
) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query
)
Use descriptive, stablequeryKeys
for React Query cache hits
ConfigurestaleTime
/cacheTime
in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-js
in server components
Files:
apps/playground-web/src/app/ai/ai-sdk/page.tsx
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
🧠 Learnings (5)
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to packages/thirdweb/src/exports/** : Export everything via `exports/` directory, grouped by feature in the SDK public API
Applied to files:
packages/ai-sdk-provider/src/exports/thirdweb.ts
📚 Learning: 2025-06-17T18:30:52.976Z
Learnt from: MananTank
PR: thirdweb-dev/js#7356
File: apps/nebula/src/app/not-found.tsx:1-1
Timestamp: 2025-06-17T18:30:52.976Z
Learning: In the thirdweb/js project, the React namespace is available for type annotations (like React.FC) without needing to explicitly import React. This is project-specific configuration that differs from typical TypeScript/React setups.
Applied to files:
packages/ai-sdk-provider/src/exports/thirdweb.ts
📚 Learning: 2025-08-07T17:24:31.965Z
Learnt from: MananTank
PR: thirdweb-dev/js#7812
File: apps/dashboard/src/app/(app)/team/~/~project/[[...paths]]/page.tsx:1-11
Timestamp: 2025-08-07T17:24:31.965Z
Learning: In Next.js App Router, page components (page.tsx files) are server components by default and do not require the "server-only" import directive. The "server-only" directive is primarily used for utility functions, API helpers, and data access modules that should never be included in the client bundle.
Applied to files:
apps/playground-web/src/app/ai/ai-sdk/page.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Prefer API routes or server actions to keep tokens secret; the browser only sees relative paths.
Applied to files:
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Interactive UI that relies on hooks (`useState`, `useEffect`, React Query, wallet hooks).
Applied to files:
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
🧬 Code graph analysis (4)
apps/playground-web/src/app/ai/ai-sdk/page.tsx (4)
packages/ai-sdk-provider/src/provider.ts (1)
ThirdwebProvider
(494-522)apps/playground-web/src/components/blocks/APIHeader.tsx (1)
PageLayout
(49-70)apps/playground-web/src/components/code/code-example.tsx (2)
TabName
(58-68)CodeExample
(17-56)apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (1)
ChatContainer
(33-150)
packages/ai-sdk-provider/src/provider.ts (2)
packages/ai-sdk-provider/src/types.ts (3)
ThirdwebConfig
(12-29)ThirdwebSettings
(31-50)DEFAULT_BASE_URL
(10-10)packages/ai-sdk-provider/src/tools.ts (1)
createTools
(51-75)
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (9)
packages/ai-sdk-provider/src/types.ts (1)
ThirdwebAiMessage
(52-71)apps/playground-web/src/components/conversation.tsx (3)
Conversation
(12-20)ConversationContent
(26-31)ConversationScrollButton
(35-62)apps/playground-web/src/components/message.tsx (2)
Message
(10-20)MessageContent
(24-41)apps/playground-web/src/components/response.tsx (1)
Response
(9-20)apps/playground-web/src/components/reasoning.tsx (3)
Reasoning
(43-111)ReasoningTrigger
(115-146)ReasoningContent
(154-167)apps/playground-web/src/components/loader.tsx (1)
Loader
(86-96)apps/playground-web/src/components/prompt-input.tsx (3)
PromptInput
(24-32)PromptInputTextarea
(39-85)PromptInputSubmit
(146-175)apps/playground-web/src/lib/client.ts (1)
THIRDWEB_CLIENT
(20-43)packages/thirdweb/src/exports/thirdweb.ts (1)
prepareTransaction
(181-181)
packages/ai-sdk-provider/src/tools.ts (2)
packages/nebula/src/client/types.gen.ts (3)
AgentActionSignTransactionData
(460-481)AgentActionSignSwapDataIntent
(352-385)AgentActionSignSwapData
(338-347)packages/ai-sdk-provider/src/types.ts (1)
ThirdwebConfig
(12-29)
🔇 Additional comments (5)
apps/playground-web/src/app/ai/ai-sdk/page.tsx (1)
25-39
: Solid page structure and demos.Layout, metadata, and live preview wiring look good and align with Playground patterns.
Also applies to: 42-107, 109-159
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (1)
33-60
: Nice chat wiring and session propagation.The
useChat
setup, session propagation viaonFinish
, and form handling look clean.packages/ai-sdk-provider/src/provider.ts (1)
494-527
: Provider surface looks good.
ThirdwebProvider
,languageModel
, and factorycreateThirdweb
are clean and align with ProviderV2.packages/ai-sdk-provider/src/tools.ts (2)
30-49
: Output types and enums look fine.Transaction/monitor outputs align with expected shapes.
77-83
: Confirm downstream usage after schema renames.If you adopt camelCase (
chainId
, intent fields), update any consumers (e.g., ChatContainer) to readinput.chainId
andinput.transaction.chainId
.Would you like me to sweep the repo and raise a follow-up PR to align these call sites?
bd4643d
to
94b7076
Compare
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
94b7076
to
37cda56
Compare
size-limit report 📦
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (9)
packages/ai-sdk-provider/tsconfig.build.json (1)
3-6
: Align moduleResolution with NodeNext to avoid ESM/CJS resolution issues.Base config uses NodeNext; overriding to "node" can break
.js
extension andpackage.json#exports
resolution."compilerOptions": { - "moduleResolution": "node", + "moduleResolution": "NodeNext", "rootDir": "./src", "sourceMap": true },packages/ai-sdk-provider/src/tools.ts (5)
2-2
: Fix zod import pathImport from "zod", not "zod/v4".
-import { z } from "zod/v4"; +import { z } from "zod";
24-28
: Add token shapes and make action nullable for Swap schemaInclude from/to token data and make action nullable. Keep token.chain_id as snake_case per generated types.
+const AgentActionSignSwapDataToken = z.object({ + address: z.string(), + chain_id: z.number(), + amount: z.string(), + symbol: z.string(), + decimals: z.number(), + price: z.number().nullable(), +}); + const AgentActionSignSwapData = z.object({ - action: z.string(), + action: z.string().nullable(), intent: AgentActionSignSwapDataIntent, + from: AgentActionSignSwapDataToken, + to: AgentActionSignSwapDataToken, transaction: AgentActionSignTransactionData, });
51-75
: Use AI SDK v4 Tool shape (parameters
), dropoutputSchema
, and tighten typingReplace
inputSchema
withparameters
; removeoutputSchema
. Return an object that satisfiesRecord<string, Tool>
.-export function createTools(_config: ThirdwebConfig): Record<string, Tool> { - return { +export function createTools(_config: ThirdwebConfig) { + return { sign_transaction: { id: "thirdweb.sign_transaction" as const, name: "sign_transaction", description: "Sign a transaction", - inputSchema: AgentActionSignTransactionData, - outputSchema: AgentActionTransactionOutputData, - } as const, + parameters: AgentActionSignTransactionData, + }, sign_swap: { id: "thirdweb.sign_swap" as const, name: "sign_swap", description: "Sign a swap transaction", - inputSchema: AgentActionSignSwapData, - outputSchema: AgentActionTransactionOutputData, - } as const, + parameters: AgentActionSignSwapData, + }, monitor_transaction: { id: "thirdweb.monitor_transaction" as const, name: "monitor_transaction", description: "Monitor a transaction", - inputSchema: AgentActionMonitorTransactionData, - outputSchema: AgentActionMonitorTransactionOutputData, - } as const, - } as const; + parameters: AgentActionMonitorTransactionData, + }, + } as const satisfies Record<string, Tool>; }
5-11
: Align SignTransaction schema with generated types (chainId)Use camelCase to match Nebula client types.
const AgentActionSignTransactionData = z.object({ - chain_id: z.number(), + chainId: z.number(), function: z.string().nullable().optional(), to: z.string(), value: z.string(), data: z.string(), });
13-22
: Align Swap Intent schema with generated types (camelCase + maxSteps)Rename fields and maxSteps to camelCase.
const AgentActionSignSwapDataIntent = z.object({ - origin_chain_id: z.number(), - origin_token_address: z.string(), - destination_chain_id: z.number(), - destination_token_address: z.string(), + originChainId: z.number(), + originTokenAddress: z.string(), + destinationChainId: z.number(), + destinationTokenAddress: z.string(), amount: z.string(), sender: z.string(), receiver: z.string(), - max_steps: z.number(), + maxSteps: z.number(), });apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (3)
30-31
: Avoid exposing secretKey in client bundleImport a client-safe instance (clientId only). Move secretKey usage to a server-only module.
-import { Loader } from "../../../../components/loader"; -import { THIRDWEB_CLIENT } from "../../../../lib/client"; +import { Loader } from "@/components/loader"; +// Client-safe thirdweb client (no secretKey) +import { THIRDWEB_PUBLIC_CLIENT as THIRDWEB_CLIENT } from "@/lib/public-client";Create apps/playground-web/src/lib/public-client.ts:
"use client"; import { createThirdwebClient } from "thirdweb"; export const THIRDWEB_PUBLIC_CLIENT = createThirdwebClient({ clientId: process.env.NEXT_PUBLIC_THIRDWEB_CLIENT_ID as string, });
184-201
: Guard against invalid tx payload before rendering TransactionButton (sign_transaction)Avoid calling prepareTransaction with empty to/data/value.
const account = useActiveAccount(); if (!account) { return <ConnectButton client={THIRDWEB_CLIENT} />; } + + const isValidTx = + Boolean(transactionData.to) || + transactionData.data !== "0x" || + transactionData.value > 0n; + if (!isValidTx) { + return ( + <div className="py-4 text-sm text-muted-foreground"> + Waiting for transaction details… + </div> + ); + }
268-286
: Mirror tx validation for swap pathSame guard for sign_swap.
const account = useActiveAccount(); if (!account) { return <ConnectButton client={THIRDWEB_CLIENT} />; } + + const isValidTx = + Boolean(transactionData.to) || + transactionData.data !== "0x" || + transactionData.value > 0n; + if (!isValidTx) { + return ( + <div className="py-4 text-sm text-muted-foreground"> + Waiting for swap transaction details… + </div> + ); + }
🧹 Nitpick comments (9)
packages/ai-sdk-provider/tsconfig.build.json (2)
4-5
: Ensure declaration outputs for consumers.If not already set in tsconfig.base.json, emit types in builds.
"compilerOptions": { "rootDir": "./src", - "sourceMap": true + "sourceMap": true, + "declaration": true, + "declarationMap": true },
7-13
: Broaden test excludes to cover common patterns.Consider excluding
.spec.*
alongside.test.*
to match both conventions."exclude": [ "src/**/*.test.ts", "src/**/*.test.tsx", + "src/**/*.spec.ts", + "src/**/*.spec.tsx", "src/**/*.test-d.ts", "src/**/*.bench.ts", "src/**/*.macro.ts" ],apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (3)
191-193
: Use Tailwind instead of inline stylesReplace style prop with className per guidelines.
- <TransactionButton - style={{ - width: "100%", - }} + <TransactionButton + className="w-full"Also applies to: 275-277
108-108
: Remove debug logDrop console.log in production UI.
- console.log("---sign_swap", part);
33-35
: Add explicit return types for exported componentsAlign with TS guidelines for explicit return types.
-export function ChatContainer() { +export function ChatContainer(): JSX.Element { @@ -const SignTransactionButton = (props: SignTransactionButtonProps) => { +const SignTransactionButton = (props: SignTransactionButtonProps): JSX.Element => { @@ -const SignSwapButton = (props: SignSwapButtonProps) => { +const SignSwapButton = (props: SignSwapButtonProps): JSX.Element => {Also applies to: 167-168, 249-250
apps/playground-web/src/components/reasoning.tsx (4)
5-6
: Import ReactElement for explicit component return typesPrepare for explicit returns below.
-import type { ComponentProps } from "react"; +import type { ComponentProps, ReactElement } from "react";
43-44
: Add explicit return types for exported componentsMake return types explicit.
-export const Reasoning = memo( +export const Reasoning = memo( ({ @@ - }: ReasoningProps) => { + }: ReasoningProps): ReactElement => { @@ -export const ReasoningTrigger = memo( +export const ReasoningTrigger = memo( ({ className, children, ...props }: ReasoningTriggerProps) => { @@ -export const ReasoningContent = memo( - ({ className, children, ...props }: ReasoningContentProps) => ( +export const ReasoningContent = memo( + ({ className, children, ...props }: ReasoningContentProps): ReactElement => (Also applies to: 115-116, 154-155
130-134
: Pluralize “second(s)”Tiny UX polish.
- <p>Thought for {duration} seconds</p> + <p> + Thought for {duration} {duration === 1 ? "second" : "seconds"} + </p>
64-86
: Rename state: not a refVariable ends with “Ref” but uses useState. Rename for clarity.
- const [hasAutoClosedRef, setHasAutoClosedRef] = useState(false); + const [hasAutoClosed, setHasAutoClosed] = useState(false); @@ - if (defaultOpen && !isStreaming && isOpen && !hasAutoClosedRef) { + if (defaultOpen && !isStreaming && isOpen && !hasAutoClosed) { @@ - setHasAutoClosedRef(true); + setHasAutoClosed(true); @@ - }, [isStreaming, isOpen, defaultOpen, setIsOpen, hasAutoClosedRef]); + }, [isStreaming, isOpen, defaultOpen, setIsOpen, hasAutoClosed]);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (29)
.changeset/many-pants-tease.md
(1 hunks)apps/dashboard/package.json
(1 hunks)apps/nebula/package.json
(1 hunks)apps/playground-web/package.json
(3 hunks)apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
(1 hunks)apps/playground-web/src/app/ai/ai-sdk/page.tsx
(1 hunks)apps/playground-web/src/app/api/chat/route.ts
(1 hunks)apps/playground-web/src/app/navLinks.ts
(1 hunks)apps/playground-web/src/components/code/code-example.tsx
(1 hunks)apps/playground-web/src/components/conversation.tsx
(1 hunks)apps/playground-web/src/components/loader.tsx
(1 hunks)apps/playground-web/src/components/message.tsx
(1 hunks)apps/playground-web/src/components/prompt-input.tsx
(1 hunks)apps/playground-web/src/components/reasoning.tsx
(1 hunks)apps/playground-web/src/components/response.tsx
(1 hunks)apps/playground-web/src/components/ui/avatar.tsx
(1 hunks)apps/portal/package.json
(1 hunks)package.json
(1 hunks)packages/ai-sdk-provider/README.md
(1 hunks)packages/ai-sdk-provider/biome.json
(1 hunks)packages/ai-sdk-provider/package.json
(1 hunks)packages/ai-sdk-provider/src/exports/thirdweb.ts
(1 hunks)packages/ai-sdk-provider/src/provider.ts
(1 hunks)packages/ai-sdk-provider/src/tools.ts
(1 hunks)packages/ai-sdk-provider/src/types.ts
(1 hunks)packages/ai-sdk-provider/tsconfig.base.json
(1 hunks)packages/ai-sdk-provider/tsconfig.build.json
(1 hunks)packages/ai-sdk-provider/tsconfig.json
(1 hunks)packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- apps/portal/package.json
- apps/playground-web/src/components/conversation.tsx
🚧 Files skipped from review as they are similar to previous changes (23)
- apps/dashboard/package.json
- apps/nebula/package.json
- packages/ai-sdk-provider/package.json
- apps/playground-web/src/components/code/code-example.tsx
- apps/playground-web/src/components/loader.tsx
- apps/playground-web/src/app/api/chat/route.ts
- package.json
- packages/ai-sdk-provider/tsconfig.base.json
- apps/playground-web/src/components/response.tsx
- packages/ai-sdk-provider/tsconfig.json
- packages/ai-sdk-provider/README.md
- .changeset/many-pants-tease.md
- packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
- packages/ai-sdk-provider/src/exports/thirdweb.ts
- apps/playground-web/src/components/message.tsx
- packages/ai-sdk-provider/src/types.ts
- apps/playground-web/src/app/ai/ai-sdk/page.tsx
- packages/ai-sdk-provider/biome.json
- apps/playground-web/src/app/navLinks.ts
- apps/playground-web/package.json
- packages/ai-sdk-provider/src/provider.ts
- apps/playground-web/src/components/ui/avatar.tsx
- apps/playground-web/src/components/prompt-input.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}
: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/types
or localtypes.ts
barrels
Prefer type aliases over interface except for nominal shapes
Avoidany
andunknown
unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial
,Pick
, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
Files:
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
apps/playground-web/src/components/reasoning.tsx
packages/ai-sdk-provider/src/tools.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
apps/playground-web/src/components/reasoning.tsx
packages/ai-sdk-provider/src/tools.ts
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}
: Import UI primitives from@/components/ui/*
(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLink
for internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()
from@/lib/utils
for conditional class logic
Use design system tokens (e.g.,bg-card
,border-border
,text-muted-foreground
)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()
to retrieve JWT from cookies on server side
UseAuthorization: Bearer
header – never embed tokens in URLs
Return typed results (e.g.,Project[]
,User[]
) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query
)
Use descriptive, stablequeryKeys
for React Query cache hits
ConfigurestaleTime
/cacheTime
in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-js
in server components
Files:
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
apps/playground-web/src/components/reasoning.tsx
🧠 Learnings (2)
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Prefer API routes or server actions to keep tokens secret; the browser only sees relative paths.
Applied to files:
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Interactive UI that relies on hooks (`useState`, `useEffect`, React Query, wallet hooks).
Applied to files:
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
🧬 Code graph analysis (3)
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (2)
packages/ai-sdk-provider/src/types.ts (1)
ThirdwebAiMessage
(52-71)apps/playground-web/src/lib/client.ts (1)
THIRDWEB_CLIENT
(20-43)
apps/playground-web/src/components/reasoning.tsx (1)
apps/playground-web/src/components/response.tsx (1)
Response
(9-20)
packages/ai-sdk-provider/src/tools.ts (2)
packages/nebula/src/client/types.gen.ts (3)
AgentActionSignTransactionData
(460-481)AgentActionSignSwapDataIntent
(352-385)AgentActionSignSwapData
(338-347)packages/ai-sdk-provider/src/types.ts (1)
ThirdwebConfig
(12-29)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Size
- GitHub Check: Socket Security: Pull Request Alerts
- GitHub Check: Analyze (javascript)
🔇 Additional comments (1)
packages/ai-sdk-provider/tsconfig.build.json (1)
14-15
: Build scripts correctly target tsconfig.build.json; ensure tsconfig.base.json’s compilerOptions include your intended module, outDir, declaration, and declarationMap settings.
const SignTransactionButton = (props: SignTransactionButtonProps) => { | ||
const { input, addToolResult, toolCallId, sendMessage, sessionId } = props; | ||
const transactionData: { | ||
chain_id: number; | ||
to: string; | ||
data: `0x${string}`; | ||
value: bigint; | ||
} = useMemo(() => { | ||
return { | ||
chain_id: input?.chain_id || 8453, | ||
to: input?.to || "", | ||
data: (input?.data as `0x${string}`) || "0x", | ||
value: input?.value ? BigInt(input.value) : BigInt(0), | ||
}; | ||
}, [input]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update to camelCase transaction fields (chainId) for sign_transaction
Reflect schema changes; pass correct chain id into defineChain.
- const transactionData: {
- chain_id: number;
+ const transactionData: {
+ chainId: number;
to: string;
data: `0x${string}`;
value: bigint;
} = useMemo(() => {
return {
- chain_id: input?.chain_id || 8453,
+ chainId: input?.chainId || 8453,
to: input?.to || "",
data: (input?.data as `0x${string}`) || "0x",
value: input?.value ? BigInt(input.value) : BigInt(0),
};
}, [input]);
...
- chain: defineChain(transactionData.chain_id),
+ chain: defineChain(transactionData.chainId),
Also applies to: 195-201
🤖 Prompt for AI Agents
In apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx around
lines 167-181 (and also update lines 195-201), the transaction object uses
snake_case fields (chain_id) but the schema and downstream defineChain expect
camelCase (chainId). Change the transaction object keys to camelCase (chainId)
and ensure any consumers/defineChain calls receive transaction.chainId instead
of chain_id; keep the same default value (8453) and preserve types/values (e.g.,
value as BigInt) when renaming the field.
const transactionData: { | ||
chain_id: number; | ||
to: string; | ||
data: `0x${string}`; | ||
value: bigint; | ||
} = useMemo(() => { | ||
return { | ||
chain_id: input?.transaction?.chain_id || 8453, | ||
to: input?.transaction?.to || "", | ||
data: (input?.transaction?.data as `0x${string}`) || "0x", | ||
value: input?.transaction?.value | ||
? BigInt(input.transaction.value) | ||
: BigInt(0), | ||
}; | ||
}, [input]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update to camelCase transaction fields (chainId) for sign_swap
Reflect schema changes inside swap payload.
- const transactionData: {
- chain_id: number;
+ const transactionData: {
+ chainId: number;
to: string;
data: `0x${string}`;
value: bigint;
} = useMemo(() => {
return {
- chain_id: input?.transaction?.chain_id || 8453,
+ chainId: input?.transaction?.chainId || 8453,
to: input?.transaction?.to || "",
data: (input?.transaction?.data as `0x${string}`) || "0x",
value: input?.transaction?.value
? BigInt(input.transaction.value)
: BigInt(0),
};
}, [input]);
...
- chain: defineChain(transactionData.chain_id),
+ chain: defineChain(transactionData.chainId),
Also applies to: 281-285
🤖 Prompt for AI Agents
In apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx around
lines 251 to 265 (and similarly update lines 281 to 285), the transaction object
uses snake_case keys (chain_id) but the schema expects camelCase (chainId);
update the transactionData type and returned object to use camelCase keys
(chainId) and ensure the data and value types remain the same (data as
`0x${string}`, value as bigint), and then update any downstream usages of
transaction.chain_id to transaction.chainId in the nearby code blocks (including
lines 281-285) so the sign_swap payload matches the new schema.
const AgentActionSignTransactionData = z.object({ | ||
chain_id: z.number(), | ||
function: z.string().nullable().optional(), | ||
to: z.string(), | ||
value: z.string(), | ||
data: z.string(), | ||
}); | ||
|
||
const AgentActionSignSwapDataIntent = z.object({ | ||
origin_chain_id: z.number(), | ||
origin_token_address: z.string(), | ||
destination_chain_id: z.number(), | ||
destination_token_address: z.string(), | ||
amount: z.string(), | ||
sender: z.string(), | ||
receiver: z.string(), | ||
max_steps: z.number(), | ||
}); | ||
|
||
const AgentActionSignSwapData = z.object({ | ||
action: z.string(), | ||
intent: AgentActionSignSwapDataIntent, | ||
transaction: AgentActionSignTransactionData, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Verify downstream usage after snake_case → camelCase migration
Search for remaining chain_id
, origin_chain_id
, etc., and update call sites (e.g., chat container) to use camelCase.
Also applies to: 51-75
🏁 Script executed:
#!/bin/bash
# Find snake_case usages that must be migrated to camelCase
rg -nP --glob '!**/node_modules/**' --glob '!**/dist/**' \
'\b(chain_id|origin_chain_id|destination_chain_id|origin_token_address|destination_token_address|max_steps)\b'
echo -e "\nCheck ai-sdk-provider exports used by playground:"
rg -nP --glob '!**/node_modules/**' 'tool-sign_(transaction|swap)|Sign(Transaction|Swap)Input|TransactionOutput|MonitorTransactionInput' apps/playground-web
Length of output: 47868
Migrate snake_case to camelCase in ai-sdk-provider and update downstream call sites
- In packages/ai-sdk-provider/src/tools.ts, rename:
• chain_id → chainId
• origin_chain_id → originChainId
• origin_token_address → originTokenAddress
• destination_chain_id → destinationChainId
• destination_token_address → destinationTokenAddress
• max_steps → maxSteps - In packages/ai-sdk-provider/src/provider.ts (around line 400), replace parsed.data.chain_id with parsed.data.chainId
- In apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (lines 96, 107, 158, 241), update “tool-sign_transaction”/“tool-sign_swap” handlers and all transactionData.chain_id accesses to chainId
🤖 Prompt for AI Agents
In packages/ai-sdk-provider/src/tools.ts around lines 5 to 28, the Zod schemas
use snake_case keys; rename them to camelCase (chain_id→chainId,
origin_chain_id→originChainId, origin_token_address→originTokenAddress,
destination_chain_id→destinationChainId,
destination_token_address→destinationTokenAddress, max_steps→maxSteps) and
update the AgentActionSignSwapDataIntent and AgentActionSignTransactionData
schema property names accordingly; then update downstream call sites: in
packages/ai-sdk-provider/src/provider.ts (around line 400) replace
parsed.data.chain_id with parsed.data.chainId, and in
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (lines ~96,
107, 158, 241) update the “tool-sign_transaction”/“tool-sign_swap” handlers and
all transactionData.chain_id accesses to use chainId and similarly rename swap
intent properties to camelCase; ensure any type imports, destructuring, and
tests are updated to match the new property names and run type checks.
37cda56
to
b9b4ce7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (1)
packages/ai-sdk-provider/biome.json (1)
3-3
: Fix invalid Biome "extends" pathBiome can’t resolve
"//"
. Point to the repo root config.- "extends": "//", + "extends": ["../../biome.json"],
🧹 Nitpick comments (3)
packages/ai-sdk-provider/biome.json (1)
6-12
: Double-check Assist action path and intentIf the goal is to keep package.json keys unsorted for this package, the setting looks right, but validate the path
assist.actions.source.useSortedKeys
against your Biome version. Also consider whether this should apply to all package.json files in the monorepo vs only this one.Optional broadened scope:
- "include": ["package.json"] + "include": ["**/package.json"]apps/portal/src/app/ai/sidebar.tsx (1)
28-31
: Unify naming with Playground navPlayground uses “AI SDK”; consider aligning this label to avoid cross-app inconsistency.
- name: "Vercel AI SDK", + name: "AI SDK",apps/portal/src/app/ai/chat/ai-sdk/page.mdx (1)
48-49
: Minor wording fix in comment[Suggest] “to handle” reads better than “to use handle.”
- tools: thirdweb.tools(), // optional, to use handle transactions and swaps + tools: thirdweb.tools(), // optional, to handle transactions and swaps
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (31)
.changeset/many-pants-tease.md
(1 hunks)apps/dashboard/package.json
(1 hunks)apps/nebula/package.json
(1 hunks)apps/playground-web/package.json
(3 hunks)apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
(1 hunks)apps/playground-web/src/app/ai/ai-sdk/page.tsx
(1 hunks)apps/playground-web/src/app/api/chat/route.ts
(1 hunks)apps/playground-web/src/app/navLinks.ts
(1 hunks)apps/playground-web/src/components/code/code-example.tsx
(1 hunks)apps/playground-web/src/components/conversation.tsx
(1 hunks)apps/playground-web/src/components/loader.tsx
(1 hunks)apps/playground-web/src/components/message.tsx
(1 hunks)apps/playground-web/src/components/prompt-input.tsx
(1 hunks)apps/playground-web/src/components/reasoning.tsx
(1 hunks)apps/playground-web/src/components/response.tsx
(1 hunks)apps/playground-web/src/components/ui/avatar.tsx
(1 hunks)apps/portal/package.json
(1 hunks)apps/portal/src/app/ai/chat/ai-sdk/page.mdx
(1 hunks)apps/portal/src/app/ai/sidebar.tsx
(1 hunks)package.json
(1 hunks)packages/ai-sdk-provider/README.md
(1 hunks)packages/ai-sdk-provider/biome.json
(1 hunks)packages/ai-sdk-provider/package.json
(1 hunks)packages/ai-sdk-provider/src/exports/thirdweb.ts
(1 hunks)packages/ai-sdk-provider/src/provider.ts
(1 hunks)packages/ai-sdk-provider/src/tools.ts
(1 hunks)packages/ai-sdk-provider/src/types.ts
(1 hunks)packages/ai-sdk-provider/tsconfig.base.json
(1 hunks)packages/ai-sdk-provider/tsconfig.build.json
(1 hunks)packages/ai-sdk-provider/tsconfig.json
(1 hunks)packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- .changeset/many-pants-tease.md
- apps/portal/package.json
🚧 Files skipped from review as they are similar to previous changes (26)
- packages/ai-sdk-provider/package.json
- packages/ai-sdk-provider/src/exports/thirdweb.ts
- apps/playground-web/src/components/loader.tsx
- apps/dashboard/package.json
- packages/ai-sdk-provider/README.md
- apps/playground-web/src/components/response.tsx
- packages/ai-sdk-provider/tsconfig.json
- apps/playground-web/src/components/code/code-example.tsx
- apps/playground-web/package.json
- apps/playground-web/src/components/conversation.tsx
- package.json
- packages/ai-sdk-provider/src/provider.ts
- packages/ai-sdk-provider/src/types.ts
- apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
- apps/playground-web/src/app/api/chat/route.ts
- apps/playground-web/src/components/ui/avatar.tsx
- apps/playground-web/src/components/message.tsx
- packages/ai-sdk-provider/src/tools.ts
- apps/nebula/package.json
- packages/ai-sdk-provider/tsconfig.base.json
- apps/playground-web/src/components/prompt-input.tsx
- packages/ai-sdk-provider/tsconfig.build.json
- apps/playground-web/src/app/navLinks.ts
- packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
- apps/playground-web/src/app/ai/ai-sdk/page.tsx
- apps/playground-web/src/components/reasoning.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}
: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/types
or localtypes.ts
barrels
Prefer type aliases over interface except for nominal shapes
Avoidany
andunknown
unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial
,Pick
, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
Files:
apps/portal/src/app/ai/sidebar.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
apps/portal/src/app/ai/sidebar.tsx
🧠 Learnings (2)
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to biome.json : Biome is the primary linter/formatter; rules are defined in `biome.json`
Applied to files:
packages/ai-sdk-provider/biome.json
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Run `pnpm biome check --apply` before committing (pre-commit hook)
Applied to files:
packages/ai-sdk-provider/biome.json
🪛 LanguageTool
apps/portal/src/app/ai/chat/ai-sdk/page.mdx
[grammar] ~3-~3: There might be a mistake here.
Context: ...t plugs thirdweb into the Vercel AI SDK. It standardizes message parts, exposes w...
(QB_NEW_EN)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: E2E Tests (pnpm, webpack)
- GitHub Check: E2E Tests (pnpm, vite)
- GitHub Check: Size
- GitHub Check: E2E Tests (pnpm, esbuild)
- GitHub Check: Unit Tests
- GitHub Check: Analyze (javascript)
🔇 Additional comments (6)
packages/ai-sdk-provider/biome.json (2)
2-2
: Schema version matches root (2.0.6) and loads with Biome CLI v2.1.2; no action required.
13-13
: Override selector key should be "include", not "includes"Using the wrong key likely makes this override a no-op.
- "includes": ["package.json"] + "include": ["package.json"]Likely an incorrect or invalid review comment.
apps/portal/src/app/ai/sidebar.tsx (1)
28-31
: LGTM: correct route and placementLink path matches the new MDX page and the section ordering is consistent.
apps/portal/src/app/ai/chat/ai-sdk/page.mdx (3)
53-61
: Verify event hook used for attaching metadataConfirm that
messageMetadata({ part })
withpart.type === "finish-step"
is supported by your stream helper and AI SDK version. If not, switch to the supported finish callback or emit a custom event to carrysession_id
.
97-99
: Confirm message shape used in the commentDocs mention
message.parts[...]
; AI SDK messages typically exposecontent
parts. Keep as-is ifThirdwebAiMessage
intentionally usesparts
, otherwise update the comment for consistency.
25-27
: Ignore outdated v4 helper suggestion
This page targets AI SDK v5—keep usingconvertToModelMessages
andUIMessage
(notconvertToCoreMessages
) and continue callingresult.toUIMessageStreamResponse()
(thetoDataStreamResponse
helper is from v4). (v5.ai-sdk.dev, ai-sdk.dev)Likely an incorrect or invalid review comment.
The `@thirdweb-dev/ai-sdk-provider` A thin provider that plugs thirdweb into the Vercel AI SDK. | ||
It standardizes message parts, exposes wallet-aware tools (`sign_transaction`, `sign_swap`), and ships types/utilities so you can build chat UIs that execute on-chain actions from AI responses. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix grammar in opening sentence
Missing verb and stray double space.
-The `@thirdweb-dev/ai-sdk-provider` A thin provider that plugs thirdweb into the Vercel AI SDK.
-It standardizes message parts, exposes wallet-aware tools (`sign_transaction`, `sign_swap`), and ships types/utilities so you can build chat UIs that execute on-chain actions from AI responses.
+`@thirdweb-dev/ai-sdk-provider` is a thin provider that plugs thirdweb into the Vercel AI SDK.
+It standardizes message parts, exposes wallet-aware tools (`sign_transaction`, `sign_swap`), and ships types/utilities so you can build chat UIs that execute on-chain actions from AI responses.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
The `@thirdweb-dev/ai-sdk-provider` A thin provider that plugs thirdweb into the Vercel AI SDK. | |
It standardizes message parts, exposes wallet-aware tools (`sign_transaction`, `sign_swap`), and ships types/utilities so you can build chat UIs that execute on-chain actions from AI responses. | |
`@thirdweb-dev/ai-sdk-provider` is a thin provider that plugs thirdweb into the Vercel AI SDK. | |
It standardizes message parts, exposes wallet-aware tools (`sign_transaction`, `sign_swap`), and ships types/utilities so you can build chat UIs that execute on-chain actions from AI responses. |
🧰 Tools
🪛 LanguageTool
[grammar] ~3-~3: There might be a mistake here.
Context: ...t plugs thirdweb into the Vercel AI SDK. It standardizes message parts, exposes w...
(QB_NEW_EN)
🤖 Prompt for AI Agents
In apps/portal/src/app/ai/chat/ai-sdk/page.mdx around lines 3 to 4, the opening
sentence is missing a verb and contains a stray double space; update it to be a
complete sentence (for example, "The `@thirdweb-dev/ai-sdk-provider` is a thin
provider that plugs thirdweb into the Vercel AI SDK.") and remove the extra
space between sentences.
const send = (message: string) => { | ||
sendMessage( | ||
{ text: message }, | ||
{ | ||
body: { | ||
// send session id for continuity | ||
sessionId, | ||
}, | ||
}, | ||
); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix client example: sendMessage signature, duplicate return, and React key
sendMessage
should pass text and usedata
(notbody
) for extra payload.- Remove
return return
. - Add
key
to mapped elements to avoid React warnings.
- const send = (message: string) => {
- sendMessage(
- { text: message },
- {
- body: {
- // send session id for continuity
- sessionId,
- },
- },
- );
- };
+ const send = (text: string) => {
+ sendMessage(text, {
+ data: {
+ // send session id for continuity
+ sessionId,
+ },
+ });
+ };
@@
- return return (
+ return (
<>
- {messages.map((message) => (
- <RenderMessage message={message} />
+ {messages.map((message) => (
+ <RenderMessage key={message.id} message={message} />
))}
<ChatInputBox send={send} />
</>
- );;
+ );
Also applies to: 99-107
b9b4ce7
to
06efc26
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (15)
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (6)
302-311
: Mirror error handling for swap to keep agent state consistent.onError={(error) => { - sendMessage( - { text: `Transaction failed: ${error.message}` }, - { - body: { - sessionId, - }, - }, - ); + addToolResult({ + tool: "sign_swap", + toolCallId, + output: { error: String(error?.message || error) } as any, + }); + sendMessage({ text: "Swap signing failed." }, { body: { sessionId } }); }}
30-33
: Do not import server client with secretKey in a client component; use a public client.Importing from
lib/client
can leakTHIRDW EB_SECRET_KEY
to the browser. Swap to a client-safe export and fix the Loader import path.-import { Loader } from "../../../../components/loader"; -import { THIRDWEB_CLIENT } from "../../../../lib/client"; +import { Loader } from "@/components/loader"; +// never ship secretKey to the browser +import { THIRDWEB_PUBLIC_CLIENT as THIRDWEB_CLIENT } from "@/lib/public-client";Create
apps/playground-web/src/lib/public-client.ts
:"use client"; import { createThirdwebClient } from "thirdweb"; export const THIRDWEB_PUBLIC_CLIENT = createThirdwebClient({ clientId: process.env.NEXT_PUBLIC_THIRDWEB_CLIENT_ID as string, });Want me to add this file and update other client imports using
lib/client
?
167-181
: Adopt camelCase chainId and pass the correct value to defineChain.Schema and SDKs use
chainId
(camelCase). Also coerce either shape on input.- const transactionData: { - chain_id: number; + const transactionData: { + chainId: number; to: string; data: `0x${string}`; value: bigint; } = useMemo(() => { return { - chain_id: input?.chain_id || 8453, + chainId: input?.chainId ?? input?.chain_id ?? 8453, to: input?.to || "", data: (input?.data as `0x${string}`) || "0x", value: input?.value ? BigInt(input.value) : BigInt(0), }; }, [input]); @@ - prepareTransaction({ + prepareTransaction({ client: THIRDWEB_CLIENT, - chain: defineChain(transactionData.chain_id), + chain: defineChain(transactionData.chainId), to: transactionData.to, data: transactionData.data, value: transactionData.value, }) } @@ output: { transaction_hash: transaction.transactionHash, - chain_id: transaction.chain.id, + chain_id: transaction.chain.id, // keep output shape if tools expect snake_case },Also applies to: 195-201, 208-211
184-201
: Guard against invalid tx payload before preparing the transaction.Prevents calling
prepareTransaction
with empty to/data/value. Also replace inline style with Tailwind per guidelines.if (!account) { return <ConnectButton client={THIRDWEB_CLIENT} />; } + const isValidTx = + Boolean(transactionData.to) || + transactionData.data !== "0x" || + transactionData.value > 0n; + if (!isValidTx) { + return ( + <div className="py-4 text-sm text-muted-foreground"> + Waiting for transaction details… + </div> + ); + } + return ( <div className="py-4"> - <TransactionButton - style={{ - width: "100%", - }} + <TransactionButton + className="w-full" transaction={() =>
251-265
: Mirror chainId rename for swap flow and pass correct chain to defineChain.- const transactionData: { - chain_id: number; + const transactionData: { + chainId: number; to: string; data: `0x${string}`; value: bigint; } = useMemo(() => { return { - chain_id: input?.transaction?.chain_id || 8453, + chainId: input?.transaction?.chainId ?? input?.transaction?.chain_id ?? 8453, to: input?.transaction?.to || "", data: (input?.transaction?.data as `0x${string}`) || "0x", value: input?.transaction?.value ? BigInt(input.transaction.value) : BigInt(0), }; }, [input]); @@ - chain: defineChain(transactionData.chain_id), + chain: defineChain(transactionData.chainId), to: transactionData.to, data: transactionData.data, value: transactionData.value,Also applies to: 281-285
268-286
: Add swap payload guard and Tailwind width.if (!account) { return <ConnectButton client={THIRDWEB_CLIENT} />; } + const isValidTx = + Boolean(transactionData.to) || + transactionData.data !== "0x" || + transactionData.value > 0n; + if (!isValidTx) { + return ( + <div className="py-4 text-sm text-muted-foreground"> + Waiting for swap transaction details… + </div> + ); + } + return ( <div className="py-4"> - <TransactionButton - style={{ - width: "100%", - }} + <TransactionButton + className="w-full" transaction={() =>packages/ai-sdk-provider/package.json (1)
15-21
: Use “require” condition in exports instead of “default”.Prevents resolver confusion for CJS.
"exports": { ".": { "types": "./dist/types/exports/thirdweb.d.ts", "import": "./dist/esm/exports/thirdweb.js", - "default": "./dist/cjs/exports/thirdweb.js" + "require": "./dist/cjs/exports/thirdweb.js" }, "./package.json": "./package.json" },packages/ai-sdk-provider/src/provider.ts (6)
120-127
: Guard lastUserMessage; avoid[undefined]
in request body.Prevents malformed payloads when no prior user/tool message exists.
- const messages = this.settings.context?.session_id - ? [lastUserMessage] - : allMessages; + const messages = + this.settings.context?.session_id && lastUserMessage + ? [lastUserMessage] + : allMessages;Also applies to: 184-191
198-206
: Advertise SSE via Accept header for streaming requests.Some backends gate behavior on
Accept: text/event-stream
.- headers: this.getHeaders(), + headers: { ...this.getHeaders(), Accept: "text/event-stream" },
356-364
: Emit session_id as response metadata, not as id.Conform to AI SDK stream contract.
- if (parsed.session_id) { - controller.enqueue({ - type: "response-metadata", - id: parsed.session_id || "", - }); - } + if (parsed.session_id) { + controller.enqueue({ + type: "response-metadata", + metadata: { session_id: parsed.session_id }, + }); + }
396-406
: Normalize tool input shapes (chainId).Accept both
chainId
andchain_id
, emit camelCase for the tool.- case "sign_transaction": + case "sign_transaction": toolName = "sign_transaction"; input = { - chain_id: parsed.data.chain_id, + chainId: + parsed.data.chainId ?? parsed.data.chain_id, to: parsed.data.to, value: parsed.data.value, data: parsed.data.data, function: parsed.data.function, } as SignTransactionInput; break;
433-441
: Do not stringify tool-call input; pass the object.Stringifying breaks downstream type safety.
- controller.enqueue({ + controller.enqueue({ type: "tool-call", toolCallId, toolName, - input: JSON.stringify(input), + input, providerExecuted: false, });
64-71
: Flatten system message content to string.thirdweb API likely expects
content: string
, not arrays.- case "system": - return { - role: message.role, - content: message.content, - }; + case "system": { + const content = Array.isArray(message.content) + ? message.content + .map((p) => (p.type === "text" ? p.text : "")) + .join("") + : String(message.content ?? ""); + return { role: "system", content }; + }apps/portal/src/app/ai/chat/ai-sdk/page.mdx (2)
3-4
: Grammar: fix opening sentence.-The `@thirdweb-dev/ai-sdk-provider` A thin provider that plugs thirdweb into the Vercel AI SDK. -It standardizes message parts, exposes wallet-aware tools (`sign_transaction`, `sign_swap`), and ships types/utilities so you can build chat UIs that execute on-chain actions from AI responses. +`@thirdweb-dev/ai-sdk-provider` is a thin provider that plugs thirdweb into the Vercel AI SDK. +It standardizes message parts, exposes wallet-aware tools (`sign_transaction`, `sign_swap`), and ships types/utilities so you can build chat UIs that execute on-chain actions from AI responses.
85-107
: Fix client example: sendMessage signature, duplicate return, and missing React keys.Aligns with current
useChat
API and avoids React warnings.- const send = (message: string) => { - sendMessage( - { text: message }, - { - body: { - // send session id for continuity - sessionId, - }, - }, - ); - }; + const send = (text: string) => { + sendMessage(text, { + data: { + // send session id for continuity + sessionId, + }, + }); + }; @@ - return return ( + return ( <> - {messages.map((message) => ( - <RenderMessage message={message} /> + {messages.map((message) => ( + <RenderMessage key={message.id} message={message} /> ))} <ChatInputBox send={send} /> </> - );; + );
🧹 Nitpick comments (4)
packages/ui/package.json (2)
30-30
: Move Prettier to devDependencies.Prettier is a build-time tool; keeping it in dependencies bloats consumers. Suggest relocating to devDependencies.
"dependencies": { - "prettier": "3.6.2", }, "devDependencies": { + "prettier": "3.6.2", }
28-28
: Consider making Next a peerDependency (and possibly devDependency).If this UI package is consumed by Next apps, declaring
next
as a peer avoids duplicate Next versions in the graph and reduces install size. Keep as a direct dep only if this package runs Next code at runtime.Also applies to: 43-46
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (2)
218-227
: Also surface tool failure to the LLM with addToolResult (optional).Today only a chat message is sent; the tool call may remain unresolved.
onError={(error) => { - sendMessage( - { text: `Transaction failed: ${error.message}` }, - { body: { sessionId } }, - ); + // mark the tool call as failed for the agent + addToolResult({ + tool: "sign_transaction", + toolCallId, + output: { + error: String(error?.message || error), + } as any, // if schema doesn't support error, skip this block + }); + sendMessage({ text: `Transaction failed: ${error.message}` }, { body: { sessionId } }); }}
107-111
: Remove console.log in production UI.Avoid noisy logs in client bundles.
- case "tool-sign_swap": - console.log("---sign_swap", part); + case "tool-sign_swap":
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (32)
.changeset/many-pants-tease.md
(1 hunks)apps/dashboard/package.json
(1 hunks)apps/nebula/package.json
(1 hunks)apps/playground-web/package.json
(3 hunks)apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
(1 hunks)apps/playground-web/src/app/ai/ai-sdk/page.tsx
(1 hunks)apps/playground-web/src/app/api/chat/route.ts
(1 hunks)apps/playground-web/src/app/navLinks.ts
(1 hunks)apps/playground-web/src/components/code/code-example.tsx
(1 hunks)apps/playground-web/src/components/conversation.tsx
(1 hunks)apps/playground-web/src/components/loader.tsx
(1 hunks)apps/playground-web/src/components/message.tsx
(1 hunks)apps/playground-web/src/components/prompt-input.tsx
(1 hunks)apps/playground-web/src/components/reasoning.tsx
(1 hunks)apps/playground-web/src/components/response.tsx
(1 hunks)apps/playground-web/src/components/ui/avatar.tsx
(1 hunks)apps/portal/package.json
(1 hunks)apps/portal/src/app/ai/chat/ai-sdk/page.mdx
(1 hunks)apps/portal/src/app/ai/sidebar.tsx
(1 hunks)package.json
(1 hunks)packages/ai-sdk-provider/README.md
(1 hunks)packages/ai-sdk-provider/biome.json
(1 hunks)packages/ai-sdk-provider/package.json
(1 hunks)packages/ai-sdk-provider/src/exports/thirdweb.ts
(1 hunks)packages/ai-sdk-provider/src/provider.ts
(1 hunks)packages/ai-sdk-provider/src/tools.ts
(1 hunks)packages/ai-sdk-provider/src/types.ts
(1 hunks)packages/ai-sdk-provider/tsconfig.base.json
(1 hunks)packages/ai-sdk-provider/tsconfig.build.json
(1 hunks)packages/ai-sdk-provider/tsconfig.json
(1 hunks)packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
(1 hunks)packages/ui/package.json
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (27)
- .changeset/many-pants-tease.md
- packages/ai-sdk-provider/tsconfig.json
- apps/nebula/package.json
- apps/playground-web/src/app/api/chat/route.ts
- apps/playground-web/package.json
- apps/dashboard/package.json
- package.json
- apps/playground-web/src/app/navLinks.ts
- apps/playground-web/src/components/code/code-example.tsx
- apps/playground-web/src/components/ui/avatar.tsx
- apps/portal/src/app/ai/sidebar.tsx
- packages/ai-sdk-provider/biome.json
- packages/ai-sdk-provider/src/tools.ts
- apps/playground-web/src/components/conversation.tsx
- packages/ai-sdk-provider/src/exports/thirdweb.ts
- apps/playground-web/src/components/loader.tsx
- packages/thirdweb/src/adapters/eip1193/to-eip1193.ts
- packages/ai-sdk-provider/README.md
- apps/portal/package.json
- apps/playground-web/src/components/message.tsx
- apps/playground-web/src/app/ai/ai-sdk/page.tsx
- apps/playground-web/src/components/response.tsx
- packages/ai-sdk-provider/tsconfig.build.json
- packages/ai-sdk-provider/tsconfig.base.json
- apps/playground-web/src/components/prompt-input.tsx
- apps/playground-web/src/components/reasoning.tsx
- packages/ai-sdk-provider/src/types.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}
: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/types
or localtypes.ts
barrels
Prefer type aliases over interface except for nominal shapes
Avoidany
andunknown
unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial
,Pick
, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
Files:
packages/ai-sdk-provider/src/provider.ts
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
packages/ai-sdk-provider/src/provider.ts
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}
: Import UI primitives from@/components/ui/*
(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLink
for internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()
from@/lib/utils
for conditional class logic
Use design system tokens (e.g.,bg-card
,border-border
,text-muted-foreground
)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()
to retrieve JWT from cookies on server side
UseAuthorization: Bearer
header – never embed tokens in URLs
Return typed results (e.g.,Project[]
,User[]
) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query
)
Use descriptive, stablequeryKeys
for React Query cache hits
ConfigurestaleTime
/cacheTime
in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-js
in server components
Files:
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
🧠 Learnings (3)
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Prefer API routes or server actions to keep tokens secret; the browser only sees relative paths.
Applied to files:
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Interactive UI that relies on hooks (`useState`, `useEffect`, React Query, wallet hooks).
Applied to files:
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to packages/thirdweb/src/exports/** : Export everything via `exports/` directory, grouped by feature in the SDK public API
Applied to files:
packages/ai-sdk-provider/package.json
🧬 Code graph analysis (2)
packages/ai-sdk-provider/src/provider.ts (2)
packages/ai-sdk-provider/src/types.ts (3)
ThirdwebConfig
(12-29)ThirdwebSettings
(31-50)DEFAULT_BASE_URL
(10-10)packages/ai-sdk-provider/src/tools.ts (4)
SignTransactionInput
(77-79)SignSwapInput
(80-80)MonitorTransactionInput
(81-83)createTools
(51-75)
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (8)
packages/ai-sdk-provider/src/types.ts (1)
ThirdwebAiMessage
(52-71)apps/playground-web/src/components/conversation.tsx (3)
Conversation
(12-20)ConversationContent
(26-31)ConversationScrollButton
(35-62)apps/playground-web/src/components/message.tsx (2)
Message
(10-20)MessageContent
(24-41)apps/playground-web/src/components/response.tsx (1)
Response
(9-20)apps/playground-web/src/components/reasoning.tsx (3)
Reasoning
(43-111)ReasoningTrigger
(115-146)ReasoningContent
(154-167)apps/playground-web/src/components/loader.tsx (1)
Loader
(86-96)apps/playground-web/src/components/prompt-input.tsx (3)
PromptInput
(24-32)PromptInputTextarea
(39-85)PromptInputSubmit
(146-175)apps/playground-web/src/lib/client.ts (1)
THIRDWEB_CLIENT
(20-43)
🪛 LanguageTool
apps/portal/src/app/ai/chat/ai-sdk/page.mdx
[grammar] ~3-~3: There might be a mistake here.
Context: ...t plugs thirdweb into the Vercel AI SDK. It standardizes message parts, exposes w...
(QB_NEW_EN)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: Unit Tests
- GitHub Check: E2E Tests (pnpm, webpack)
- GitHub Check: E2E Tests (pnpm, vite)
- GitHub Check: E2E Tests (pnpm, esbuild)
- GitHub Check: Size
- GitHub Check: Build Packages
- GitHub Check: Lint Packages
- GitHub Check: Analyze (javascript)
🔇 Additional comments (1)
apps/playground-web/src/app/ai/ai-sdk/components/chat-container.tsx (1)
47-60
: Ensure consistentsendMessage
payload key (body
vsdata
). This component sends the sessionId underbody
, but the official examples usedata
. Confirm which property your installed@ai-sdk/react
/ai
version expects and align all calls across the codebase.
@@ -31,7 +31,7 @@ | |||
"react-children-utilities": "^2.10.0", | |||
"react-markdown": "10.1.0", | |||
"remark-gfm": "4.0.1", | |||
"shiki": "1.27.0", | |||
"shiki": "3.12.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Major bump to Shiki v3 — verify API/usage across the repo.
Shiki 3 has breaking changes (e.g., getHighlighter
options, BUNDLED_LANGUAGES
/BUNDLED_THEMES
moved, dropped setCDN/loadLanguage/loadTheme
, theme handling via themes
, etc.). Please scan usages and migrate or add @shikijs/compat
if needed. (shiki.style)
Run this to find likely-breaking patterns:
🏁 Script executed:
#!/bin/bash
# Shiki v3 migration check
rg -nP -C2 --type=ts --type=tsx '\b(getHighlighter|codeToThemedTokens|BUNDLED_LANGUAGES|BUNDLED_THEMES|setCDN|load(Language|Theme)|setWasm|ansiToHtml)\b|from\s+[\'"]shiki[\'"]'
Length of output: 116
🏁 Script executed:
#!/bin/bash
# Shiki v3 migration check: find direct imports and API usage patterns
rg -nP -C2 --type ts --type tsx \
-e "\\b(getHighlighter|codeToThemedTokens|BUNDLED_LANGUAGES|BUNDLED_THEMES|setCDN|load(?:Language|Theme)|setWasm|ansiToHtml)\\b" \
-e "from\\s+['\\\"]shiki['\\\"]"
Length of output: 222
🏁 Script executed:
#!/bin/bash
# Shiki v3 migration check: search .ts and .tsx files for Shiki v2 APIs and imports
rg -nP -C2 -g '*.ts' -g '*.tsx' \
-e '\\b(getHighlighter|codeToThemedTokens|BUNDLED_LANGUAGES|BUNDLED_THEMES|setCDN|load(?:Language|Theme)|setWasm|ansiToHtml)\\b' \
-e "from\\s+['\\\"]shiki['\\\"]"
Length of output: 4194
Migrate all Shiki v2 API usages to v3 or include @shikijs/compat
The following files import Shiki v2 APIs that will break under v3:
- packages/ui/src/components/code/getCodeHtml.tsx: codeToHtml, BundledLanguage
- apps/portal/src/app/changelog/utils/transform.tsx: BundledLanguage, SpecialLanguage
- apps/nebula/src/@/components/ui/code/getCodeHtml.tsx: codeToHtml, BundledLanguage
- packages/ui/src/components/code/code.client.tsx & code.server.tsx: BundledLanguage
- apps/playground-web/src/components/code/code-example.tsx: BundledLanguage
Refer to the Shiki v3 migration guide to replace codeToHtml/getHighlighter, update types, and handle themes: https://shiki.style/guide/migrate?utm_source=openai
🤖 Prompt for AI Agents
In packages/ui/package.json around line 34, the project pins "shiki": "3.12.0"
but multiple files still use Shiki v2 APIs; update all listed files to use Shiki
v3 APIs (or add the compatibility package) by either installing and importing
@shikijs/compat or migrating to getHighlighter/getHighlighter options: replace
codeToHtml usages with the v3 highlighter API (create a highlighter via
getHighlighter({ theme, langs }) and call highlighter.codeToHtml or
highlighter.codeToHtml with appropriate theme options), replace
BundledLanguage/SpecialLanguage types/usages with v3 equivalents or import types
from @shikijs/compat, and ensure language bundles and themes are loaded via the
v3 loader pattern; apply these changes in the specific files mentioned in the
review (packages/ui/src/components/code/getCodeHtml.tsx,
apps/portal/src/app/changelog/utils/transform.tsx,
apps/nebula/src/@/components/ui/code/getCodeHtml.tsx,
packages/ui/src/components/code/code.client.tsx & code.server.tsx,
apps/playground-web/src/components/code/code-example.tsx), updating imports and
types accordingly so the code compiles against Shiki v3 or use @shikijs/compat
as a temporary shim.
PR-Codex overview
This PR focuses on enhancing the
thirdweb
integration with the Vercel AI SDK, improving wallet management, and adding new functionalities for AI-driven blockchain interactions.Detailed summary
eip1193
provider.@thirdweb-dev/ai-sdk-provider
for seamless integration with the Vercel AI SDK.playground-web
for chat and messaging features.shiki
version across multiple packages.ai-sdk-provider
.Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Chores