An adaptive knowledge graph pipeline. Upload documents and images, the system classifies them into domains, builds extraction specs through iterative refinement, extracts entities, and visualizes the result as an interactive galaxy map.
- Ingest — upload text files or images (drag-and-drop, dual upload zones)
- Classify — LLM assigns documents to a hierarchical domain taxonomy it builds incrementally
- Extract — entities extracted immediately using built-in general specs (text + image)
- Simmer — background worker iteratively refines domain-specific extraction specs
- Normalize — entities deduplicated via string rules, embedding similarity, and review queue
- Visualize — interactive galaxy map with UMAP-based semantic domain layout
The system is queryable from the first upload. Domain-specific richness comes later as simmering completes.
git clone https://github.com/2389-research/Noospheric-Orrery.git
cd Noospheric-Orrery
cp .env.example .env # edit with your credentials
docker compose up # or: docker-compose upOpen http://localhost:3100 — no sign-in required.
If you have an older standalone
docker-composebinary (nodocker composev2 subcommand), usedocker-composein place ofdocker composeeverywhere in this README. If you're upgrading from an older cloud-era checkout that ran a Firebase emulator container, add--remove-orphansto the firstupto clean it up.
Configure in .env:
| Backend | Config | Models | What You Need |
|---|---|---|---|
| AWS Bedrock | ANTHROPIC_BACKEND=bedrock |
Sonnet/Haiku | AWS credentials with Bedrock access |
| Anthropic API | ANTHROPIC_BACKEND=gateway |
Sonnet/Haiku | Anthropic API key (sk-ant-...) |
| Ollama (fully local) | ANTHROPIC_BACKEND=ollama |
gemma4:26b/e4b | Ollama installed, models pulled |
# Install Ollama and pull models
ollama pull gemma4:26b # 17GB — classification, judging, generation
ollama pull gemma4:e4b # 9.6GB — extraction (follows structured prompts reliably)
# Configure .env
ANTHROPIC_BACKEND=ollama
OLLAMA_URL=http://host.docker.internal:11434
CLASSIFICATION_MODEL=gemma4:26b
EXTRACTION_MODEL=gemma4:e4b
# Launch
docker compose upZero cloud dependencies. Text and image extraction, search, simmering all work locally.
┌─────────────────────────────────────────────────────────┐
│ orchestrator (FastAPI, :8100) │
│ Ingest → Classify → Extract → Normalize → Search │
│ Image serving, graph data, WebSocket broadcasts │
└──────────────────────┬──────────────────────────────────┘
│ shared SQLite (WAL mode)
┌──────────────────────┴──────────────────────────────────┐
│ worker (Python, background) │
│ Polls jobs table every 5s │
│ Runs simmer (text + image), extract_batch, normalize │
│ Uses simmer-sdk with direct API agent loop (no CLI) │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ frontend (Next.js, :3100) │
│ Upload / Pipeline / Entities / Orrery / Simmer detail │
│ Multi-noosphere, image search, ImagePane │
└─────────────────────────────────────────────────────────┘
All LLM calls go through orrery-relay (packages/orrery-relay/), which handles backend routing:
- Bedrock/Gateway: Anthropic SDK with tool_use for structured output
- Ollama: Native
/api/chatendpoint — supports text, vision, and structured output
- Upload
.txt,.md,.json,.csv - Classification into hierarchical domains
- Entity extraction with general spec (immediate) + domain specs (after simmering)
- Co-occurrence edges, domain cascade, normalization
- Upload
.jpg,.png,.webp,.gif - Vision LLM describes and classifies images
- Entity extraction from image descriptions
- Image search (toggle in orrery view)
- ImagePane renders actual images with entity tags
- General specs: Built-in defaults for text and image make every upload queryable immediately;
/simmer/generalcan manually refine the text general spec for a corpus - Text domains: 2-phase (golden set → extraction spec), board judge with 2 panelists
- Image domains: Single-phase per-domain recognition context layered on the static general image spec
- API backends: Uses simmer-sdk direct API agent loop (2x faster than CLI, no hangs)
- Ollama: Deterministic pipeline — pre-scan → evaluate → review → score → generate
- Pipeline page shows per-domain text/image breakdown with conditional refine buttons
- 100 anchor domains seed the UMAP space for well-distributed initial layout
transform()places new domains without re-fitting (stable positions)NUMBA_CPU_NAME=genericfixes ARM Docker SIGILL (numba#10388)
- Each noosphere is a fully isolated knowledge graph — its own SQLite file under
data/workspaces/{id}/, tracked by a JSON registry atdata/workspaces/registry.json - Create/switch noospheres from the UI at
/settings/noospheres, or via the/workspacesAPI (the endpoint keeps the legacy name from an earlier cloud-era design) - API calls scope to a noosphere via the
X-Workspace-Idheader; omitting it targets thedefaultnoosphere
# Orchestrator
cd orchestrator && pip install -e . && uvicorn src.main:app --reload --port 8000
# Worker (separate terminal)
cd worker && python -m src.main
# Frontend (separate terminal)
cd frontend && NEXT_PUBLIC_AUTH_MODE=noop BACKEND_URL=http://localhost:8000 npm run dev# Run in Docker (recommended — matches production environment)
docker run --rm \
-v $(pwd)/orchestrator/tests:/app/orchestrator/tests \
-v $(pwd)/orchestrator/src:/app/orchestrator/src \
-v $(pwd)/orchestrator/specs:/app/orchestrator/specs \
-w /app/orchestrator \
ghcr.io/2389-research/orrery-orchestrator:latest \
sh -c "uv pip install pytest httpx pytest-asyncio && uv run python -m pytest tests/ -v"73+ tests covering: DB schema + migrations, config defaults, auth/noosphere CRUD, ingest pipeline, image pipeline, entity normalization, domain layout, search, simmer triggers, REST hygiene (201 + Location headers on resource creation).
Requires the stack to be running at localhost:3100 and :8100 (i.e. docker-compose up).
cd frontend
npm install
npx playwright install chromium # one-time
npm run test:e2e # headless
npm run test:e2e:ui # Playwright UI mode (great for debugging)18+ tests covering:
- Smoke: every top-level route renders, including a real file upload via the dropzone
- Accessibility: WCAG 2.1 Level A + AA (via
@axe-core/playwright) across upload / pipeline / entities / orrery / settings — fails on any violation - Copy: no raw internal identifiers (
simmer_domain,extract_batch) leak into the UI, placeholders are sentence-cased, timestamps render "just now" not "0s ago"
Each test creates a throwaway noosphere via the /workspaces API and soft-deletes on teardown, so tests don't depend on local state.
| Method | Path | Description |
|---|---|---|
POST |
/ingest |
Upload file (text or image) |
GET |
/documents |
List documents with content_type |
GET |
/domains |
Domain taxonomy with text/image counts |
GET |
/entities |
Entities, filterable by type/domain/job |
GET |
/search?q=...&include_images=true |
Hybrid search with optional image results |
GET |
/graph |
Graph data (UMAP positions, entities, trade routes) |
GET |
/images/{id} |
Serve image file |
POST |
/simmer/general |
Trigger text spec simmering |
POST |
/simmer/{domain_path} |
Trigger domain-specific text simmering |
POST |
/simmer/{domain_path}/image |
Trigger domain-specific image simmering |
GET |
/stats |
Counts (documents, entities, domains, images, active jobs) |
GET |
/workspaces |
List noospheres |
POST |
/workspaces |
Create a noosphere |
PATCH |
/workspaces/{id} |
Rename a noosphere |
DELETE |
/workspaces/{id} |
Archive a noosphere (soft delete) |
GET |
/health |
Health check |
All data endpoints (ingest, documents, entities, graph, search, simmer, …) accept an optional X-Workspace-Id: <id> header to scope the request to a specific noosphere. Omit the header to target default. The /workspaces path is the legacy name from a cloud-era multi-tenancy design — the UI calls these "noospheres."
Full interactive docs at http://localhost:8100/docs
- Extraction specs are the artifact that improves, not the code. The pipeline stays fixed; specs evolve through simmering.
- Expensive work is amortized. Classification and simmering happen once. Per-document extraction is cheap.
- Queryable from moment one. Every document produces entities immediately via built-in general specs.
- Works offline. Ollama backend requires zero internet after model download.
- Prompt quality > model quality. A simmered spec on a small model outperforms a generic prompt on a large one.