Walkthrough: Resource to Action

Copy Markdown View Source

This walkthrough shows the core AshJido flow:

  1. Define Ash resources.
  2. Expose selected actions through the jido DSL.
  3. Execute generated Jido.Action modules with runtime context.
  4. Use generated actions directly in a Jido.AI.Agent.

1. Define a Domain and Resources

defmodule MyApp.Blog do
  use Ash.Domain

  resources do
    resource MyApp.Blog.Author
    resource MyApp.Blog.Post
  end
end

defmodule MyApp.Blog.Author do
  use Ash.Resource,
    domain: MyApp.Blog,
    data_layer: AshPostgres.DataLayer

  postgres do
    table "authors"
    repo MyApp.Repo
  end

  attributes do
    uuid_primary_key :id
    attribute :name, :string, allow_nil?: false
  end

  actions do
    defaults [:read, :destroy]

    create :create do
      accept [:name]
    end
  end
end
defmodule MyApp.Blog.Post do
  use Ash.Resource,
    domain: MyApp.Blog,
    extensions: [AshJido],
    data_layer: AshPostgres.DataLayer

  postgres do
    table "posts"
    repo MyApp.Repo
  end

  attributes do
    uuid_primary_key :id
    attribute :title, :string, allow_nil?: false
    attribute :status, :atom, default: :draft
    timestamps()
  end

  relationships do
    belongs_to :author, MyApp.Blog.Author, allow_nil?: false, public?: true
  end

  actions do
    defaults [:read, :destroy]

    create :create do
      accept [:title, :author_id]
    end

    update :publish do
      accept []
      change set_attribute(:status, :published)
    end
  end

  jido do
    action :create, name: "create_post"
    action :read, name: "list_posts", load: [:author]
    action :publish, name: "publish_post"
  end
end

2. Run Generated Actions

alias MyApp.Blog.{Author, Post}

# Create related data with Ash directly
author =
  Author
  |> Ash.Changeset.for_create(:create, %{name: "Ada"}, domain: MyApp.Blog)
  |> Ash.create!(domain: MyApp.Blog)

# Execute generated Jido actions
{:ok, post} =
  Post.Jido.Create.run(
    %{title: "Ash + Jido", author_id: author.id},
    %{domain: MyApp.Blog}
  )

{:ok, posts} = Post.Jido.Read.run(%{}, %{domain: MyApp.Blog})
loaded = Enum.find(posts, &(&1[:id] == post[:id]))

# Static load from DSL is applied to read actions
loaded[:author][:name] # => "Ada"

{:ok, published} =
  Post.Jido.Publish.run(
    %{id: post[:id]},
    %{domain: MyApp.Blog}
  )

published[:status] # => :published

3. Pass Ash Context Through Runtime Context

context[:domain] is optional when the resource has a static domain: configuration. When present, it overrides the resource domain. Other Ash options are optional and passed through when present:

context = %{
  domain: MyApp.Blog,
  actor: current_user,
  tenant: "org_123",
  scope: %{actor: current_user},
  authorize?: true,
  tracer: [MyApp.Tracer],
  context: %{request_id: "req_123"},
  timeout: 15_000
}

Post.Jido.Create.run(params, context)

Notes:

  • If actor is omitted, Ash can still resolve actor from scope.
  • If actor: nil is explicitly set, it intentionally overrides actor from scope.

4. Rules to Remember

  • Update and destroy generated actions require the resource primary key field or fields in params.
  • Destroy generated actions also accept declared Ash destroy action arguments.
  • load is static DSL configuration for read actions only.
  • output_map?: true (default) returns maps instead of Ash structs.

5. Use Generated Resource Actions in a Jido.AI.Agent

This is the core integration demo: generated AshJido action modules can be used directly as agent tools.

defmodule MyApp.Blog.PostAgent do
  use Jido.AI.Agent,
    name: "post_agent",
    model: :fast,
    tools: [
      MyApp.Blog.Post.Jido.Create,
      MyApp.Blog.Post.Jido.Read,
      MyApp.Blog.Post.Jido.Publish
    ],
    tool_context: %{domain: MyApp.Blog},
    system_prompt: "You manage blog posts. Use tools for data operations."
end
{:ok, pid} = Jido.AgentServer.start(agent: MyApp.Blog.PostAgent)

{:ok, answer} =
  MyApp.Blog.PostAgent.ask_sync(
    pid,
    "Create a post titled 'Hello' for author #{author_id} and then list posts.",
    tool_context: %{
      domain: MyApp.Blog,
      actor: current_user,
      tenant: "org_123"
    }
  )

Important:

  • The agent tools: list should use generated module names (MyApp.Blog.Post.Jido.*).
  • Pass domain in tool_context when overriding the resource's static domain or when the resource has domain: nil.
  • Add actor, tenant, and other Ash options in tool_context for policy and tenancy-aware calls.