chore: initial checkpoint - Tesla Roadtrip planner
- Proactive Grok integration (xAI API + local CLI fallback) - Real road routing via OSRM (no more bird's-eye lines) - Heavy structured logging for fast iteration - Strong sanitization + geocoding + ErrorBoundary (no black screens) - Playwright E2E tests (API diagnostic + full UI flow) - scripts/dev.sh for one-command startup - Clean .env.example + documentation This is a stable checkpoint before further prompt/UI refinement.
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
import { Router } from 'express';
|
||||
import { z } from 'zod';
|
||||
import { grok } from '../services/llm/GrokHeadlessClient.js';
|
||||
import { createLogger } from '../lib/logger.js';
|
||||
import crypto from 'crypto';
|
||||
|
||||
const log = createLogger('chat-api');
|
||||
const router = Router();
|
||||
|
||||
const ChatRequestSchema = z.object({
|
||||
message: z.string().min(1).max(2000),
|
||||
vehicle: z.object({ name: z.string(), rangeKm: z.number() }),
|
||||
itinerary: z.any().optional(),
|
||||
history: z.array(z.object({ role: z.enum(['user', 'assistant']), content: z.string() })).optional(),
|
||||
});
|
||||
|
||||
router.post('/chat', async (req, res) => {
|
||||
const requestId = crypto.randomUUID().slice(0, 8);
|
||||
const start = Date.now();
|
||||
|
||||
log.info({ requestId, body: req.body }, '=== INCOMING /api/chat REQUEST ===');
|
||||
|
||||
try {
|
||||
const parsed = ChatRequestSchema.safeParse(req.body);
|
||||
if (!parsed.success) {
|
||||
log.error({ requestId, errors: parsed.error.format() }, 'Invalid request body');
|
||||
return res.status(400).json({ error: 'Invalid request' });
|
||||
}
|
||||
|
||||
const { message, vehicle, itinerary, history = [] } = parsed.data;
|
||||
|
||||
log.info({
|
||||
requestId,
|
||||
userMessage: message,
|
||||
vehicle: vehicle.name,
|
||||
historyLength: history.length,
|
||||
currentItineraryDays: itinerary?.days?.length || 0,
|
||||
}, 'Parsed chat request');
|
||||
|
||||
// Call Grok (this will produce very detailed logs inside GrokHeadlessClient)
|
||||
const result = await grok.chat(
|
||||
[...history, { role: 'user' as const, content: message }],
|
||||
itinerary,
|
||||
vehicle
|
||||
);
|
||||
|
||||
const duration = Date.now() - start;
|
||||
|
||||
const payload: any = { reply: result.text };
|
||||
if (result.updatedItinerary) {
|
||||
payload.itinerary = result.updatedItinerary;
|
||||
}
|
||||
|
||||
log.info({
|
||||
requestId,
|
||||
durationMs: duration,
|
||||
replyLength: result.text.length,
|
||||
itineraryUpdated: !!result.updatedItinerary,
|
||||
newDays: result.updatedItinerary?.days?.length || 0,
|
||||
}, '=== SENDING RESPONSE TO FRONTEND ===');
|
||||
|
||||
if (result.updatedItinerary) {
|
||||
log.debug({ requestId, fullItinerary: result.updatedItinerary }, 'Full updated itinerary being sent');
|
||||
}
|
||||
|
||||
res.json(payload);
|
||||
} catch (err) {
|
||||
log.error({ requestId, err }, 'Chat route crashed');
|
||||
res.status(500).json({ reply: "Something went wrong on the server." });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user