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

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:

  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

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