Add OpenAI-compatible LLM client service (v0.19.0)
CI / build + unit (pull_request) Successful in 1m14s
CI / build + unit (pull_request) Successful in 1m14s
LlmService wraps the local OpenAI-compatible server (default http://192.168.1.70:1111/v1, no auth) with native fetch only — no new dependencies. listModels() -> data[].id; chat(messages, opts?) -> choices[0].message.content (non-2xx throws with status + body snippet); summarize(text) = chat() with a concise-summary system prompt. Env: LLM_API_BASE / LLM_MODEL (qwen3.8-27b) / LLM_TIMEOUT_MS (60000, AbortSignal.timeout). Plain feature module wired into app.module.ts; docs synced (AGENTS.md, README env list).
This commit is contained in:
@@ -1,5 +1,12 @@
|
||||
# Changelog
|
||||
|
||||
## v0.19.0 — 2026-09-25 18:03
|
||||
|
||||
- New LLM client module (`src/llm`): `LlmService` speaks the OpenAI-compatible chat-completions API of the local server at `LLM_API_BASE` (default `http://192.168.1.70:1111/v1`, no auth) using native fetch only — no new dependencies. Plain feature module (`LlmModule`), wired into `app.module.ts`; no Mesh/Schedule dependency.
|
||||
- Methods: `listModels()` (GET `/models` → `data[].id` strings), `chat(messages, opts?)` (POST `/chat/completions` with `{ model, messages, ...opts }` → `choices[0].message.content`; non-2xx throws an Error carrying the HTTP status and a short body snippet; malformed responses without content throw too), and `summarize(text)` — thin wrapper over `chat()` with system prompt "Summarize the following text concisely." (the stated primary use).
|
||||
- Env: `LLM_API_BASE` (default `http://192.168.1.70:1111/v1`), `LLM_MODEL` (default `qwen3.8-27b`), `LLM_TIMEOUT_MS` (default `60000` — the local 27B model can be slow; applied via `AbortSignal.timeout`).
|
||||
- Tests: +4 specs — listModels parses ids, chat passes model/messages/opts through and returns content, chat throws on non-2xx with status in the message, summarize sends system prompt + user text.
|
||||
|
||||
## v0.18.1 — 2026-09-25 17:30
|
||||
|
||||
- Command replies now ride `MeshService.respond(id, reply)` instead of direct `sendText` routing: when a handler returns a non-empty string and the trigger has a packet id, the reply is auto-routed (rx → DM back to the original sender unless the original was a broadcast; tx → its destination) on the **original message's channel** with `replyId = id`, so the recipient app renders it as a threaded quote of the triggering packet. `COMMAND_CHANNEL` is now only the trigger/listen gate — the reply channel comes from the record.
|
||||
|
||||
Reference in New Issue
Block a user