Jidoka

Hex.pmHex DocsCILicenseWebsiteEcosystemDiscord

A data-driven agent framework for the Jido ecosystem with a Spark DSL and durable turn runtime.

Jidoka is a small Elixir agent framework for the Jido ecosystem.

Use it when you want an application agent that can call a real LLM, expose Jido actions as tools, keep turns inspectable, and pause safely for approval or resume later.

DSL / JSON / YAML
-> Jidoka.Agent.Spec
-> Jidoka.chat / Jidoka.turn / Jidoka.Session
-> model calls + tool calls
-> text, Turn.Result, or snapshot

Jidoka keeps the authoring surface narrow: agent, tools, and controls. The rest is data: specs, requests, results, journals, snapshots, sessions, and events.

Install

If your project uses Igniter, install the current beta from Hex:

mix igniter.install jidoka@0.8.0-beta.1

For manual installation, add jidoka to your dependencies:

def deps do
[
{:jidoka, "~> 0.8.0-beta"}
]
end
mix deps.get

Export a provider key before running live examples:

export OPENAI_API_KEY=...
# or
export ANTHROPIC_API_KEY=...

Jidoka does not load .env files for applications. Put that policy in your app, release config, example app, or shell.

Jidoka is beta software. The 0.8.x beta line is intended for early adopters while the public API is still allowed to change before a stable release.

Define An Agent

defmodule MyApp.Assistant do
use Jidoka.Agent
agent :assistant do
model "openai:gpt-4o-mini"
instructions "Answer clearly and briefly."
end
end
{:ok, text} = MyApp.Assistant.chat("What can you help me with?")

Use chat/3 when you only need the final assistant text. Use turn/3 when you need the full result:

{:ok, result} = Jidoka.turn(MyApp.Assistant, "What can you help me with?")
result.content
result.events
result.journal.results

Add A Tool

Tools are Jido actions exposed to the model as operations.

defmodule MyApp.LocalTime do
use Jidoka.Action,
name: "local_time",
description: "Returns the local time for a city.",
schema: Zoi.object(%{city: Zoi.string() |> Zoi.default("Chicago")})
@impl true
def run(params, _context) do
city = Map.get(params, :city) || Map.get(params, "city") || "Chicago"
{:ok, %{city: city, time: "09:30"}}
end
end
defmodule MyApp.TimeAgent do
use Jidoka.Agent
agent :time_agent do
model "openai:gpt-4o-mini"
instructions "Use local_time when the user asks for the time."
end
tools do
action MyApp.LocalTime
end
end
{:ok, text} = MyApp.TimeAgent.chat("What time is it in Chicago?")

The model decides whether to call local_time. Jidoka runs the action, feeds the observation back to the model, and returns the final answer.

Keep A Conversation

Use Jidoka.Session when the same agent should answer across turns.

{:ok, session} = Jidoka.session(MyApp.Assistant, "support-thread-123")
{:ok, session, _text} =
Jidoka.chat(session, "Remember that my team is called Platform.")
{:ok, session, text} =
Jidoka.chat(session, "What is my team called?")

Sessions can run in memory for development and tests, or through a custom store in production.

Pause For Approval

Controls run at explicit boundaries. An operation control can pause before a risky tool call:

defmodule MyApp.RequireRefundApproval do
use Jidoka.Control, name: "require_refund_approval"
@impl true
def call(%Jidoka.Runtime.Controls.OperationContext{} = operation) do
if operation.operation == "refund_order" do
{:interrupt, :approval_required}
else
:cont
end
end
end

An interrupt returns a snapshot. Store it, review it, then resume:

{:hibernate, snapshot} = Jidoka.turn(MyApp.SupportAgent, "Refund order A1001")
approval =
snapshot.turn_state.pending_interrupt
|> Jidoka.Review.Response.approve()
{:ok, result} = Jidoka.resume(snapshot, approval: approval)

Inspect Before You Spend Tokens

Jidoka.inspect(MyApp.TimeAgent)
{:ok, preflight} =
Jidoka.preflight(MyApp.TimeAgent, "What time is it in Chicago?")
preflight.prompt.messages
preflight.prompt.tools

preflight/3 validates the prompt and tool metadata without calling a model.

Author With Data

Agents can also be imported from JSON or YAML:

version: 1
agent:
id: assistant
model: openai:gpt-4o-mini
instructions: Answer clearly and briefly.
{:ok, spec} = Jidoka.import(yaml)
{:ok, text} = Jidoka.chat(spec, "Hello")

Executable refs such as actions, controls, Ash resources, and Zoi schemas are resolved through explicit registries during import.

Examples And Livebooks

The Phoenix showcase app lives in example/.

cd example
mix deps.get
mix phx.server

Livebooks live in livebook/ and focus on contracts, controls, sessions, imports, evals, and trace output.

Test

mix test
mix test --cover
mix format --check-formatted

Live provider tests are opt-in:

mix test --include live test/jidoka/live_req_llm_test.exs

Unit tests should inject fake llm: and operations: capabilities. Product guides and examples should use real provider keys.

Guides

Start here:

Useful next topics:

Status

This is the 1.0.0-beta.1 baseline. The public vocabulary is centered on:

Native provider tool calling, richer workflow authoring, and production store/runtime adapters are still active design areas.

License

Licensed under the Apache License, Version 2.0. See LICENSE.