Documentation
¶
Overview ¶
Package ai implements an autonomous LLM-driven browser agent that drives the existing ghostchrome agent ops. It is provider-agnostic (Anthropic, OpenAI) and goes through a Runner adapter so cmd/ai.go can inject the real session without exposing internal types.
Index ¶
Constants ¶
const SystemPrompt = `` /* 769-byte string literal not displayed */
SystemPrompt is the role-priming message prepended to every conversation. Keep it tight — verbose system prompts hurt latency and cost.
Variables ¶
var ErrFakeExhausted = errors.New("fake provider: scripted steps exhausted")
ErrFakeExhausted is returned when the script is consumed.
Functions ¶
This section is empty.
Types ¶
type AnthropicProvider ¶
type AnthropicProvider struct {
APIKey string
Model string
MaxTokens int
Temperature float64
Endpoint string // override for self-hosted gateways
Timeout time.Duration
}
AnthropicProvider talks directly to the Messages API. Stays HTTP-native to avoid a heavyweight SDK dependency and the version drift that comes with it.
func (*AnthropicProvider) Name ¶
func (p *AnthropicProvider) Name() string
type FakeProvider ¶
type FakeProvider struct {
Steps []Step
// CalledWith is appended on every Step invocation; useful for asserts.
CalledWith []FakeCall
// contains filtered or unexported fields
}
FakeProvider replays a scripted sequence of Steps without ever calling a real LLM. Used by loop_test.go and as a way for downstream callers to integration-test pipelines without burning tokens.
One Step is consumed per Provider.Step call. When the queue is empty, Step returns ErrFakeExhausted.
func (*FakeProvider) Name ¶
func (p *FakeProvider) Name() string
type FakeRunner ¶
type FakeRunner struct {
URL string
Results map[string]any // op → result
OnOp func(op string, args json.RawMessage) // optional spy
}
FakeRunner is a deterministic Runner that returns a canned result for each op. Use to drive Run end-to-end without a real browser.
func (*FakeRunner) CurrentURL ¶
func (r *FakeRunner) CurrentURL() string
func (*FakeRunner) RunOp ¶
func (r *FakeRunner) RunOp(op string, args json.RawMessage) (any, *engine.Observation, error)
type LoopOpts ¶
type LoopOpts struct {
Goal string
MaxSteps int // hard cap (default 15)
Verbose bool // emit one stderr trace line per tool call
OnStep func(StepRecord)
}
LoopOpts configures one Run call.
type Message ¶
type Message struct {
Role string `json:"role"` // "system" | "user" | "assistant" | "tool"
Content string `json:"content,omitempty"`
Tool *ToolMessage `json:"-"` // present when Role=="tool"
Calls []ToolCallReq `json:"-"` // present on assistant messages with tool calls
}
Message is the cross-provider chat message shape used by the loop.
type OpenAIProvider ¶
type OpenAIProvider struct {
APIKey string
Model string
Temperature float64
Endpoint string
Timeout time.Duration
}
OpenAIProvider hits the Chat Completions endpoint with function-style tool calls. Same HTTP-native rationale as AnthropicProvider.
func (*OpenAIProvider) Name ¶
func (p *OpenAIProvider) Name() string
type Provider ¶
type Provider interface {
Name() string
Step(ctx context.Context, history []Message, tools []ToolSpec) (Step, error)
}
Provider is the LLM backend abstraction. Implementations are stateless; the loop accumulates the conversation and replays it on every Step call.
type Result ¶
type Result struct {
Success bool `json:"success"`
Goal string `json:"goal"`
Provider string `json:"provider"`
Model string `json:"model"`
Steps []StepRecord `json:"steps"`
StepsTaken int `json:"steps_taken"`
FinalURL string `json:"final_url,omitempty"`
FinalAnswer string `json:"final_answer,omitempty"`
Error string `json:"error,omitempty"`
}
Result is the JSON envelope emitted by `ghostchrome ai`.
type Runner ¶
type Runner interface {
RunOp(op string, args json.RawMessage) (result any, obs *engine.Observation, err error)
CurrentURL() string
}
Runner is the adapter the loop uses to actually execute tools against the browser. cmd/ai.go provides an implementation that wraps an agentSession.
type Step ¶
type Step struct {
Text string // assistant prose (kept for trace)
ToolCalls []ToolCallReq // 0+ tool calls to execute (often 1)
StopReason string // "end_turn" | "tool_use" | "max_tokens" | …
FinalAnswer string // populated when the model emits the `done` tool
}
Step is the LLM's decision for one iteration.
type StepRecord ¶
type StepRecord struct {
Index int `json:"index"`
Op string `json:"op,omitempty"`
Args json.RawMessage `json:"args,omitempty"`
OK bool `json:"ok"`
Error string `json:"error,omitempty"`
Observation *engine.Observation `json:"observation,omitempty"`
Text string `json:"text,omitempty"` // assistant prose for this step
}
StepRecord is appended to Result.Steps for each iteration. Compact by design — never embed raw DOM/HTML payloads here.
type ToolCallReq ¶
type ToolCallReq struct {
ID string
Name string
Input json.RawMessage
}
ToolCallReq is a tool call requested by the assistant.
type ToolMessage ¶
type ToolMessage struct {
CallID string
Name string
Result string // serialised JSON result (or error)
IsErr bool
}
ToolMessage carries the result of a tool execution back to the LLM.
type ToolSpec ¶
type ToolSpec struct {
Name string
Description string
InputSchema map[string]any // JSON Schema object
}
ToolSpec is the JSON-Schema-shaped tool definition shared with the LLM.
func ToolSpecs ¶
func ToolSpecs() []ToolSpec
ToolSpecs returns the tool catalog exposed to the LLM. The names match the JSONL ops dispatched by cmd/agent.go's agentSession.dispatch — this is intentional: the LLM can read CLAUDE.md / agent docs and pick the same op names a human operator would.
Schemas are deliberately minimal — overspec'd schemas hurt model recall (extra fields the model has to reason about) without buying us safety beyond what the agent ops already enforce at runtime.