orchestrator-llm/module.yaml
flemming-it 86eb72154e
Some checks failed
CI / Linux x86_64 (Forgejo) (push) Failing after 1m36s
feat: probe Ollama model digest, emit as model_digest output (v0.3.1)
Closes Compliance Gap 1 from fai/platform :: docs/advanced/
compliance-gaps.md. The module now probes the LLM endpoint's
/api/show on every successful chat invocation and surfaces
the model's SHA-256 digest as a separate output field.

The probe is strictly best-effort:

  - derive_show_url returns None unless the configured endpoint
    ends in /api/chat, so non-Ollama deployments (OpenAI,
    Anthropic) skip the probe entirely.
  - probe_model_digest swallows every failure path (network
    error, non-200 status, malformed JSON, missing digest field)
    and yields None. The plan generation succeeds either way.
  - extract_show_digest looks for digest at top level, then
    details.digest, then model_info.digest — the fields shift
    across Ollama versions, so the lookup is tolerant.

A new generate_plan_with_identity returns a PlanWithIdentity
struct ({plan_json, model_digest}). The original generate_plan
remains as a #[allow(dead_code)] convenience wrapper that
returns just the plan JSON.

The invoke entry point in lib.rs now emits four outputs:
  plan          (json)   — unchanged
  model_endpoint (text)  — unchanged
  model_name    (text)   — unchanged
  model_digest  (text)   — NEW; empty for non-Ollama or stub paths

module.yaml documents the new output and bumps the orchestrator
capability version to 0.3.0 (additive minor; downstream flows
that referenced @^0 keep working).

Eight new tests in src/llm.rs cover the new code paths:
  - URL transform (/api/chat → /api/show)
  - URL transform skipped for non-Ollama endpoints
  - Digest extraction at all three documented JSON locations
  - Digest extraction graceful failure (missing, empty, bad JSON)
  - End-to-end probe success
  - End-to-end probe skipped for non-Ollama
  - End-to-end probe failure swallowed without breaking plan

Total tests: 26 (was 18). Wasm artifact builds with v1.0
imports baked in. Platform-side integration tests in
fai/platform :: orchestrator_llm_path.rs pass against the
new module unchanged.

Bumps the module to 0.3.1 (patch — output schema additively
extended, behaviour unchanged for existing flows).

Signed-off-by: flemming-it <sf@flemming.it>
2026-05-03 23:06:27 +02:00

57 lines
2 KiB
YAML

schema_version: 1
name: orchestrator-llm
version: 0.3.1
# Capability provided by this module.
provides:
- capability: orchestrator.plan
version: 0.3.0
# Inputs the invoke function accepts.
inputs:
goal: text
# LLM HTTP endpoint, e.g. "http://localhost:11434/api/chat"
# for Ollama, "https://api.openai.com/v1/chat/completions",
# or "https://api.anthropic.com/v1/messages".
llm_endpoint: text
# Model identifier, e.g. "qwen2.5:14b" for Ollama or "gpt-4o-mini".
llm_model: text
# Optional bearer token for cloud providers. Empty string means
# no Authorization header is sent.
llm_api_key: text
# Optional JSON-encoded list of installed capabilities. If
# omitted or empty, the module plans without capability context.
available_capabilities: text
# Optional JSON-encoded list of all known store capabilities
# (installed + planned + alpha). Used when the orchestrator
# needs to suggest installations.
store_capabilities: text
# Outputs produced.
outputs:
# JSON-encoded Plan compatible with `fai_hub::plan::Plan`.
plan: json
# The endpoint the plan was generated against. Empty when the
# deterministic stub path (no llm_endpoint) ran.
model_endpoint: text
# The model name as supplied to the LLM API.
model_name: text
# SHA-256 digest of the served Ollama model, when reachable.
# Empty for cloud providers (OpenAI / Anthropic do not expose
# a digest API) and for the deterministic stub path. Together
# with model_endpoint and model_name this answers the audit
# question "which exact model produced this plan?"
model_digest: text
# Permissions required.
#
# The module makes outbound HTTP to the configured LLM endpoint.
# Default declarations cover the common providers (cloud OpenAI,
# Anthropic, local Ollama on loopback). Operators with different
# LLM endpoints add a host-level entry via operator config
# (Phase 1+) — for Phase 0.5 they fork module.yaml.
permissions:
- "net: api.openai.com"
- "net: api.anthropic.com"
- "net: localhost"
- "net: 127.0.0.1"