Spaces:
Sleeping
Sleeping
File size: 7,303 Bytes
3c665d2 aa3ae1f 44ef33f aa3ae1f 3c665d2 44ef33f 3c665d2 aa3ae1f 3c665d2 aa3ae1f 44ef33f aa3ae1f 44ef33f aa3ae1f 3c665d2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | import { useState, useEffect } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import { Brain, ChevronDown, ChevronUp, Zap, History } from 'lucide-react'
import { useStore } from '../store/useStore'
import { fetchPromptHistory } from '../lib/api'
const SEED_PROMPT = `You are a SQL expert. Given a natural language question and a SQLite database schema, write a correct SQL query.
Rules:
- Output ONLY the SQL query, nothing else
- No markdown, no code fences, no explanation
- Use SQLite syntax
- Always qualify column names with table aliases when using JOINs`
export function PromptEvolution() {
const { currentPrompt, promptGeneration, promptHistory, setPromptData } = useStore()
const [expanded, setExpanded] = useState(false)
const [historyExpanded, setHistoryExpanded] = useState(false)
const [loading, setLoading] = useState(false)
const prompt = currentPrompt || SEED_PROMPT
const generation = promptGeneration
const [queryCount, setQueryCount] = useState(0)
const [optimizeEvery, setOptimizeEvery] = useState(4)
const [cycleProgress, setCycleProgress] = useState(0)
const loadHistory = async () => {
setLoading(true)
try {
const data = await fetchPromptHistory()
setPromptData(data)
const d = data as Record<string, unknown>
if (d.queryCount !== undefined) setQueryCount(d.queryCount as number)
if (d.optimizeEvery !== undefined) setOptimizeEvery(d.optimizeEvery as number)
if (d.cycleProgress !== undefined) setCycleProgress(d.cycleProgress as number)
} catch {
// noop
} finally {
setLoading(false)
}
}
useEffect(() => {
void loadHistory()
// Poll for updates every 30s
const interval = setInterval(() => void loadHistory(), 30000)
return () => clearInterval(interval)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return (
<div className="flex flex-col gap-2">
{/* Header */}
<button
onClick={() => setExpanded((v) => !v)}
className="flex items-center justify-between w-full group"
>
<div className="flex items-center gap-2">
<Brain size={14} className="text-violet-400" />
<span className="text-xs font-semibold text-white/70">System Prompt</span>
{generation > 0 ? (
<span className="text-[10px] bg-violet-500/20 text-violet-300 border border-violet-500/30 rounded-full px-2 py-0.5">
Gen {generation} · Optimized
</span>
) : (
<span className="text-[10px] bg-white/5 text-gray-500 rounded-full px-2 py-0.5">
Seed
</span>
)}
</div>
{expanded ? (
<ChevronUp size={13} className="text-gray-500" />
) : (
<ChevronDown size={13} className="text-gray-500" />
)}
</button>
{/* Progress toward next optimization */}
{queryCount > 0 && (
<div className="flex flex-col gap-1">
<div className="flex items-center justify-between text-[9px] text-gray-600">
<span>{queryCount} queries processed</span>
<span className="text-gray-700">
{cycleProgress}/{optimizeEvery} · optimizes every {optimizeEvery}
</span>
</div>
<div className="h-1 bg-white/5 rounded-full overflow-hidden">
<div
className="h-full rounded-full bg-violet-500/50 transition-all duration-500"
style={{ width: `${(cycleProgress / optimizeEvery) * 100}%` }}
/>
</div>
</div>
)}
<AnimatePresence>
{expanded && (
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
transition={{ duration: 0.2 }}
className="overflow-hidden"
>
{/* Prompt preview */}
<div className="max-h-40 overflow-y-auto">
<pre className="text-[11px] font-mono text-violet-200/70 bg-violet-950/30 rounded-xl p-3 border border-violet-500/20 whitespace-pre-wrap leading-relaxed">
{prompt}
</pre>
</div>
{/* History button */}
{promptHistory.length > 0 && (
<button
onClick={() => setHistoryExpanded((v) => !v)}
className="mt-2 w-full flex items-center justify-center gap-2 px-3 py-2 text-xs font-medium bg-violet-600/15 text-violet-300 border border-violet-500/25 rounded-xl hover:bg-violet-600/25 hover:border-violet-500/40 transition-all"
>
<History size={12} />
{historyExpanded ? 'Hide' : 'View'} Evolution History
<span className="text-[10px] text-violet-400/60 ml-1">
({promptHistory.length} gen{promptHistory.length !== 1 ? 's' : ''})
</span>
</button>
)}
{/* Generation history */}
<AnimatePresence>
{historyExpanded && promptHistory.length > 0 && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: 'auto', opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={{ duration: 0.15 }}
className="overflow-hidden mt-2"
>
<div className="flex flex-col gap-1.5">
<div className="text-[10px] text-gray-500 font-medium flex items-center gap-1">
<Zap size={10} className="text-violet-400" />
Optimization History
</div>
{promptHistory.map((snap) => (
<div
key={snap.generation}
className="border border-white/5 rounded-xl p-2.5 hover:border-white/10 hover:bg-white/[0.02] transition-all"
>
<div className="flex items-center justify-between mb-1">
<span className="text-[10px] font-semibold text-violet-400">
Generation {snap.generation}
</span>
<span className="text-[10px] font-mono text-green-400">
{(snap.score * 100).toFixed(0)}%
</span>
</div>
<p className="text-[10px] text-gray-400 leading-relaxed line-clamp-2">
{snap.summary}
</p>
<p className="text-[9px] text-gray-600 mt-1">{snap.timestamp}</p>
</div>
))}
</div>
</motion.div>
)}
</AnimatePresence>
{loading && (
<div className="flex items-center gap-2 text-[10px] text-gray-500 mt-2 px-1">
<span className="w-3 h-3 border border-violet-500/40 border-t-violet-400 rounded-full animate-spin inline-block" />
Loading history...
</div>
)}
</motion.div>
)}
</AnimatePresence>
</div>
)
}
|