feat(archief-assistent): preserve SPARQL queries in semantic cache
- Add sparqlQuery field to CachedResponse interface - Extract SPARQL query before cache storage (not after) - Include sparqlQuery in cache HIT message objects - Handle both snake_case (server) and camelCase field names SPARQL queries are now displayed for both fresh API responses and cached responses, improving debugging and transparency.
This commit is contained in:
parent
c3387ef3f1
commit
d5f2f542ce
2 changed files with 83 additions and 1 deletions
|
|
@ -54,6 +54,7 @@ export interface CachedResponse {
|
|||
website?: string;
|
||||
score?: number;
|
||||
}>;
|
||||
sparqlQuery?: string; // SPARQL query used for knowledge graph search
|
||||
}
|
||||
|
||||
export interface CacheConfig {
|
||||
|
|
@ -436,6 +437,7 @@ export class SemanticCache {
|
|||
answer: data.entry.response.answer,
|
||||
sources: data.entry.response.sources || [],
|
||||
institutions: data.entry.response.institutions || [],
|
||||
sparqlQuery: data.entry.response.sparql_query || data.entry.response.sparqlQuery,
|
||||
},
|
||||
timestamp: data.entry.timestamp,
|
||||
hitCount: data.entry.hit_count || 0,
|
||||
|
|
|
|||
|
|
@ -38,6 +38,9 @@ import BusinessIcon from '@mui/icons-material/Business'
|
|||
import OpenInNewIcon from '@mui/icons-material/OpenInNew'
|
||||
import BoltIcon from '@mui/icons-material/Bolt'
|
||||
import DeleteSweepIcon from '@mui/icons-material/DeleteSweep'
|
||||
import ContentCopyIcon from '@mui/icons-material/ContentCopy'
|
||||
import CheckIcon from '@mui/icons-material/Check'
|
||||
import CodeIcon from '@mui/icons-material/Code'
|
||||
|
||||
// Import semantic cache
|
||||
import {
|
||||
|
|
@ -206,6 +209,7 @@ interface Message {
|
|||
cacheMethod?: 'semantic' | 'fuzzy' | 'exact'
|
||||
cacheSimilarity?: number
|
||||
lookupTimeMs?: number
|
||||
sparqlQuery?: string // SPARQL query used for knowledge graph search
|
||||
}
|
||||
|
||||
interface Source {
|
||||
|
|
@ -354,6 +358,7 @@ function ChatPage() {
|
|||
const [input, setInput] = useState('')
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [expandedMessage, setExpandedMessage] = useState<string | null>(null)
|
||||
const [copiedQueryId, setCopiedQueryId] = useState<string | null>(null) // Track which query was copied
|
||||
const [selectedModel, setSelectedModel] = useState<string>(() => {
|
||||
const saved = localStorage.getItem(STORAGE_KEY_LLM_MODEL)
|
||||
return saved || 'glm-4.6'
|
||||
|
|
@ -392,6 +397,13 @@ function ChatPage() {
|
|||
updateCacheStats()
|
||||
}, [updateCacheStats])
|
||||
|
||||
// Copy SPARQL query to clipboard
|
||||
const handleCopyQuery = useCallback((messageId: string, query: string) => {
|
||||
navigator.clipboard.writeText(query)
|
||||
setCopiedQueryId(messageId)
|
||||
setTimeout(() => setCopiedQueryId(null), 2000) // Reset after 2 seconds
|
||||
}, [])
|
||||
|
||||
const sendMessage = useCallback(async (text: string) => {
|
||||
if (!text.trim() || isLoading) return
|
||||
|
||||
|
|
@ -452,6 +464,7 @@ function ChatPage() {
|
|||
cacheMethod: cacheResult.method === 'none' ? undefined : cacheResult.method,
|
||||
cacheSimilarity: cacheResult.similarity,
|
||||
lookupTimeMs: cacheResult.lookupTimeMs,
|
||||
sparqlQuery: cached.sparqlQuery, // Include cached SPARQL query
|
||||
} : m
|
||||
))
|
||||
|
||||
|
|
@ -680,14 +693,18 @@ function ChatPage() {
|
|||
score: scores.combined as number | undefined,
|
||||
}
|
||||
})
|
||||
|
||||
// ========================================
|
||||
// STORE IN CACHE (after successful response)
|
||||
// ========================================
|
||||
|
||||
// Extract SPARQL query from visualization BEFORE caching
|
||||
const sparqlQuery = (data.visualization as Record<string, unknown> | undefined)?.sparql_query as string | undefined
|
||||
|
||||
const cacheResponse: CachedResponse = {
|
||||
answer: (data.answer as string) || '',
|
||||
sources,
|
||||
institutions,
|
||||
sparqlQuery, // Include SPARQL query in cache
|
||||
}
|
||||
|
||||
// Generate embedding NOW (after cache miss, for local storage)
|
||||
|
|
@ -717,6 +734,7 @@ function ChatPage() {
|
|||
institutions,
|
||||
fromCache: false,
|
||||
isStreaming: false, // Mark streaming as complete
|
||||
sparqlQuery, // Include SPARQL query if available
|
||||
} : m
|
||||
))
|
||||
} catch (error) {
|
||||
|
|
@ -1036,6 +1054,68 @@ function ChatPage() {
|
|||
))}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* SPARQL Query Display */}
|
||||
{message.role === 'assistant' && !message.isLoading && message.sparqlQuery && (
|
||||
<Box sx={{ mt: 2 }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
mb: 1,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
|
||||
<CodeIcon sx={{ fontSize: 16, color: naColors.primary }} />
|
||||
<Typography variant="caption" sx={{ fontWeight: 600, color: naColors.primary }}>
|
||||
SPARQL Query
|
||||
</Typography>
|
||||
</Box>
|
||||
<Tooltip title={copiedQueryId === message.id ? 'Gekopieerd!' : 'Kopieer query'}>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => handleCopyQuery(message.id, message.sparqlQuery!)}
|
||||
sx={{
|
||||
width: 28,
|
||||
height: 28,
|
||||
color: copiedQueryId === message.id ? naColors.green : 'text.secondary',
|
||||
}}
|
||||
>
|
||||
{copiedQueryId === message.id ? (
|
||||
<CheckIcon sx={{ fontSize: 16 }} />
|
||||
) : (
|
||||
<ContentCopyIcon sx={{ fontSize: 16 }} />
|
||||
)}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<Paper
|
||||
variant="outlined"
|
||||
sx={{
|
||||
p: 1.5,
|
||||
bgcolor: '#1e1e1e',
|
||||
borderRadius: 1,
|
||||
overflow: 'auto',
|
||||
maxHeight: 200,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
component="pre"
|
||||
sx={{
|
||||
fontFamily: 'Monaco, Consolas, monospace',
|
||||
fontSize: '0.75rem',
|
||||
color: '#d4d4d4',
|
||||
margin: 0,
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordBreak: 'break-word',
|
||||
}}
|
||||
>
|
||||
{message.sparqlQuery}
|
||||
</Typography>
|
||||
</Paper>
|
||||
</Box>
|
||||
)}
|
||||
</Paper>
|
||||
</Box>
|
||||
</Fade>
|
||||
|
|
|
|||
Loading…
Reference in a new issue