# 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