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>
146 lines
5.7 KiB
Markdown
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
|
|
```
|