TUCAN Engine — Standalone Mastra Service
Audiences: developer, internal
⚠️ HISTORICAL — the standalone engine has been PARKED (flagged 2026-07-02).
ddx-tucan-engine/no longer exists at the repo root; it was moved to_claude-archive/parked-2026-06-14/dead-code-twins/ddx-tucan-engineas a confirmed dead twin ofddx-api/src/ai/tucan(rootCLAUDE.mdHousekeeping 2026-06-14). There is no longer a port-6150 sidecar. This shard is retained as a record of the isolation-test-bench pattern; for the live agent stack read tucan-assistant, tucan-dispatcher, and tucan-orchestrator (all embedded in ddx-api), and the current second engine condor-engine.
ddx-tucan-engine/was a standalone NestJS sidecar running TUCAN on Mastra 1.x at port 6150 — a development surface for working on the agent stack in isolation from the full ddx-api. Production traffic always flowed through ddx-api's embedded TUCAN module (tucan-assistant); the standalone engine was the test bench, not a deploy target.
Business Purpose
The full ddx-api (port 6100) carries 123 NestJS modules, 5 Prisma schemas, 6 databases, FHIR + MinIO + LiveKit + Redis dependencies. Running a single TUCAN turn for local development requires booting all of that. That is slow (cold-start tens of seconds) and brittle (any clinical module's typecheck error blocks AI work).
ddx-tucan-engine/ is the answer: a 1-app NestJS service that runs
TUCAN's Mastra 1.x stack standalone, calling out to ddx-api for any
real EMR mutation. Developers iterating on:
- Tool definitions (
createTucanTool), - Agent templates,
- Tool-RAG embeddings,
- Dispatcher / orchestrator changes,
- LLM prompt tuning,
… can run the engine, hit it on port 6150, and reload it in seconds without touching the rest of the platform.
This is a dev surface, not a production deploy target. The
canonical TUCAN code lives at ddx-api/src/ai/tucan/** and is what
ships. The standalone engine is intentionally a sandbox — see the
parent project memory for cross-references.
Audiences
- Developer / partner: To iterate on TUCAN in isolation, run
cd ddx-tucan-engine && pnpm start:dev. Port 6150. Calls into ddx-api via fetch + X-API-Key for any EMR operation. - Internal (ops / support): This service is not deployed to production. If you see traffic on port 6150 in a prod environment, that is a misconfiguration.
- Investor: Sandboxing the AI stack matches Dudoxx's engineering discipline pattern — every domain has a runnable isolation harness. Reduces tribal knowledge to runnable code.
Architecture
ddx-tucan-engine (port 6150) internals
| Component |
|---|
AgentController + AgentService |
AgentDispatcherService (dev-isolated) |
AgentFactory / SpecialistFactory |
ContextBuilderService |
| Tool-RAG (Qdrant) |
File-level parity with ddx-api/src/ai/tucan
The engine source tree mirrors a subset of the ddx-api TUCAN layout:
| ddx-tucan-engine path | ddx-api equivalent |
|---|---|
src/agent/agent.controller.ts | src/ai/tucan/session/session.controller.ts (subset) |
src/agent/agent.service.ts | src/ai/tucan/session/session.service.ts (subset) |
src/agent/dispatcher/agent-dispatcher.service.ts | src/ai/tucan/dispatch/agent-dispatcher.service.ts |
src/agent/factory/agent-factory.ts | src/ai/tucan/registry/agent-factory.ts |
src/agent/factory/specialist-factory.ts | src/ai/tucan/registry/specialist-factory.ts |
src/agent/factory/network-factory.ts | src/ai/tucan/registry/network-factory.ts |
src/agent/factory/llm-config.factory.ts | src/ai/tucan/registry/llm-config.factory.ts |
src/agent/factory/tool-converter.ts | src/ai/tucan/registry/tool-converter.ts |
src/agent/factory/tool-rag-prompt.builder.ts | src/ai/tucan/registry/tool-rag-prompt.builder.ts |
src/agent/context/context-builder.service.ts | src/ai/tucan/context/context-builder.service.ts |
src/agent/context/patient-extractor.ts | src/ai/tucan/context/patient-extractor.ts |
src/agent/builder/agent-builder.ts | src/ai/tucan/registry/template-loader.service.ts (subset) |
src/agent/builder/template-renderer.ts | Jinja2 rendering equivalent |
The engine has its own copy of these files — they are not symlinks
or shared modules. Changes intended for production must be made in
ddx-api/src/ai/tucan/** first; the engine copy is a snapshot for
isolation testing.
Tech Stack & Choices
| Component | Choice | Why |
|---|---|---|
| Service shape | NestJS 11 single-app monorepo (@nestjs/core, helmet, ValidationPipe) | Reuses the platform's request shape and conventions. |
| Mastra | @mastra/core@1.x | Same Mastra version as ddx-api; reproduces production behavior. |
| Auth | X-API-Key (TUCAN_API_KEY + DDX_API_KEY for downstream) | Two separate keys — incoming (from ddx-web / ddx-api callers) and outgoing (to ddx-api). |
| LLM transport | Dudoxx LiteLLM proxy | Same routing as production. |
| Embedder | Dudoxx embedder URL | Same model as production tool-RAG. |
| Qdrant | Dev Qdrant instance | Same vector store as production but isolated collection. |
| Persistence | None (dev only — no Prisma) | All clinical writes fan out to ddx-api. |
| Port | 6150 | Distinct from ddx-api (6100), ddx-web (6200). |
| Process manager | PM2 (ecosystem.config.js) | Optional; pnpm start:dev for hot reload. |
Why NOT promote the standalone engine to production:
- Production needs the full ddx-api request pipeline (Trusted Gateway v2.1, RBAC, tenant isolation, FHIR partition routing). The engine implements a subset.
- The engine has no Prisma access — every write becomes an extra hop through ddx-api over HTTP. Double-encoded for no gain.
- Two TUCAN deployments means two upgrade paths; codebase parity drift is inevitable.
The engine is deliberately a dev sandbox.
Data Flow
Business outcome: A developer iterating on the clinical agent prompt edits the YAML template, restarts the engine in 2s, dispatches a turn, sees the new behavior — without touching ddx-api or any clinical module.
Technical mechanism:
- Developer edits
ddx-tucan-engine/src/agent/builder/agent-builder.tsor the local template.pnpm start:devwatcher restarts the engine on port 6150.- Developer sends
POST http://localhost:6150/agent/processwith X-API-Key + message body.- Engine's
AgentDispatcherServiceruns locally; the LLM call goes to Dudoxx LiteLLM as in production.- Any FHIR / Prisma side-effect is fetched out to
ddx-api:6100over HTTP — so the tool'sexecutestill talks to a real backend.- SSE stream returns to the developer's client on
GET /sse/session?sessionId=....
Implicated Code
ddx-tucan-engine/package.json:1—"name":"ddx-tucan-engine","version":"0.7.7","description":"TUCAN AI Agent Engine - Mastra 1.x Runtime for Dudoxx Healthcare Platform".ddx-tucan-engine/README.md:1— overview + arch diagram.ddx-tucan-engine/src/main.ts:1— NestFactory bootstrap + helmet + ValidationPipe.ddx-tucan-engine/src/agent/agent.controller.ts—POST /agent/process+ SSE endpoint.ddx-tucan-engine/src/agent/agent.service.ts— service that orchestrates a turn.ddx-tucan-engine/src/agent/agent.module.ts— module wiring.ddx-tucan-engine/src/agent/dispatcher/agent-dispatcher.service.ts— standalone dispatcher (parity with production).ddx-tucan-engine/src/agent/factory/agent-factory.ts— agent factory (parity with ddx-api).ddx-tucan-engine/src/agent/factory/specialist-factory.ts— specialist factory (parity).ddx-tucan-engine/src/agent/factory/network-factory.ts— network factory (parity).ddx-tucan-engine/src/agent/factory/llm-config.factory.ts— LLM config (parity).ddx-tucan-engine/src/agent/factory/tool-converter.ts— Mastra tool conversion (parity).ddx-tucan-engine/src/agent/factory/tool-rag-prompt.builder.ts— build-time tool-RAG ranking (parity).ddx-tucan-engine/src/agent/context/context-builder.service.ts— context builder (subset).ddx-tucan-engine/src/agent/builder/agent-builder.ts— Mastra Agent assembly.ddx-tucan-engine/src/agent/builder/template-renderer.ts— Jinja2-style rendering.ddx-tucan-engine/CLAUDE.md— engine-local Claude rules.ddx-tucan-engine/ARCHITECTURE.md— architectural notes.ddx-tucan-engine/ecosystem.config.js— PM2 process config.ddx-tucan-engine/scripts/kill-port.sh— port-clear helper in dev start scripts.
Operational Notes
- NOT a production deploy target. Production traffic flows
through ddx-api's embedded TUCAN module (tucan-assistant).
Don't add the engine to
pm2on a production server. - Source parity must be maintained by hand. Changes to the shared logic (dispatcher / factory / context-builder) should land in ddx-api first; the engine snapshot is updated intentionally, not automatically.
- Two API keys:
TUCAN_API_KEYfor inbound requests +DDX_API_KEYfor outbound calls to ddx-api. Both must be set or the engine refuses to boot. - No local Prisma. Engine has no DB connection of its own —
every write is a fetch to
ddx-api:6100. Don't add raw Prisma here; that breaks the boundary. - Engine uses the SAME Qdrant tool-RAG collection as
production by default. For experimental tool changes, override
the collection name in
.envto avoid contaminating the production index. - PM2 is optional. The
ecosystem.config.jsships for deploying the engine onto a developer's persistent box, but the recommended workflow ispnpm start:devwith hot reload. pnpm typecheckrunstsc --noEmit. Run this before pushing engine-side changes; ddx-api's typechecker has its own scope.- The fix loop sibling project
ddx-tucan-usage/is a separate sandbox for end-to-end TUCAN scenarios. Don't confuse it withddx-tucan-engine/. - Don't treat the engine as authoritative. When a behavior differs between engine and ddx-api, ddx-api is the truth. Repro in the engine, fix in ddx-api, re-snapshot the engine.
Related Topics
- tucan-assistant — production TUCAN entry inside ddx-api.
- mastra-codebase — Mastra usage patterns shared across both surfaces.
- ai-medical-tools — the catalog the engine consumes unchanged.
- agent-templates — the template format the engine parses with its own template-renderer.
- tucan-rag-tools — Qdrant tool-RAG used by both surfaces.
- sse-event-engine (W1) — SSE contract the engine emits against (development-scoped channels).
docs/claude-context/CLAUDE_MASTRA_CODEBASE_USAGE.md— Mastra reference.ddx-tucan-engine/CLAUDE.md— engine-scoped Claude rules.ddx-tucan-engine/ARCHITECTURE.md— fuller architecture reference.