191 lines
8.7 KiB
Markdown
191 lines
8.7 KiB
Markdown
# Mock LLM (OpenAI-compatible stub for CI / local processing)
|
||||
|
|
|
|||
|
|
Tiny local Chat Completions server that exercises Descrybe’s 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).
|