feat: AI chat now more accurate, has access to data from user, and can generate graphs, charts etc

This commit is contained in:
Jakob Wennberg
2026-02-27 08:48:28 +01:00
parent 0c422fcd25
commit 547fd053ec
24 changed files with 1954 additions and 51 deletions
+43 -2
View File
@@ -3,12 +3,14 @@
import { useState } from 'react'
import ReactMarkdown from 'react-markdown'
import { cn } from '@/lib/utils'
import { ChevronDown, ChevronUp, FileText, User, Bot } from 'lucide-react'
import { ChevronDown, ChevronUp, FileText, User, Bot, Database, Loader2 } from 'lucide-react'
import type { ChatMessage as ChatMessageType, SourceReference } from '@/types/chat'
import { ArtifactRenderer } from './artifacts/ArtifactRenderer'
interface ChatMessageProps {
message: ChatMessageType
isStreaming?: boolean
toolsExecuting?: string[]
}
function SourcesList({ sources }: { sources: SourceReference[] }) {
@@ -58,7 +60,36 @@ function SourcesList({ sources }: { sources: SourceReference[] }) {
)
}
export function ChatMessage({ message, isStreaming }: ChatMessageProps) {
const TOOL_LABELS: Record<string, string> = {
get_invoices: 'Hämtar fakturor',
get_supplier_invoices: 'Hämtar leverantörsfakturor',
get_account_balances: 'Hämtar kontosaldon',
get_transactions: 'Hämtar transaktioner',
get_journal_entries: 'Hämtar verifikationer',
get_income_statement: 'Genererar resultaträkning',
get_balance_sheet: 'Genererar balansräkning',
get_vat_summary: 'Beräknar momssammanställning',
get_company_overview: 'Hämtar företagsöversikt',
get_aging_report: 'Genererar åldersanalys',
}
function ToolExecutingIndicator({ tools }: { tools: string[] }) {
if (tools.length === 0) return null
return (
<div className="flex flex-col gap-1 mb-2">
{tools.map((toolName, i) => (
<div key={i} className="flex items-center gap-1.5 text-xs text-muted-foreground">
<Loader2 className="h-3 w-3 animate-spin" />
<Database className="h-3 w-3" />
<span>{TOOL_LABELS[toolName] || toolName}...</span>
</div>
))}
</div>
)
}
export function ChatMessage({ message, isStreaming, toolsExecuting }: ChatMessageProps) {
const isUser = message.role === 'user'
return (
@@ -93,6 +124,11 @@ export function ChatMessage({ message, isStreaming }: ChatMessageProps) {
: 'bg-muted/60 text-foreground'
)}
>
{/* Tool execution indicator */}
{!isUser && isStreaming && toolsExecuting && toolsExecuting.length > 0 && !message.content && (
<ToolExecutingIndicator tools={toolsExecuting} />
)}
<div className={cn(
"text-sm break-words",
!isUser && "prose prose-sm max-w-none prose-headings:text-foreground prose-headings:font-semibold prose-h1:text-base prose-h2:text-sm prose-h3:text-sm prose-p:text-foreground prose-p:my-1.5 prose-headings:my-2 prose-ul:my-1.5 prose-ol:my-1.5 prose-li:my-0.5 prose-li:text-foreground prose-strong:text-foreground prose-table:my-2 prose-table:text-xs prose-th:px-2 prose-th:py-1 prose-td:px-2 prose-td:py-1 prose-th:bg-muted/50 prose-th:border prose-td:border prose-th:border-border/50 prose-td:border-border/50 prose-th:text-foreground prose-td:text-foreground"
@@ -107,6 +143,11 @@ export function ChatMessage({ message, isStreaming }: ChatMessageProps) {
)}
</div>
{/* Artifact visualization */}
{!isUser && message.artifact && (
<ArtifactRenderer artifact={message.artifact} />
)}
{!isUser && message.sources && message.sources.length > 0 && (
<SourcesList sources={message.sources} />
)}