Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
252 lines
10 KiB
Markdown
252 lines
10 KiB
Markdown
# 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 Descrybe’s 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
|