llm-chat/MODULE.de.md
flemming-it 305ab1cecd
Some checks failed
CI / Linux x86_64 (Forgejo) (push) Failing after 2s
sign-bundle / sign (push) Has been cancelled
feat: OpenAI and Anthropic wire-format adapters via new api input
New optional input api: ollama|openai|anthropic (default ollama —
existing flows unchanged).

- openai: OpenAI Chat Completions format (/v1/chat/completions),
  Bearer auth — targets vLLM and compatible self-hosted servers.
- anthropic: Messages API (/v1/messages), x-api-key +
  anthropic-version headers, top-level system field, mandatory
  max_tokens (fixed 4096).
- Audit outputs unchanged: model_endpoint/model_name always set;
  model_digest stays Ollama-only (no digest API on openai/anthropic,
  field is left empty rather than fabricated).
- api_key is only ever placed in auth headers; never in outputs,
  errors, or audit events (verified against the event log).
- No streaming, no tool calls.

Bump module + capability version to 0.2.0. Wire-format unit tests
for request serialization and response parsing against fixed JSON
fixtures; live smoke green on the ollama path and on the openai
path against an OpenAI-compatible local endpoint.

Signed-off-by: flemming-it <sf@flemming.it>
2026-07-11 23:19:32 +02:00

146 lines
5.7 KiB
Markdown

# llm.chat
Generischer LLM-Chat-Adapter. Das Baustein-Modul für jeden
Flow, der eine LLM-Einzelantwort braucht — klassifizieren,
Felder extrahieren, umformulieren, entscheiden — ohne einen
eigenen HTTP-Client zu bauen. Spricht drei Wire-Formate,
wählbar über die optionale Eingabe `api`: Ollama (Default),
OpenAI Chat Completions (OpenAI, vLLM, kompatible Server) und
die Anthropic Messages API.
## Capability
- `llm.chat@0.2.0`
## Eingaben
| Name | Typ | Beschreibung |
| ---------------- | ---- | ------------------------------------------------------------------------- |
| `prompt` | text | Der User-Prompt. |
| `endpoint` | text | Chat-Endpunkt-URL passend zur gewählten `api` (siehe unten). |
| `model` | text | Modell-ID (z. B. `qwen2.5:14b`, `claude-fable-5`). |
| `api_key` | text | Optionaler API-Key. Erscheint nie in Ausgaben, Logs oder Events. |
| `system_prompt` | text | Optionale System-Nachricht. Leer = Default des Modells. |
| `api` | text | Optionales Wire-Format: `ollama` (Default), `openai`, `anthropic`. |
### Wire-Formate (`api`)
| `api` | Endpunkt-Form | Auth-Header | Hinweise |
| ----------- | ----------------------------------------- | ---------------------------------- | ------------------------------------------------------------ |
| `ollama` | `http://localhost:11434/api/chat` | `Authorization: Bearer` (optional) | Verhalten wie v0.1.0, unverändert. Modell-Digest-Probe via `/api/show`. |
| `openai` | `https://.../v1/chat/completions` | `Authorization: Bearer` | OpenAI-Chat-Completions-Format — sprechen auch vLLM und die meisten selbst gehosteten Inferenz-Server. |
| `anthropic` | `https://api.anthropic.com/v1/messages` | `x-api-key` + `anthropic-version` | Messages API: `system` ist Top-Level-Feld; `max_tokens` ist Pflicht und steht fest auf 4096. |
## Ausgaben
| Name | Typ | Beschreibung |
| ---------------- | ---- | --------------------------------------------------------------------- |
| `response` | text | Die Antwort des Assistants als Plain-Text. |
| `model_endpoint` | text | URL, gegen die die Antwort erzeugt wurde. |
| `model_name` | text | Modell-ID wie an die LLM-API gesendet. |
| `model_digest` | text | SHA-256-Digest des bedienenden Ollama-Modells. Leer bei `openai`/`anthropic` — diese APIs bieten keinen Digest; das Feld bleibt leer statt erfunden. |
Zusammen beantworten die drei `model_*`-Ausgaben die Audit-
Frage: „Welches genaue Modell hat diese Antwort erzeugt?" —
diese Spur ist der Grund, warum Flows dieses Modul nutzen statt
eigene HTTP-Calls zu sprechen.
## Berechtigungen
```yaml
permissions:
- "net: localhost"
- "net: 127.0.0.1"
- "net: api.openai.com"
- "net: api.anthropic.com"
```
Loopback (lokales Ollama / vLLM) per Default. Cloud-Endpunkte
brauchen einen Operator-Policy-Override in
`~/.chain/config.yaml#security.max_permissions`.
## Warum dieses Modul statt Inline-HTTP
Drei Gründe:
1. **Audit.** Jeder LLM-Aufruf erzeugt `model_endpoint`,
`model_name`, `model_digest` als separate Ausgaben, die
im hash-verketteten Audit-Log neben der Response landen.
Eine Aufsichts-Reproduzierbarkeits-Prüfung vergleicht
einfach das Digest-Feld mit dem Modell-Snapshot.
2. **Permission-Posture.** Operator:innen sehen `llm.chat`
in den installierten Modulen + dessen deklarierte
`net:`-Liste. Ein hausgemachtes HTTP-Modul würde seine
Endpunkte entweder verstecken oder bei jeder Installation
eine frische Review-Oberfläche schaffen.
3. **Endpunkt-Portabilität.** Endpunkt, Modell und Wire-Format
sind Flow-Inputs, keine Compile-Time-Konstanten. Derselbe
Flow läuft in der Entwicklung gegen `localhost:11434` und
in Produktion gegen ein vLLM-Cluster — nur die Eingaben
wechseln.
## Grenzen in v0.2.0
- Kein Streaming. Die ganze Antwort wird gepuffert, bevor der
Output-Step feuert.
- Keine Tool-Call- / Function-Call-Oberfläche. Ein Flow, der
Tool-Use braucht, kombiniert mehrere `llm.chat`-Schritte mit
Prompt-Engineering oder nutzt MCP via Bridge.
- Anthropic-`max_tokens` steht fest auf 4096 (die API verlangt
das Feld; eine konfigurierbare Eingabe kommt, sobald ein
Flow sie braucht).
## Beispiel-Flows
```yaml
name: classify-incoming
inputs:
text: text
steps:
- id: classify
use: llm.chat@^0
with:
prompt: $inputs.text
system_prompt: |
Klassifiziere den Text als: question, complaint,
feedback, spam. Antworte nur mit dem Label.
endpoint: "http://localhost:11434/api/chat"
model: "qwen2.5:14b"
outputs:
category: $classify.response
audit_model: $classify.model_digest
```
Gegen einen vLLM-Server (OpenAI-kompatibel):
```yaml
steps:
- id: classify
use: llm.chat@^0
with:
api: "openai"
endpoint: "http://inference.internal:8000/v1/chat/completions"
model: "meta-llama/Llama-3.1-8B-Instruct"
prompt: $inputs.text
```
Gegen die Anthropic Messages API:
```yaml
steps:
- id: classify
use: llm.chat@^0
with:
api: "anthropic"
endpoint: "https://api.anthropic.com/v1/messages"
model: "claude-fable-5"
api_key: $inputs.anthropic_key
prompt: $inputs.text
```
## Build
```bash
cargo build --release --target wasm32-wasip2
# Ausgabe: target/wasm32-wasip2/release/llm_chat.wasm
```