Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,31 @@ jobs:

- name: Build
run: make build

# examples:local — off until if: true and EXAMPLES_* repo secrets (examples/.env.defaults).
examples:
if: false
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: Install Task
run: go install github.com/go-task/task/v3/cmd/task@latest

- name: Configure examples/.env
run: |
cat > examples/.env <<EOF
LLM_PROVIDER=${{ secrets.EXAMPLES_LLM_PROVIDER }}
LLM_MODEL=${{ secrets.EXAMPLES_LLM_MODEL }}
LLM_APIKEY=${{ secrets.EXAMPLES_LLM_APIKEY }}
EMBEDDING_OPENAI_APIKEY=${{ secrets.EXAMPLES_EMBEDDING_OPENAI_APIKEY }}
EOF

- name: Run examples (local)
run: task examples:local
12 changes: 10 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
dist/
examples/.env
cmd/config.yaml
cmd/bin/
logs/
Expand All @@ -19,8 +18,17 @@ yarn-debug.log*
yarn-error.log*
pnpm-debug.log*

# Local env overrides (never commit secrets)
# Local env overrides (never commit secrets) — .env at any path in the repo
.env
.env.local
.env*.local
# Committed templates stay tracked: .env.defaults, etc.

# Reports of example runs
reports/**

# Examples infra runtime files
examples/.a2a-server.pid
examples/logs/

.DS_Store
16 changes: 9 additions & 7 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Contributing to agent-sdk-go

Thank you for your interest in contributing. **agent-sdk-go** is a community Go SDK for AI agents that **run on the [Temporal](https://temporal.io)** runtime (workflows and activities). You need a **running Temporal server** for examples and the CLI; see **[temporal-setup.md](temporal-setup.md)**. This document explains how to set up your environment and what we expect from contributors.
Thank you for your interest in contributing. **agent-sdk-go** is a community Go SDK for AI agents — backed by [Temporal](https://temporal.io) for durable execution, or running in-process with no external dependencies. This document explains how to set up your environment and what we expect from contributors.

## Prerequisites

Expand All @@ -9,14 +9,14 @@ Before contributing, ensure you have:
| Requirement | Version / Notes |
|-------------|-----------------|
| **Go** | **Minimum `go 1.26.0`** (see the `go` line in `go.mod`; use that version or newer). |
| **Temporal server** | Required for examples, CLI, and tests — see [Temporal setup](temporal-setup.md) |
| **Temporal server** | Required only for Temporal runtime examples, CLI, and Temporal-specific tests — see [Temporal setup](temporal-setup.md). Unit tests and in-process runtime examples run without it. |
| **golangci-lint** | Required for `make lint` — install **v2** with Go **≥** the `go` line in `go.mod`: `go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest` |
| **gofmt** | `make lint` runs `gofmt -s` check first; run `make fmt` to apply `gofmt -s -w` project-wide |
| **misspell** | `make spell` or `make lint` — typos via `misspell` (similar to Go Report Card) |

## Temporal setup

Run Temporal locally (or point at Cloud/self-hosted) before examples and tests. Full steps: **[temporal-setup.md](temporal-setup.md)**.
Only needed for the **Temporal runtime** path — examples and tests that use `AGENT_RUNTIME=temporal` or `WithTemporalConfig`. In-process runtime examples and unit tests run without it. Full steps: **[temporal-setup.md](temporal-setup.md)**.

## Development Workflow

Expand Down Expand Up @@ -89,11 +89,13 @@ make test-coverage

### 6. Run examples (optional)

Copy the env sample and set your LLM API key:
Examples load **`examples/.env.defaults`** automatically. Set LLM credentials via environment or an optional override file:

```bash
cp examples/env.sample examples/.env
# Edit examples/.env: set LLM_APIKEY, LLM_MODEL
export LLM_APIKEY=your-key
export LLM_PROVIDER=your-provider
export LLM_MODEL=your-model
# LLM_PROVIDER: openai, anthropic, or gemini. Or append the same keys to examples/.env
```

Then run any example:
Expand All @@ -102,7 +104,7 @@ Then run any example:
go run ./examples/simple_agent "Hello"
```

See [examples/README.md](examples/README.md) for all examples and env vars.
See [examples/README.md](examples/README.md) for all examples, env vars, Task install, and infra commands (`task infra:*`, `task examples:local`).

## Ways to Contribute

Expand Down
91 changes: 64 additions & 27 deletions README.md

Large diffs are not rendered by default.

19 changes: 19 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
version: '3'

dotenv: ['examples/.env']

includes:
examples:
taskfile: taskfiles/examples.yml
flatten: true
reports:
taskfile: taskfiles/reports.yml
flatten: true

tasks:
# ── Default ───────────────────────────────────────────────

default:
desc: Show available tasks
cmds:
- task --list
30 changes: 30 additions & 0 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,40 @@ import (
)

type Config struct {
// Runtime selects the agent execution backend: "local" (default) or "temporal".
// Override with AGENT_RUNTIME env var or set runtime: temporal in config.yaml.
Runtime string `mapstructure:"runtime"`
Temporal *TemporalConfig `mapstructure:"temporal"`
LLM *LLMConfig `mapstructure:"llm"`
Logger *LoggerConfig `mapstructure:"logger"`
MCP *MCPRootConfig `mapstructure:"mcp"`
}

// UseTemporalRuntime reports whether the temporal backend is selected.
func (c *Config) UseTemporalRuntime() bool {
return c != nil && strings.ToLower(strings.TrimSpace(c.Runtime)) == "temporal"
}

// RuntimeOption returns [agent.WithTemporalConfig] when runtime is "temporal", or nil for
// the local runtime. Spread into the options slice:
//
// opts = append(opts, RuntimeOption(cfg)...)
//
// To hard-code a runtime regardless of config, call [agent.WithTemporalConfig] directly.
func RuntimeOption(cfg *Config) []agent.Option {
if !cfg.UseTemporalRuntime() || cfg.Temporal == nil {
return nil
}
return []agent.Option{
agent.WithTemporalConfig(&agent.TemporalConfig{
Host: cfg.Temporal.Host,
Port: cfg.Temporal.Port,
Namespace: cfg.Temporal.Namespace,
TaskQueue: cfg.Temporal.TaskQueue,
}),
}
}

// MCPRootConfig holds optional MCP server definitions for the CLI (see config.sample.yaml).
type MCPRootConfig struct {
Servers []MCPServerYAML `mapstructure:"servers"`
Expand Down Expand Up @@ -202,6 +230,7 @@ func LoadConfig(path string) (*Config, error) {
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))

// Explicit BindEnv so AGENT_* env vars reliably override (AutomaticEnv can be inconsistent with nested keys)
_ = v.BindEnv("runtime", "AGENT_RUNTIME")
_ = v.BindEnv("temporal.host", "AGENT_TEMPORAL_HOST")
_ = v.BindEnv("temporal.port", "AGENT_TEMPORAL_PORT")
_ = v.BindEnv("temporal.namespace", "AGENT_TEMPORAL_NAMESPACE")
Expand All @@ -217,6 +246,7 @@ func LoadConfig(path string) (*Config, error) {
_ = v.BindEnv("logger.tee_stderr", "AGENT_LOGGER_TEE_STDERR")

// Set defaults so env can override even when file is missing or key absent
v.SetDefault("runtime", "local")
v.SetDefault("temporal.host", "localhost")
v.SetDefault("temporal.port", 7233)
v.SetDefault("temporal.namespace", "default")
Expand Down
6 changes: 6 additions & 0 deletions cmd/config.sample.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
# Environment variables with AGENT_ prefix override file values.
# Example: AGENT_LLM_APIKEY=sk-xxx AGENT_LLM_PROVIDER=openai go run ./cmd

# Runtime: local (default) or temporal.
# local — runs entirely in-process; no Temporal server required.
# temporal — durable execution via Temporal; requires a running Temporal server.
# Override per-run: AGENT_RUNTIME=temporal go run ./cmd
runtime: local

# Temporal connection (for durable agent execution)
temporal:
host: localhost # Temporal server host; use AGENT_TEMPORAL_HOST to override
Expand Down
7 changes: 1 addition & 6 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,19 +87,14 @@ func main() {
opts := []agent.Option{
agent.WithName("agentctl"),
agent.WithSystemPrompt("You are a helpful assistant."),
agent.WithTemporalConfig(&agent.TemporalConfig{
Host: cfg.Temporal.Host,
Port: cfg.Temporal.Port,
Namespace: cfg.Temporal.Namespace,
TaskQueue: cfg.Temporal.TaskQueue,
}),
agent.WithLLMClient(llmClient),
agent.WithStream(true),
agent.WithToolRegistry(reg),
agent.WithConversation(conv),
agent.WithConversationSize(20),
agent.WithLogger(lgr),
}
opts = append(opts, RuntimeOption(cfg)...)
if len(mcpServers) > 0 {
opts = append(opts,
agent.WithMCPConfig(mcpServers),
Expand Down
78 changes: 78 additions & 0 deletions examples/.env.defaults
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Committed defaults for examples. Loaded automatically by examples/config.go.
# Optional overrides: create examples/.env (gitignored) for secrets and local changes.
# Load order: .env.defaults → examples/.env → process environment (export / Task / CI).
# Put LLM_APIKEY and EMBEDDING_OPENAI_APIKEY in examples/.env (never commit keys).

# --- Logging ---
# error | warn | info | debug
LOG_LEVEL=error

# --- Agent runtime ---
# local = in-process (default). temporal = requires Temporal server (see temporal-setup.md).
AGENT_RUNTIME=local

# --- Temporal (when AGENT_RUNTIME=temporal) ---
# TEMPORAL_TASKQUEUE is a base prefix; each example appends its suffix (e.g. agent-sdk-go-simple_agent).
TEMPORAL_HOST=localhost
TEMPORAL_PORT=7233
TEMPORAL_NAMESPACE=default
TEMPORAL_TASKQUEUE=agent-sdk-go

# --- LLM (all examples) ---
# Provider: openai | anthropic | gemini. Set LLM_APIKEY in examples/.env.
LLM_PROVIDER=openai
LLM_APIKEY=
LLM_MODEL=gpt-4o
# Used for OpenAI client and as embedding base URL fallback when EMBEDDING_OPENAI_BASEURL is unset.
LLM_BASEURL=https://api.openai.com/v1

# --- MCP (agent_with_mcp_config, agent_with_mcp_client) ---
# Default: stdio + local filesystem sandbox (matches Task infra). Override in .env for streamable_http:
# MCP_TRANSPORT=streamable_http
# MCP_STREAMABLE_HTTP_URL=https://your-mcp-host/mcp
MCP_TRANSPORT=stdio
MCP_STDIO_COMMAND=npx
MCP_STDIO_ARGS=["-y","@modelcontextprotocol/server-filesystem","./mcp-filesystem-sandbox"]

# --- A2A outbound (agent_with_a2a_config, agent_with_a2a_client) ---
# Base URL of remote agent (no path). Default matches agent_with_a2a_server from task infra:a2a:up.
A2A_URL=http://localhost:9999

# --- A2A inbound server (agent_with_a2a_server) ---
A2A_SERVER_HOST=localhost
A2A_SERVER_PORT=9999

# --- Retriever mode (weaviate and pgvector examples) ---
# agentic | prefetch | hybrid
RETRIEVER_MODE=agentic

# --- Weaviate (agent_with_retriever/weaviate) ---
# Run task infra:weaviate:up from examples/ first.
WEAVIATE_HOST=localhost:8080
WEAVIATE_SCHEME=http
WEAVIATE_CLASS=Document
WEAVIATE_RETRIEVER_NAME=weaviate-kb
WEAVIATE_CONTENT_FIELD=content
WEAVIATE_SOURCE_FIELD=source

# --- pgvector (agent_with_retriever/pgvector) ---
# Run task infra:pgvector:up from examples/ first.
PGVECTOR_DSN=postgres://postgres:secret@localhost:5432/vectordb?sslmode=disable
PGVECTOR_TABLE=documents
PGVECTOR_RETRIEVER_NAME=pgvector-kb
PGVECTOR_CONTENT_COL=content
PGVECTOR_SOURCE_COL=source
PGVECTOR_EMBEDDING_COL=embedding
PGVECTOR_MIN_SCORE=0.35

# --- OpenAI-compatible embeddings (pgvector client-side; Weaviate text2vec-openai in Docker) ---
# Set EMBEDDING_OPENAI_APIKEY in examples/.env (separate from chat LLM_APIKEY for Anthropic/Gemini).
EMBEDDING_OPENAI_APIKEY=
EMBEDDING_OPENAI_MODEL=text-embedding-3-small
EMBEDDING_OPENAI_BASEURL=https://api.openai.com/v1

# --- OpenTelemetry (agent_with_observability; LGTM from task infra:lgtm:up) ---
# Host:port without scheme. grpc uses port 4317; http often 4318.
OTEL_EXPORTER_OTLP_ENDPOINT=localhost:4317
OTLP_PROTOCOL=grpc
OTLP_INSECURE=true
Loading
Loading