dive

package module
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 26, 2025 License: Apache-2.0 Imports: 9 Imported by: 0

README

Dive - AI Agent Framework

Introduction

Dive is a flexible Go framework for building AI agent systems. Whether you need a single specialized agent or a complex workflow of AI tasks, Dive makes it easy to accomplish tasks with AI.

Dive can be embedded into existing Go applications or run standalone using workflow definitions.

Project Status

⚠️ Early Development Stage ⚠️

Dive is in early development. While much core functionality is in place, the project is still evolving rapidly.

  • Not recommended for production use at this time
  • Breaking changes will happen as the API matures
  • Feedback is highly valued on concepts, APIs, and usability

We welcome your input! Please reach out in GitHub Discussions with questions, suggestions, or feedback.

Features

  • Workflow-Based Architecture: Define complex AI tasks as workflows with multiple steps
  • Flexible Agent System: Create specialized agents with different roles and capabilities
  • Declarative Configuration: Define workflows, agents, and tasks using YAML or programmatically in Go
  • Multi-Provider Support: Unified Go interface for multiple LLM providers (Anthropic, OpenAI, Groq)
  • Tool System: Extend agent capabilities with tools like web search, document retrieval, and more
  • Streaming Support: Stream events for chats and task progress in real-time
  • Variable Support: Pass variables into workflows for dynamic execution

Quick Start

Prerequisites

  • Go 1.20 or higher
  • API keys for any LLM providers you plan to use (Anthropic, OpenAI, Groq)
  • API keys for any external tools you plan to use (Google Search, Firecrawl, etc.)

Environment Setup

Set up your shell environment:

# LLM Provider API Keys
export ANTHROPIC_API_KEY="your-key"
export OPENAI_API_KEY="your-key"
export GROQ_API_KEY="your-key"

# Tool API Keys
export GOOGLE_SEARCH_API_KEY="your-key"
export GOOGLE_SEARCH_CX="your-key"
export FIRECRAWL_API_KEY="your-key"

As a Library

To get started with Dive as a library, use go get:

go get github.com/getstingrai/dive

Here's a simple example of creating a chat agent:

provider := anthropic.New()
googleClient, _ := google.New()

agent, err := agent.NewAgent(agent.AgentOptions{
    Name: "Assistant",
    Backstory: "You are a helpful assistant.",
    LLM: provider,
    Tools: []llm.Tool{toolkit.NewGoogleSearch(googleClient)},
    CacheControl: "ephemeral",
})

if err := agent.Start(ctx); err != nil {
    log.Fatal(err)
}
defer agent.Stop(ctx)

// Start chatting with the agent
iterator, err := agent.Stream(ctx, llm.NewUserMessage("Hello!"))
// Handle the streaming response...

Using Workflows

Dive supports defining complex AI tasks as workflows. Here's an example workflow in YAML:

Name: Research
Description: Research a Topic

Config:
  LLM:
    DefaultProvider: anthropic
    DefaultModel: claude-3-7-sonnet-20250219

Agents:
  - Name: Research Analyst
    Description: Research Analyst who specializes in topic research
    Tools:
      - Google.Search
      - Firecrawl.Scrape

Workflows:
  - Name: Research
    Inputs:
      - Name: topic
        Type: string
    Steps:
      - Name: Historical Research
        Agent: Research Analyst
        Prompt:
          Text: "Research the history of: ${inputs.topic}"
          Output: A historical overview
          OutputFormat: Markdown
        Store: historical_research

Run a workflow using the simple runner:

dive run workflow.yaml --vars "topic=history of the internet"

Scripting and Variables

Each workflow execution maintains its own scripting environment with variables that can be read and written. Variables can be used in:

  1. Step Prompts: Use ${variable_name} syntax to include variables in prompts
  2. Action Parameters: Parameters can reference variables using the same syntax
  3. Conditional Logic: Use variables in edge conditions to control workflow branching

Variables can come from several sources:

  • Workflow Inputs: Available as ${inputs.name}
  • Step Outputs: Use Store: variable_name to save a step's output
  • Action Results: Some actions may store their results in variables

Example of variable usage:

Steps:
  - Name: Get Current Time
    Action: Time.Now
    Store: current_time

  - Name: Analyze Files
    Agent: Analyst
    Prompt:
      Text: |
        The current time is: ${current_time}
        
        Respond with the current wall clock time.

Available Actions

Actions are pre-defined operations that can be used in workflow steps. The core actions include:

Document.Write

Writes content to a document in the document repository.

Parameters:

  • Path: Target path for the document
  • Content: Content to write (supports variable templates)

Example:

- Name: Save Report
  Action: Document.Write
  Parameters:
    Path: reports/analysis.md
    Content: ${analysis_result}
Document.Read

Reads content from a document in the document repository.

Parameters:

  • Path: Path of the document to read

Example:

- Name: Load Previous Report
  Action: Document.Read
  Parameters:
    Path: reports/previous.md
  Store: previous_report

Actions can be extended by registering custom implementations in the environment. Each action:

  • Has a unique name
  • Accepts a set of parameters
  • Can read from and write to the execution's variable environment
  • May interact with external systems or resources

LLM Integration

Dive provides a unified interface for working with different LLM providers:

  • Anthropic (Claude 3)
  • OpenAI (GPT-4)
  • Groq (Llama, DeepSeek)

Each provider implementation handles API communication, token counting, and streaming:

provider := anthropic.New(
    anthropic.WithModel("claude-3-7-sonnet-20250219"),
)

provider := openai.New(
    openai.WithModel("gpt-4"),
)

provider := groq.New(
    groq.WithModel("deepseek-r1-distill-llama-70b"),
)

Tested Models

These are the models that have been tested with Dive:

Provider Model
Anthropic claude-3-7-sonnet-20250219
OpenAI gpt-4
Groq deepseek-r1-distill-llama-70b
Groq llama-3.3-70b-versatile

Tool Use

Tools extend agent capabilities. Dive includes these built-in tools:

  • Google.Search: Web search using Google Custom Search
  • Firecrawl.Scrape: Web scraping with content extraction
  • Document.Write: Write content to files
  • Document.Read: Read content from files

Creating custom tools is straightforward:

type WeatherTool struct {
    apiKey string
}

func (t *WeatherTool) Definition() *llm.ToolDefinition {
    return &llm.ToolDefinition{
        Name: "GetWeather",
        Description: "Get the current weather for a location",
        Parameters: llm.Schema{
            Type: "object",
            Required: []string{"location"},
            Properties: map[string]*llm.SchemaProperty{
                "location": {
                    Type: "string",
                    Description: "The city and state/country",
                },
            },
        },
    }
}

Contributing

We welcome contributions to Dive! Whether you're fixing bugs, adding features, improving documentation, or spreading the word, your help is appreciated.

At this early stage, we're particularly interested in feedback on the workflow system, API design, and any use cases you'd like to see supported.

Roadmap

  • Enhanced workflow capabilities
  • More built-in tools
  • Agent memory systems
  • Workflow persistence
  • Integration with popular services (Slack, Google Drive, etc.)
  • Expanded testing coverage
  • CLI improvements

FAQ

What makes Dive different from other agent frameworks?

Dive is meant to be a highly practical, batteries-included agent framework. Key differentiators include:

  • Workflow-first approach for complex AI tasks
  • Simple but powerful configuration system
  • Strong streaming support for real-time updates
  • Easy integration with existing Go applications
  • Built-in support for popular LLM providers
  • Flexible tool system for extending capabilities

How do I handle LLM rate limits?

Dive includes built-in retry mechanisms for handling rate limits. This includes exponential backoff and jitter.

Should I use Dive in production?

No, Dive is not recommended for production use at this time. As mentioned in the Project Status section, Dive is in its early development stages and breaking changes will occur as the API matures.

We recommend using it for experimentation, prototyping, and providing feedback during this early stage. Once the project reaches a more stable state, we'll provide clear guidance on production readiness.

How can I extend or customize Dive?

Dive is designed to be highly extensible:

  • Create custom tools by implementing the llm.Tool interface
  • Add support for new LLM providers by implementing the llm.Provider interface
  • Create custom workflow actions
  • Define your own agent behaviors

Is there a hosted or managed version available?

Not at this time. Dive is provided as an open-source framework that you can self-host and integrate into your own applications.

Who is Behind Dive?

Dive is developed by Stingrai.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AgentNames

func AgentNames(agents []Agent) []string

func DateString

func DateString(t time.Time) string

func FormatMessages

func FormatMessages(messages []*llm.Message) string

func RandomName

func RandomName() string

func TruncateText

func TruncateText(text string, maxWords int) string

func WaitForEvent

func WaitForEvent[T any](ctx context.Context, stream Stream) (T, error)

WaitForEvent waits for an event with a payload of the specified type and returns it. It will return an error if the context is canceled or if an error event is received.

Types

type Agent

type Agent interface {

	// Name of the Agent
	Name() string

	// Goal of the Agent
	Goal() string

	// IsSupervisor indicates whether the Agent can assign work to other Agents
	IsSupervisor() bool

	// SetEnvironment sets the runtime Environment to which this Agent belongs
	SetEnvironment(env Environment)

	// Generate gives the agent a message to respond to
	Generate(ctx context.Context, message *llm.Message, opts ...GenerateOption) (*llm.Response, error)

	// Stream gives the agent a message to respond to and returns a stream of events
	Stream(ctx context.Context, message *llm.Message, opts ...GenerateOption) (Stream, error)

	// Work gives the agent a task to complete
	Work(ctx context.Context, task Task) (Stream, error)
}

Agent represents an AI agent that can perform tasks

type DiskOutputPlugin

type DiskOutputPlugin struct {
	// contains filtered or unexported fields
}

func NewDiskOutputPlugin

func NewDiskOutputPlugin(dir string) (*DiskOutputPlugin, error)

func (*DiskOutputPlugin) Name

func (p *DiskOutputPlugin) Name() string

func (*DiskOutputPlugin) OutputExists

func (p *DiskOutputPlugin) OutputExists(ctx context.Context, name, fingerprint string) (bool, error)

func (*DiskOutputPlugin) ReadOutput

func (p *DiskOutputPlugin) ReadOutput(ctx context.Context, name, fingerprint string) (string, error)

func (*DiskOutputPlugin) WriteOutput

func (p *DiskOutputPlugin) WriteOutput(ctx context.Context, name, fingerprint string, output string) error

type Environment

type Environment interface {

	// Name of the Environment
	Name() string

	// Agents returns the list of all Agents belonging to this Environment
	Agents() []Agent

	// RegisterAgent adds an Agent to this Environment
	RegisterAgent(agent Agent) error

	// GetAgent returns the Agent with the given name, if found
	GetAgent(name string) (Agent, error)

	// DocumentRepository returns the DocumentRepository for this Environment
	DocumentRepository() document.Repository
}

Environment is a container for running Agents and Workflow Executions. Interactivity between Agents is scoped to a single Environment.

type Event

type Event struct {
	// Type of the event
	Type string

	// Origin describes what produced the Event
	Origin EventOrigin

	// Payload contains arbitrary data associated with the Event
	Payload any

	// Error is set if this Event corresponds to an error
	Error error
}

Event generated by a Dive Agent or Workflow Execution.

type EventHandlerAgent

type EventHandlerAgent interface {
	Agent

	// AcceptedEvents returns the names of supported events
	AcceptedEvents() []string

	// HandleEvent passes an event to the event handler
	HandleEvent(ctx context.Context, event *Event) error
}

EventHandlerAgent is an Agent that can handle events

type EventOrigin

type EventOrigin struct {
	AgentID         string `json:"agent_id,omitempty"`
	AgentName       string `json:"agent_name,omitempty"`
	TaskID          string `json:"task_id,omitempty"`
	TaskName        string `json:"task_name,omitempty"`
	WorkflowID      string `json:"workflow_id,omitempty"`
	WorkflowName    string `json:"workflow_name,omitempty"`
	EnvironmentID   string `json:"environment_id,omitempty"`
	EnvironmentName string `json:"environment_name,omitempty"`
}

EventOrigin carries information about what produced the event

type GenerateOption

type GenerateOption func(*GenerateOptions)

GenerateOption is a type signature for defining new LLM generation options.

func WithThreadID

func WithThreadID(threadID string) GenerateOption

WithThreadID associates the given conversation thread ID with a generation. This appends the new messages to any previous messages belonging to this thread.

func WithUserID

func WithUserID(userID string) GenerateOption

WithUserID associates the given user ID with a generation, indicating what person is the speaker in the conversation.

type GenerateOptions

type GenerateOptions struct {
	ThreadID string
	UserID   string
}

GenerateOptions contains configuration for LLM generations.

func (*GenerateOptions) Apply

func (o *GenerateOptions) Apply(opts []GenerateOption)

Apply invokes any supplied options. Used internally in Dive.

type InMemoryOutputPlugin

type InMemoryOutputPlugin struct {
	// contains filtered or unexported fields
}

func NewInMemoryOutputPlugin

func NewInMemoryOutputPlugin() *InMemoryOutputPlugin

func (*InMemoryOutputPlugin) Name

func (p *InMemoryOutputPlugin) Name() string

func (*InMemoryOutputPlugin) OutputExists

func (p *InMemoryOutputPlugin) OutputExists(ctx context.Context, name, fingerprint string) (bool, error)

func (*InMemoryOutputPlugin) ReadOutput

func (p *InMemoryOutputPlugin) ReadOutput(ctx context.Context, name, fingerprint string) (string, error)

func (*InMemoryOutputPlugin) WriteOutput

func (p *InMemoryOutputPlugin) WriteOutput(ctx context.Context, name, fingerprint string, output string) error

type Input

type Input struct {
	Name        string      `json:"name"`
	Type        string      `json:"type,omitempty"`
	Description string      `json:"description,omitempty"`
	Required    bool        `json:"required,omitempty"`
	Default     interface{} `json:"default,omitempty"`
}

Input defines an expected input parameter

type Output

type Output struct {
	Name        string      `json:"name"`
	Type        string      `json:"type,omitempty"`
	Description string      `json:"description,omitempty"`
	Format      string      `json:"format,omitempty"`
	Default     interface{} `json:"default,omitempty"`
	Document    string      `json:"document,omitempty"`
}

Output defines an expected output parameter

type OutputFormat

type OutputFormat string

OutputFormat defines the desired output format for a Task

const (
	OutputText     OutputFormat = "text"
	OutputMarkdown OutputFormat = "markdown"
	OutputJSON     OutputFormat = "json"
)

type OutputPlugin

type OutputPlugin interface {
	// Name of the output plugin
	Name() string

	// OutputExists returns true if the output for the given task and fingerprint
	// already exists.
	OutputExists(ctx context.Context, name, fingerprint string) (bool, error)

	// ReadOutput reads the output for the given task and fingerprint
	ReadOutput(ctx context.Context, name, fingerprint string) (string, error)

	// WriteOutput writes the output for the given task and fingerprint
	WriteOutput(ctx context.Context, name, fingerprint string, output string) error
}

OutputPlugin is a plugin that can be used to store and retrieve task outputs

type Prompt

type Prompt struct {
	Name         string           `json:"name"`
	Text         string           `json:"text,omitempty"`
	Context      []*PromptContext `json:"context,omitempty"`
	Output       string           `json:"output,omitempty"`
	OutputFormat string           `json:"output_format,omitempty"`
}

type PromptContext

type PromptContext struct {
	Name        string `json:"name"`
	Description string `json:"description,omitempty"`
	Text        string `json:"text,omitempty"`
}

type Publisher

type Publisher interface {
	// Send sends an event to the stream
	Send(ctx context.Context, event *Event) error

	// Close closes the publisher and releases any resources
	Close()
}

Publisher is an interface used to send events.

type RunnableAgent

type RunnableAgent interface {
	Agent

	// Start the agent
	Start(ctx context.Context) error

	// Stop the agent
	Stop(ctx context.Context) error

	// IsRunning returns true if the agent is running
	IsRunning() bool
}

RunnableAgent is an Agent that can be started and stopped

type Stream

type Stream interface {
	// Next advances the stream to the next event. It returns false when the stream
	// is complete or if an error occurs. The caller should check Err() after Next
	// returns false to distinguish between normal completion and errors.
	Next(ctx context.Context) bool

	// Event returns the current event in the stream. It should only be called
	// after a successful call to Next.
	Event() *Event

	// Err returns any error that occurred while reading from the stream.
	// It should be checked after Next returns false.
	Err() error

	// Close closes the stream and releases any associated resources.
	Close() error

	// Publisher returns a publisher for the stream
	Publisher() Publisher
}

Stream is an interface used to consume Events.

func NewStream

func NewStream() Stream

NewStream creates a new event stream

type Task

type Task interface {
	// Name returns the name of the task
	Name() string

	// Timeout returns the maximum duration allowed for task execution
	Timeout() time.Duration

	// Prompt returns the LLM prompt for the task
	Prompt() (*Prompt, error)
}

Task represents a unit of work that can be executed by an Agent

type TaskResult

type TaskResult struct {
	// Task is the task that was executed
	Task Task

	// Content contains the raw output
	Content string

	// Format specifies how to interpret the content
	Format OutputFormat

	// Object holds parsed JSON output if applicable
	Object interface{}

	// Error is set if task execution failed
	Error error

	// Usage tracks LLM token usage
	Usage llm.Usage
}

TaskResult holds the output of a completed task

type TaskStatus

type TaskStatus string

TaskStatus indicates a Task's execution status

const (
	TaskStatusQueued    TaskStatus = "queued"
	TaskStatusActive    TaskStatus = "active"
	TaskStatusPaused    TaskStatus = "paused"
	TaskStatusCompleted TaskStatus = "completed"
	TaskStatusBlocked   TaskStatus = "blocked"
	TaskStatusError     TaskStatus = "error"
	TaskStatusInvalid   TaskStatus = "invalid"
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL