Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
263 changes: 104 additions & 159 deletions README.md

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 9 additions & 8 deletions backend/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
fastapi>=0.104
uvicorn>=0.24
pydantic>=2.0
python-multipart>=0.0.6
numpy>=1.24.0
pandas>=2.0.0
numpy>=1.24.0
scikit-learn>=1.3.0
joblib>=1.3.0
streamlit>=1.28.0
fastapi>=0.104
uvicorn>=0.24
pydantic>=2.0
python-dateutil>=2.8
python-multipart>=0.0.6


# --- AI Co-pilot Additions ---
google-generativeai>=0.8.0
python-dotenv>=1.0.0
google-generativeai
python-dotenv
29 changes: 0 additions & 29 deletions docs/PROGRESS_LOG.md

This file was deleted.

391 changes: 187 additions & 204 deletions docs/architecture_explanation.html

Large diffs are not rendered by default.

Binary file modified docs/architecture_image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
385 changes: 132 additions & 253 deletions docs/design.html

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions driveintel_stress_model/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@ pandas>=2.0.0
numpy>=1.24.0
scikit-learn>=1.3.0
joblib>=1.3.0
fastapi==0.104.1
uvicorn==0.24.0
python-multipart==0.0.6
pydantic==2.5.0
pydantic-settings==2.1.0
1 change: 1 addition & 0 deletions frontend/.env.production
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VITE_API_URL=https://your-vercel-deployment.vercel.app
5 changes: 4 additions & 1 deletion frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
crossorigin=""
/>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet">
</head>
<body class="bg-uber-gray-50 text-uber-gray-900 antialiased">
<body class="bg-slate-950 text-slate-300 antialiased font-outfit">
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
Expand Down
11 changes: 1 addition & 10 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 23 additions & 19 deletions frontend/src/components/AICopilot.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,51 +78,55 @@ const AICopilot = () => {
return (
<div className="fixed bottom-6 right-6 z-50 font-sans">
{isOpen && (
<div className="mb-4 w-80 h-[450px] bg-white rounded-2xl shadow-2xl flex flex-col border border-gray-200 overflow-hidden animate-in fade-in slide-in-from-bottom-4">
<div className="bg-blue-600 p-4 text-white flex justify-between items-center shadow-md">
<div className="flex items-center gap-2">
<div className="w-2 h-2 bg-green-400 rounded-full animate-pulse"></div>
<h3 className="font-bold tracking-wide">DriveIntel Safety Assistant</h3>
<div className="mb-4 w-[340px] h-[480px] bg-slate-900/60 backdrop-blur-xl rounded-3xl shadow-2xl flex flex-col border border-white/10 overflow-hidden animate-in fade-in slide-in-from-bottom-4">
<div className="bg-slate-950/80 p-5 border-b border-white/10 flex justify-between items-center shadow-md relative overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-r from-indigo-500/10 to-purple-500/10 pointer-events-none" />
<div className="flex items-center gap-3 relative">
<div className="w-2.5 h-2.5 bg-emerald-400 rounded-full shadow-[0_0_8px_rgba(52,211,153,0.8)] animate-pulse"></div>
<div>
<h3 className="font-bold text-[14px] text-white tracking-wide leading-tight">Safety Copilot</h3>
<p className="text-[11px] text-indigo-300 font-medium">Online & analyzing</p>
</div>
</div>
<button onClick={() => setIsOpen(false)} className="hover:bg-blue-500 p-1 rounded transition">
<X size={20} />
<button onClick={() => setIsOpen(false)} className="hover:bg-white/10 text-slate-400 hover:text-white p-1.5 rounded-xl transition-colors relative z-10">
<X size={18} />
</button>
</div>

<div className="flex-1 p-4 overflow-y-auto space-y-4 bg-gray-50 text-sm">
<div className="flex-1 p-5 overflow-y-auto space-y-5 bg-gradient-to-b from-slate-900/40 to-slate-900/20 text-[14px]">
{messages.map((m, i) => (
<div
key={i}
className={`p-3 rounded-2xl shadow-sm ${
className={`p-3.5 rounded-2xl shadow-sm border ${
m.role === 'ai'
? 'bg-white text-gray-800 border border-gray-100 rounded-tl-none'
: 'bg-blue-600 text-white ml-auto rounded-tr-none'
? 'bg-slate-800/80 text-slate-200 border-white/5 rounded-tl-sm shadow-md'
: 'bg-indigo-600 text-white border-indigo-500 ml-auto rounded-tr-sm shadow-[0_0_15px_rgba(79,70,229,0.3)]'
} max-w-[90%] break-words`}
>
{/* USE THE NEW HELPER HERE */}
<MessageContent text={m.text} role={m.role} />
</div>
))}
{isLoading && (
<div className="flex items-center gap-2 text-gray-400 text-xs italic ml-1">
<div className="flex items-center gap-2 text-indigo-400 text-xs font-medium ml-1">
<Loader2 className="animate-spin" size={14} />
Co-pilot is calculating...
Copilot is typing...
</div>
)}
</div>

<div className="p-3 border-t bg-white flex gap-2">
<div className="p-4 border-t border-white/10 bg-slate-950/80 flex gap-3 relative">
<input
className="flex-1 border border-gray-200 rounded-full px-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all"
placeholder="Ask about goals, breaks, or surge zones..."
className="flex-1 bg-slate-900/80 border border-white/10 rounded-xl px-4 py-3 text-[13px] text-white focus:outline-none focus:border-indigo-500 focus:ring-1 focus:ring-indigo-500 transition-all placeholder-slate-500 shadow-inner"
placeholder="Ask about goals, breaks..."
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && sendMessage()}
/>
<button
onClick={sendMessage}
disabled={isLoading || !input.trim()}
className="bg-blue-600 text-white p-2 rounded-full hover:bg-blue-700 disabled:bg-gray-300 disabled:cursor-not-allowed transition-all shadow-md"
className="bg-indigo-600 text-white w-[46px] flex items-center justify-center rounded-xl hover:bg-indigo-500 disabled:opacity-50 disabled:cursor-not-allowed transition-all shadow-md active:scale-95"
>
<Send size={18} />
</button>
Expand All @@ -132,9 +136,9 @@ const AICopilot = () => {

<button
onClick={() => setIsOpen(!isOpen)}
className="bg-blue-600 hover:bg-blue-700 text-white p-4 rounded-full shadow-2xl transition-all transform hover:scale-110 active:scale-95 flex items-center justify-center border-2 border-white/20"
className="bg-gradient-to-r from-indigo-600 to-purple-600 hover:from-indigo-500 hover:to-purple-500 text-white p-4 rounded-full shadow-[0_0_20px_rgba(99,102,241,0.5)] transition-all transform hover:scale-110 active:scale-95 flex items-center justify-center border border-white/20 z-50 group"
>
{isOpen ? <X size={28} /> : <MessageCircle size={28} />}
{isOpen ? <X size={26} className="group-hover:rotate-90 transition-transform" /> : <MessageCircle size={26} className="group-hover:animate-pulse" />}
</button>
</div>
);
Expand Down
10 changes: 5 additions & 5 deletions frontend/src/components/ConfidenceBadge.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
const levelConfig = {
low: { bg: 'bg-green-100', text: 'text-green-800', label: 'Low' },
medium: { bg: 'bg-yellow-100', text: 'text-yellow-800', label: 'Med' },
high: { bg: 'bg-red-100', text: 'text-red-800', label: 'High' },
low: { bg: 'bg-emerald-500/10 border-emerald-500/20', text: 'text-emerald-400', label: 'Low' },
medium: { bg: 'bg-amber-500/10 border-amber-500/20', text: 'text-amber-400', label: 'Med' },
high: { bg: 'bg-rose-500/10 border-rose-500/20', text: 'text-rose-400', label: 'High' },
}

export default function ConfidenceBadge({ level, score }) {
const cfg = levelConfig[level] || levelConfig.low
return (
<span className={`inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-semibold ${cfg.bg} ${cfg.text}`}>
<span className={`inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-md border text-[10px] uppercase tracking-wider font-bold shadow-lg ${cfg.bg} ${cfg.text}`}>
{cfg.label}
{score !== undefined && <span className="font-mono">{Math.round(score * 100)}%</span>}
{score !== undefined && <span className="font-mono text-[11px] opacity-90 tracking-normal">{Math.round(score * 100)}%</span>}
</span>
)
}
29 changes: 15 additions & 14 deletions frontend/src/components/EventCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,43 +8,44 @@ export default function EventCard({ event, onJumpTo, onFeedback }) {
const [showExplain, setShowExplain] = useState(false)

const severityColor = {
low: 'border-l-uber-green',
medium: 'border-l-uber-yellow',
high: 'border-l-uber-red',
}[event.severity] || 'border-l-uber-gray-300'
low: 'border-l-emerald-500 shadow-[0_4px_20px_rgba(16,185,129,0.1)]',
medium: 'border-l-amber-500 shadow-[0_4px_20px_rgba(245,158,11,0.1)]',
high: 'border-l-rose-500 shadow-[0_4px_20px_rgba(244,63,94,0.15)]',
}[event.severity] || 'border-l-slate-700'

const time = event.timestamp
? new Date(event.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' })
: `+${Math.round(event.offset_sec / 60)}m`

return (
<>
<div className={`bg-white rounded-lg border border-uber-gray-100 border-l-4 ${severityColor} p-4 shadow-sm`}>
<div className="flex items-start justify-between gap-2">
<div className={`bg-slate-800/40 backdrop-blur-md rounded-xl border border-white/5 border-l-4 ${severityColor} p-4 shadow-lg hover:-translate-y-1 transition-all duration-300 relative overflow-hidden group`}>
<div className="absolute inset-0 bg-gradient-to-r from-white/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" />
<div className="flex items-start justify-between gap-2 relative z-10">
<div className="flex-1">
<div className="flex items-center gap-2 mb-1">
<span className="text-lg">{event.emoji}</span>
<span className="font-semibold text-sm">{event.label.replace(/_/g, ' ')}</span>
<span className="text-lg drop-shadow-md">{event.emoji}</span>
<span className="font-semibold text-[15px] text-slate-100">{event.label.replace(/_/g, ' ')}</span>
<ConfidenceBadge level={event.confidence_level} score={event.confidence} />
</div>
<p className="text-xs text-uber-gray-500">{time}</p>
<p className="text-[13px] text-slate-400">{time}</p>
{event.explain?.summary && (
<p className="text-xs text-uber-gray-600 mt-1">{event.explain.summary}</p>
<p className="text-[13px] text-slate-300 mt-1.5 leading-relaxed">{event.explain.summary}</p>
)}
</div>

<div className="flex items-center gap-1 shrink-0">
<div className="flex items-center gap-1 shrink-0 relative z-10">
<button
onClick={() => setShowExplain(true)}
className="p-1.5 rounded-lg hover:bg-uber-gray-100 text-uber-gray-500 transition-colors"
className="p-1.5 rounded-lg hover:bg-slate-700/50 text-slate-400 hover:text-white transition-colors"
title="Why this happened"
>
<Info className="w-4 h-4" />
</button>
{onJumpTo && (
<button
onClick={() => onJumpTo(event.offset_sec)}
className="p-1.5 rounded-lg hover:bg-uber-gray-100 text-uber-blue transition-colors"
className="p-1.5 rounded-lg hover:bg-indigo-500/20 text-indigo-400 hover:text-indigo-300 transition-colors"
title="Jump to"
>
<MapPin className="w-4 h-4" />
Expand All @@ -53,7 +54,7 @@ export default function EventCard({ event, onJumpTo, onFeedback }) {
</div>
</div>

<div className="mt-3 pt-3 border-t border-uber-gray-100">
<div className="mt-4 pt-3 border-t border-white/5 relative z-10">
<FeedbackButtons
eventId={event.id}
current={event.feedback?.label}
Expand Down
Loading