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>
5.7 KiB
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
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:
- Audit. Jeder LLM-Aufruf erzeugt
model_endpoint,model_name,model_digestals 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. - Permission-Posture. Operator:innen sehen
llm.chatin den installierten Modulen + dessen deklariertenet:-Liste. Ein hausgemachtes HTTP-Modul würde seine Endpunkte entweder verstecken oder bei jeder Installation eine frische Review-Oberfläche schaffen. - Endpunkt-Portabilität. Endpunkt, Modell und Wire-Format
sind Flow-Inputs, keine Compile-Time-Konstanten. Derselbe
Flow läuft in der Entwicklung gegen
localhost:11434und 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_tokenssteht fest auf 4096 (die API verlangt das Feld; eine konfigurierbare Eingabe kommt, sobald ein Flow sie braucht).
Beispiel-Flows
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):
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:
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
cargo build --release --target wasm32-wasip2
# Ausgabe: target/wasm32-wasip2/release/llm_chat.wasm