pithsdk

package module
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Jun 5, 2026 License: MIT Imports: 16 Imported by: 0

README

pith-sdk

A minimal, OpenAI Agents-style SDK for Go app developers — built on pith.

Define an agent, run it, get text back. No gateway wiring, no ModelDescriptor, no EventBus parsing.

Who this is for

App developers who want ClientAgentSession.Run in ~15 lines, with optional custom providers (Anthropic, Groq, Ollama, etc.).

Who this is not for

Library authors and provider implementers who need direct control over the gateway, loop, or protocol layers. Use pith directly.

Defended requirements

pith-sdk optimizes for a small, stable surface:

  • ~15-line run pathClientAgentSession.Run → text back
  • Hidden pith wiring — no gateway, loop, or protocol imports in app code
  • Typed toolsNewTool[T] with struct-based JSON Schema
  • Sessions — multi-turn transcript via Session.Messages() / Reset()
  • Providers — built-in OpenAI-compat plus RegisterProvider for custom backends
  • Hooks — HITL approval, logging, and output redaction via BeforeToolCall / AfterToolCall
  • Tracing IDsSessionID, RunID, CallID for external observability (OpenTelemetry, Datadog, etc.)
  • MCP as tool sourcemcp.Tools() composes with local tools

Escape hatches deferred for v1 review (removed in v0.3.0; use alternatives below):

Removed API Replacement
RunOnce NewSession + Run (two lines)
RawTool NewClientFromGateway + pith/loop directly
NewDynamicTool mcp.Tools() for schema-driven tools
ShouldStopAfterTurn WithMaxTurns or app-side abort
Agent.Name Pass labels via WithContext

Quick start

package main

import (
    "context"
    "fmt"
    "os"

    pithsdk "github.com/chinudotdev/pith-sdk"
)

func main() {
    client, _ := pithsdk.NewClient(pithsdk.ClientConfig{
        APIKey: os.Getenv("OPENAI_API_KEY"),
    })

    agent, _ := pithsdk.NewAgent(pithsdk.AgentConfig{
        Instructions: "You are helpful. Be concise.",
        Model:        "gpt-4o-mini",
    })

    session, _ := client.NewSession(agent)
    result, _ := session.Run(context.Background(), "What is Go?")
    fmt.Println(result.Text)
}
With tools
weather := pithsdk.NewTool("get_weather", "Return weather for a city.",
    func(ctx pithsdk.ToolContext, args struct {
        City string `json:"city"`
    }) (string, error) {
        return fmt.Sprintf("Sunny in %s", args.City), nil
    },
)

agent, _ := pithsdk.NewAgent(pithsdk.AgentConfig{
    Instructions: "You are a helpful weather bot.",
    Model:        "gpt-4o-mini",
    Tools:        []pithsdk.Tool{weather},
})

Pass run-scoped dependencies to tools with WithContext:

session.Run(ctx, "What's the weather?", pithsdk.WithContext(myDeps))
Multi-turn sessions

Reuse a Session to keep conversation history across runs:

session, _ := client.NewSession(agent)
session.Run(ctx, "My name is Alex.")
result, _ := session.Run(ctx, "What is my name?")
fmt.Println(result.Text)
fmt.Println(len(session.Messages())) // full transcript
session.Reset()                      // start fresh

Stream assistant text as it arrives:

session.Run(ctx, "Tell me a joke.", pithsdk.WithStream(func(c pithsdk.TextChunk) {
    fmt.Print(c.Delta)
}))
Custom providers

Register any gateway.ProviderPort (e.g. Anthropic) once on the client:

client, _ := pithsdk.NewClient(pithsdk.ClientConfig{})

client.RegisterProvider(pithsdk.ProviderRegistration{
    Provider:  myAnthropicProvider,
    APIKeyEnv: "ANTHROPIC_API_KEY",
    Models: []pithsdk.ModelPreset{
        {ID: "claude-sonnet-4-20250514", ContextWindow: 200_000, MaxTokens: 8192},
    },
})

agent, _ := pithsdk.NewAgent(pithsdk.AgentConfig{
    Model: "anthropic/claude-sonnet-4-20250514",
    // ... same Agent API as OpenAI
})

Use provider/model strings when not using the default OpenAI provider. See examples/04-anthropic-provider for a full custom provider implementation.

Limit tool-calling loop iterations per run (default 10):

session.Run(ctx, input, pithsdk.WithMaxTurns(5))

Tracing IDs

Every session and run gets a unique ID for observability. Override or read them:

session, _ := client.NewSession(agent, pithsdk.WithSessionID("my-session"))
fmt.Println(session.ID()) // "my-session"

result, _ := session.Run(ctx, "Hi", pithsdk.WithRunID("run-42"))
fmt.Println(result.RunID) // "run-42"

IDs are also available inside tool handlers:

func(ctx pithsdk.ToolContext, args struct{ City string `json:"city"` }) (string, error) {
    fmt.Println(ctx.SessionID, ctx.RunID, ctx.ToolName, ctx.CallID)
    return "Sunny", nil
}

Auto-generated UUIDs are used when IDs are not provided.

Hooks

Add lifecycle callbacks to tool execution:

session.Run(ctx, "Go", pithsdk.WithHooks(pithsdk.Hooks{
    BeforeToolCall: func(ctx pithsdk.BeforeToolContext) (*pithsdk.BeforeToolResult, error) {
        if ctx.ToolName == "dangerous_tool" {
            return &pithsdk.BeforeToolResult{Block: true, Reason: "not allowed"}, nil
        }
        return nil, nil
    },
    AfterToolCall: func(ctx pithsdk.AfterToolContext) (*pithsdk.AfterToolResult, error) {
        log.Printf("tool %s returned: %s", ctx.ToolName, ctx.Result)
        return nil, nil
    },
}))

Returning an error from a hook becomes tool-result text; the run continues.

Hooks apply to all SDK-created tools, including typed tools (NewTool) and MCP-discovered tools from mcp.Tools().

MCP tools

Discover tools from MCP (Model Context Protocol) servers and compose them with local tools:

import "github.com/chinudotdev/pith-sdk/mcp"

mcpTools, close, _ := mcp.Tools(ctx, mcp.Config{
    Command: "path/to/mcp-server",
    Args:    []string{"--flag"},
})
defer close()

allTools := append(localTools, mcpTools...)

API overview

Type Role
Client Gateway, credentials, defaults; creates sessions
Agent Specialist definition: instructions, model, tools
Session Runs the agent; holds multi-turn transcript
NewTool Typed tool with struct-based JSON Schema
mcp.Tools Discover tools from MCP servers
RegisterProvider Custom ProviderPort + model catalog
NewClientFromGateway Escape hatch for tests and custom setups

Installation

go get github.com/chinudotdev/pith-sdk@latest

Requires Go 1.24+.

Examples

Example Description
01-hello Minimal agent run
02-tools Agent with custom tools
03-multi-turn Multi-turn conversation with streaming
04-anthropic-provider Custom Anthropic provider via RegisterProvider
05-mcp Compose local and MCP tools

Run from the repo root:

OPENAI_API_KEY="sk-..." go run ./examples/01-hello/main.go
OPENAI_API_KEY="sk-..." go run ./examples/02-tools/main.go
OPENAI_API_KEY="sk-..." go run ./examples/03-multi-turn/main.go
ANTHROPIC_API_KEY="sk-ant-..." go run ./examples/04-anthropic-provider/
OPENAI_API_KEY="sk-..." go run ./examples/05-mcp/main.go

Releases

v0.1.0 — Core run path, tools, sessions, providers. See CHANGELOG.md for details.

v0.2.0 — Hooks, tracing IDs, pithsdk/mcp.

v0.3.0 — API trim before v1 lock. See CHANGELOG.md for migration notes.

Future work is tracked in plan.md.

License

MIT — see LICENSE.

Documentation

Overview

Package pithsdk is a minimal, OpenAI Agents-style SDK for Go app developers. It wraps github.com/chinudotdev/pith primitives so you can define an agent, run it, and get text back without wiring gateways, ModelDescriptors, or EventBus.

Quick start:

client, _ := pithsdk.NewClient(pithsdk.ClientConfig{APIKey: os.Getenv("OPENAI_API_KEY")})
agent, _ := pithsdk.NewAgent(pithsdk.AgentConfig{
    Instructions: "You are helpful.",
    Model:        "gpt-4o-mini",
})
session, _ := client.NewSession(agent)
result, _ := session.Run(ctx, "Hello!")

Import with the recommended alias:

import pithsdk "github.com/chinudotdev/pith-sdk"

See the examples/ directory and https://github.com/chinudotdev/pith-sdk for more.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type AfterToolContext added in v0.2.0

type AfterToolContext struct {
	// RunID is the unique identifier for the current run.
	RunID string
	// SessionID is the unique identifier for the current session.
	SessionID string
	// ToolName is the name of the tool that was called.
	ToolName string
	// CallID is the provider-assigned tool call identifier.
	CallID string
	// Args is the parsed tool arguments.
	Args map[string]any
	// Result is the text output from the tool.
	Result string
	// Error is non-nil when the tool returned an error.
	Error error
}

AfterToolContext is passed to the AfterToolCall hook.

type AfterToolResult added in v0.2.0

type AfterToolResult struct {
	// OverrideResult replaces the tool's output text when non-empty.
	OverrideResult string
}

AfterToolResult controls tool result override from the AfterToolCall hook.

type Agent

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

Agent is a specialist definition: instructions, model, tools, and settings. Agents are immutable configuration; create a Session to run them.

func NewAgent

func NewAgent(cfg AgentConfig) (*Agent, error)

NewAgent creates an agent definition.

type AgentConfig

type AgentConfig struct {
	// Instructions is the system prompt sent to the model.
	Instructions string
	// Model is a bare ID (e.g. "gpt-4o-mini") or provider/model (e.g. "anthropic/claude-...").
	Model string
	// Tools are custom tools available during runs.
	Tools []Tool
	// Settings overrides client-level generation defaults for this agent.
	Settings *ModelSettings
}

AgentConfig configures a new Agent.

type BeforeToolContext added in v0.2.0

type BeforeToolContext struct {
	// RunID is the unique identifier for the current run.
	RunID string
	// SessionID is the unique identifier for the current session.
	SessionID string
	// ToolName is the name of the tool being called.
	ToolName string
	// CallID is the provider-assigned tool call identifier.
	CallID string
	// Args is the parsed tool arguments.
	Args map[string]any
}

BeforeToolContext is passed to the BeforeToolCall hook.

type BeforeToolResult added in v0.2.0

type BeforeToolResult struct {
	// Block prevents the tool from executing when true.
	Block bool
	// Reason is the message returned when blocking a tool call.
	Reason string
}

BeforeToolResult controls tool execution from the BeforeToolCall hook.

type Client

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

Client holds gateway configuration, credentials, and defaults for running agents.

func NewClient

func NewClient(cfg ClientConfig) (*Client, error)

NewClient creates a client with a built-in OpenAI-compatible provider. An OpenAI API key is optional at construction time; it is required when using the default OpenAI provider at run time.

func NewClientFromGateway

func NewClientFromGateway(gw *gateway.LLMGateway) *Client

NewClientFromGateway wraps a pre-wired gateway (e.g. for testing or custom setups).

func (*Client) NewSession

func (c *Client) NewSession(agent *Agent, opts ...SessionOption) (*Session, error)

NewSession creates a session for the given agent definition.

func (*Client) RegisterProvider

func (c *Client) RegisterProvider(reg ProviderRegistration) error

RegisterProvider registers a custom provider, its models, and credentials.

type ClientConfig

type ClientConfig struct {
	// APIKey is the OpenAI-compatible API key. When empty, OPENAI_API_KEY is used at run time.
	APIKey string
	// DefaultProvider is the provider ID for bare model strings (default "openai").
	DefaultProvider string
	// DefaultModel is used when an Agent has no Model set (default "gpt-4o-mini").
	DefaultModel string
	// DefaultSettings applies generation defaults to all agents unless overridden.
	DefaultSettings *ModelSettings
}

ClientConfig configures a new Client.

type Hooks added in v0.2.0

type Hooks struct {
	// BeforeToolCall is called before each tool execution.
	// Return a result with Block=true to prevent execution.
	// Returning an error becomes tool-result text; the run continues.
	BeforeToolCall func(ctx BeforeToolContext) (*BeforeToolResult, error)

	// AfterToolCall is called after each tool execution.
	// Use OverrideResult to replace the tool's output.
	// Returning an error becomes tool-result text; the run continues.
	AfterToolCall func(ctx AfterToolContext) (*AfterToolResult, error)
}

Hooks are lifecycle callbacks for a single Session.Run call. All hooks are optional; nil hooks are skipped.

Hook errors: returning an error from BeforeToolCall or AfterToolCall is converted to tool-result text; the agent run continues with that text as the tool output.

type MessageSummary

type MessageSummary = summary.MessageSummary

MessageSummary is a simplified view of a transcript message.

type ModelPreset

type ModelPreset struct {
	// ID is the model identifier used in provider/model strings.
	ID string
	// Name is an optional display name; defaults to ID.
	Name string
	// BaseURL is an optional API base URL; the provider may use its own default.
	BaseURL string
	// ContextWindow is the model context size; zero uses the SDK default.
	ContextWindow int
	// MaxTokens is the default max output tokens; zero uses the SDK default.
	MaxTokens int
}

ModelPreset describes a model to register in the gateway catalog.

type ModelSettings

type ModelSettings struct {
	// Temperature controls randomness. Nil leaves the provider default.
	Temperature *float64
	// MaxTokens caps output tokens. Nil leaves the provider default.
	MaxTokens *int
}

ModelSettings configures generation parameters for an agent run.

type ProviderRegistration

type ProviderRegistration struct {
	// Provider implements gateway.ProviderPort for the custom API.
	Provider gateway.ProviderPort

	// Credentials — set exactly one of the following:
	// APIKey is a static API key string.
	APIKey string
	// APIKeyEnv names an environment variable holding the API key (e.g. "ANTHROPIC_API_KEY").
	APIKeyEnv string
	// Credential is a custom resolver returning the API key for the provider ID.
	Credential func(providerID string) (string, error)

	// Models lists model presets to register in the gateway catalog.
	Models []ModelPreset
}

ProviderRegistration registers a custom LLM provider and its models on the client.

type RunOption

type RunOption func(*RunOptions)

RunOption configures a Session.Run call.

func WithContext

func WithContext(local any) RunOption

WithContext sets run-scoped local dependencies available as ToolContext.Local.

func WithHooks added in v0.2.0

func WithHooks(h Hooks) RunOption

WithHooks registers lifecycle callbacks for a single run.

func WithInstructions

func WithInstructions(instructions string) RunOption

WithInstructions overrides the agent's system prompt for this run only.

func WithMaxTurns

func WithMaxTurns(n int) RunOption

WithMaxTurns limits how many agent loop turns a single Run may take. Zero uses the SDK default of 10.

func WithRunID added in v0.2.0

func WithRunID(id string) RunOption

WithRunID sets an explicit run identifier. When omitted, a UUID is generated per Run.

func WithStream

func WithStream(fn func(TextChunk)) RunOption

WithStream registers a callback for streaming assistant text deltas during the run. When nil (default), Run blocks until the full response is ready.

type RunOptions

type RunOptions struct {
	// RunID overrides the auto-generated run identifier. Empty triggers auto-generation.
	RunID string
	// Context holds run-scoped local dependencies exposed as ToolContext.Local.
	Context any
	// Instructions overrides the agent system prompt for this run only.
	Instructions string
	// Stream receives assistant text deltas during the run; nil blocks until complete.
	Stream func(chunk TextChunk)
	// MaxTurns limits tool-calling loop iterations for this run. Zero uses defaultMaxTurns (10).
	MaxTurns int
	// Hooks are lifecycle callbacks for this run.
	Hooks *Hooks
}

RunOptions configures a single run. Apply via RunOption functions.

type RunResult

type RunResult struct {
	// RunID is the unique identifier for this run, auto-generated unless overridden via WithRunID.
	RunID string
	// Text is the final assistant response text.
	Text string
	// Messages is the session transcript after this run, as simplified summaries.
	Messages []MessageSummary
	// Usage reports token usage from the last assistant response, if available.
	Usage *UsageSummary
}

RunResult is the outcome of a single Session.Run call.

type Session

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

Session runs an agent and holds its transcript across multiple Run calls.

func (*Session) ID added in v0.2.0

func (s *Session) ID() string

ID returns the session identifier, unique per conversation.

func (*Session) Messages

func (s *Session) Messages() []MessageSummary

Messages returns the current session transcript.

func (*Session) Reset

func (s *Session) Reset()

Reset clears the session transcript and queued messages.

func (*Session) Run

func (s *Session) Run(ctx context.Context, input string, opts ...RunOption) (*RunResult, error)

Run sends input to the agent and returns the final text result.

Concurrent Run calls on the same Session are not supported; serialize calls or use separate sessions.

type SessionOption added in v0.2.0

type SessionOption func(*sessionOpts)

SessionOption configures session creation via Client.NewSession.

func WithSessionID added in v0.2.0

func WithSessionID(id string) SessionOption

WithSessionID sets an explicit session identifier. When omitted, a UUID is generated.

type TextChunk

type TextChunk struct {
	// Delta is the incremental text from the latest EventTextDelta.
	Delta string
	// Text is the accumulated assistant text so far.
	Text string
}

TextChunk is a streaming text delta from the assistant response.

type Tool

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

Tool is an opaque tool definition. Create with NewTool.

func NewTool

func NewTool[T any](name, description string, fn func(ToolContext, T) (string, error)) Tool

NewTool creates a typed tool from a struct argument type T and handler function. T must be a struct; JSON Schema is generated from json and optional desc struct tags.

func ToolFromDynamicSchema added in v0.3.0

func ToolFromDynamicSchema(name, description string, schema map[string]any,
	fn func(context.Context, map[string]any) (string, error)) Tool

ToolFromDynamicSchema creates a schema-driven tool with untyped map arguments. It is used by the mcp adapter; prefer mcp.Tools() for MCP-discovered tools.

type ToolContext

type ToolContext struct {
	// Run is the context for the current Session.Run call.
	Run context.Context
	// RunID is the unique identifier for the current run.
	RunID string
	// SessionID is the unique identifier for the current session.
	SessionID string
	// Local holds run-scoped dependencies from WithContext; not sent to the model.
	Local any
	// ToolName is the name of the tool being invoked.
	ToolName string
	// CallID is the provider-assigned tool call identifier.
	CallID string
}

ToolContext is passed to tool handlers during execution.

type UsageSummary

type UsageSummary struct {
	// Input is the number of input/prompt tokens.
	Input int
	// Output is the number of output/completion tokens.
	Output int
	// Total is the combined token count when reported by the provider.
	Total int
}

UsageSummary reports token usage from the last assistant response.

Directories

Path Synopsis
examples
01-hello command
Example 01: Hello — minimal agent run with the pith-sdk.
Example 01: Hello — minimal agent run with the pith-sdk.
02-tools command
Example 02: Tools — agent with a custom tool the model can call.
Example 02: Tools — agent with a custom tool the model can call.
03-multi-turn command
Example 03: Multi-turn — conversation across multiple Session.Run calls.
Example 03: Multi-turn — conversation across multiple Session.Run calls.
04-anthropic-provider command
Example 04: Custom Anthropic provider via pith-sdk RegisterProvider.
Example 04: Custom Anthropic provider via pith-sdk RegisterProvider.
05-mcp command
05-mcp demonstrates composing local and MCP tools and running an agent session.
05-mcp demonstrates composing local and MCP tools and running an agent session.
05-mcp/echo-server command
Minimal stdio MCP server with an echo tool.
Minimal stdio MCP server with an echo tool.
internal
Package mcp discovers tools from MCP (Model Context Protocol) servers and converts them to pith-sdk Tools for use in agent sessions.
Package mcp discovers tools from MCP (Model Context Protocol) servers and converts them to pith-sdk Tools for use in agent sessions.

Jump to

Keyboard shortcuts

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