Files
2026-07-12 10:17:17 -04:00

128 lines
3.7 KiB
Markdown

# DeepSeek API Integration with PocketBase React Apps
## Pattern
React frontend → nginx `/deepseek/` proxy → `https://api.deepseek.com/` with API key.
## Server Proxy (nginx)
```nginx
location /deepseek/ {
proxy_pass https://api.deepseek.com/;
proxy_ssl_server_name on;
proxy_ssl_name api.deepseek.com;
proxy_http_version 1.1;
proxy_set_header Host api.deepseek.com;
proxy_set_header Authorization "Bearer sk-...";
proxy_set_header Content-Type "application/json";
proxy_buffering off;
proxy_read_timeout 120s;
}
```
## Client Module (`src/lib/ai.ts`)
Core fetch helper — all AI calls go through this:
```typescript
async function callDeepSeek(
systemPrompt: string,
userContent: string,
maxTokens = 500,
temperature = 0,
): Promise<string | null> {
const res = await fetch('/deepseek/v1/chat/completions', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model: 'deepseek-v4-flash',
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: userContent },
],
temperature,
thinking: { type: 'disabled' as const },
max_tokens: maxTokens,
}),
});
if (!res.ok) return null;
const data = await res.json();
return data?.choices?.[0]?.message?.content || null;
}
```
**Critical:** Set `thinking: { type: 'disabled' }` — without it, `deepseek-v4-flash` spends all token budget on reasoning content and returns empty `content`.
## Three Functions We Built
### 1. AI Write Explanation — generates professional customer-facing service descriptions
```typescript
export async function aiWriteExplanation(
params: ExplanationParams,
): Promise<ExplanationResult | null>
```
Takes `serviceName`, `recommendation`, optional `technicianNotes`, `vehicleInfo`, `mileage`, `maintenanceInterval`. Returns `{level: 'CRITICAL'|'RECOMMENDED'|'OPTIONAL'|'PREVENTIVE'|'MAINTENANCE', explanation: string}`.
Parses `LEVEL:` and `EXPLANATION:` from the AI response.
### 2. Generate Priorities — ranks services by safety criticality
```typescript
export async function getPriorityAnalysis(
services: ServiceItem[],
): Promise<PriorityResult[] | null>
```
Returns `{name, rank, priority_reason}[]` sorted by rank. AI prompt forces safety-first ordering: CRITICAL_SAFETY > SAFETY_CONCERN > RECOMMENDED > MAINTENANCE > OPTIONAL.
### 3. AI Suggest Services — recommends services from catalog based on vehicle + mileage
```typescript
export async function aiSuggestServices(
vehicle: SuggestVehicle,
selectedServices: string[],
catalog: CatalogItem[],
): Promise<SuggestResult[] | null>
```
Only returns services that exist in the provided catalog (cross-references by name). Uses mileage-based maintenance intervals.
## Response Parsing Safety
Always strip markdown code blocks before JSON parsing:
```typescript
function stripCodeBlocks(text: string): string {
return text.trim().replace(/```json\s*/gi, '').replace(/```\s*/g, '').trim();
}
```
All functions return `null` on failure (not throwing) so callers can handle gracefully with toast notifications.
## Fallback: Local Ollama
If the DeepSeek API is unavailable, proxy to Ollama instead. Change the proxy URL and model name:
```nginx
location /deepseek/ {
proxy_pass http://127.0.0.1:11434/;
...
}
```
```typescript
// In ai.ts
model: 'qwen2.5:14b', // or llava:13b
```
## UI Integration
Add Sparkles/ListOrdered/Lightbulb buttons to the service management UI. Each button:
1. Sets loading state (disables button, shows spinner)
2. Calls the AI function
3. On success: updates the service/quote state, shows success toast
4. On failure: shows error toast, re-enables button
5. Always: re-enables button in finally block