Repository navigation
feat!: Remove openassistant dependency - #423
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds an SDK-native tool() pattern and a typed toolRenderers registry to AI slices, flattens tool outputs (removes nested llmResult/additionalData), renames session field toolAdditionalData → toolEditState, and updates transports, components, types, examples, tests, and docs to use renderer-focused flow. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as Client (UI)
participant Store as AI Store
participant Transport as Chat Transport
participant Tool as Tool (AI SDK)
participant Renderer as Tool Renderer
UI->>Store: read tools & toolRenderers
UI->>Transport: emit tool invocation (tool call)
Transport->>Tool: tool.execute(input)
Tool-->>Transport: return output
Transport->>Store: addToolResult / setToolEditState
UI->>Store: lookup toolRenderers[toolName]
Store-->>UI: ToolRenderer component
UI->>Renderer: render with ToolRendererProps(output,input,toolCallId,state)
Renderer-->>UI: display result
sequenceDiagram
participant App as App Init
participant Renderers as createDefaultAiToolRenderers()
participant CreateSlice as createAiSlice
participant Store as AI Store
App->>Renderers: createDefaultAiToolRenderers()
Renderers-->>App: renderer registry
App->>CreateSlice: createAiSlice({ tools, toolRenderers })
CreateSlice->>Store: initialize state.toolRenderers = merged registry
Store-->>App: store ready with toolRenderers
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Looks like this includes breaking API changes. Could you add an entry to the upcoming 0.29.0 release in |
|
👍 looks good. Than you, @mihajlo-stojkovic! Some questions:
async onChunk({chunk}) {
switch (chunk.type) {
case 'tool-result':
// Send tool additional data as a data part that will be handled by onData callback
// This data will be sent to the client but not included in UIMessages
writer.write({
type: 'data-tool-additional-output',
transient: true, // Won't be added to message history
data: {
toolCallId: chunk.toolCallId,
toolName: chunk.toolName,
output: getToolAdditionalData(chunk.toolCallId),
timestamp: new Date().toISOString(),
},
});
break;
}
},
Thanks! 🚀 |
|
@copilot This PR separates tool renderers from tools which makes the consumer API more awkward to use. Should we try to find a way to avoid it? What are pro's an cons of them being separate? |
Hey @ilyabo! Yeah, removing the dependency on |
Hi @lixun910! Thanks for the kind reply. To address your questions:
|
There was a problem hiding this comment.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
examples/ai-core/package.json (1)
19-33:⚠️ Potential issue | 🟠 MajorBump
eslint-plugin-react-hooksfor React 19.2 and ESLint 9 compatibility.This example uses React 19.2 with ESLint 9.28.0, but
eslint-plugin-react-hooksis pinned to v5.2.0. Version 5.x does not support ESLint 9—update to v6.1.0 or newer (note: v6.0.0 was deprecated on npm).🔧 Suggested change
- "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-hooks": "^6.1.0",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/ai-core/package.json` around lines 19 - 33, Update the devDependency for eslint-plugin-react-hooks in package.json to a version compatible with ESLint 9 and React 19 (bump "eslint-plugin-react-hooks" from "5.2.0" to "6.1.0" or newer); locate the dependency entry `"eslint-plugin-react-hooks": "5.2.0"` and change its version string to "^6.1.0" (or the latest 6.x patch) so the project uses a release that supports ESLint 9 and avoids the deprecated 6.0.0 release.packages/ai-core/src/chatTransport.ts (1)
277-300:⚠️ Potential issue | 🟠 MajorFail fast when a tool has neither
execute()nor a renderer.Right now that branch just returns. The tool call stays incomplete,
toolCallId -> sessionIdis never cleared, andonChatFinish()will keep the analysis open because the assistant tail still contains a tool part.Proposed fix
} else { // Tool has no execute function - wait for UI component to call addToolResult const hasToolRenderer = !!state.ai.findToolRenderer(toolName); if (hasToolRenderer && state.ai.waitForToolResult) { try { await state.ai.waitForToolResult( sessionId, toolCallId, abortController?.signal, ); state.ai.setToolCallSession(toolCallId, undefined); } catch (error) { if (addToolResult && error instanceof Error) { addToolResult({ tool: toolName, toolCallId, state: 'output-error', errorText: error.message, }); } throw error; } + } else { + addToolResult?.({ + tool: toolName, + toolCallId, + state: 'output-error', + errorText: `Tool "${toolName}" has no execute() and no registered renderer`, + }); + state.ai.setToolCallSession(toolCallId, undefined); } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/ai-core/src/chatTransport.ts` around lines 277 - 300, The code currently returns silently when a tool has no renderer, leaving the tool call mapping and assistant tail open; update the branch after computing hasToolRenderer to detect the absence of both a renderer and an execute implementation (lookup the tool by toolName via your AI tool registry accessor), then immediately clear the mapping with state.ai.setToolCallSession(toolCallId, undefined), call addToolResult({ tool: toolName, toolCallId, state: 'output-error', errorText: 'Tool has no execute() or renderer' }) if addToolResult exists, and then throw a descriptive Error (or otherwise reject) so the caller fails fast instead of returning no-op. Ensure this logic sits where hasToolRenderer is checked (near state.ai.findToolRenderer / state.ai.waitForToolResult) so the toolCallId -> sessionId mapping is always cleared.
🧹 Nitpick comments (7)
packages/ai-core/src/agents/AgentUtils.ts (1)
43-66: Rename the helper/type to matchtoolEditState.The implementation now writes through
setToolEditState, but the local type and docs still use the removed “additional data” term. In this migration, that mismatch makes the stored payload harder to reason about.♻️ Suggested rename
-export type AgentToolCallAdditionalData = { +export type AgentToolCallEditState = { agentToolCalls: AgentToolCall[]; finalOutput?: string; timestamp: string; }; -export function updateAgentToolCallData(params: { +export function updateAgentToolCallEditState(params: { store: StoreApi<AiSliceState>; parentToolCallId: string; agentToolCalls: AgentToolCall[]; sessionId: string; finalOutput?: string; }): void { const {store, parentToolCallId, agentToolCalls, sessionId, finalOutput} = params; const state = store.getState(); state.ai.setToolEditState(sessionId, parentToolCallId, { agentToolCalls: [...agentToolCalls], finalOutput, timestamp: new Date().toISOString(), - } as AgentToolCallAdditionalData); + } as AgentToolCallEditState); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/ai-core/src/agents/AgentUtils.ts` around lines 43 - 66, Rename the AgentToolCallAdditionalData type and any references to "additional data" to match the new toolEditState terminology and make the stored payload names consistent with setToolEditState; specifically rename the type AgentToolCallAdditionalData (and its doc comment) to something like ToolEditState (or ToolEditStatePayload), update the JSDoc above updateAgentToolCallData to reference toolEditState, and ensure updateAgentToolCallData still calls state.ai.setToolEditState with the renamed type and matching field names (agentToolCalls, finalOutput, timestamp) so the API and types align.packages/vega/src/index.ts (1)
22-22: Consider exportingvegaChartToolRendererhere too.
@sqlrooms/ai-ragnow exposes a renderer alias, but Vega still makes consumers import the component name directly. Re-exporting avegaChartToolRendereralias from the barrel would keep the new tool/renderer API more uniform across packages.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vega/src/index.ts` at line 22, Add a re-export for the renderer alias so consumers can import vegaChartToolRenderer from the package barrel; update packages/vega/src/index.ts to export the renderer symbol (vegaChartToolRenderer) from './VegaChartTool' alongside the existing type exports (VegaChartToolOptions, VegaChartToolOutput) so the package follows the new tool/renderer API convention.examples/ai-rag/src/components/EchoToolResult.tsx (1)
4-9: Share the echo output type with the tool definition.This renderer owns a local
EchoToolOutputshape, while the echo tool inexamples/ai-core/src/store.tsindependently returns the same object fromexecute. Keeping those definitions separate makes future tool/result changes easy to desynchronize. Export the output type from one place and reuse it in both files.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/ai-rag/src/components/EchoToolResult.tsx` around lines 4 - 9, EchoToolResult defines a local EchoToolOutput type that duplicates the output shape returned by the echo tool's execute in the tool definition; export the output type from the canonical tool module (the echo tool in store.ts) and remove the local EchoToolOutput here, then import and use that exported type in the EchoToolResult component signature (FC<ToolRendererProps<...>>), ensuring both the tool's execute return type and the renderer reference the same exported type.examples/app-builder/src/store/store.ts (1)
79-83: Co-locate tool and renderer registration.
toolRenderersnow repeats the same keys that already exist intools. That makes add/rename/remove changes easy to drift, and the failure mode is a working tool with no UI renderer. Consider keeping a single registry and deriving both maps from it.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/app-builder/src/store/store.ts` around lines 79 - 83, The code duplicates keys between the tools map and toolRenderers (toolRenderers: { listFiles, getFileContent, updateFileContent }), which can drift; instead create a single registry (e.g., an object or array) that pairs each tool identifier with its implementation and renderer (reference the existing symbols tools, toolRenderers, listFilesToolRenderer, getFileContentToolRenderer, updateFileContentToolRenderer), then derive both the tools map and the toolRenderers map from that registry so adding/renaming/removing a tool only requires one change.examples/ai-nextjs/src/app/lib/tools.ts (1)
3-4: Split this example by runtime boundary.This module now mixes server-only tools, client-side executors, and React renderers. That makes
getClientTools()handwebSearchto the browser and forces server callers ofgetServerAiSDKTools()to importWebSearchToolResulttoo. Prefer separate server-tools, client-tools, and tool-renderers modules so the example does not teach cross-bundle leakage.Also applies to: 53-60
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/ai-nextjs/src/app/lib/tools.ts` around lines 3 - 4, This module mixes server-only tools, client executors, and React renderers causing cross-bundle leakage: split into three modules — a server tools module exporting getServerAiSDKTools (containing server-only tool logic like webSearch), a client tools/executors module exporting getClientTools (containing browser-safe executors only), and a renderers module exporting UI components/ToolRendererRegistry entries (e.g., WebSearchToolResult). Move WebSearchToolResult and any UI/renderer registrations into the renderers module and remove UI imports from the server module; update getClientTools and getServerAiSDKTools to import only from their respective new modules so server code never imports UI and browser bundles never import server-only code.packages/ai-rag/src/createRagTool.tsx (2)
89-99: Unused destructured variablesqueryanddatabase.The variables
queryanddatabaseare destructured fromoutputon line 99 but never used in the component. This appears to be leftover from a previous implementation.♻️ Proposed fix
- const {query, results, database} = output; + const {results} = output;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/ai-rag/src/createRagTool.tsx` around lines 89 - 99, In RagToolResult, the destructured variables query and database from output are unused; update the destructuring to only pull the needed property (e.g., const {results} = output) or reference output.results directly to remove the unused variables (modify the destructuring in the RagToolResult component to eliminate query and database).
164-173: Global store access pattern is an established pattern necessary for AI tool execution.This approach of accessing the store via
(globalThis as any).__ROOM_STORE__is documented throughout the codebase (README.md, RAG_TOOL.md, RAG_INTEGRATION.md) and required by the AI SDK architecture. Since AI tools are callback functions invoked by the AI framework (not controlled by application code), dependency injection isn't feasible. The pattern includes error handling that returns a structured error response when the store is unavailable.While the pattern is type-unsafe, improving it would require either:
- Creating a type-safe utility wrapper around the global access
- Documenting the pattern more explicitly in code comments to clarify why it's necessary
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/ai-rag/src/createRagTool.tsx` around lines 164 - 173, The global store access via (globalThis as any).__ROOM_STORE__ inside the execute function (signature: execute(params: RagToolParameters): Promise<RagToolOutput>) is an intentional pattern required by the AI SDK; please add a concise explanatory comment immediately above the store lookup referencing __ROOM_STORE__, stating that AI tools are invoked by the framework (not via DI), linking to the README/RAG_TOOL.md design, and preserving the existing structured error return when the store is missing—alternatively, if you prefer, replace the direct cast with a single small helper (e.g., getRoomStore()) that wraps the globalThis access to centralize the unsafe cast and document the rationale there.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@examples/ai-agent/src/agents/WeatherAgent.ts`:
- Around line 13-16: The nested agent started inside the execute function isn't
receiving the caller's tool-call abortSignal, so parent cancellations don't stop
the inner stream; update the call that starts the inner Agent/session (the
nested Agent invocation used with processAgentStream) to pass
options?.abortSignal through (or combine it with the session-level controller
via AbortSignal.any([...]) if both signals must be honored) so the nested agent
and processAgentStream are aborted when the tool-call is cancelled.
In `@examples/ai-nextjs/src/app/lib/tools.ts`:
- Around line 7-35: Update the repo lint/rule that enforces tool authorship to
match the new tool shape used by tool({ inputSchema, ... }) with separate
renderers instead of the old parameters/component contract: change the rule that
currently requires tools to be "composable functions with name, parameters (Zod
schema), execute, and optional component" to accept the new signature used by
the tool factory (referenced by the tool helper and examples like webSearchTool
and weatherTool) so it validates a Zod schema provided as inputSchema (or
similar property name used by the tool factory), ensures an execute async
function exists, and allows separate renderer components to be defined elsewhere
rather than requiring a component property.
In `@packages/ai-config/src/migration/AnalysisSession-v0.26.0.ts`:
- Around line 65-71: The current presence check uses Array.isArray(d.uiMessages)
which treats explicit null/{} as missing; change it to a key-presence check so
explicit values are considered present: replace the hasUiMessages assignment
(currently referencing d.uiMessages) with a check like "'uiMessages' in d" and
keep hasToolState as-is (it already uses 'toolEditState'/'toolAdditionalData' in
d) so the return condition uses key-presence semantics for both.
- Around line 79-82: The migration is copying legacy toolAdditionalData into
toolEditState but leaves toolAdditionalData on the returned payload, causing
duplication; update the migration so after computing const toolEditState =
(session.toolEditState as UnknownRecord) ?? (session.toolAdditionalData as
UnknownRecord) ?? {} you remove/drop the deprecated toolAdditionalData key from
the object you return (e.g., build the new session by spreading ...session but
explicitly omit toolAdditionalData and set toolEditState), and apply the same
change for the second occurrence referenced around the 165–169 block so the
migrated object no longer contains toolAdditionalData.
In `@packages/ai-core/README.md`:
- Around line 17-21: Add a direct link to the 0.29.0 migration guide in the
README section describing createAiSlice so users can find the breaking-change
checklist; update the paragraph that lists createAiSlice requirements to include
a parenthetical or sentence pointing to the 0.29.0 migration guide and
explicitly mention the key renames/migrations (parameters -> inputSchema,
component -> toolRenderers, toolAdditionalData -> toolEditState) and the need to
adjust ToolSet/toolRenderers usage accordingly (referencing createAiSlice,
toolRenderers, toolAdditionalData, and toolEditState to make it easy to locate
relevant code).
In `@packages/ai-core/src/components/PromptSuggestions.tsx`:
- Around line 188-214: The Button inside TooltipTrigger in PromptSuggestions.tsx
currently uses the title prop which causes duplicate browser tooltips; remove
the title prop and add an aria-label instead that mirrors the tooltip text (use
the same conditional string based on isVisible: 'Hide prompt suggestions' or
'Show prompt suggestions') so the accessible label matches the Tooltip; update
the Button JSX (the element using toggleVisibility, variant="ghost",
size="icon", className={cn(...)} and rendering {icon ?? <Lightbulb ... />}) to
use aria-label rather than title.
In `@packages/ai-core/src/index.ts`:
- Line 8: The upgrade guide is missing a 0.29.0 migration section for
`@sqlrooms/ai-core` breaking changes; add a new "0.29.0 - `@sqlrooms/ai-core`"
subsection in upgrade-guide.md that lists concrete migration steps for clients:
describe switching to the new tool() API, the changed shape of inputSchema (and
how to map previous schemas to the new shape), the flattened tool results format
and how to transform prior nested results, updates to toolRenderers (what
props/signatures changed) and the rename of toolEditState (old name → new name),
and mention exported types AiSliceState and AiSliceOptions are part of the
public surface so consumers should check for API usage; keep each step terse
with example mappings and the old→new identifier references so consumers can
update code.
In `@packages/ai/package.json`:
- Line 30: Update the toModelOutput handler in
packages/ai/src/tools/query/queryTool.ts to accept a destructured parameter ({
output }) instead of (output), then adjust all internal uses to reference the
inner output via that name; additionally, make any calls to
convertToModelMessages() await its result (convertToModelMessages(...) -> await
convertToModelMessages(...)) since it is now async—apply these two changes (wrap
toModelOutput parameter and add await where convertToModelMessages is invoked)
across the file/related call sites so they comply with the ai v6 breaking
changes.
In `@packages/ai/src/tools/query/queryTool.ts`:
- Around line 45-47: The re-export "export {QueryToolResult as
queryToolRenderer}" in queryTool.ts creates a circular dependency with
QueryToolResult.tsx (which imports QueryToolOutput/QueryToolParameters from this
file); fix it by removing that re-export from queryTool.ts and placing it into a
separate module (e.g., create a new renderers file that exports QueryToolResult
as queryToolRenderer) or by adding the export to the package index instead,
ensuring QueryToolResult, queryToolRenderer, QueryToolOutput and
QueryToolParameters remain imported from their original modules without cyclic
imports.
In `@packages/ai/src/tools/query/QueryToolResult.tsx`:
- Around line 1-8: QueryToolResult.tsx currently imports QueryToolParameters and
QueryToolOutput from queryTool.ts which creates a circular dependency because
queryTool.ts imports renderer-side exports; extract the shared types into a new
module (e.g., queryToolTypes.ts) and update both QueryToolResult.tsx and
queryTool.ts to import QueryToolParameters and QueryToolOutput from that new
file, or alternatively move the renderer alias export out of queryTool.ts into
the renderer package so queryTool.ts no longer imports renderer code; ensure you
update all import sites (references to QueryToolParameters, QueryToolOutput, and
any renderer alias export) to the new shared module to break the cycle.
- Around line 66-71: The current code passes sqlQuery into DataTableModal
(props: title, query, tableModal, formatValue), which causes the modal to
re-execute the SQL; instead, change QueryToolResult to pass the persisted result
snapshot (serialized in Apache Arrow format) to DataTableModal (e.g., a prop
named resultArrow or rows) and remove the query prop so the modal renders the
original output without reissuing the statement; update DataTableModal to accept
and deserialize the Arrow payload from the browser <-> DuckDB connector and
render that snapshot (using Arrow utilities) rather than executing sqlQuery,
ensuring all browser/connector serialization uses Apache Arrow format.
---
Outside diff comments:
In `@examples/ai-core/package.json`:
- Around line 19-33: Update the devDependency for eslint-plugin-react-hooks in
package.json to a version compatible with ESLint 9 and React 19 (bump
"eslint-plugin-react-hooks" from "5.2.0" to "6.1.0" or newer); locate the
dependency entry `"eslint-plugin-react-hooks": "5.2.0"` and change its version
string to "^6.1.0" (or the latest 6.x patch) so the project uses a release that
supports ESLint 9 and avoids the deprecated 6.0.0 release.
In `@packages/ai-core/src/chatTransport.ts`:
- Around line 277-300: The code currently returns silently when a tool has no
renderer, leaving the tool call mapping and assistant tail open; update the
branch after computing hasToolRenderer to detect the absence of both a renderer
and an execute implementation (lookup the tool by toolName via your AI tool
registry accessor), then immediately clear the mapping with
state.ai.setToolCallSession(toolCallId, undefined), call addToolResult({ tool:
toolName, toolCallId, state: 'output-error', errorText: 'Tool has no execute()
or renderer' }) if addToolResult exists, and then throw a descriptive Error (or
otherwise reject) so the caller fails fast instead of returning no-op. Ensure
this logic sits where hasToolRenderer is checked (near state.ai.findToolRenderer
/ state.ai.waitForToolResult) so the toolCallId -> sessionId mapping is always
cleared.
---
Nitpick comments:
In `@examples/ai-nextjs/src/app/lib/tools.ts`:
- Around line 3-4: This module mixes server-only tools, client executors, and
React renderers causing cross-bundle leakage: split into three modules — a
server tools module exporting getServerAiSDKTools (containing server-only tool
logic like webSearch), a client tools/executors module exporting getClientTools
(containing browser-safe executors only), and a renderers module exporting UI
components/ToolRendererRegistry entries (e.g., WebSearchToolResult). Move
WebSearchToolResult and any UI/renderer registrations into the renderers module
and remove UI imports from the server module; update getClientTools and
getServerAiSDKTools to import only from their respective new modules so server
code never imports UI and browser bundles never import server-only code.
In `@examples/ai-rag/src/components/EchoToolResult.tsx`:
- Around line 4-9: EchoToolResult defines a local EchoToolOutput type that
duplicates the output shape returned by the echo tool's execute in the tool
definition; export the output type from the canonical tool module (the echo tool
in store.ts) and remove the local EchoToolOutput here, then import and use that
exported type in the EchoToolResult component signature
(FC<ToolRendererProps<...>>), ensuring both the tool's execute return type and
the renderer reference the same exported type.
In `@examples/app-builder/src/store/store.ts`:
- Around line 79-83: The code duplicates keys between the tools map and
toolRenderers (toolRenderers: { listFiles, getFileContent, updateFileContent }),
which can drift; instead create a single registry (e.g., an object or array)
that pairs each tool identifier with its implementation and renderer (reference
the existing symbols tools, toolRenderers, listFilesToolRenderer,
getFileContentToolRenderer, updateFileContentToolRenderer), then derive both the
tools map and the toolRenderers map from that registry so
adding/renaming/removing a tool only requires one change.
In `@packages/ai-core/src/agents/AgentUtils.ts`:
- Around line 43-66: Rename the AgentToolCallAdditionalData type and any
references to "additional data" to match the new toolEditState terminology and
make the stored payload names consistent with setToolEditState; specifically
rename the type AgentToolCallAdditionalData (and its doc comment) to something
like ToolEditState (or ToolEditStatePayload), update the JSDoc above
updateAgentToolCallData to reference toolEditState, and ensure
updateAgentToolCallData still calls state.ai.setToolEditState with the renamed
type and matching field names (agentToolCalls, finalOutput, timestamp) so the
API and types align.
In `@packages/ai-rag/src/createRagTool.tsx`:
- Around line 89-99: In RagToolResult, the destructured variables query and
database from output are unused; update the destructuring to only pull the
needed property (e.g., const {results} = output) or reference output.results
directly to remove the unused variables (modify the destructuring in the
RagToolResult component to eliminate query and database).
- Around line 164-173: The global store access via (globalThis as
any).__ROOM_STORE__ inside the execute function (signature: execute(params:
RagToolParameters): Promise<RagToolOutput>) is an intentional pattern required
by the AI SDK; please add a concise explanatory comment immediately above the
store lookup referencing __ROOM_STORE__, stating that AI tools are invoked by
the framework (not via DI), linking to the README/RAG_TOOL.md design, and
preserving the existing structured error return when the store is
missing—alternatively, if you prefer, replace the direct cast with a single
small helper (e.g., getRoomStore()) that wraps the globalThis access to
centralize the unsafe cast and document the rationale there.
In `@packages/vega/src/index.ts`:
- Line 22: Add a re-export for the renderer alias so consumers can import
vegaChartToolRenderer from the package barrel; update packages/vega/src/index.ts
to export the renderer symbol (vegaChartToolRenderer) from './VegaChartTool'
alongside the existing type exports (VegaChartToolOptions, VegaChartToolOutput)
so the package follows the new tool/renderer API convention.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: cebc8589-3ea4-453b-a682-6f8e36295ee9
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (64)
apps/sqlrooms-cli-ui/src/store.tsexamples/ai-agent/src/agents/WeatherAgent.tsexamples/ai-agent/src/room.tsxexamples/ai-core/package.jsonexamples/ai-core/src/components/EchoToolResult.tsxexamples/ai-core/src/room.tsxexamples/ai-core/src/store.tsexamples/ai-nextjs/README.mdexamples/ai-nextjs/package.jsonexamples/ai-nextjs/src/app/api/chat/route.tsexamples/ai-nextjs/src/app/lib/tools.tsexamples/ai-nextjs/src/app/store.tsexamples/ai-nextjs/src/components/WebSearchToolResult.tsxexamples/ai-nextjs/src/components/room.tsxexamples/ai-rag/package.jsonexamples/ai-rag/src/components/EchoToolResult.tsxexamples/ai-rag/src/store.tsexamples/ai/package.jsonexamples/ai/src/components/EchoToolResult.tsxexamples/ai/src/store.tsexamples/app-builder/package.jsonexamples/app-builder/src/store/store.tsexamples/app-builder/src/tools/getFileContent/GetFileContentToolResult.tsxexamples/app-builder/src/tools/getFileContent/getFileContentTool.tsexamples/app-builder/src/tools/listFiles/ListFilesToolResult.tsxexamples/app-builder/src/tools/listFiles/createListFilesTool.tsexamples/app-builder/src/tools/updateFileContent/UpdateFileContentToolResult.tsxexamples/app-builder/src/tools/updateFileContent/updateFileContentTool.tspackages/ai-config/src/AiSliceConfig.tspackages/ai-config/src/migration/AnalysisSession-v0.26.0.tspackages/ai-config/src/schema/AnalysisSessionSchema.tspackages/ai-core/README.mdpackages/ai-core/__tests__/AiSlice.modelSelection.test.tspackages/ai-core/__tests__/migration.test.tspackages/ai-core/__tests__/mocks/openassistant-utils.tspackages/ai-core/jest.config.jspackages/ai-core/package.jsonpackages/ai-core/src/AiSlice.tspackages/ai-core/src/agents/AgentUtils.tspackages/ai-core/src/chatTransport.tspackages/ai-core/src/components/AnalysisResult.tsxpackages/ai-core/src/components/GroupedMessageParts.tsxpackages/ai-core/src/components/MessagePartsList.tsxpackages/ai-core/src/components/PromptSuggestions.tsxpackages/ai-core/src/components/ToolPartRenderer.tsxpackages/ai-core/src/components/tools/ToolResult.tsxpackages/ai-core/src/hooks/useToolGrouping.tsxpackages/ai-core/src/index.tspackages/ai-core/src/types.tspackages/ai-rag/package.jsonpackages/ai-rag/src/createRagTool.tsxpackages/ai-rag/src/index.tspackages/ai/package.jsonpackages/ai/src/index.tspackages/ai/src/tools/commandTools.tspackages/ai/src/tools/defaultTools.tspackages/ai/src/tools/query/QueryToolResult.tsxpackages/ai/src/tools/query/queryTool.tspackages/cells/__tests__/mocks/openassistant-utils.jspackages/cells/jest.config.jspackages/vega/package.jsonpackages/vega/src/VegaChartTool.tsxpackages/vega/src/VegaChartToolResult.tsxpackages/vega/src/index.ts
💤 Files with no reviewable changes (10)
- packages/ai-rag/package.json
- packages/cells/tests/mocks/openassistant-utils.js
- packages/ai-core/src/components/MessagePartsList.tsx
- examples/ai-nextjs/package.json
- packages/ai-core/tests/mocks/openassistant-utils.ts
- packages/ai-core/src/hooks/useToolGrouping.tsx
- packages/ai-core/jest.config.js
- packages/cells/jest.config.js
- packages/ai-core/src/components/GroupedMessageParts.tsx
- packages/ai-core/package.json
…/ai` transport for server-side agents
…ate the migration
| ### `@sqlrooms/ai`: Remote transport — drop `data-tool-additional-output` custom chunk (breaking) | ||
|
|
||
| If you have a custom Next.js (or other server-side) route that manually wrote `data-tool-additional-output` data chunks to ferry `additionalData` to the client, you can remove that code entirely. | ||
|
|
||
| #### Before | ||
|
|
||
| ```ts | ||
| // app/api/chat/route.ts | ||
| result.pipeThrough( | ||
| new TransformStream({ | ||
| async onChunk({chunk}) { | ||
| if (chunk.type === 'tool-result') { | ||
| writer.write({ | ||
| type: 'data-tool-additional-output', | ||
| transient: true, | ||
| data: { | ||
| toolCallId: chunk.toolCallId, | ||
| toolName: chunk.toolName, | ||
| output: getToolAdditionalData(chunk.toolCallId), | ||
| }, | ||
| }); | ||
| } | ||
| }, | ||
| }), | ||
| ); | ||
| ``` | ||
|
|
||
| #### After | ||
|
|
||
| Remove the `onChunk` handler. The AI SDK's `toUIMessageStream()` now embeds the full tool `execute()` output directly into the `UIMessage` stream as a `tool-result` part, which the renderer receives via `ToolRendererProps.output`. No side-channel is needed. | ||
|
|
||
| ```ts | ||
| // app/api/chat/route.ts | ||
| writer.merge( | ||
| result.toUIMessageStream({originalMessages: messages}), | ||
| ); | ||
| ``` | ||
|
|
||
| **Why this works:** Previously, `execute()` returned `{llmResult, additionalData}` — the UI data (`additionalData`) was separate and had to be sent manually. Now `execute()` returns a single flat output object. The AI SDK propagates the full output to the client through the standard `UIMessage` parts, so `ToolRendererProps.output` is populated automatically without any custom data chunks. | ||
|
|
||
| ### `@sqlrooms/ai-core`: Removed exports | ||
|
|
||
| - `convertToAiSDKTools` — removed (tools are now native AI SDK tools) | ||
| - `findToolComponent` — replaced by `findToolRenderer` | ||
| - `VegaChartToolParametersType` from `@sqlrooms/vega` — removed (use `VegaChartToolParameters` directly) | ||
|
|
There was a problem hiding this comment.
@lixun910 I decided to add a comment about your second question in the upgrade-guide.md. Basically, I've outlined here why there's no need to transform stream chunks anymore. It's because additionalData is no longer decoupled from the tool response passed to LLM, and we no longer need to populate chunks with additional data.
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@CLAUDE.md`:
- Around line 213-231: Update the retrieved learning to reflect the AI SDK
migration: replace the old description that says "AI tools must be composable
functions with `name`, `parameters` (Zod schema), `execute`, and optional
`component` properties" with the new SDK conventions—tools are created via the
`tool()` factory (not inline objects), use `inputSchema` (not `parameters`),
return a flat output shape from `execute` (no `llmResult`/`additionalData`
nesting), and register renderers separately via `toolRenderers` in
`createAiSlice` (instead of attaching a `component` to the tool); reference
examples like `tool()`, `inputSchema`, `execute`, `createAiSlice`, and
`createQueryTool` when updating the text.
- Line 226: The tools registration is incorrectly spreading the complete tool
object (myTool) into the tools object; instead register the tool itself — stop
using the spread operator with myTool in the tools initializer and add myTool as
a value (either as a keyed entry like myTool: myTool or by including myTool in
the tools collection/array), keeping createQueryTool(store) as the query entry;
update the object that currently reads tools: { query: createQueryTool(store),
...myTool } to register myTool directly so the tool() object (myTool) is treated
as one tool rather than its internal properties being spread.
In `@docs/upgrade-guide.md`:
- Around line 261-299: The docs currently tell server-side removals for
`data-tool-additional-output` but omit client-side cleanup; update the upgrade
guide to instruct removing any client `onChatData` handlers (e.g., in
chatTransport.ts) that await/branch on parts with type
`'data-tool-additional-output'` and calls like `setToolEditState`, since those
branches are now dead, and note that tool edit state is populated from
`ToolRendererProps.output` instead.
In `@examples/ai-agent/src/agents/WeatherAgent.ts`:
- Around line 29-38: The code is fetching a redundant session-based abort signal
(sessionAbortSignal via currentSession/getAbortController) which binds the Agent
to the currently focused session; remove the sessionAbortSignal logic and any
use of getAbortController/currentSession and instead pass only
options?.abortSignal (or undefined) into the Agent constructor's abortSignal;
update the Agent instantiation (symbol: Agent) to set abortSignal:
options?.abortSignal and remove the conditional combining logic so cancellation
is driven solely by the abort signal provided by tool.execute.
In `@packages/ai-config/src/migration/AnalysisSession-v0.26.0.ts`:
- Around line 68-71: The current check treats the deprecated key
toolAdditionalData as if the new structure exists, so update the detection logic
in AnalysisSession-v0.26.0 by making hasToolState only true when 'toolEditState'
is present (i.e., remove 'toolAdditionalData' from the hasToolState check) so
sessions that still only have toolAdditionalData will not skip the migration;
reference the symbols hasUiMessages, hasToolState, uiMessages,
toolAdditionalData and toolEditState when making this change.
- Around line 76-84: Destructuring removed toolAdditionalData from session but
the fallback for toolEditState still reads session.toolAdditionalData (now
undefined), losing legacy payload; update the toolEditState assignment to fall
back to the captured _legacyToolAdditionalData (i.e., use (session.toolEditState
as UnknownRecord) ?? (_legacyToolAdditionalData as UnknownRecord) ?? {}), or
merge both sources if needed, so legacy toolAdditionalData is preserved when
computing toolEditState.
In `@packages/ai/src/tools/query/queryTool.ts`:
- Around line 127-134: In execute() currently catching errors and returning a
failure result object, re-throw the caught error instead so callers/renderers
can detect and render error states; locate the catch block in the execute()
function (where it returns { success: false, details: ..., error: ..., title:
'Query Result', sqlQuery: params.sqlQuery }) and replace the return with throw
error (or wrap and throw a new Error with context) so failed or aborted queries
are not treated as successful tool results by QueryToolResult.jsx/tsx.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 3078d06d-e0ad-411f-bb66-9761e9024638
📒 Files selected for processing (10)
CLAUDE.mddocs/upgrade-guide.mdexamples/ai-agent/src/agents/WeatherAgent.tspackages/ai-config/src/migration/AnalysisSession-v0.26.0.tspackages/ai-config/src/schema/AnalysisSessionSchema.tspackages/ai-core/README.mdpackages/ai-core/__tests__/migration.test.tspackages/ai-core/src/components/PromptSuggestions.tsxpackages/ai/src/index.tspackages/ai/src/tools/query/queryTool.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- packages/ai-config/src/schema/AnalysisSessionSchema.ts
- packages/ai-core/tests/migration.test.ts
- packages/ai-core/README.md
…tests to prevent message duplication
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
examples/ai-agent/src/agents/WeatherAgent.ts (1)
13-31:⚠️ Potential issue | 🟠 MajorResolve the nested agent from the tool-call session, not the focused one.
Line 18 uses
state.ai.getCurrentSession()to pickproviderandmodel. If the user switches chats while this tool is queued or running, the nested agent executes against the wrong session's model configuration. UsegetToolCallSession(options.toolCallId)instead (seepackages/ai-core/src/agents/AgentUtils.ts:91for the pattern), and only fall back to the focused session when there is no tool-call context.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/ai-agent/src/agents/WeatherAgent.ts` around lines 13 - 31, The execute function is using state.ai.getCurrentSession() to pick provider and model, which causes the nested Agent to use the focused session instead of the tool-call session; update execute to first resolve the tool-call session via state.ai.getToolCallSession(options?.toolCallId) (falling back to state.ai.getCurrentSession() if that returns undefined), and use that session's modelProvider and model when constructing the createOpenAICompatible(...) chatModel and new Agent(...) so the nested agent runs with the tool-call session configuration.
🧹 Nitpick comments (1)
examples/ai-agent/src/agents/WeatherAgent.ts (1)
70-75: Expose only the summary to the model usingtoModelOutput.In Vercel AI SDK v5, tools that return objects without
toModelOutputsend the entire JSON wrapper to the model. Instead, explicitly shape the model-facing output to show only the summary text—this prevents the bookkeeping fields from influencing the model's next action. AddtoModelOutputto extract and expose onlydetails:Suggested shape
return tool({ description: 'My weather agent', inputSchema: z.object({ prompt: z.string().describe('The prompt to the agent'), }), + toModelOutput: ({details}) => ({ + type: 'text', + value: details, + }), execute: async (🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/ai-agent/src/agents/WeatherAgent.ts` around lines 70 - 75, The tool currently returns a JSON wrapper (success/details) which Vercel AI SDK v5 will forward whole to the model; add a toModelOutput method on the returned object so only the summary text is exposed to the model. Locate the object returned in WeatherAgent (the block returning { success: true, details: resultText }) and implement toModelOutput() { return { details: resultText } } (or return just the summary string) so the model receives only the summary field instead of the bookkeeping fields.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@examples/ai-agent/src/agents/WeatherAgent.ts`:
- Around line 13-31: The execute function is using state.ai.getCurrentSession()
to pick provider and model, which causes the nested Agent to use the focused
session instead of the tool-call session; update execute to first resolve the
tool-call session via state.ai.getToolCallSession(options?.toolCallId) (falling
back to state.ai.getCurrentSession() if that returns undefined), and use that
session's modelProvider and model when constructing the
createOpenAICompatible(...) chatModel and new Agent(...) so the nested agent
runs with the tool-call session configuration.
---
Nitpick comments:
In `@examples/ai-agent/src/agents/WeatherAgent.ts`:
- Around line 70-75: The tool currently returns a JSON wrapper (success/details)
which Vercel AI SDK v5 will forward whole to the model; add a toModelOutput
method on the returned object so only the summary text is exposed to the model.
Locate the object returned in WeatherAgent (the block returning { success: true,
details: resultText }) and implement toModelOutput() { return { details:
resultText } } (or return just the summary string) so the model receives only
the summary field instead of the bookkeeping fields.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 150377bd-5bc2-43cc-8eb2-292ec6872b2b
📒 Files selected for processing (4)
CLAUDE.mdexamples/ai-agent/src/agents/WeatherAgent.tspackages/ai-config/src/migration/AnalysisSession-v0.26.0.tspackages/ai-core/__tests__/migration.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/ai-core/tests/migration.test.ts
- CLAUDE.md
… and update exports
# Conflicts: # packages/vega/package.json # pnpm-lock.yaml
There was a problem hiding this comment.
🧹 Nitpick comments (1)
CLAUDE.md (1)
226-229: Complete the example by adding a renderer formyTool.The example registers
myToolin thetoolsobject (line 226) but doesn't include a corresponding renderer intoolRenderers(lines 227-229). This makes the example incomplete and may confuse developers about whether renderers are required for all tools.Consider either:
- Adding a renderer entry for
myToolto demonstrate the complete pattern, or- Adding a clarifying comment explaining that tools can optionally omit renderers if they don't need custom UI rendering.
📚 Proposed documentation improvement
Option 1: Add renderer for myTool (complete example)
createAiSlice({ tools: {query: createQueryTool(store), myTool}, toolRenderers: { query: QueryToolResult, // ToolRenderer<Output, Input> + myTool: MyToolResult, // Renderer for myTool }, });Option 2: Clarify when renderers are optional
// Renderers registered separately in createAiSlice createAiSlice({ tools: {query: createQueryTool(store), myTool}, toolRenderers: { - query: QueryToolResult, // ToolRenderer<Output, Input> + query: QueryToolResult, // ToolRenderer<Output, Input> + // Note: myTool omitted - tools without renderers use default display }, });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@CLAUDE.md` around lines 226 - 229, The example registers myTool in the tools object but omits a corresponding entry in toolRenderers, leaving the example incomplete; add a renderer entry for myTool inside toolRenderers (e.g., myTool: MyToolRenderer) and ensure the referenced component (MyToolRenderer or MyToolResult) is declared/imported and typed consistently (ToolRenderer<Output, Input>) like QueryToolResult; alternatively, if you prefer to show renderers are optional, add a short clarifying comment next to toolRenderers stating that tools may omit a renderer if no custom UI is needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@CLAUDE.md`:
- Around line 226-229: The example registers myTool in the tools object but
omits a corresponding entry in toolRenderers, leaving the example incomplete;
add a renderer entry for myTool inside toolRenderers (e.g., myTool:
MyToolRenderer) and ensure the referenced component (MyToolRenderer or
MyToolResult) is declared/imported and typed consistently (ToolRenderer<Output,
Input>) like QueryToolResult; alternatively, if you prefer to show renderers are
optional, add a short clarifying comment next to toolRenderers stating that
tools may omit a renderer if no custom UI is needed.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: eeca4c60-6157-42a2-877c-8231c3babc9c
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (6)
CLAUDE.mdpackages/ai-core/package.jsonpackages/data-table/src/QueryDataTableActionsMenu.tsxpackages/notebook/src/cellOperations/AddNewCellDropdown.tsxpackages/vega/package.jsonpackages/vega/src/index.ts
💤 Files with no reviewable changes (2)
- packages/ai-core/package.json
- packages/data-table/src/QueryDataTableActionsMenu.tsx
Previously the renderer always showed the SQL block and "Show Query Result" button regardless of success, causing the modal to re-run an already-failed query. Branch on output.success === false and render the error/details message instead. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Signed-off-by: Ilya Boyandin <[email protected]> Co-authored-by: Ilya Boyandin <[email protected]> Co-authored-by: Xun Li <[email protected]> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Copilot <[email protected]> Co-authored-by: dmitriy-kostianetskiy <[email protected]> Co-authored-by: Dmitriy Kostianetskiy <[email protected]>
# Conflicts: # CLAUDE.md # CLAUDE.md~main
Signed-off-by: Ilya Boyandin <[email protected]>
Signed-off-by: Ilya Boyandin <[email protected]>
…ions and related references
Signed-off-by: Ilya Boyandin <[email protected]> Co-authored-by: Ilya Boyandin <[email protected]> Co-authored-by: Ilya Boyandin <[email protected]> Co-authored-by: Xun Li <[email protected]> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Copilot <[email protected]> Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Summary
This PR removes the dependency on
@openassistant/*across all AI packages and replaces it with a native implementation built on top of the Vercel AI SDK (aiv5). The result is a leaner, more standard integration that aligns tool definitions and chat transport directly with the AI SDK primitives.Affected packages:
@sqlrooms/ai-core,@sqlrooms/ai,@sqlrooms/ai-rag,@sqlrooms/vega.Breaking Changes
Tool definition format
Tools must now be created using
tool()from theaipackage directly. The OpenAssistant-specific format (name,parameters,component) is no longer supported.Before
After
Tool output format
The
{ llmResult, additionalData }split is gone. Toolexecutefunctions now return a single flat object. UsetoModelOutputif you need to control what the LLM sees vs. what the renderer receives (e.g. to strip large payloads from the model context).Tool renderers are now separate from tools
The
componentfield on tool definitions has been removed. Renderers are registered separately via the newtoolRenderersoption oncreateAiSlice.Before
After
Renamed APIs
setSessionToolAdditionalDatasetToolEditStatefindToolComponentfindToolRendererconvertToAiSDKToolsQueryToolLlmResult+QueryToolAdditionalDataQueryToolOutputRagToolAdditionalData+RagToolContextRagToolOutputtoolAdditionalData(persisted session field)toolEditStateRemoved exports
convertToAiSDKTools(@sqlrooms/ai-core/@sqlrooms/ai)createOnToolCompletedHandler(@sqlrooms/ai-core)New APIs
createDefaultAiToolRenderers()—@sqlrooms/aiReturns the renderer registry for the built-in tools. Pass it to
createAiSlicealongside your own renderers.Convenience renderer re-exports
Each tool package now exports a ready-to-use renderer alias:
queryToolRenderer@sqlrooms/airagToolRenderer@sqlrooms/ai-ragvegaChartToolRenderer@sqlrooms/vegaNew types —
@sqlrooms/ai-coreToolRendererProps<TOutput>,ToolRenderer<TOutput>,ToolRendererRegistry,StoredTool,StoredToolSetMigration Guide
tool()fromai, renameparameters→inputSchema, flatten the return value.componentreferences into atoolRenderersmap and pass it tocreateAiSlice.QueryToolLlmResult/QueryToolAdditionalDatawithQueryToolOutput; same for RAG types.setSessionToolAdditionalData→setToolEditState,findToolComponent→findToolRenderer.toolAdditionalData→toolEditStatein stored sessions. No manual action required.See the updated examples (
examples/ai,examples/ai-core,examples/ai-agent,examples/ai-rag,examples/ai-nextjs) for complete reference implementations.What's Unchanged
Chatcomponent API and all UI components@sqlrooms/ai-settings,@sqlrooms/ai-config)processAgentStreamfor the agent-as-tool patternSummary by CodeRabbit
New Features
Refactor
Chores