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

3.7 KiB

DeepSeek API Integration with PocketBase React Apps

Pattern

React frontend → nginx /deepseek/ proxy → https://api.deepseek.com/ with API key.

Server Proxy (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:

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

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

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

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:

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:

location /deepseek/ {
    proxy_pass http://127.0.0.1:11434/;
    ...
}
// 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