Initial commit of Descrybe v2 without local scratch artifacts.

Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
This commit is contained in:
2026-08-09 22:47:43 +02:00
commit 8580c996c3
1285 changed files with 325780 additions and 0 deletions
+251
View File
@@ -0,0 +1,251 @@
# Green Chat → Descrybe v2 (OpenAI-compatible)
Use LAN **green-chat** (`systemd` unit `green-chat.service`, binary `/usr/local/bin/ge chat ser` / `ge chat serve`) as Descrybes LLM the same way you would use OpenAI.
Descrybe already speaks OpenAI Chat Completions via one shared client (`processing.OpenAIClient`). **Preferred setup:** company admin → **`/integrations/ai`** (custom OpenAI-compatible base URL + key, or a popular provider). Optional process-env `OPENAI_*` remains a platform fallback when the company uses mode **internal**.
| Feature | Process | Path |
|---------|---------|------|
| Product AI enhance | **worker** | Completer → `POST {base}/chat/completions` |
| Campaign email AI | **API** | same Completer |
| SEO meta AI | **API** | same Completer |
| Brand voice | API/worker | Brand kit `PromptBlock()` injected into those Completer prompts (no separate LLM call) |
Config resolve: `apps/api/internal/aiprovider` (company BYOK) → optional `OPENAI_*` from `apps/api/internal/config`
Client: `apps/api/internal/processing/openai.go`
Wiring: `cmd/worker/main.go`, `internal/httpapi/server.go`
**Do not commit real API keys, tokens, or production LAN endpoints.** Use a single **root** `.env` for bootstrap secrets — do not duplicate `apps/api/.env`.
For **CI / local processing without a real model**, use the OpenAI-compatible stub: **[mock-llm.md](mock-llm.md)** (`go run ./cmd/mock-llm`). Prefer tenant **`demo@descrybe.local`** (not A1).
---
## Setup (preferred: dashboard)
1. Ensure root `.env` has `APP_ENCRYPTION_KEY` (secrets at rest) and the API + worker are running.
2. Sign in as a **company admin****`/integrations/ai`**.
3. Choose **custom** (or a popular provider), set base URL to `http://GREEN_HOST:PORT/v1`, model id from `GET /v1/models`, paste API key, enable, **Test**.
4. Restart is **not** required for dashboard-saved keys (unlike process env).
## Optional env fallback (`OPENAI_*`)
When the company leaves AI mode **internal**, the platform Completer can use process env:
| Variable | Default | Role |
|----------|---------|------|
| `OPENAI_API_KEY` | _(empty)_ | Bearer token. **Must be non-empty** to enable the platform fallback Completer. Use the key from green-chat drop-ins, or any placeholder (e.g. `local`) if the server ignores auth. |
| `OPENAI_BASE_URL` | `https://api.openai.com/v1` | Root **including** `/v1`. Client appends `/chat/completions`. |
| `OPENAI_MODEL` | `gpt-4o-mini` | Model id from green-chat `GET /v1/models` (often a GGUF path/name, not `gpt-4o-mini`). |
| `PROCESSING_RPM` | `60` | Client-side spacing between calls |
| `PROCESSING_MAX_RETRIES` | `3` | Retries on 429 / 5xx / transport errors |
There is no `OPENAI_API_BASE` — use **`OPENAI_BASE_URL`**.
Typical Green Chat path:
```text
POST http://GREEN_HOST:PORT/v1/chat/completions
└──────── OPENAI_BASE_URL ────────┘└── client appends ──┘
```
Also useful: `GET /v1/models`, and often `GET /health`.
### Example root `.env` (placeholders only — optional fallback)
Put these in the **repo root** `.env` only if you need platform-env fallback. Prefer `/integrations/ai`. Restart **both** API and worker after env changes.
```env
OPENAI_API_KEY=YOUR_GREEN_API_KEY_OR_local
OPENAI_BASE_URL=http://192.168.x.x:PORT/v1
OPENAI_MODEL=YOUR_MODEL_ID_FROM_v1_models
```
PowerShell (session only):
```powershell
$env:OPENAI_API_KEY = "YOUR_GREEN_API_KEY_OR_local"
$env:OPENAI_BASE_URL = "http://192.168.x.x:PORT/v1"
$env:OPENAI_MODEL = "YOUR_MODEL_ID_FROM_v1_models"
```
Free plan blocks AI credits (`can_use_ai=false`). Use a paid/demo company with credits for real smoke tests — see [free-tier.md](free-tier.md) and [demo-user.md](demo-user.md).
---
## Discover IP, port, path, and API key ON THE GREEN MACHINE
Run these on the Linux host that runs `green-chat` (SSH as a user that can `sudo` where needed).
### Unit + drop-ins
```bash
sudo systemctl cat green-chat
sudo systemctl cat green-chat.service.d/99-gemma4.conf
sudo systemctl cat green-chat.service.d/runtime.conf
```
Also useful:
```bash
sudo systemctl status green-chat --no-pager -l
ls -la /etc/systemd/system/green-chat.service.d/
sudo systemctl show green-chat -p Environment -p EnvironmentFiles -p ExecStart -p MainPID
```
Look for listen host/port, `--model` / `MODEL=`, and anything named `API_KEY` / `OPENAI_*` / `AUTH` / `TOKEN`. **Do not paste real secrets into git or chat logs.**
### Listen address and LAN IP
```bash
ss -tlnp | grep -E 'ge|green|chat'
hostname -I
sudo journalctl -u green-chat -n 50 --no-pager
```
| Bind you see | Meaning |
|--------------|---------|
| `0.0.0.0:PORT` or `*:PORT` | Reachable from Windows on the LAN (if firewall allows) |
| `127.0.0.1:PORT` only | **Not** reachable from other machines — rebind to `0.0.0.0` (or add a reverse proxy) |
Default Green Engine chat port is often **8767** (`GE_CHAT_PORT` / `--port`); your drop-ins may override it. Always trust `ss` / the unit over assumptions.
### API key
```bash
# Unit environment (may include keys — redact before sharing)
sudo systemctl show green-chat -p Environment --no-pager
sudo systemctl show green-chat -p EnvironmentFiles --no-pager
# Grep drop-ins / nearby config (redact output)
grep -RInE 'API_KEY|OPENAI|BEARER|TOKEN|AUTH' \
/etc/systemd/system/green-chat.service.d/ 2>/dev/null | head -40
```
If green-chat does not require auth, Descrybe still needs a **non-empty** `OPENAI_API_KEY` (e.g. `local`) so the Completer turns on.
### Model id
```bash
# From drop-ins
grep -RInE 'model|gemma|MODEL' /etc/systemd/system/green-chat.service.d/ 2>/dev/null
# Or from the running server (on the green host)
curl -sS -H "Authorization: Bearer YOUR_KEY_OR_local" \
"http://127.0.0.1:PORT/v1/models"
```
Use the `data[].id` value as `OPENAI_MODEL`.
### Loopback smoke test (on green host)
```bash
PORT=CHANGEME
KEY='YOUR_GREEN_API_KEY_OR_local'
MODEL='CHANGEME'
curl -sS -w "\nHTTP %{http_code}\n" \
-H "Authorization: Bearer $KEY" \
"http://127.0.0.1:${PORT}/v1/models"
curl -sS -w "\nHTTP %{http_code}\n" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d "{\"model\":\"${MODEL}\",\"messages\":[{\"role\":\"user\",\"content\":\"Say hi in one word.\"}],\"temperature\":0.2}" \
"http://127.0.0.1:${PORT}/v1/chat/completions"
```
Expect HTTP 200 and `choices[0].message.content`.
### Firewall (green host)
```bash
sudo ufw status || true
# Example once PORT is known (restrict to your LAN):
# sudo ufw allow from 192.168.0.0/16 to any port PORT proto tcp
```
If loopback works but Windows does not: fix **bind** (`0.0.0.0`) and/or **firewall** first.
---
## Smoke test FROM Windows (Laragon)
Replace placeholders with values from the green host. This PC is typically on `192.168.50.x`.
```powershell
$GreenIP = "192.168.x.x" # from hostname -I on green machine
$Port = "PORT" # from ss / unit (often 8767)
$Key = "YOUR_GREEN_API_KEY_OR_local"
$Model = "YOUR_MODEL_ID"
Test-NetConnection -ComputerName $GreenIP -Port $Port
curl.exe -sS -w "`nHTTP %{http_code}`n" `
-H "Authorization: Bearer $Key" `
"http://${GreenIP}:${Port}/v1/models"
curl.exe -sS -w "`nHTTP %{http_code}`n" `
-H "Authorization: Bearer $Key" `
-H "Content-Type: application/json" `
-d "{\"model\":\"$Model\",\"messages\":[{\"role\":\"user\",\"content\":\"Say hi in one word.\"}],\"temperature\":0.2}" `
"http://${GreenIP}:${Port}/v1/chat/completions"
```
Then configure **`/integrations/ai`** (preferred) or set root `.env` `OPENAI_*` and start both processes:
```powershell
cd f:\laragon\www\_MY\descrybe-v2
.\scripts\run-api.ps1 # campaigns + SEO AI
.\scripts\run-api.ps1 worker # product AI enhance (other terminal)
```
Root `.env` is loaded by `loadDotEnv` / `scripts/run-api.ps1` — do not recreate `apps/api/.env`. Prefer `/integrations/ai` for new setups ([UNCERTAIN] env platform OpenAI fallback may be removed later).
### App-level checks (after AI config + worker up)
| Surface | How |
|---------|-----|
| Campaign AI | `POST /api/campaigns/{id}/generate` with `{"mode":"ai"}` (paid/demo company) |
| SEO AI | `POST /api/seo/apply` with `{"product_id":"…","mode":"ai"}` |
| Processing | Worker running; start a job that runs enhance; notes must not say `openai_not_configured` |
| Brand | Fill brand kit; AI generate/enhance should reflect voice (same Completer) |
Email **send** (Resend/SMTP / `EMAIL_DRY_RUN`) is separate from Green Chat; AI only affects **copy generation**.
---
## Reachability checklist (this workstation)
Run when Green Chat IP/port is unknown or LAN calls fail:
1. On green: `sudo systemctl cat green-chat` (+ drop-ins above) → port, bind, model, key env names.
2. On green: `ss -tlnp | grep -E 'ge|green|chat'` → confirm `0.0.0.0` (not only `127.0.0.1`).
3. On green: `hostname -I` → LAN IP for Windows `.env`.
4. On green: loopback `curl` to `/v1/models` and `/v1/chat/completions`.
5. On green: firewall allows your Windows host (or LAN) to that TCP port.
6. On Windows: `Test-NetConnection GREEN_IP -Port PORT`, then the curl smoke tests.
7. Configure AI in **`/integrations/ai`** (or optional root `.env` `OPENAI_API_KEY` / `OPENAI_BASE_URL=http://GREEN_IP:PORT/v1` / `OPENAI_MODEL`).
8. Ensure API + worker are running; retest campaign/SEO/processing on a **non-Free** company.
### Probe notes (2026-08-04, Laragon PC `192.168.50.119`)
| Target | Result |
|--------|--------|
| Process env `OPENAI_*` | Unset |
| DNS `green` / `green-chat` / `gemma` | No resolution |
| LAN neighbors (`192.168.50.39`, `.100`, `.143`, `.181`) on default chat ports (`8767`, `8080`, …) | **No** OpenAI-compatible green-chat endpoint reachable |
| Open ports seen | `.143:8443`/`:9000` (not chat API); `.181:80`/`:443`; `.100:80` — not usable as `/v1/chat/completions` |
| Local `http://127.0.0.1:8767` (`ge chat serve` on this PC) | **OK**`GET /v1/models` 200; `POST /v1/chat/completions` 200 (`Hello.`) |
**Conclusion:** Descrybe wiring is ready. Remote gemma4 `green-chat` IP/port/key must come from the discovery commands on that host (likely loopback-bound or firewalled today).
---
## Related
- [ai-full-smoke.md](ai-full-smoke.md) — full AI feature pass/fail against LAN Green Chat
- [local-llm-tuning.md](local-llm-tuning.md) — weak-model prompt/JSON/token defaults
- [process-and-sell-summary.md](process-and-sell-summary.md) — `OPENAI_*` table
- [ops-runtime.md](ops-runtime.md) — worker / credits
- [free-tier.md](free-tier.md) / [demo-user.md](demo-user.md) — AI gates and demo credits