checking system…
Docs / MiroFish — Crowd simulation
5-stage crowd-simulation flow, sidecar contract, MAF integration.

MiroFish-Offline (vendored)

MiroFish-Offline is the multi-agent crowd-simulation engine MAF calls into for the crowd_simulation arena. We vendor it (no fork) at vendor/MiroFish-Offline/ and bring it up via a MAF-side compose file, without editing anything inside the submodule.

What this stack runs

Service Container name Image / build Host port (default) Container port
MiroFish (Flask + Vue dev) maf-mirofish built from vendor/MiroFish-Offline/Dockerfile 5101 (backend), 3001 (frontend) 5001, 3000
Neo4j 5.18 community maf-mirofish-neo4j neo4j:5.18-community 7474 (browser), 7687 (bolt) 7474, 7687

The vendor's ollama service is not brought up — we redirect at the env-var layer to Ollama Cloud (see below).

Bring it up / down

# From MAF repo root:
docker compose -f docker-compose.mirofish.yml up -d
docker compose -f docker-compose.mirofish.yml ps
docker compose -f docker-compose.mirofish.yml logs -f mirofish

# Tear down (keeps Neo4j data volumes):
docker compose -f docker-compose.mirofish.yml down

# Tear down + wipe Neo4j data:
docker compose -f docker-compose.mirofish.yml down -v

The first up -d builds the MiroFish image and runs npm ci && uv sync inside the container. Allow ~5–10 min on a cold cache.

URL the MAF adapter should use

http://localhost:5101

Point MirofishCrowdSource at MIROFISH_BASE_URL=http://localhost:5101. From inside another compose stack on the same Docker network use http://maf-mirofish:5001.

API surface (all served by the Flask backend, see vendor/MiroFish-Offline/backend/app/__init__.py):

Method Path Notes
GET /health returns {"status": "ok", ...} (HTTP 200)
* /api/graph/* knowledge graph build / query
* /api/simulation/* OASIS simulation run + status
* /api/report/* post-simulation report agent

Ollama Cloud redirect (env-var only — no edits inside vendor/)

MiroFish was built against a local Ollama at http://localhost:11434. The MAF compose file overrides this entirely via environment variables in docker-compose.mirofish.yml. The vendor/MiroFish-Offline/backend/ code reads all of these via os.environ.get(...) — see vendor/MiroFish-Offline/backend/app/config.py and vendor/MiroFish-Offline/backend/app/storage/embedding_service.py. We never patch the vendored files; Compose injects the override values.

MiroFish var Default in vendor MAF override (Ollama Cloud)
LLM_BASE_URL http://localhost:11434/v1 https://ollama.com/v1 (OpenAI-compat)
LLM_API_KEY ollama (any non-empty) ${OLLAMA_API_KEY} from MAF .env
LLM_MODEL_NAME qwen2.5:32b gpt-oss:20b-cloud
OPENAI_API_BASE_URL http://localhost:11434/v1 https://ollama.com/v1
OPENAI_API_KEY ollama ${OLLAMA_API_KEY}
EMBEDDING_BASE_URL http://localhost:11434 https://ollama.com (native /api/embed)
EMBEDDING_MODEL nomic-embed-text nomic-embed-text (verify availability)

The trick: LLM_BASE_URL must point at the OpenAI-compatible Ollama Cloud shim (https://ollama.com/v1) because MiroFish's LLMClient uses the openai Python SDK. EMBEDDING_BASE_URL points one path up at https://ollama.com because the embedder calls the native /api/embed endpoint directly (it appends the path itself — see embedding_service.py).

Required env vars (read by MAF's compose)

Set in MAF .env (see .env.example):

OLLAMA_API_KEY=...                       # required — your Ollama Cloud key
NEO4J_USER=neo4j                         # optional, default neo4j
NEO4J_PASSWORD=mirofish                  # optional, default mirofish
MIROFISH_BACKEND_PORT=5101               # optional, host port for Flask
MIROFISH_FRONTEND_PORT=3001              # optional, host port for Vue dev server
MIROFISH_NEO4J_HTTP_PORT=7474            # optional
MIROFISH_NEO4J_BOLT_PORT=7687            # optional
MIROFISH_LLM_BASE_URL=https://ollama.com/v1
MIROFISH_LLM_MODEL=gpt-oss:20b-cloud
MIROFISH_EMBEDDING_BASE_URL=https://ollama.com
MIROFISH_EMBEDDING_MODEL=nomic-embed-text

Health check

curl -fsS http://localhost:5101/health
# → {"service":"MiroFish-Offline Backend","status":"ok"}

Neo4j browser: http://localhost:7474 (login: neo4j / mirofish).

Port-collision notes

The vendor's compose binds 3000 / 5001 / 11434 directly. On this host 3000, 5001, and 11434 are already taken (paperclip, dev fomo2 backend, local Ollama). The MAF compose remaps host ports to 3001 / 5101 (Neo4j keeps its defaults because they were free). All host ports are overridable via the MIROFISH_*_PORT env vars above.

Caveats

  • nomic-embed-text may or may not be available on Ollama Cloud — confirm against your account on first run. If not available, swap MIROFISH_EMBEDDING_MODEL to a cloud-supported embedding model, or temporarily run a local Ollama and set MIROFISH_EMBEDDING_BASE_URL=http://host.docker.internal:11434.
  • The MiroFish image runs npm run dev, which spins both the Vue dev server (port 3000 in-container) and the Flask backend (5001 in-container) via concurrently. Only the backend matters to the MAF adapter; the Vue frontend is exposed for manual debugging.
  • Do not edit anything under vendor/MiroFish-Offline/. All MAF-side customization lives in docker-compose.mirofish.yml. Run git status -- vendor/MiroFish-Offline/ after any compose change to confirm.