MicroFish/.kiro/specs/graphiti-neo4j-finalize/tasks.md

10 KiB

Implementation Plan — graphiti-neo4j-finalize

Two-phase ordering: Foundation tasks (Config, Compose, env example) unblock the core adapter rewrite. Core tasks rewrite the Graphiti adapter and clean up the misleading reranker kwarg in callers. Validation closes the loop with a structural review and a manual smoke test.

1. Foundation — runtime configuration and infrastructure wiring

  • 1.1 (P) Extend the central configuration module with the new provider switch and decoupled embedder credentials

    • Add a GRAPHITI_LLM_PROVIDER configuration knob with allowed values openai and gemini, defaulting to openai when the environment variable is unset.
    • Add optional EMBEDDING_API_KEY and EMBEDDING_BASE_URL fields that fall back to the existing chat-LLM credentials when unset.
    • Preserve every existing Config attribute exactly (no removals, no renames); existing Gemini deployments must keep reading the same env vars.
    • Observable completion: importing the configuration module exposes the three new attributes with documented defaults, and existing attributes report identical values to before.
    • Requirements: 3.1, 3.2, 5.1, 5.2, 5.3, 5.5, 8.3
    • Boundary: Config
  • 1.2 (P) Add a healthchecked Neo4j service to the Docker Compose stack

    • Declare a neo4j service using neo4j:5-community, exposing the HTTP browser and Bolt ports, mounting named volumes for data and logs, and reading its admin password from the same project env file.
    • Add a Bolt-level healthcheck (using cypher-shell) so dependent services start only after Neo4j accepts queries.
    • Wire the existing application service so that it depends on Neo4j being healthy and overrides the connection URI for the in-container case while leaving the host-mode default untouched.
    • Keep Compose v2 syntax: do not introduce a top-level version: key.
    • Observable completion: docker compose config parses cleanly, and docker compose up -d from a clean checkout brings both services up with Neo4j reporting healthy before the application starts.
    • Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 2.3
    • Boundary: docker-compose.yml
  • 1.3 (P) Refresh the env example to mirror the README and the code's actual reads

    • Add the Neo4j connection variables, the embedding model, and the new optional provider/embedder variables alongside their fallback rules.
    • Drop the deprecated Zep variable (or keep a single commented "deprecated" line) and add a comment guiding Qwen/Dashscope users to point the embedder at OpenAI directly.
    • Ensure no real secret values are present.
    • If the environment-guard hook blocks editing the example file, document the same content in the README's environment section instead and note the discrepancy in the PR description.
    • Observable completion: copying the example to a fresh .env plus filling in only the LLM key is sufficient to boot the stack against the documented default provider; the variable set in the example matches the variable set in the README's environment section.
    • Requirements: 6.1, 6.2, 6.3, 6.4, 6.5, 6.6, 6.7
    • Boundary: .env.example, README

2. Core — Graphiti adapter rewrite

  • 2.1 Replace the no-op Gemini reranker with a provider-agnostic passthrough

    • Remove the _GeminiReranker class that depends on a Gemini client and replace it with a renamed passthrough that returns its input list with synthetic descending scores and holds no provider-specific state.
    • Always inject this passthrough into the Graphiti constructor so the framework does not silently fall back to its OpenAI-only default reranker (which would 401 against Qwen/Dashscope keys).
    • Observable completion: the graph adapter module exposes a passthrough reranker with no Gemini dependency, and a grep for _GeminiReranker in backend/app/services/ returns zero hits.
    • Requirements: 7.1, 7.4
  • 2.2 Implement the Graphiti provider switch inside the singleton factory

    • Read the new provider configuration once when constructing the singleton; branch between an OpenAI-compatible client/embedder pair and the existing Gemini client/embedder pair.
    • Lazy-import the provider-specific Graphiti classes inside their respective branches so a missing optional dependency for one provider does not break the other.
    • For the OpenAI-compatible branch, use the chat triple (api_key, base_url, model) for the LLM client and the embedder credentials with fallback to the chat triple for the embedder.
    • For the Gemini branch, preserve the current behaviour byte-for-byte.
    • When the provider value is unrecognised, raise an error that names the offending value and lists the allowed set, so misconfiguration is surfaced loudly rather than silently.
    • Preserve the existing singleton pattern, double-checked lock, persistent event-loop binding, and build_indices_and_constraints() call exactly.
    • Observable completion: with the documented default configuration plus a Qwen/Dashscope key, the adapter constructs a Graphiti instance whose internal LLM client targets the configured base URL; with GRAPHITI_LLM_PROVIDER=gemini and an existing Gemini setup, the constructed instance is functionally identical to the pre-change behaviour.
    • Requirements: 3.3, 3.4, 3.5, 4.1, 4.2, 4.3, 5.4, 8.1, 8.2, 9.4
    • Depends: 1.1, 2.1
  • 2.3 Make the search namespace honest about reranker support

    • Drop the reranker keyword argument from the adapter's search method since the adapter has never honoured it.
    • Observable completion: the adapter's search method signature contains no reranker parameter, and a grep for reranker= in backend/app/services/graphiti_adapter.py returns zero hits.
    • Requirements: 7.2
    • Depends: 2.2

3. Core — caller cleanup so the new signature stays consistent

  • 3.1 (P) Remove the misleading reranker keyword from the report-tool search call

    • Update the graph search invocation that asks for a cross_encoder reranker (which the adapter never honoured) so it no longer passes the keyword.
    • Leave behaviour unchanged otherwise; the reranker argument was already a no-op.
    • Observable completion: a grep for reranker= in backend/app/services/zep_tools.py returns zero hits, and report-tool search code paths still execute end-to-end without TypeError.
    • Requirements: 7.3, 7.4
    • Boundary: zep_tools.py
    • Depends: 2.3
  • 3.2 (P) Remove the misleading reranker keyword from the profile-generator search calls

    • Update both of the graph search invocations that ask for an rrf reranker (also a no-op in the adapter) so they no longer pass the keyword.
    • Observable completion: a grep for reranker= in backend/app/services/oasis_profile_generator.py returns zero hits, and profile-generation search code paths still execute end-to-end without TypeError.
    • Requirements: 7.3, 7.4
    • Boundary: oasis_profile_generator.py
    • Depends: 2.3

4. Validation — structural checks and manual smoke

  • 4.1 Static verification of the rewrite

    • Confirm that no references to _GeminiReranker remain anywhere under backend/.
    • Confirm that no reranker= keyword arguments remain anywhere under backend/app/services/.
    • Confirm that docker compose config parses the new compose file without warnings about deprecated keys.
    • Confirm that the host-mode default for the Neo4j URI in the configuration is bolt://localhost:7687 (Requirement 2.1) and is not mutated by the Compose service-level override.
    • Observable completion: all four checks pass and their commands exit zero / produce empty greps; results captured in the PR description.
    • Requirements: 1.8, 2.1, 7.1, 7.2, 7.3
    • Depends: 3.1, 3.2
  • 4.2 Compose stack smoke (no LLM keys required)

    • Boot the full stack via docker compose up -d from a clean state (volumes pruned).
    • Confirm Neo4j reaches healthy status before the application container starts (verifies the depends_on wiring).
    • Confirm cypher-shell against the running Neo4j accepts a trivial RETURN 1 using the configured password.
    • Confirm the application's /health endpoint returns OK after Neo4j is healthy.
    • Observable completion: docker compose ps shows both services running with Neo4j healthy; curl localhost:5001/health returns the expected JSON.
    • Requirements: 1.9, 2.2
    • Depends: 1.2, 4.1
  • 4.3 Provider misconfiguration smoke

    • Set GRAPHITI_LLM_PROVIDER to an unrecognised value with an LLM key configured and trigger a graph-build request.
    • Confirm the adapter raises an error that names the offending value and lists the allowed providers.
    • Observable completion: the application logs contain the expected named-and-allowed error message; the failure surface is the provider error itself, not a generic 500.
    • Requirements: 3.5, 9.4
    • Depends: 2.2, 4.2
  • * 4.4 End-to-end pipeline smoke against the documented default provider

    • Run by the ticket reviewer using real keys (Qwen for chat, OpenAI for embeddings).
    • Configure LLM_API_KEY (Qwen), EMBEDDING_API_KEY (OpenAI), keep GRAPHITI_LLM_PROVIDER at its default (openai), then upload a small .txt and run ontology generation followed by graph build.
    • Verify the graph data endpoint returns a non-zero count of nodes and edges and that report tools (InsightForge, Panorama) return non-empty results.
    • Marked optional because it depends on real API keys not present in the implementation environment; required for ticket reviewer sign-off.
    • Observable completion: graph build completes within ~10 minutes; data and report endpoints return non-empty payloads.
    • Requirements: 9.1, 9.2, 9.3
    • Depends: 2.2, 4.2
  • * 4.5 Backwards-compatibility smoke against Gemini

    • Run by a reviewer with a Gemini key.
    • Set GRAPHITI_LLM_PROVIDER=gemini, leave LLM_API_KEY as the Gemini key, and set EMBEDDING_MODEL=text-embedding-004.
    • Run the same upload + build flow and confirm completion.
    • Marked optional for the same reason as 4.4 (no Gemini key in implementation environment); required for ticket reviewer sign-off.
    • Observable completion: graph build completes successfully with no behavioural difference from the pre-change implementation.
    • Requirements: 8.1
    • Depends: 2.2, 4.2