Client Wrapper
defineDatabaseChat configuration and methods.
defineDatabaseChat creates a DatabaseChatClient that wraps the component
endpoints with a consistent API.
import { defineDatabaseChat } from "./components/databaseChat/client";
const chat = defineDatabaseChat(components.databaseChat, {
model: "openai/gpt-4o",
systemPrompt: "You are a helpful assistant.",
toolGuidance: "auto",
tools,
maxMessagesForDisplay: 100,
maxMessagesForLLM: 50,
// Recommended: resolve identity server-side once, and every method
// routes through ownership-checked endpoints automatically.
getExternalId: async (ctx) => {
const userId = await getAuthUserId(ctx); // your auth
if (!userId) throw new Error("Unauthorized");
return `user:${userId}`;
},
});Configuration options
model: default model forchat.send(default:openai/gpt-4o).systemPrompt: default prompt forchat.send.toolGuidance: standard tool-result guidance. Use"auto"or omit for generated guidance,"disabled"to opt out, or a custom string to append instead.tools: explicit tool definitions with already-created handler strings.autoTools: generate tools from schema-like definitions.maxMessagesForDisplay: default message limit forgetMessages(default: 100).maxMessagesForLLM: default message limit for LLM context (default: 50).maxToolLoops: maximum tool-calling rounds per message before giving up (default: 5).streamThrottleMs: minimum ms between stream delta writes (default: 100).maxToolResultChars: max characters of a serialized tool result sent to the LLM (default: 16000). Oversized results are truncated and flagged with{ truncated: true }.httpReferer/xTitle: OpenRouter attribution headers.getExternalId: async resolver called inside your app's Convex functions to derive the caller'sexternalId. Works with Convex Auth, Clerk, or any identity provider.
Identity is required
Data-access methods throw when no identity can be resolved - neither a
configured getExternalId nor an explicit server-derived externalId
argument. This prevents accidentally skipping access control. Raw component
endpoints remain available for advanced setups; see
Security & Multi-tenant Access.
autoTools shape
autoTools: {
tables: TableInfo[];
handlers: {
query: string;
count: string;
aggregate?: string;
search?: string;
getById?: string;
};
allowedTables: string[];
excludeFields?: Record<string, string[]>;
tableDescriptions?: Record<string, string>;
fieldDescriptions?: Record<string, string>;
}Common methods
All data-access methods accept an optional { externalId } override (must be
derived server-side, never from client input) and route through
ownership-checked endpoints when identity is resolvable.
createConversation(ctx, { externalId?, title? })getConversation(ctx, conversationId, { externalId? }?)listConversations(ctx, externalId?)getMessages(ctx, conversationId, { externalId?, limit? }?)getStreamState(ctx, conversationId, { externalId? }?)getStreamDeltas(ctx, streamId, cursor, { externalId? }?)abortStream(ctx, conversationId, reason?, { externalId? }?)send(ctx, { conversationId, message, apiKey, model?, systemPrompt?, toolGuidance?, toolContext?, externalId?, maxToolLoops?, streamThrottleMs?, maxToolResultChars? })
send always routes through the scoped chat.sendForExternalId action.
Streaming behavior
- Only the final round of a tool-calling loop streams to clients.
- If generation is interrupted (user abort, timeout, error), content streamed
so far is persisted as an assistant message with
partial: true.
Advanced methods
addMessage(ctx, conversationId, role, content, { toolCalls?, toolResults? })getMessagesForLLM(ctx, conversationId, { systemPrompt?, includeTools?, toolGuidance? })getTools()andgetToolsForLLM()getSystemPromptWithTools(basePrompt?, toolGuidance?)
Runtime function handles
Convex createFunctionHandle(...) is asynchronous and should be called inside
a Convex function. If your app builds tool handles at request time, call
components.databaseChat.chat.send directly and pass config.tools from that
action. Use defineDatabaseChat when your tool handler strings are already
available at construction time.