initial commit
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user