AI Translation Layer#
Trust Boundary: AI can read and suggest — it CANNOT write to the database, spawn processes, or modify files. Every AI action is a proposal that requires human confirmation.
Overview#
The AI Translation Layer converts natural language descriptions of bioinformatics
analyses into validated .oxoflow pipelines. It calls the deterministic Core API
endpoints — it does not bypass them.
Intent (NL) → AI Translator → /api/pipelines/validate → Validated .oxoflow
│
└─ Template keyword match → prompt hints + failure suggestion
(deterministic, zero AI cost — never produces a pipeline)
Endpoints#
POST /api/ai/translate#
Convert natural language intent to a validated .oxoflow pipeline.
Input: { intent: "RNA-seq, PE, hg38, STAR + featureCounts, strand-specific" }
Process (one harness across all generation surfaces):
1. The shared `PipelineGenAgent` (oxo-flow-ai) runs through the agent
orchestrator: engine-accurate prompt, read-only knowledge tools
(Bioconda lookup, bioSkills, pipeline graph), and the provider
fallback chain (the caller's own provider → configured provider →
env-discovered Claude → OpenAI → Ollama)
2. Requests fail fast with a structured `AI_NOT_CONFIGURED` error when
no provider is usable
3. Template names enter the prompt as hints; keyword matching also
shapes the structured failure message when AI generation fails
("Try the '{name}' template") — it never produces a pipeline
4. Every draft is validated by the engine via the web workflow service;
validation errors feed the orchestrator's correction loop (bounded)
Output: { pipeline_id, toml_content, explanation, alternatives, confidence }
**Draft id semantics**: the returned `pipeline_id` is an unsaved draft id —
the generated TOML lives only in the server's in-memory request cache and is
never persisted to the pipeline store. `GET /api/pipelines/{pipeline_id}`
returns 404 for it. To keep a translated pipeline, save the returned
`toml_content` yourself (e.g. `POST /api/pipelines` or `oxo-flow run`), then
pass the resulting saved id to `POST /api/ai/optimize`.
POST /api/ai/explain#
Explain why a run failed and suggest fixes.
Runs the same deterministic diagnosis engine behind
/api/runs/{run_id}/diagnostics, then augments it with a human-readable
explanation.
POST /api/ai/interpret#
Interpret run results (DEGs, variants, QC metrics).
Input: { run_id, result_type: "deg"|"variants"|"qc" }
Output: { narrative, highlights, caveats, suggested_next }
Always includes caveats and limitations. Does NOT replace biologist judgment.
POST /api/ai/optimize#
Suggest parameter optimizations for speed, cost, or sensitivity.
Input: { pipeline_id, goal: "speed"|"cost"|"sensitivity" }
Output: { optimized_toml, changes, estimated: { time_saved, memory_reduction } }
pipeline_id must reference a saved pipeline (the id of a
GET /api/pipelines entry) — the unsaved draft id returned by
POST /api/ai/translate is not accepted and fails with NOT_FOUND.
Provider Architecture#
The AI layer uses an enum-based dispatch system:
caller's own provider → configured provider → env-discovered Claude →
env-discovered OpenAI → Ollama (only when OLLAMA_HOST is set)
Fallback chain: DeepSeek is the default provider. Candidates only enter
the chain when they are actually usable — the caller's provider if usable,
the runtime-configured provider if configured, Claude/OpenAI only when
discoverable from the environment, and Ollama only when the operator opted
in via OLLAMA_HOST (it is the only credential-free backend, so admitting
it unconditionally would turn "nothing configured" into a doomed
localhost connection attempt). If the candidate chain is empty, the request
fails fast with a structured AI_NOT_CONFIGURED error. If every candidate
fails, the request fails with an error suggesting the best-matching
template name — no pipeline is produced without AI.
Request dedup: Same intent + same data characteristics → cached result, avoiding redundant API calls.
Trust Boundary (Hard Constraint)#
The AI service layer has:
| Operation | Allowed? |
|---|---|
| Read pipeline from DB | ✅ |
| Call deterministic API endpoints | ✅ |
| Generate .oxoflow TOML text | ✅ |
| Write to database | ❌ |
| Spawn processes | ❌ |
| Delete files | ❌ |
| Modify pipelines directly | ❌ |
| Start execution without confirmation | ❌ |
The AI service is zero-write, zero-execute. It can only propose changes that the deterministic core API implements after human confirmation.
Configuration#
# Set AI provider
export OXO_FLOW_AI_PROVIDER=claude # claude | openai | deepseek | ollama
export OXO_FLOW_AI_API_KEY=sk-<YOUR-KEY>
# Or via API
POST /api/ai/config { "provider": "claude", "api_key": "..." }
GET /api/ai/config → { provider, model, api_url, is_configured }
POST /api/ai/test → { success, message, provider, model }
Non-AI Intelligence (Deterministic)#
These functions look like AI but are 100% rule-based and deterministic:
| Function | Method | Why Not AI |
|---|---|---|
| File format detection | Extension matching (+ paired-end naming) | Deterministic |
| Reference genome discovery | File existence in fixed search dirs | Deterministic |
| Pipeline template matching | Keyword scoring | Reproducible — used for prompt hints and failure suggestions |
| Failure classification | Error patterns + exit codes | Rule-based |
| DAG optimization | Topological sort + critical path | Math problem |