conjure

package module
v0.0.0-...-8a2188e Latest Latest
Warning

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

Go to latest
Published: Mar 30, 2026 License: MIT Imports: 11 Imported by: 0

README

conjure

A lightweight framework for adding the CodeAgent pattern to Go applications.

The LLM generates code (JavaScript/Python) and calls Go functions directly from within that code.

agent, _ := conjure.New(
    conjure.WithModel(myModel),
    conjure.WithFunc("queryDB", myDBFunc),
    conjure.WithFunc("sendSlack", mySlackFunc),
)
defer agent.Close()

result, _ := agent.Run(ctx, "Detect anomalies in the last hour and notify via Slack")
fmt.Println(result.Value)

Why CodeAgent?

With traditional JSON tool-calling (Eino, ADK Go, Blades, etc.), the LLM can only invoke one tool per turn and performs all computation in its head.

// JSON tool calling: 3 turns, computation done by the LLM
Turn 1: LLM → queryDB → result
Turn 2: LLM → "analyzing..." → text
Turn 3: LLM → sendSlack → result

CodeAgent generates code in a single turn, calling multiple functions and running computations accurately in code.

// CodeAgent: 1 turn, computation done precisely in code
var data = queryDB("SELECT ...");
var anomalies = data.filter(d => d.value > threshold);
sendSlack("#alerts", formatReport(anomalies));
return { count: anomalies.length, details: anomalies };

Installation

go get github.com/i2y/conjure

bucephalus adapter (optional):

go get github.com/i2y/conjure/bucephalus

Quick Start

Minimal (3 lines)
agent, _ := conjure.New(conjure.WithModel(myModel))
result, _ := agent.Run(ctx, "Calculate the first 10 Fibonacci numbers")
fmt.Println(result.Value) // [1,1,2,3,5,8,13,21,34,55]
With bucephalus adapter
import (
    "github.com/i2y/conjure"
    cb "github.com/i2y/conjure/bucephalus"
    _ "github.com/i2y/bucephalus/anthropic" // register provider
)

agent, _ := conjure.New(
    conjure.WithModel(cb.New("anthropic", "claude-sonnet-4-5-20250929")),
)
Custom Model implementation

Just implement the conjure.Model interface.

type myModel struct{}

func (m *myModel) Generate(ctx context.Context, system string, msgs []conjure.Message) (string, error) {
    // call any LLM API
}

agent, _ := conjure.New(conjure.WithModel(&myModel{}))

Registering Go Functions

Go functions registered with WithFunc can be called directly from LLM-generated code.

agent, _ := conjure.New(
    conjure.WithModel(myModel),
    conjure.WithFunc("queryDB", func(sql string) ([]map[string]any, error) {
        return db.Query(ctx, sql)
    }),
    conjure.WithFunc("sendSlack", func(channel, message string) error {
        return slack.PostMessage(channel, message)
    }),
)

Functions with any signature can be registered. Argument type conversion (e.g., JS float64 to Go int) is handled automatically.

Python Runtime

agent, _ := conjure.New(
    conjure.WithModel(myModel),
    conjure.WithLanguage("python"),
    conjure.WithDependencies("pandas", "scikit-learn"),
    conjure.WithFunc("fetchData", myDataFetcher),
)

In Python mode, Go functions are called via the go_bridge module.

import go_bridge
data = go_bridge.fetchData("sales_2024")

Conversation Mode

RunConversation preserves conversation history across calls.

r1, _ := agent.RunConversation(ctx, "Describe the users table schema")
r2, _ := agent.RunConversation(ctx, "Get users older than 30") // remembers context

agent.ResetConversation() // clear history

Decoding Results

result, _ := agent.Run(ctx, "...")

// Access as JSON string
fmt.Println(result.Value)

// Decode into a Go type
var data []Item
result.Decode(&data)

Options

Option Description Default
WithModel(m) LLM backend (required) -
WithFunc(name, fn) Register a Go function -
WithLanguage(lang) "javascript" or "python" "javascript"
WithDependencies(pkgs...) npm/pip packages -
WithPermissions(p) JS sandbox permissions Net/Read: allow, Write/Run/Env: deny
WithMaxRetries(n) Max retries on code execution failure 3
WithMaxTurns(n) Max LLM call turns 10
WithSystemPrompt(s) Additional system prompt instructions -
WithPostCondition(expr) Result validation expression -
WithRunner(r) Use a pre-built codeact Runner -
WithLogger(fn) Debug log function -

Post-Conditions

Validate execution results with expressions. If a condition fails, the LLM receives feedback and retries.

agent, _ := conjure.New(
    conjure.WithModel(myModel),
    conjure.WithFunc("sendSlack", mySlackFunc),
    conjure.WithPostCondition("result.notified === true"),
)

Examples

calculator — Minimal, no Go functions

The LLM generates JavaScript code to perform calculations.

agent, _ := conjure.New(conjure.WithModel(myModel))
defer agent.Close()

result, _ := agent.Run(ctx, "Calculate the first 15 Fibonacci numbers and return them as an array")
fmt.Println(result.Value) // [0,1,1,2,3,5,8,13,21,34,55,89,144,233,377]
weather — Go function calls

Register Go functions and let the LLM generate code that calls them.

agent, _ := conjure.New(
    conjure.WithModel(myModel),
    conjure.WithFunc("getWeather", getWeather),
    conjure.WithFunc("sendAlert", sendAlert),
)
defer agent.Close()

result, _ := agent.Run(ctx, `
    Check the weather for Tokyo, London, and New York.
    If any city has wind speed over 20 km/h, send an alert to "#weather-alerts".
    Return a summary with cities and alerts_count.
`)

LLM-generated code:

const cities = ["Tokyo", "London", "New York"];
const results = cities.map(city => getWeather(city));
let alerts = 0;
results.forEach(w => {
    if (w.wind > 20) {
        sendAlert("#weather-alerts", `High wind warning: ${w.city} at ${w.wind} km/h`);
        alerts++;
    }
});
return { cities: results, alerts_count: alerts };

Runnable examples are in the examples/ directory.

How It Works

Agent.Run(ctx, prompt)
  |
  +-- 1. Initialize Runner (once) + register Go functions
  +-- 2. Build system prompt (once, cached)
  +-- 3. Call LLM (Model.Generate)
  +-- 4. Extract code from response
  +-- 5. Execute code (codeact Runner)
  +-- 6. Evaluate result
  |     +-- Success -> return Result
  |     +-- Error -> feedback to LLM -> back to 3
  |     +-- Post-condition fail -> feedback to LLM -> back to 3
  +-- 7. MaxRetries/MaxTurns reached -> return error

Architecture

conjure is built on top of codeact.

  • conjure — Use CodeAgent standalone, without an outer framework
  • codeact/*/aifunc — Use the CodeAct pattern inside existing frameworks (Eino, ADK Go, Blades)

Both are complementary and can share a codeact Runner.

Dependencies
conjure
  └── codeact
       ├── codeact/aifunc  ... code extraction, retry, post-condition validation
       ├── codeact/js      ... JS runtime (ramune)
       └── codeact/python  ... Python runtime (pyffi)

conjure/bucephalus (adapter, separate module)
  ├── conjure       ... Model interface
  ���── bucephalus    ... LLM client

conjure itself does not depend on any specific LLM client.

License

MIT

Documentation

Index

Constants

View Source
const (
	RoleUser      = "user"
	RoleAssistant = "assistant"
)

Role constants for Message.

Variables

View Source
var (
	ErrNoModel    = errors.New("conjure: model is required")
	ErrMaxRetries = errors.New("conjure: max retries exceeded")
	ErrMaxTurns   = errors.New("conjure: max turns exceeded")
)

Sentinel errors.

Functions

This section is empty.

Types

type Agent

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

Agent is a CodeAgent instance that generates and executes code via an LLM.

func New

func New(opts ...Option) (*Agent, error)

New creates a new Agent with the given options.

func (*Agent) Close

func (a *Agent) Close() error

Close releases the runner and associated resources.

func (*Agent) ResetConversation

func (a *Agent) ResetConversation()

ResetConversation clears the conversation history. This does not reset the runtime state (variables in JS/Python).

func (*Agent) Run

func (a *Agent) Run(ctx context.Context, prompt string) (*Result, error)

Run executes a single prompt. Each call is independent (no conversation history).

func (*Agent) RunConversation

func (a *Agent) RunConversation(ctx context.Context, prompt string) (*Result, error)

RunConversation executes a prompt while preserving conversation history across calls.

type Message

type Message = aifunc.Message

Message represents a single message in an LLM conversation.

type Model

type Model = aifunc.Model

Model is the minimal interface conjure requires from an LLM backend.

type ModelFunc

type ModelFunc = aifunc.ModelFunc

ModelFunc adapts a plain function to the Model interface.

type Option

type Option func(*config)

Option configures an Agent.

func WithDependencies

func WithDependencies(pkgs ...string) Option

WithDependencies declares npm/pip packages available to generated code.

func WithFunc

func WithFunc(name string, fn any) Option

WithFunc registers a Go function that LLM-generated code can call. The function signature is inferred at init time using the configured language.

func WithLanguage

func WithLanguage(lang string) Option

WithLanguage sets the code generation language ("javascript" or "python").

func WithLogger

func WithLogger(fn func(string, ...any)) Option

WithLogger sets a log function for debug output.

func WithMaxRetries

func WithMaxRetries(n int) Option

WithMaxRetries sets the maximum consecutive retry count on code execution failure.

func WithMaxTurns

func WithMaxTurns(n int) Option

WithMaxTurns sets the absolute upper limit on LLM call turns.

func WithModel

func WithModel(m Model) Option

WithModel sets the LLM backend.

func WithPermissions

func WithPermissions(p Permissions) Option

WithPermissions sets sandbox permissions for JS code execution.

func WithPostCondition

func WithPostCondition(expr string) Option

WithPostCondition adds a post-condition expression to validate the result.

func WithRunner

func WithRunner(r codeact.Runner) Option

WithRunner supplies a pre-built codeact Runner. When set, WithLanguage/WithPermissions/WithDependencies are ignored.

func WithSystemPrompt

func WithSystemPrompt(prompt string) Option

WithSystemPrompt appends additional instructions to the system prompt.

type PermissionState

type PermissionState int

PermissionState controls whether a permission is granted or denied.

const (
	PermGranted PermissionState = iota
	PermDenied
)

type Permissions

type Permissions struct {
	Read  PermissionState
	Write PermissionState
	Net   PermissionState
	Env   PermissionState
	Run   PermissionState

	ReadPaths  []string
	WritePaths []string
	NetHosts   []string
	EnvVars    []string
	RunCmds    []string
}

Permissions controls sandbox capabilities for JS code execution. Ignored when language is Python.

type Result

type Result struct {
	Value string   // Final result value (JSON string)
	Logs  []string // Captured console.log / print output
	Code  string   // The last code that was executed
	Turns int      // Number of LLM call turns taken
	// contains filtered or unexported fields
}

Result holds the outcome of a CodeAgent execution.

func (*Result) Decode

func (r *Result) Decode(v any) error

Decode unmarshals the result value into v.

Jump to

Keyboard shortcuts

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