Skip to content

agentjido/jido_ai

Repository files navigation

Jido AI

Jido AI is an extension of the Jido framework for building AI Agents and Workflows in Elixir. At present, it provides a single action for interacting with Anthropic's Claude models via the Instructor library.

Installation

Note: You must install instructor from GitHub until a new version is released. Hex does not yet have a release of instructor that supports the Instructor.Adapters.Anthropic adapter.

def deps do
  [
    {:jido, "~> 1.0.0"},
    {:jido_ai, "~> 1.0.0"},

    # Must install from github until a new version is released
    {:instructor, github: "thmsmlr/instructor_ex"}
  ]
end

Configuration

You will need to properly configure the Instructor library to use the Anthropic adapter:

# config/config.exs
config :instructor,
  adapter: Instructor.Adapters.Anthropic,
  anthropic: [
    api_key: System.get_env("ANTHROPIC_API_KEY")
  ]

Supported Providers

Jido AI supports multiple LLM providers:

  • Anthropic (Claude models)
  • OpenAI (GPT models)
  • OpenRouter (proxy for multiple models)
  • Cloudflare (Workers AI models)
  • Google (Gemini models)

Using Google Gemini

To use Google's Gemini models with the OpenAI-compatible API:

# Set your Google API key
Jido.AI.Keyring.set_session_value(:google_api_key, "your_gemini_api_key")

# Create a model using the Google provider
{:ok, model} = Jido.AI.Model.from({:google, [model: "gemini-2.0-flash"]})

# Use the model with any OpenAI-compatible action
{:ok, result} = Jido.AI.Actions.OpenaiEx.run(
  %{
    model: model,
    messages: [
      %{role: "user", content: "Tell me about Elixir programming language"}
    ],
    temperature: 0.7
  },
  %{}
)

You can also set the Google API key using environment variables:

GOOGLE_API_KEY=your_gemini_api_key

Prompt and Message Handling

Jido AI provides a robust system for handling prompts and messages when interacting with LLMs.

MessageItem

The Jido.AI.Prompt.MessageItem module is used to represent messages in conversations with LLMs. It supports:

  • Basic text messages with different roles (user, assistant, system, function)
  • Rich content including images and files
  • Template-based messages using EEx
alias Jido.AI.Prompt.MessageItem

# Create a simple user message
user_msg = MessageItem.new(%{role: :user, content: "Hello"})

# Create a system message
system_msg = MessageItem.new(%{role: :system, content: "You are a helpful assistant"})

# Create a message with rich content (image)
rich_msg = MessageItem.new_multipart(:user, [
  MessageItem.text_part("Check out this image:"),
  MessageItem.image_part("https://example.com/image.jpg")
])

# Create a message with a template
template_msg = MessageItem.new(%{
  role: :system,
  content: "You are a <%= @assistant_type %>",
  engine: :eex
})

For more details on MessageItem usage, refer to the documentation.

Example

Here's how to use Jido AI with Jido.Exec to get structured information about US politicians. See the examples/politician.ex for more a full example.

# Define a simple workflow
defmodule JidoAi.Examples.Politician do
  defmodule Schema do
    use Ecto.Schema
    use Instructor
    @primary_key false
    embedded_schema do
      field(:first_name, :string)
      field(:last_name, :string)

      embeds_many :offices_held, Office, primary_key: false do
        field(:office, Ecto.Enum,
          values: [:president, :vice_president, :governor, :congress, :senate]
        )

        field(:from_date, :date)
        field(:to_date, :date)
      end
    end
  end

  use Jido.Action,
    name: "politician",
    description: "A description of United States Politicians and the offices that they held",
    schema: [
      query: [type: :string, required: true, doc: "The query to search for"]
    ]

  def run(params, _context) do
    # Run the Anthropic ChatCompletion action
    JidoAi.Actions.Anthropic.ChatCompletion.run(
      %{
        model: "claude-3-5-haiku-latest",
        messages: [
          %{
            role: "user",
            content: params.query
          }
        ],
        response_model: Schema,
        temperature: 0.5,
        max_tokens: 1000
      },
      %{}
    )
    |> case do
      {:ok, %{result: politician}} -> {:ok, %{result: politician}}
      {:error, reason} -> {:error, reason}
    end
  end
end

# Run the workflow
iex> {:ok, result} = Jido.Exec.run(JidoAi.Examples.Politician, %{query: "Tell me about Barack Obama's political career"})
iex> result.result
%JidoAi.Examples.Politician.Schema{
  first_name: "Barack",
  last_name: "Obama",
  offices_held: [
    %JidoAi.Examples.Politician.Schema.Office{
      office: :senate,
      from_date: ~D[2005-01-03],
      to_date: ~D[2008-11-16]
    },
    %JidoAi.Examples.Politician.Schema.Office{
      office: :president,
      from_date: ~D[2009-01-20],
      to_date: ~D[2017-01-20]
    }
  ]
}

The example demonstrates how JidoAi can:

  • Write Actions that can wrap other Actions
  • Use Jido.Exec to orchestrate AI operations
  • Parse natural language queries about politicians
  • Return structured data using Ecto schemas
  • Handle complex nested data structures
  • Provide type validation through Ecto's type system

Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/jido_ai.

LLM Keyring

The Jido.AI application includes a Keyring system that manages API keys for various LLM providers. The Keyring is a singleton GenServer that helps to manage LLM keys for convenience, not for security.

Key Sources

Keys are loaded with the following priority:

  1. Environment variables (highest priority)
  2. Application environment
  3. Default values (lowest priority)

Session-based Keys

Keys can also be set on a per-session (per-process) basis. This allows different parts of your application to use different API keys without affecting other processes.

Usage

# Get a key (checks session keys first, then environment keys)
api_key = Jido.AI.Keyring.get_key(:anthropic)

# Get only the environment-level key
env_key = Jido.AI.Keyring.get_env_key(:anthropic)

# Set a session-specific key (only affects the current process)
Jido.AI.Keyring.set_session_key(:anthropic, "my_session_key")

# Clear a session key
Jido.AI.Keyring.clear_session_key(:anthropic)

# Clear all session keys for the current process
Jido.AI.Keyring.clear_all_session_keys()

# Check if a key is valid (non-nil and non-empty)
Jido.AI.Keyring.has_valid_key?(api_key)

# Test if a key is valid by making an API request
Jido.AI.Keyring.test_key(:anthropic, api_key)

Configuration

You can configure keys in your config.exs file:

config :jido_ai, :instructor,
  anthropic: [
    api_key: "your_anthropic_key"
  ]

config :jido_ai, :openai,
  api_key: "your_openai_key"

Or using environment variables:

ANTHROPIC_API_KEY=your_anthropic_key
OPENAI_API_KEY=your_openai_key

Environment variables take precedence over application configuration.

About

Jido Actions and Skills for interacting with LLMs

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages