Skip to content

i2y/conjure

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages