README.md

# SwarmAi

[![Hex.pm](https://img.shields.io/hexpm/v/swarm_ai.svg)](https://hex.pm/packages/swarm_ai)
[![Docs](https://img.shields.io/badge/hex-docs-blue.svg)](https://hexdocs.pm/swarm_ai)

A functional AI agent execution framework for Elixir with protocol-based LLM and tool integration.

SwarmAi implements a **functional core / imperative shell** architecture: a pure state machine produces effects (instructions for side effects) which are interpreted by an execution layer. This makes agent logic deterministic and testable while keeping I/O at the edges.

## Installation

Add `swarm_ai` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:swarm_ai, "~> 0.1.0"}
  ]
end
```

## Quick Start

### Define an Agent

Agents are structs that implement the `SwarmAi.Agent` protocol:

```elixir
defmodule MyAgent do
  use TypedStruct

  typedstruct do
    field :model, String.t(), default: "gpt-4o"
  end

  defimpl SwarmAi.Agent do
    def system_prompt(_agent), do: "You are a helpful assistant."
    def init(_agent), do: {%{}, []}
    def should_terminate?(_agent, _loop, _response), do: false
    def llm(agent), do: MyLLMClient.new(agent.model)
  end
end
```

### Blocking Execution

```elixir
agent = %MyAgent{}

{:ok, result, loop_id} = SwarmAi.run_blocking(agent, "Hello!", fn tool_call ->
  case tool_call.name do
    "search" -> {:ok, "search results here"}
    _ -> {:error, "unknown tool"}
  end
end)
```

### Streaming Execution

```elixir
{:ok, result, loop_id} = SwarmAi.run_streaming(agent, "Analyze this code",
  tool_executor: &execute_tool/1,
  on_chunk: fn chunk -> IO.write(chunk.text || "") end,
  on_response: fn response -> Logger.info("Response complete") end,
  on_tool_call: fn tc -> Logger.info("Calling #{tc.name}") end
)
```

### Manual Control

For fine-grained control over tool execution:

```elixir
case SwarmAi.run(agent, "What's the weather?") do
  {:completed, loop} ->
    loop.result

  {:tool_calls, loop, tool_calls} ->
    results = Enum.map(tool_calls, &execute_tool/1)
    SwarmAi.continue(loop, results)

  {:error, loop} ->
    {:error, loop.error}
end
```

## Architecture

```
┌──────────────────────────────────────────────────┐
│            SwarmAi Module (Impure Shell)          │
│  Interprets effects, makes LLM calls,            │
│  executes tools, spawns child agents              │
└────────────────────────┬─────────────────────────┘
                         │ produces/consumes
                         ▼
┌──────────────────────────────────────────────────┐
│        Loop + Runner (Pure Functional Core)       │
│  State machine for agent execution,               │
│  returns {loop, effects} tuples, no side effects  │
└──────────────────────────────────────────────────┘
```

### Key Concepts

- **Agent Protocol** - Define agents as structs implementing `SwarmAi.Agent` (system prompt, tools, LLM config, termination logic)
- **LLM Protocol** - Bring your own LLM client by implementing `SwarmAi.LLM` (streaming interface)
- **Effect System** - Pure functions produce effects (`{:call_llm, ...}`, `{:execute_tool, ...}`) instead of performing I/O directly
- **Tool Execution** - Tools are pure data structures; execution is delegated to your `tool_executor` function
- **Child Agents** - Tools can spawn sub-agents by returning `{:spawn, SpawnChildAgent.t()}`
- **Telemetry** - Built-in `:telemetry` events for runs, steps, LLM calls, tool executions, and child spawns

## Telemetry Events

SwarmAi emits telemetry under the `[:swarm_ai, ...]` prefix:

| Event | Description |
|-------|-------------|
| `[:swarm_ai, :run, :start\|:stop\|:exception]` | Full agent run lifecycle |
| `[:swarm_ai, :step, :start\|:stop]` | Individual step within a run |
| `[:swarm_ai, :llm, :call, :start\|:stop\|:exception]` | LLM API calls |
| `[:swarm_ai, :tool, :execute, :start\|:stop\|:exception]` | Tool executions |
| `[:swarm_ai, :child, :spawn, :start\|:stop\|:exception]` | Child agent spawns |

## License

Apache-2.0 - see the LICENSE file for details.