Packages

Spooks is an agentic workflow library for Elixir. It is intended to be used with Ecto and Phoenix. LLMs can be used to provide additional functionality.

Current section

Files

Jump to
spooks README.md
Raw

README.md

# spooks
An agentic workflow framework in Elixir
## Installation
You must run an ecto migration to create the `spooks_workflow_checkpoints` table if you plan on using checkpoints.
```elixir
defmodule MyApp.Repo.Migrations.AddSpooksTables do
use Ecto.Migration
def down do
drop table("spooks_workflow_checkpoints")
end
def up do
create table("spooks_workflow_checkpoints") do
add :workflow_identifier, :string
add :workflow_module, :string
add :workflow_context, :map
add :workflow_event_module, :string
add :workflow_event, :map
add :checkpoint_timeout, :naive_datetime
timestamps()
end
create unique_index("spooks_workflow_checkpoints", [:workflow_identifier])
end
end
```
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `spooks` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:spooks, "~> 0.1.0"}
]
end
```
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at <https://hexdocs.pm/spooks>.
## Usage
Create your own workflow! Be sure you include the following line in your workflow module:
```elixir
use Spooks.Workflow
```
Create a struct for each event in your workflow. You can add whichever fields you like to your struct.
> [!NOTE]
> Note that all your events must `@derive Jason.Encoder` because they are saved to the database checkpoints.
```elixir
defmodule MyApp.MyCustomEvent do
@derive Jason.Encoder
defstruct []
end
```
In your workflow, add a function for each event. The name of the function *must* be the same as the event name only with underscores and with the final `_event` replaced with `_step`. Your function must take 2 arguments. The event and the context. It must return either the `{:ok, next_event, updated_ctx}` or `{:error, reason}` tuple.
For example:
```elixir
def my_custom_step(event, ctx) do
next_event = ...
udpated_ctx = ...
{:ok, next_event, updated_ctx}
end
```
You _should_ add a `@step` annotation to your step functions. This is used for generating diagrams of your agentic workflow.
`in` must be
- an event module name
`out` can be
- nil (the end of the agentic workflow)
- an event module name
- a list of event module names (for branching)
```elixir
@step %Step{in: MyFirstEvent, out: MySecondEvent}
def my_first_step(event, ctx) do
...
end
@step %Step{in: MySecondEvent, out: [MyThirdEvent,StopEvent]}
def my_second_step(event, ctx) do
...
end
@step %Step{in: MyFirstEvent, out: nil}
def my_last_step(event, ctx) do
{:ok, nil, ctx}
end
```
There are two built in events for `Spooks.Event.StartEvent` and `Spooks.Event.EndEvent`.
If you would like to add data or get data from the context you should use the following functions:
> [!HINT]
> You can also use a list of keys for `put_data` and `get_data` for nested data structures.
```elixir
@step %Step{in: MyFirstEvent, out: MySecondEvent}
def my_first_step(event, ctx) do
new_ctx = Spooks.Context.SpooksContext.put_data(:greeting, "hello world!")
{:ok, %MySecondEvent{}, new_ctx}
end
@step %Step{in: MySecondEvent, out: MyThirdEvent}
def my_second_step(event, ctx) do
custom_greeting = Spooks.Context.SpooksContext.get_data(:greeting)
IO.puts(custom_greeting)
{:ok, %MyThirdEvent{}, ctx}
end
```
If you wish to save checkpoints after each step, pass in your repository to the workflow context.
```elixir
workflow = Spooks.Sample.SampleWorkflow
repo = MyApp.Repo
workflow_context = Spooks.Context.SpooksContext.new(workflow, repo)
Spooks.WorkflowEngine.run_workflow(workflow_context)
```
If you do not wish to save checkpoints:
```elixir
workflow = Spooks.Sample.SampleWorkflow
workflow_context = Spooks.Context.SpooksContext.new(workflow, nil)
Spooks.WorkflowEngine.run_workflow(workflow_context)
```