No description
Find a file
m2 (AI Agent) 04d2f5b9e4 fix: align router with memory-api's actual payload shape (smoke findings)
End-to-end smoke against the live coolify memory stack surfaced three
gaps between the spec and what memory-api actually writes:

1. memory-api wraps the /ingest payload under `metadata.*` in Qdrant —
   our domain fields (fact_type, scope.states, …) don't live at the top
   of the payload, they're nested. `build_qdrant_filter` now auto-prefixes
   any non-top-level key with `metadata.`. Callers can write
   `{"fact_type": "legal"}` and it just works; top-level fields
   (`agent_id`, `memory_type`, `source`, …) pass through unprefixed.

2. The deployed BGE-M3 TEI image doesn't serve /embed_sparse (returns
   424 Failed Dependency). Stored points have `has_sparse=false`.
   `BGEClient.embed_sparse` now returns `{}` on 4xx and logs a warning;
   `QdrantReader.hybrid_query` falls back to a dense-only query when
   sparse is empty. RRF fusion is skipped in that path.

3. `scripts/smoke.sh` had a missing `shift` after reading the limit
   positional arg — caused arg leakage onto the CLI (`--limit 5 5`).
   The cleanup hint also referenced a stale agent_id; now reads from
   $ARTENSCHUTZ_AGENT_ID.

Smoke is now green end-to-end: 18 records ingested across bnatschg /
mhbasp / biotopwertliste(BY) / state-berlin under
agent_id=gruenstifter_smoketest, hybrid + payload-filter searches all
return hits. 24/24 unit tests pass.

Companion fixes pushed to github.com/machine-machine/agent.memory.system:
  d0e9529  fix: /memory/store passed wrong kwarg name to AgentMemory
  ebc896c  fix: don't close caller-supplied clients in AgentMemory.close()
2026-05-14 23:30:57 +02:00
scripts fix: align router with memory-api's actual payload shape (smoke findings) 2026-05-14 23:30:57 +02:00
sources Initial commit — phase 1 + 2 of artenschutz-digest concept 2026-05-14 16:36:00 +02:00
src/artenschutz_router fix: align router with memory-api's actual payload shape (smoke findings) 2026-05-14 23:30:57 +02:00
tests fix: align router with memory-api's actual payload shape (smoke findings) 2026-05-14 23:30:57 +02:00
.env.example Initial commit — phase 1 + 2 of artenschutz-digest concept 2026-05-14 16:36:00 +02:00
.gitignore Initial commit — phase 1 + 2 of artenschutz-digest concept 2026-05-14 16:36:00 +02:00
COOLIFY-DEPLOY.md Initial commit — phase 1 + 2 of artenschutz-digest concept 2026-05-14 16:36:00 +02:00
docker-compose.yml Initial commit — phase 1 + 2 of artenschutz-digest concept 2026-05-14 16:36:00 +02:00
Dockerfile Initial commit — phase 1 + 2 of artenschutz-digest concept 2026-05-14 16:36:00 +02:00
pyproject.toml Initial commit — phase 1 + 2 of artenschutz-digest concept 2026-05-14 16:36:00 +02:00
README.md Initial commit — phase 1 + 2 of artenschutz-digest concept 2026-05-14 16:36:00 +02:00

artenschutz-router

Sidecar service for Artenschutzgutachten domain queries against agent.memory.system. m2-memory stays untouched.

  • Writes proxy memory-api /memory/store — the existing endpoint accepts arbitrary metadata: dict, so our Fact / Exemplar payloads slot in.
  • Reads go directly to Qdrant for rich payload-filtered hybrid queries (dense + sparse via the existing BGE-M3 TEI service).

See .planning/notes/artenschutz-digest-concept.md in GST-DATA for the draft concept that birthed this.

Endpoints

Method Path Purpose
GET /health Live-probe Qdrant, BGE-M3, memory-api
POST /search/generic Hybrid search (or scroll if no query); arbitrary payload filters
POST /ingest Push a Fact or Exemplar record

Domain-specific endpoints (/facts/search, /exemplar/search, /mitigation/search) are deferred to a later phase — they will be thin wrappers over /search/generic.

Quick start (local dev)

cp .env.example .env
# Edit .env to point QDRANT_URL / BGE_TEI_URL / MEMORY_API_URL at your stack
docker compose up --build
curl http://localhost:8080/health

If you're targeting the production Coolify memory stack, leave the default container-name URLs and ensure the coolify Docker network is joined.

Running ingest

# After you've placed authoritative source files into sources/...
artenschutz-ingest --source bnatschg --router-url http://localhost:8080
artenschutz-ingest --source mhbasp --router-url http://localhost:8080
artenschutz-ingest --source biotopwertliste --router-url http://localhost:8080
artenschutz-ingest --source state-berlin --router-url http://localhost:8080

See sources/README.md for which files each source expects.

Tests

pip install -e .[dev]
pytest

Architecture

m2-gpt agents
   ↓
artenschutz-router (this repo)
   ├─ /ingest        ─→ memory-api /memory/store (unchanged)
   └─ /search/generic ─→ Qdrant /points/query + BGE-M3 TEI /embed[_sparse]
                            ↓
                       agent_memory collection (Qdrant)