Files

191 lines
8.7 KiB
Markdown
Raw Permalink Normal View History

# Mock LLM (OpenAI-compatible stub for CI / local processing)
Tiny local Chat Completions server that exercises Descrybes real `processing.OpenAIClient` path **without** production API keys or Green Chat.
| Item | Value |
|------|--------|
| Command | `apps/api/cmd/mock-llm` |
| Default listen | `127.0.0.1:18767` |
| Base URL | `http://127.0.0.1:18767/v1` |
| Model id | `mock-llm` |
| API key | `local-test` (placeholder; Completer requires non-empty) |
| Responses | `processing.HeuristicCompleter` (deterministic JSON for enhance) |
**Do not use the A1 tenant.** Prefer **`demo@descrybe.local`** → **Platform Demo** ([demo-user.md](demo-user.md), [safe-test-fixtures.md](safe-test-fixtures.md)).
Related: [green-chat-llm.md](green-chat-llm.md) (real local/LAN model), [local-llm-tuning.md](local-llm-tuning.md), [e2e-processing.md](e2e-processing.md).
---
## Start
```powershell
cd apps/api
go run ./cmd/mock-llm -addr 127.0.0.1:18767
# defaults: key=local-test model=mock-llm
# env overrides: MOCK_LLM_API_KEY, MOCK_LLM_MODEL
```
Health: `GET http://127.0.0.1:18767/healthz``{"status":"ok","service":"mock-llm",...}`.
Smoke:
```powershell
curl.exe -sS http://127.0.0.1:18767/v1/models -H "Authorization: Bearer local-test"
# POST /v1/chat/completions with JSON messages (same shape as OpenAI)
```
Endpoints:
| Method | Path | Notes |
|--------|------|--------|
| `GET` | `/healthz` | No auth |
| `GET` | `/v1/models` | Bearer required |
| `POST` | `/v1/chat/completions` | Bearer; assistant content from HeuristicCompleter |
| `POST` | `/v1/embeddings` | Bearer; tiny fixed vector for role probes |
Loopback is allowed by `OpenAIClient` dial policy (`localhost` / `127.0.0.1`).
---
## Config keys
Process-env platform fallback (root `.env` — restart **api** + **worker** after change):
```env
OPENAI_API_KEY=local-test
OPENAI_BASE_URL=http://127.0.0.1:18767/v1
OPENAI_MODEL=mock-llm
# optional tuning
# PROCESSING_RPM=60
# PROCESSING_MAX_RETRIES=3
```
Preferred for a single tenant: company admin → **`/integrations/ai`** → custom OpenAI-compatible:
- Base URL: `http://127.0.0.1:18767/v1`
- Model: `mock-llm`
- API key: `local-test`
`OPENAI_API_KEY` must be non-empty or the Completer stays disabled (`Enabled()`).
Unset `OPENAI_*` → worker skips AI enhance (or uses heuristic only if explicitly wired). Mock-llm is for proving the **HTTP** Completer path.
---
## How processing tests should call it
### 1. Unit / package tests (no process)
Prefer in-process stubs — already used:
- `stubCompleter` in `processing/steps_test.go`
- `HeuristicCompleter` for offline enhance shapes
- `httptest.NewServer` + `NewOpenAIClient(..., srv.URL+"/v1", ...)` as in `aiprovider/platform_role_test.go`
`go test ./cmd/mock-llm` covers health, auth, enhance JSON, and an `OpenAIClient` round-trip against the stub handlers.
### 2. Integration / E2E (worker + API)
1. Start mock-llm (above).
2. Set `OPENAI_*` **or** configure `/integrations/ai` on **Platform Demo** (not A1).
3. Ensure demo plan has `can_use_ai=true` and credits ([free-tier.md](free-tier.md)).
4. Run API + worker so the worker inherits env (or uses dashboard BYOK).
5. Enqueue `enhance_only` (or `full`) for a demo product:
```powershell
# After CSRF + login as demo@descrybe.local (cookie jar):
# POST /api/processing/jobs {"raw_product_ids":["…"],"processing_type":"enhance_only"}
# Poll GET /api/processing/jobs/{id} until completed
# Expect ai_enhance step done; gpt/step log shows OpenAI-compatible usage (not openai_not_configured)
```
Worker log should show OpenAI configured with `base=http://127.0.0.1:18767/v1 model=mock-llm`, not “platform OpenAI unset”.
### 3. CI
- Default CI: keep unit tests with `stubCompleter` / `httptest` (no long-lived process).
- Optional job: `go run ./cmd/mock-llm` in background → set `OPENAI_*` → run a narrow processing smoke. Do **not** commit real keys; use `local-test`.
---
## Translation + processing verification
Two independent axes — do not confuse them:
| Axis | What it controls | Where |
|------|------------------|--------|
| **UI locale** | Dashboard chrome strings | localStorage key descrybe-ui-locale (en es fr de it pt nl pl ja) — pps/web/src/lib/i18n/locales.ts |
| **Content language** | AI/product titles & descriptions | Company /settings → Content Language (companies.language) — allowlist in content-languages.ts / company/language.go |
### A. Translation (UI + content language)
Use **demo@descrybe.local** (not A1).
1. Web up (PUBLIC_API_URL / WEB_ORIGIN from root .env.example).
2. Login → open language switcher (or set localStorage.setItem("descrybe-ui-locale","fr") then reload) — expect French chrome, not raw
amespace.key paths; missing keys fall back to English.
3. /settings → Content Language → pick e.g. de or sl → save — company payload / GET /api/me shows the new language.
4. Optional checks: web
pm run check; Go company.ParseLanguage / IsAllowedLanguage.
Root .env.example documents both locale lists (comments only — not process-env).
### B. Processing against mock-llm
1. cd apps/api && go run ./cmd/mock-llm -addr 127.0.0.1:18767
2. Uncomment mock OPENAI_* in a **private** root .env (placeholders only — see .env.example) **or** set /integrations/ai on Platform Demo.
3. Restart **api** + **worker** if using process env.
4. Smoke stub: curl.exe -sS http://127.0.0.1:18767/v1/models -H "Authorization: Bearer local-test"
5. Package tests: go test ./cmd/mock-llm -count=1
6. Enqueue enhance (Integration / E2E section above) on a **demo** product — poll until i_enhance is done.
7. Optional: change content language, re-run enhance, confirm job still completes (heuristic mock text is language-agnostic; real Green Chat honors content language in prompts).
Checklist copy lives in root .env.example under **Locales** and **LLM test provider**.
---
## Failure modes (processing + mock-llm)
Prefer a **temporary** probe (closed port / wrong key / httptest timeout) over changing demo BYOK. If you stop mock-llm, restore it before leaving the session.
| Mode | How to induce (safe) | Completer / job behavior | User-facing note (after TruncateError) |
|------|----------------------|--------------------------|----------------------------------------|
| Provider down | Point client at closed port `127.0.0.1:18768`, **or** briefly stop mock-llm | `ai_enhance` **failed**; job item still completes with passthrough title/description | `AI provider unreachable — check base URL and that the service is running` |
| Bad API key | `OPENAI_API_KEY=wrong-key` against live mock-llm (or `/integrations/ai` test with wrong key) | Non-retryable 401; enhance fails; passthrough copy | `AI provider rejected the API key` |
| Timeout | Short `HTTPClient.Timeout` / `context.DeadlineExceeded` (unit: `TestRunSteps_enhanceViaHTTPLLMTimeout`) | Retries then fail; passthrough copy; step `failed` | `AI provider timed out — try again or check provider load` |
| Upstream 5xx | Mock returns 502 + message | Retries then fail; note keeps provider message when present | e.g. `green-chat unavailable` (retry prefix stripped) |
| Platform unset | Empty `OPENAI_API_KEY` and no company BYOK | Step **skipped** (not failed) | `ai_enhance: skipped (platform OpenAI unset; …)` |
| Free plan | `AllowAI=false` | Step **skipped** | Free-plan upgrade note |
Package coverage: `go test ./internal/processing -run 'HTTPLLM|mockLLM|TruncateError_classifies' -count=1`.
**Do not** permanently change Platform Demo `/integrations/ai` or root `.env` `OPENAI_*` for failure probes — use closed-port clients, stubs, or a short mock-llm stop.
### Restore mock-llm (after a stop)
```powershell
# Confirm down
curl.exe -sS http://127.0.0.1:18767/healthz
# Start again
cd apps/api
go run ./cmd/mock-llm -addr 127.0.0.1:18767
# Expect healthz status=ok; key/model defaults: local-test / mock-llm
curl.exe -sS http://127.0.0.1:18767/healthz
curl.exe -sS http://127.0.0.1:18767/v1/models -H "Authorization: Bearer local-test"
```
If api/worker were started with `OPENAI_BASE_URL=http://127.0.0.1:18767/v1` and `OPENAI_API_KEY=local-test`, no `.env` change is needed after restore. If you temporarily pointed a **non-demo** test company at a bad base URL, set it back to `http://127.0.0.1:18767/v1` + `local-test` (or clear BYOK to use platform env).
---
## What this is / is not
| Is | Is not |
|----|--------|
| OpenAI Chat Completions wire format for local/CI | A real GGUF / Ollama / Green Chat model |
| Deterministic enhance JSON via HeuristicCompleter | Substitute for production model quality |
| Safe placeholder key `local-test` | Something to commit as a secret |
For a real tiny local model, point the same `OPENAI_*` keys at Ollama/LM Studio/Green Chat (`OPENAI_BASE_URL=http://127.0.0.1:PORT/v1`) — see [green-chat-llm.md](green-chat-llm.md).