@sqlrooms/ai
High-level AI package for SQLRooms.
This package combines:
- AI slice state/logic (
@sqlrooms/ai-core) - AI settings UI/state (
@sqlrooms/ai-settings) - AI config schemas (
@sqlrooms/ai-config) - SQL query and schema discovery tool helpers (
createDefaultAiTools,createQueryTool)
Use this package when you want AI chat + tool execution in a SQLRooms app without wiring low-level pieces manually.
createDefaultAiInstructions includes a hybrid DuckDB table context: small current-database main catalogs include full schemas for every table, while larger catalogs include a few full schemas, additional table names with row counts, and instructions to call read_table_schema before querying tables whose columns are not shown. createDefaultAiTools registers list_tables and read_table_schema by default so apps can expose the same table discovery workflow. These tools search the current database main schema by default, and accept broader schema, database, and pattern filters for other visible schemas or attached databases.
Installation
npm install @sqlrooms/ai @sqlrooms/room-shell @sqlrooms/duckdb @sqlrooms/uiQuick start
import {
AiSettingsSliceState,
AiSliceState,
createAiSettingsSlice,
createAiSlice,
createDefaultAiInstructions,
createDefaultAiTools,
} from '@sqlrooms/ai';
import {
createRoomShellSlice,
createRoomStore,
RoomShellSliceState,
} from '@sqlrooms/room-shell';
type RoomState = RoomShellSliceState & AiSliceState & AiSettingsSliceState;
export const {roomStore, useRoomStore} = createRoomStore<RoomState>(
(set, get, store) => ({
...createRoomShellSlice({
config: {
dataSources: [
{
type: 'url',
tableName: 'earthquakes',
url: 'https://huggingface.co/datasets/sqlrooms/earthquakes/resolve/main/earthquakes.parquet',
},
],
},
})(set, get, store),
...createAiSettingsSlice()(set, get, store),
...createAiSlice({
tools: {
...createDefaultAiTools(store),
},
getInstructions: () => createDefaultAiInstructions(store),
// Optional: observe completed, non-aborted turns for app-owned follow-up
// behavior such as handoff into a newly selected workspace artifact.
onChatFinish: ({sessionId, messages}) => {
void sessionId;
void messages;
},
})(set, get, store),
}),
);Render chat UI
import {Chat} from '@sqlrooms/ai';
import {useRoomStore} from './store';
function AiPanel() {
const updateProvider = useRoomStore(
(state) => state.aiSettings.updateProvider,
);
return (
<Chat>
<Chat.Sessions />
<Chat.Messages />
<Chat.PromptSuggestions>
<Chat.PromptSuggestions.Item text="Summarize the available tables" />
</Chat.PromptSuggestions>
<Chat.Composer placeholder="Ask a question about your data">
<Chat.InlineApiKeyInput
onSaveApiKey={(provider, apiKey) => {
updateProvider(provider, {apiKey});
}}
/>
<Chat.ModelSelector />
</Chat.Composer>
</Chat>
);
}Generate Chat Titles
generateSessionTitle turns a session's early user messages into a concise title via ai.sendPrompt, cleans the model output, and renames the session. useGenerateSessionTitle wraps that helper for React surfaces that should watch the current session and trigger title generation after new user messages. Apps can keep product-specific policy outside the shared package by passing options such as enabled, isDefaultSessionName, and getPromptOptions.
import {Chat, useGenerateSessionTitle} from '@sqlrooms/ai';
function AiPanel() {
useGenerateSessionTitle({
enabled: true,
getPromptOptions: () => ({useTools: false}),
});
return (
<Chat>
<Chat.Messages />
<Chat.Composer />
</Chat>
);
}Chat search
Chat renders a ChatSearchProvider and exposes Chat.Search, an in-conversation find bar that highlights matches in the current session's messages.
For building search UIs outside the chat (e.g. a session list that searches across all sessions), the underlying matching primitives are re-exported and can be used without the provider:
normalizeChatSearchQuery(query)— trims + lower-cases a query (the casing rule the search uses).findChatSearchMatches(blocks, query)— returns positional matches (ChatSearchMatch[]) for a list ofChatSearchBlocks. Useful for highlighting matched substrings consistently withChat.Search.markdownToPlainText(markdown)— extracts plain text from markdown so message content can be made searchable.
import {findChatSearchMatches, type ChatSearchBlock} from '@sqlrooms/ai';
const blocks: ChatSearchBlock[] = [
{id: 'title', resultId: 'title', text: title},
];
const matches = findChatSearchMatches(blocks, query);Chat Session Types
Use ChatSessionSchema for persisted chat session validation and isChatSessionEmpty for session emptiness checks. AnalysisSessionSchema, AnalysisResultSchema, isAnalysisSessionEmpty, AnalysisResultsContainer, and AnalysisResult remain compatibility exports for existing apps, but new code should prefer Chat.Messages, uiMessages, and derived ChatTurn helpers such as getChatTurnsFromUiMessages.
Old persisted sessions that contain analysisResults still load, but parsed and new ChatSessionSchema state no longer includes that field.
Devtools
@sqlrooms/ai/devtools exposes development-oriented inspection components and helpers without adding CodeMirror-heavy debug UI to the main @sqlrooms/ai barrel.
import {ChatSessionDebugView} from '@sqlrooms/ai/devtools';
function DebugPanel({
sessionId,
onClose,
}: {
sessionId: string;
onClose?: () => void;
}) {
return <ChatSessionDebugView sessionId={sessionId} onClose={onClose} />;
}ChatSessionDebugView reads the existing AI store context and shows session metadata, model selection, registered tools, run context, raw uiMessages, and a tabbed chronological timeline that keeps message parts, tool calls, nested agentProgress, optional agent snapshots, and copyable JSON blocks together.
Agent snapshot capture is opt-in on the AI slice:
createAiSlice({
tools,
getInstructions,
devtools: {
captureAgentSnapshots: true,
persistAgentSnapshots: true,
maxAgentSnapshotBytes: 64_000,
},
});Enable persistence when you need post-mortem or cross-tab debugging in saved workspace state. Snapshots are serializable metadata only; tool names, descriptions, capability flags, and approval hints may be stored, but implementations, closures, secrets, and unbounded prompt/output content should not be stored.
Add custom tools
import {z} from 'zod';
import {
createAiSlice,
createDefaultAiInstructions,
createDefaultAiTools,
} from '@sqlrooms/ai';
// inside createRoomStore(...):
createAiSlice({
tools: {
...createDefaultAiTools(store),
echo: {
name: 'echo',
description: 'Return user text back to the chat',
parameters: z.object({
text: z.string(),
}),
execute: async ({text}) => ({
llmResult: {
success: true,
details: `Echo: ${text}`,
},
}),
},
},
getInstructions: () => createDefaultAiInstructions(store),
})(set, get, store);Tool execute callbacks receive hidden run-context helpers in their second argument. Apps can use getRunContext to capture selected artifacts at the start of a run, expose them in formatRunContextInstructions, and then let tools update the effective primary context with setPrimaryRunContextItem. Old contexts without primaryItemId remain valid; the first item is treated as primary. Artifact-specific context tools live in @sqlrooms/artifacts/ai.
Use remote endpoint mode
If you want server-side model calls, set chatEndPoint and optional chatHeaders:
// inside createRoomStore(...):
...createAiSlice({
tools: {
...createDefaultAiTools(store),
},
getInstructions: () => createDefaultAiInstructions(store),
chatEndPoint: '/api/chat',
chatHeaders: {
'x-app-name': 'my-sqlrooms-app',
},
})(set, get, store),Skills
The skills subsystem lets you define, store, and author reusable AI "skills" — named instruction sets that can be loaded into an agent at runtime.
Storage and types
SkillStorage is the interface that abstracts where skills live (filesystem, database, cloud, etc.). Implement it to plug in your own backend:
listRoots()— enumerate available skill root locationslistSkills(rootId)— list all skills under a rootreadSkill(ref)/writeSkill(ref, content)/deleteSkill(ref)— CRUD on individual skillsresolveSkillId(id)— resolve a bare id to its highest-prioritySkillRefsubscribe?(listener)— optional; subscribe to change notifications. Returns an unsubscribe function. Implementations that don't mutate (read-only/static) may omit this method.
Supporting types: SkillRoot, SkillManifest, SkillRef, SkillRecord, SkillListing, SkillWriteContent, SkillFile.
Composite storage
CompositeSkillStorage priority-merges multiple SkillStorage instances behind a single SkillStorage interface. Children are passed in priority order (highest first); they win conflicts in resolveSkillId and appear first in listRoots. Each child must own a unique set of rootIds.
subscribe fans out to every child that exposes the optional subscribe? method and aggregates the unsubscribes. If no child supports subscribe, composite.subscribe(...) is a noop returning a noop unsubscribe — consumers can call it unconditionally.
import {CompositeSkillStorage} from '@sqlrooms/ai';
// Higher-priority `userStorage` wins on id collisions; both contribute roots
// and listings to the merged view.
const storage = new CompositeSkillStorage([userStorage, builtInStorage]);
const roots = await storage.listRoots(); // [user roots..., built-in roots...]
const all = await storage.listSkills(); // union, with duplicates
// Optional change notification: composite forwards from any subscribe-capable
// child.
const unsubscribe = storage.subscribe(() => {
void refreshUi();
});
// later: unsubscribe();Manifest utilities
parseSkillManifest(raw)— parse and validate a skill manifest (Zod-backed, throwsSkillManifestErroron failure)serializeSkillManifest(manifest)— serialize a manifest back to its raw formloadSkillFromFiles(files)— assemble aSkillRecordfrom a set ofSkillFileobjects (manifest + instruction body)
Error types
All skill errors extend SkillError and carry a typed SkillErrorCode:
| Class | When thrown |
|---|---|
SkillManifestError | Manifest parse/validation failure |
SkillNotFoundError | Skill ref does not exist in storage |
SkillRootReadOnlyError | Write attempted on a read-only root |
SkillConflictError | Skill ID collision on write |
Skill authoring
A built-in agent-driven authoring flow that generates skill content through a conversational UI:
createSkillAuthoringAgent(options)— construct aToolLoopAgentscoped to skill creation; acceptsCreateSkillAuthoringAgentOptionscreateSkillDraftStore()— Zustand store for tracking the in-progress draft (SkillDraftStore,SkillDraftState)SkillAuthoringPanel— drop-in panel component that wiresChat.LocalAgentRootto the authoring agent; acceptsSkillAuthoringPanelPropsSkillDraftPreview— read-only preview of the current draft manifest and instructions; acceptsSkillDraftPreviewPropsDefaultSkillAuthoringPanelHeader— default header forSkillAuthoringPanel
Types: SkillAuthoringContext, SkillDraft, SkillDraftStatus, SaveSkillCallback, CreateSkillAuthoringAgentOptions.
Lower-level authoring tools (exported for advanced use): createWriteManifestTool, createWriteInstructionsTool, createSaveSkillTool, buildSkillAuthoringSystemPrompt, containsForbidden, DEFAULT_SKILL_AUTHORING_STOP_STEPS.
import {
createSkillAuthoringAgent,
createSkillDraftStore,
SkillAuthoringPanel,
} from '@sqlrooms/ai';
const draftStore = createSkillDraftStore();
const agent = createSkillAuthoringAgent({
model: myLanguageModel,
draftStore,
onSave: async (skill) => {
await mySkillStorage.writeSkill(
{rootId: 'default', skillId: skill.id},
skill,
);
},
});
function SkillCreator() {
return <SkillAuthoringPanel agent={agent} draftStore={draftStore} />;
}Related packages
@sqlrooms/ai-corefor lower-level AI slice and chat primitives@sqlrooms/ai-settingsfor settings slice/components only@sqlrooms/ai-configfor Zod schemas and migrations
Classes
- CompositeSkillStorage
- SkillError
- SkillManifestError
- SkillNotFoundError
- SkillRootReadOnlyError
- SkillConflictError
- ToolAbortError
Interfaces
- CreateSkillAuthoringAgentOptions
- SkillAuthoringContext
- SkillDraft
- SkillDraftState
- SkillErrorContext
- SkillFile
- SkillRoot
- SkillRef
- SkillRecord
- SkillListing
- SkillWriteContent
- SkillStorage
- AiSliceOptions
- StoredTool
- ModelUsageData
Type Aliases
- SkillAuthoringPanelProps
- SkillDraftPreviewProps
- SkillDraftStatus
- SkillDraftStore
- SaveSkillCallback
- SkillErrorCode
- SkillManifest
- ListCommandsToolParameters
- CommandToolDescriptor
- ListCommandsToolLlmResult
- ExecuteCommandToolParameters
- ExecuteCommandToolLlmResult
- CommandToolsOptions
- DefaultCommandTools
- DefaultToolsOptions
- DefaultAiToolRenderers
- QueryToolRendererOptions
- QueryToolParameters
- QueryToolOutput
- QueryToolOptions
- TableSchemaContextLimits
- AiTableScope
- AiTableScopeOptions
- AiSettingsSliceConfig
- AiSessionForkOrigin
- AiSliceConfig
- ErrorMessageSchema
AnalysisResultSchema- AiRunContextItem
- AiRunContext
- ChatSessionSchema
AnalysisSessionSchema- ToolUIPart
- UIMessagePart
- AiSliceState
- ForkSessionFromMessageArgs
- ChatRequestErrorMessage
- ChatMessageMetadata
- ChatTurn
- LocalAgentChatRootProps
- ChatSearchBlock
- ChatSearchMatch
- ChatTurnViewProps
- ErrorMessageComponentProps
- ToolStructureBehavior
- ToolDisplayBehavior
- ToolRenderBehavior
- MessageContentProps
- ContextSelectorItem
- ContextSelectorRootProps
- SessionType
- GenerateSessionTitlePromptOptions
- GenerateSessionTitleOptions
- GenerateSessionTitleArgs
- GenerateSessionTitleResult
- UseGenerateSessionTitleOptions
- AgentToolCall
- AgentToolCallAdditionalData
- AgentStreamOutput
- AgentSnapshot
- StoredToolSet
- AddToolOutput
- AiToolExecutionContext
- ToolRendererProps
- ToolRenderer
- ToolRendererRegistry
- ToolRenderers
- AiSettingsSliceState
Variables
- DEFAULT_SKILL_AUTHORING_STOP_STEPS
- SKILL_AUTHORING_TOOL_NAMES
- SkillAuthoringPanel
- DefaultSkillAuthoringPanelHeader
- SkillDraftPreview
- SkillManifestSchema
- ListCommandsToolParameters
- ExecuteCommandToolParameters
- QueryToolResult
- QueryToolParameters
- DEFAULT_TABLE_SCHEMA_CONTEXT_LIMITS
- AiSettingsSliceConfig
- AiSessionForkOrigin
- AiSliceConfig
- ErrorMessageSchema
AnalysisResultSchema- AiRunContextItemSchema
- AiRunContextSchema
- ChatSessionSchema
AnalysisSessionSchema- AiThinkingDots
- Chat
- ChatMessagesContainer
- ChatTurnView
- ShowToolCallDetailsProvider
- processMessageContent
- MessageContent
- ModelSelector
- PromptSuggestions
- QueryControls
- SessionControls
- ToolCallInfo
- ContextSelector
- CHAT_CONTEXT_SELECTOR_SLOT
- DeleteSessionDialog
- SessionActions
- SessionDropdown
- SessionTitle
isAnalysisSessionEmptycleanupPendingAnalysisResults- AiModelParameters
- AiModelUsage
- AiModelsSettings
- AiProvidersSettings
- AiSettingsPanel
Functions
- createSkillAuthoringAgent
- createSkillDraftStore
- buildSkillAuthoringSystemPrompt
- containsForbidden
- createWriteManifestTool
- createWriteInstructionsTool
- createSaveSkillTool
- parseSkillManifest
- serializeSkillManifest
- loadSkillFromFiles
- createCommandTools
- createDefaultAiInstructions
- createDefaultAiTools
- createDefaultAiToolRenderers
- createQueryToolRenderer
- createQueryTool
- getQuerySummary
- getAiTableSchemaContextLimits
- getTablesForAiScope
- getAiTableScopeSummary
- formatOtherTableScopesForAi
- formatTableSchemaForAi
- formatTableSummaryForAi
- formatTablesForLLM
- createDefaultAiConfig
- getAiRunContextItems
- getAiRunContextPrimaryItem
- setAiRunContextPrimaryItem
- createAiSlice
- useStoreWithAi
- updateAgentToolCallData
- streamSubAgent
- getChatRequestErrorMessage
- getChatTurnsFromUiMessages
- getAnalysisResultsFromUiMessages
- markdownToPlainText
- normalizeChatSearchQuery
- findChatSearchMatches
- ErrorMessage
- ToolErrorMessage
- isChatSessionEmpty
- getRunContextItemIds
- getVisibleSessionContextItemIds
- getEffectiveSessionContextItemIds
- isDefaultGeneratedSessionName
- getSessionUserMessageText
- cleanGeneratedSessionTitle
- generateSessionTitle
- useGenerateSessionTitle
- useScrollToBottom
- fixIncompleteToolCalls
- createAiSettingsSlice
- useStoreWithAiSettings
- createDefaultAiSettingsConfig
References
AnalysisResultsContainer
Renames and re-exports ChatMessagesContainer
AnalysisResult
Renames and re-exports ChatTurnView
AnalysisAnswer
Renames and re-exports MessageContent
processAnalysisAnswerContent
Renames and re-exports processMessageContent
