Skip to content

Configuration & providers

The Assistant is configured entirely through the root Assistant configuration section (bound to AssistantOptions) plus two host calls on the backend and one module flag on the frontend. This page covers the providers, the wiring, and the runtime behaviour you can tune.

For what the feature does, see the AI Assistant overview. For DB-enforced tenant isolation, see Tenant isolation & SQL hardening.

Four providers ship, selected by Assistant:Provider (case-insensitive). The right endpoint and model go with each:

Provider Endpoint Example Model API key
OpenAI https://api.openai.com/v1 gpt-4o-mini, gpt-4 required
Gemini https://generativelanguage.googleapis.com/v1beta gemini-pro required
DeepSeek https://api.deepseek.com/v1 deepseek-chat required
Ollama http://localhost:11434 codellama:7b, llama3:8b not required (local)

Provider selection happens at DI time (RegisterAssistantProvider): the configured name maps to OpenAiProvider / GeminiProvider / DeepSeekProvider / OllamaProvider, each implementing IAssistantProvider. An unknown name throws at startup with the supported list.

The umbrella AddGenieApp already registers the Assistant module and maps the hub. A host that composes the engine directly adds two calls:

Program.cs
services.AddGenieAssistant(builder.Configuration); // provider + MCP tools + services
// …
app.MapAssistantHubs(); // the /hubs/assistant-chat SignalR hub

AddGenieAssistant (ServiceCollectionExtensions) binds AssistantOptions, registers the HTTP clients, the core services (SchemaContextBuilder, NaturalLanguageQueryService, ConversationService, ChatHistoryService, AppDocsService, AssistantQueryExecutor, …), the four MCP tools, the McpOrchestratorService, the selected provider, and the AssistantChat authorization policy that gates the hub.

Configuration lives in the root Assistant section (not under Genie):

appsettings.json
"Assistant": {
"Provider": "OpenAI", // OpenAI | Gemini | DeepSeek | Ollama
"Endpoint": "https://api.openai.com/v1",
"Model": "gpt-4o-mini",
"ConversationMode": true, // keep chat context across turns
"AutoGenerateTitle": true, // AI-summarise the first message into a chat title
"DocsPath": "AssistantDocs", // optional: a dir of *.md help docs (merged over baseline)
"ChatWidgetEnabled": true, // gates the AssistantChat auth policy / hub
"ChatWidgetEnabledForRoles": "*", // "*" or a list/CSV of role names, e.g. ["Admin","Analyst"]
"ConnectionString": "" // optional read-only DB login for generated SQL (see hardening)
}

Other notable AssistantOptions knobs (all optional, sensible defaults):

  • MaxTokens (10000), Temperature (0.1), TopP (0.9), TimeoutSeconds (60) — generation parameters.
  • ConversationHistoryLimit (3) — how many recent Q&A pairs (beyond the system prompt) are sent each turn, to control token usage.
  • SchemaTableLimit (15) — how many tables are described inline in the system prompt; the rest remain reachable via get_table_schema.
  • ToolResultMaxChars (8000) — cap on a single tool result before truncation.
  • WelcomeMessage / WelcomeChips — the greeting and quick-action chips shown on an empty chat.

ASP.NET Core layers the environment-variable provider after the JSON files, and nested keys map via the __ separator. So set the key as an OS variable rather than committing it:

Terminal window
# Windows — the double underscore maps to Assistant:ApiKey
setx Assistant__ApiKey "sk-…"
# then restart the shell/IDE so the process inherits it

The same applies to the hardening connection string (Assistant__ConnectionString). Ollama is local and needs no key.

Mount the assistant by enabling the module. It renders app-wide over every route — an AI icon in the header (left of the notification bell) opening a themed bottom-right panel:

createGenieApp({
apiBase: "/api/v1",
modules: { assistant: true },
});

The panel is multi-conversation: a conversations list (open / rename / delete / new, plus multi-select bulk delete) and the conversation view — user/assistant bubbles as sanitised Markdown (GFM tables, lists, code) via marked + DOMPurify, a live Thinking disclosure, a collapsible SQL block and result-table preview for data answers, suggestion chips, a Shift+Enter composer, and a stop control while a reply is in flight. It is accent-/theme-aware.

The UI talks to the backend over AssistantChatHub when connected and falls back to REST (api/assistant: POST /query, thread CRUD GET/POST /chats, GET/PATCH/DELETE /chats/{id}, POST /chats/delete for bulk delete) otherwise. GenieAssistant and GenieAssistantButton are also exported for hosts that want to place or configure them manually (custom title, subtitle, suggestions, welcome). See Frontend configuration.

  • Persistence. Conversations live in the database (schema Genie, tables AssistantChat + AssistantChatMessage), owned per user (UserId + CompanyId). Non-admins only see their own threads. A thread is saved only once its first reply succeeds; a failed first turn is rolled back. Host apps must add the migration for the two tables (dotnet ef migrations add AddAssistantChat -c YourContext).
  • Titles. New threads show a truncated placeholder immediately. With AutoGenerateTitle on (default) the provider summarises the first message into a concise title after the first reply, pushed live to the list (ConversationRenamed); on failure the placeholder is kept silently. A manual rename (PATCH /chats/{id}) always overrides a generated title.
  • Error masking. Internal error detail (a missing/invalid key, a raw provider failure) is surfaced only to users holding the System role (AssistantErrors.ForUser). Everyone else sees a generic “The assistant is temporarily unavailable…” message. Generated SQL is likewise returned to admins only.