Skip to content

Commit 9bb0d72

Browse files
Verify production operator paths end to end
1 parent c7e83d4 commit 9bb0d72

29 files changed

Lines changed: 251 additions & 240 deletions

File tree

caedo-api/pages/5_Reports.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
st.header("AI_INFRASTRUCTURE_V2")
2020
usage = AIUsageRepository.get_summary()
2121
if usage:
22-
st.metric("TOTAL_AI_CALLS", usage.get('total_calls', 0))
23-
st.metric("TOTAL_TOKENS", f"{usage.get('total_tokens', 0):,}")
22+
st.metric("TOTAL_AI_CALLS", usage.get('total_calls') or 0)
23+
st.metric("TOTAL_TOKENS", f"{usage.get('total_tokens') or 0:,}")
2424

2525
# 1. Fetch Data
2626
all_jobs = JobsRepository.get_all()

caedo-web/app/api/v1/ai/generate/route.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -343,8 +343,9 @@ export async function POST(request: NextRequest) {
343343
// Log usage to backend for cost tracking
344344
try {
345345
const backendUrl = process.env.BACKEND_API_URL || 'http://localhost:8000';
346-
const promptTokens = (usage as any).promptTokens || 0;
347-
const completionTokens = (usage as any).completionTokens || 0;
346+
const usageStats = usage as { promptTokens?: number; completionTokens?: number };
347+
const promptTokens = usageStats.promptTokens || 0;
348+
const completionTokens = usageStats.completionTokens || 0;
348349

349350
await fetch(`${backendUrl}/api/v1/ai/usage`, {
350351
method: 'POST',

caedo-web/app/business/page.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ interface CostResult {
3939

4040
export default function BusinessPage() {
4141
const handoff = useSceneStore((state) => state.handoff.pendingBusinessAnalysis);
42-
const clearHandoff = useSceneStore((state) => () => state.setPendingBusinessAnalysis(null));
42+
const setPendingBusinessAnalysis = useSceneStore((state) => state.setPendingBusinessAnalysis);
4343
const setPendingDesignPrompt = useSceneStore((state) => state.setPendingDesignPrompt);
4444
const router = useRouter();
4545

@@ -158,7 +158,7 @@ export default function BusinessPage() {
158158
</p>
159159
</div>
160160
</div>
161-
<button onClick={clearHandoff} className="text-muted-foreground hover:text-primary transition-colors">
161+
<button onClick={() => setPendingBusinessAnalysis(null)} className="text-muted-foreground hover:text-primary transition-colors">
162162
<XCircle className="w-4 h-4" />
163163
</button>
164164
</div>
@@ -370,4 +370,3 @@ export default function BusinessPage() {
370370
</div>
371371
);
372372
}
373-

caedo-web/app/facility/inventory/page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export default function InventoryPage() {
6363
setNewItem({ ...newItem, color: '' });
6464
fetchInventory();
6565
}
66-
} catch (error) {
66+
} catch {
6767
toast.error('Failed to add inventory');
6868
}
6969
};
@@ -76,7 +76,7 @@ export default function InventoryPage() {
7676
toast.success('Item removed');
7777
fetchInventory();
7878
}
79-
} catch (error) {
79+
} catch {
8080
toast.error('Failed to remove item');
8181
}
8282
};
@@ -215,7 +215,7 @@ export default function InventoryPage() {
215215
)}
216216
]}
217217
data={items}
218-
emptyMessage="VAULT_EMPTY // NO_MATERIALS_REGISTERED"
218+
emptyMessage={loading ? "LOADING_RESERVES..." : "VAULT_EMPTY // NO_MATERIALS_REGISTERED"}
219219
/>
220220
</CyberCard>
221221

caedo-web/app/facility/page.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ interface Printer {
2323
current_status: string;
2424
}
2525

26+
type PrinterFormData = Omit<Printer, 'id' | 'current_status'> & {
27+
id?: number;
28+
current_status?: string;
29+
notes?: string;
30+
};
31+
2632
interface PrinterStatus {
2733
success: boolean;
2834
status: string;
@@ -82,7 +88,7 @@ export default function FacilityPage() {
8288
}
8389
}
8490

85-
const handleSavePrinter = async (printerData: any) => {
91+
const handleSavePrinter = async (printerData: PrinterFormData) => {
8692
try {
8793
const method = printerData.id ? 'PUT' : 'POST';
8894
const url = printerData.id ? `/api/v1/printfarm/printers/${printerData.id}` : '/api/v1/printfarm/printers';
@@ -297,4 +303,3 @@ export default function FacilityPage() {
297303
</div>
298304
);
299305
}
300-

caedo-web/app/settings/appearance/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const ACCENT_PRESETS = [
1818
{ name: 'Warning Orange', color: '#f97316' },
1919
{ name: 'Cyber Yellow', color: '#eab308' },
2020
];
21+
type ThemeMode = 'light' | 'dark' | 'system';
2122

2223
export default function AppearanceSettingsPage() {
2324
const { theme, setTheme, accentColor, setAccentColor } = useTheme();
@@ -47,7 +48,7 @@ export default function AppearanceSettingsPage() {
4748
].map((t) => (
4849
<button
4950
key={t.id}
50-
onClick={() => setTheme(t.id as any)}
51+
onClick={() => setTheme(t.id as ThemeMode)}
5152
className={cn(
5253
"flex flex-col items-center gap-3 p-4 border rounded transition-all group",
5354
theme === t.id
@@ -166,4 +167,3 @@ export default function AppearanceSettingsPage() {
166167
</div>
167168
);
168169
}
169-

caedo-web/app/settings/usage/page.tsx

Lines changed: 60 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export default function AIUsagePage() {
7777
{ label: 'Estimated Cost', value: summary?.total_cost_usd ? `$${summary.total_cost_usd.toFixed(4)}` : '$0.0000', Icon: DollarSign },
7878
{ label: 'Avg Cost / Call', value: summary?.total_calls ? `$${(summary.total_cost_usd / summary.total_calls).toFixed(5)}` : '$0.00000', Icon: Zap },
7979
];
80+
const featureChartData: Array<Record<string, string | number>> = featureData.map((item) => ({ ...item }));
8081

8182
return (
8283
<div className="flex flex-col gap-6 p-6 min-h-screen bg-[#050505] text-foreground">
@@ -122,59 +123,71 @@ export default function AIUsagePage() {
122123
{/* Cost Over Time */}
123124
<CyberCard className="lg:col-span-2" title="COST_TRENDS_30D" subtitle="Daily AI expenditure in USD">
124125
<div className="h-[300px] w-full mt-4">
125-
<ResponsiveContainer width="100%" height="100%">
126-
<LineChart data={dailyData}>
127-
<CartesianGrid strokeDasharray="3 3" stroke="#222" />
128-
<XAxis
129-
dataKey="date"
130-
stroke="#666"
131-
fontSize={10}
132-
tickFormatter={(val) => val.split('-').slice(1).join('/')}
133-
/>
134-
<YAxis stroke="#666" fontSize={10} />
135-
<Tooltip
136-
contentStyle={{ backgroundColor: '#0a0a0a', border: '1px solid #333', fontSize: '10px' }}
137-
itemStyle={{ color: '#00FFCC' }}
138-
/>
139-
<Legend iconType="circle" wrapperStyle={{ fontSize: '10px', paddingTop: '10px' }} />
140-
<Line
141-
type="monotone"
142-
dataKey="cost"
143-
name="Cost ($)"
144-
stroke="#00FFCC"
145-
strokeWidth={2}
146-
dot={{ r: 3, fill: '#00FFCC' }}
147-
activeDot={{ r: 5, stroke: '#00FFCC', strokeWidth: 2, fill: '#050505' }}
148-
/>
149-
</LineChart>
150-
</ResponsiveContainer>
126+
{dailyData.length > 0 ? (
127+
<ResponsiveContainer width="100%" height="100%" minWidth={1} minHeight={1}>
128+
<LineChart data={dailyData}>
129+
<CartesianGrid strokeDasharray="3 3" stroke="#222" />
130+
<XAxis
131+
dataKey="date"
132+
stroke="#666"
133+
fontSize={10}
134+
tickFormatter={(val) => val.split('-').slice(1).join('/')}
135+
/>
136+
<YAxis stroke="#666" fontSize={10} />
137+
<Tooltip
138+
contentStyle={{ backgroundColor: '#0a0a0a', border: '1px solid #333', fontSize: '10px' }}
139+
itemStyle={{ color: '#00FFCC' }}
140+
/>
141+
<Legend iconType="circle" wrapperStyle={{ fontSize: '10px', paddingTop: '10px' }} />
142+
<Line
143+
type="monotone"
144+
dataKey="cost"
145+
name="Cost ($)"
146+
stroke="#00FFCC"
147+
strokeWidth={2}
148+
dot={{ r: 3, fill: '#00FFCC' }}
149+
activeDot={{ r: 5, stroke: '#00FFCC', strokeWidth: 2, fill: '#050505' }}
150+
/>
151+
</LineChart>
152+
</ResponsiveContainer>
153+
) : (
154+
<div className="h-full border border-dashed border-border/40 flex items-center justify-center text-xs uppercase tracking-widest opacity-40">
155+
No cost trend telemetry available
156+
</div>
157+
)}
151158
</div>
152159
</CyberCard>
153160

154161
{/* Feature Distribution */}
155162
<CyberCard title="FEATURE_DISTRIBUTION" subtitle="Cost by AI capability">
156163
<div className="h-[300px] w-full flex flex-col items-center">
157-
<ResponsiveContainer width="100%" height={220}>
158-
<PieChart>
159-
<Pie
160-
data={featureData}
161-
cx="50%"
162-
cy="50%"
163-
innerRadius={60}
164-
outerRadius={80}
165-
paddingAngle={5}
166-
dataKey="cost"
167-
nameKey="feature"
168-
>
169-
{featureData.map((_, index) => (
170-
<Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
171-
))}
172-
</Pie>
173-
<Tooltip
174-
contentStyle={{ backgroundColor: '#0a0a0a', border: '1px solid #333', fontSize: '10px' }}
175-
/>
176-
</PieChart>
177-
</ResponsiveContainer>
164+
{featureChartData.length > 0 ? (
165+
<ResponsiveContainer width="100%" height={220} minWidth={1} minHeight={1}>
166+
<PieChart>
167+
<Pie
168+
data={featureChartData}
169+
cx="50%"
170+
cy="50%"
171+
innerRadius={60}
172+
outerRadius={80}
173+
paddingAngle={5}
174+
dataKey="cost"
175+
nameKey="feature"
176+
>
177+
{featureChartData.map((_, index) => (
178+
<Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
179+
))}
180+
</Pie>
181+
<Tooltip
182+
contentStyle={{ backgroundColor: '#0a0a0a', border: '1px solid #333', fontSize: '10px' }}
183+
/>
184+
</PieChart>
185+
</ResponsiveContainer>
186+
) : (
187+
<div className="h-[220px] w-full border border-dashed border-border/40 flex items-center justify-center text-xs uppercase tracking-widest opacity-40">
188+
No feature telemetry available
189+
</div>
190+
)}
178191
<div className="grid grid-cols-2 gap-x-4 gap-y-1 w-full px-4">
179192
{featureData.map((entry, index) => (
180193
<div key={entry.feature} className="flex items-center gap-2">
@@ -233,4 +246,3 @@ export default function AIUsagePage() {
233246
</div>
234247
);
235248
}
236-

caedo-web/components/__tests__/integration.test.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ describe('Caedo 3D - End-to-End Integration', () => {
171171
resumeStream: vi.fn(),
172172
addToolResult: vi.fn(),
173173
addToolOutput: vi.fn(),
174+
addToolApprovalResponse: vi.fn(),
174175
clearError: vi.fn(),
175176
error: undefined,
176177
id: 'test-chat',

caedo-web/components/canvas/Scene.tsx

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { Suspense, useRef, useMemo, useEffect } from 'react';
44
import { Canvas, useThree } from '@react-three/fiber';
55
import {
66
Grid,
7-
Environment,
87
Preload,
98
PerformanceMonitor,
109
AdaptiveDpr,
@@ -141,14 +140,6 @@ function SceneContent() {
141140
return Array.from(objects.values());
142141
}, [objects]);
143142

144-
// Environment lighting
145-
const environment = useMemo(() => (
146-
<Environment
147-
preset="studio"
148-
background={false} // We'll use our own background
149-
/>
150-
), []);
151-
152143
// Build plate grid - Orca Slicer style
153144
const grid = useMemo(() => {
154145
if (!gridVisible) return null;
@@ -218,9 +209,6 @@ function SceneContent() {
218209
shadow-bias={RENDER.SHADOW_BIAS}
219210
/>
220211

221-
{/* Environment */}
222-
{environment}
223-
224212
{/* Build plate visualization - Orca Slicer style */}
225213
{/* Main build plate surface */}
226214
<mesh position={[0, -0.5, 0]} receiveShadow rotation={[-Math.PI / 2, 0, 0]}>

caedo-web/components/panels/AIPanel.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,9 @@ export function AIPanel({ onToggle, initialInput }: AIPanelProps) {
264264
body: JSON.stringify({
265265
messages: messagesArray.map(m => ({
266266
role: m.role,
267-
content: m.content || (Array.isArray(m.parts) ? m.parts.map((p: any) => p.text || '').join('') : '')
267+
content: m.content || (Array.isArray(m.parts) ? m.parts.map((p) => (
268+
typeof p === 'object' && p !== null && 'text' in p && typeof p.text === 'string' ? p.text : ''
269+
)).join('') : '')
268270
}))
269271
})
270272
}).catch(err => console.error('Failed to extract memories:', err));

0 commit comments

Comments
 (0)