gralkor_ex

OTP supervisor + HTTP client for Gralkor — a temporally-aware knowledge-graph memory service (Graphiti + FalkorDB) wrapped as a Python/FastAPI server.

Renamed from :gralkor. The Hex package was renamed :gralkor → :gralkor_ex at v1.3.0 to match the npm side's @susu-eng/gralkor-ts and make the naming symmetric: both are adapters with their language suffix, and both depend on the shared gralkor/server/ Python core. Old :gralkor is retired on Hex with a pointer here. Update: {:gralkor_ex, "~> 1.3"}; module names (Gralkor.Client, Gralkor.Server, etc.) are unchanged.

Embed Gralkor.Server in your Jido (or any Elixir) supervision tree. The GenServer spawns the Python server as a Port, polls /health during boot, monitors it, and handles graceful shutdown. Your application talks to it over HTTP on a loopback port.

Prerequisites

The Python source ships inside the package (priv/server/); no separate clone or Docker image needed.

Auth: the server binds to loopback and expects its consumer to supervise it — so there is no authentication. All endpoints are mounted on a single router with no middleware. If a multi-host or shared-service deployment ever changes the threat model, add a bearer-token dependency on the Python side and attach Authorization: Bearer … on the client.

Install

def deps do
  [
    {:gralkor_ex, "~> 1.3"}
  ]
end

Using Gralkor from a Jido agent? Install :jido_gralkor instead — it pulls :gralkor_ex transitively and ships the Jido-shaped glue (a plugin + two ReAct tools) so you don't wire the HTTP client by hand. :jido_gralkor's README is the Jido-dev entry point.

Elixir API surface

The package ships:

Install into a non-Jido consumer

  1. Add {:gralkor_ex, "~> 1.3"} to your deps.

  2. Do not supervise Gralkor.Server yourself. The :gralkor_ex application already does when GRALKOR_DATA_DIR is set. Double-supervising raises already started.

  3. Gate your startup on Gralkor's readiness. Add Gralkor.Connection to your own supervision tree — it blocks boot until /health returns 200:

    children = [
      Gralkor.Connection,
      # ... your app's children
    ]
  4. Wire the HTTP client config. In Application.start/2:

    url = System.get_env("GRALKOR_URL", "http://127.0.0.1:4000")
    Application.put_env(:gralkor_ex, :client_http, url: url)
  5. Call the client. From anywhere in your app:

    Gralkor.Client.impl().memory_add(group_id, "stored insight", "source-desc")
    Gralkor.Client.impl().memory_search(group_id, session_id, "query")
  6. (Optional) Abort-recovery for mix start. If you use mix start as your dev entrypoint and Ctrl+C → abort sometimes leaves uvicorn orphaned on port 4000:

    defmodule Mix.Tasks.Start do
      use Mix.Task
      def run(_args) do
        Gralkor.OrphanReaper.reap()
        Mix.Task.run("app.start")
        Process.flag(:trap_exit, true)
        receive do: (_ -> :ok)
      end
    end

Usage

Add Gralkor.Server to your supervision tree and configure via env vars:

# application.ex
children = [
  # ... your other children
  Gralkor.Server
]

Required env vars:

Optional:

HTTP endpoints

Your application talks to Gralkor over HTTP:

All endpoints are unauthenticated — see the Auth note above.

Lifecycle

Gralkor.Server:

Running locally

From ex/:

export GRALKOR_DATA_DIR=/tmp/gralkor-dev
export GOOGLE_API_KEY=...           # or ANTHROPIC_API_KEY / OPENAI_API_KEY / GROQ_API_KEY
iex -S mix

curl http://127.0.0.1:4000/health should return 200.

License

MIT.