Documentation
¶
Index ¶
- type Config
- type GitStateChangeFunc
- type Loop
- type MessageRecordFunc
- type PredictableService
- func (s *PredictableService) ClearRequests()
- func (s *PredictableService) Do(ctx context.Context, req *llm.Request) (*llm.Response, error)
- func (s *PredictableService) GetLastRequest() *llm.Request
- func (s *PredictableService) GetRecentRequests() []*llm.Request
- func (s *PredictableService) MaxImageDimension() int
- func (s *PredictableService) TokenContextWindow() int
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
LLM llm.Service
History []llm.Message
Tools []*llm.Tool
RecordMessage MessageRecordFunc
Logger *slog.Logger
System []llm.SystemContent
WorkingDir string // working directory for tools
OnGitStateChange GitStateChangeFunc
// ActiveToolsFn returns the tools to send to the LLM for the current turn.
// If nil, all Tools are sent. This is used for deferred tool loading where
// only a subset of tools are active initially.
ActiveToolsFn func() []*llm.Tool
// GetWorkingDir returns the current working directory for tools.
// If set, this is called at end of turn to check for git state changes.
// If nil, Config.WorkingDir is used as a static value.
GetWorkingDir func() string
}
Config contains all configuration needed to create a Loop.
type GitStateChangeFunc ¶
GitStateChangeFunc is called when the git state changes at the end of a turn. This is used to record user-visible notifications about git changes.
type Loop ¶
type Loop struct {
// contains filtered or unexported fields
}
Loop manages a conversation turn with an LLM including tool execution and message recording. Notably, when the turn ends, the "Loop" is over. TODO: maybe rename to Turn?
Example ¶
package main
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/tgruben-circuit/percy/llm"
"github.com/tgruben-circuit/percy/loop"
)
func main() {
// Create a simple tool
testTool := &llm.Tool{
Name: "greet",
Description: "Greets the user with a friendly message",
InputSchema: llm.MustSchema(`{"type": "object", "properties": {"name": {"type": "string"}}}`),
Run: func(ctx context.Context, input json.RawMessage) llm.ToolOut {
var req struct {
Name string `json:"name"`
}
if err := json.Unmarshal(input, &req); err != nil {
return llm.ErrorToolOut(err)
}
return llm.ToolOut{
LLMContent: llm.TextContent(fmt.Sprintf("Hello, %s! Nice to meet you.", req.Name)),
}
},
}
// Message recording function (in real usage, this would save to database)
recordMessage := func(ctx context.Context, message llm.Message, usage llm.Usage) error {
roleStr := "user"
if message.Role == llm.MessageRoleAssistant {
roleStr = "assistant"
}
fmt.Printf("Recorded %s message with %d content items\n", roleStr, len(message.Content))
return nil
}
// Create a loop with initial history
initialHistory := []llm.Message{
{
Role: llm.MessageRoleUser,
Content: []llm.Content{
{Type: llm.ContentTypeText, Text: "Hello, I'm Alice"},
},
},
}
// Set up a predictable service for this example
service := loop.NewPredictableService()
myLoop := loop.NewLoop(loop.Config{
LLM: service,
History: initialHistory,
Tools: []*llm.Tool{testTool},
RecordMessage: recordMessage,
})
// Queue a user message that triggers a simple response
myLoop.QueueUserMessage(llm.Message{
Role: llm.MessageRoleUser,
Content: []llm.Content{{Type: llm.ContentTypeText, Text: "hello"}},
})
// Run the loop for a short time
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()
_ = myLoop.Go(ctx)
// Check usage
usage := myLoop.GetUsage()
fmt.Printf("Total usage: %s\n", usage.String())
}
Output: Recorded assistant message with 1 content items Total usage: in: 31, out: 3
func (*Loop) GetHistory ¶
GetHistory returns a copy of the current conversation history
func (*Loop) ProcessOneTurn ¶
ProcessOneTurn processes queued messages through one complete turn (user message + assistant response) It stops after the assistant responds, regardless of whether tools were called
func (*Loop) QueueUserMessage ¶
QueueUserMessage adds a user message to the queue to be processed
type MessageRecordFunc ¶
MessageRecordFunc is called to record new messages to persistent storage.
type PredictableService ¶
type PredictableService struct {
// AlwaysMaxTokens makes every response return StopReasonMaxTokens for testing truncation retries.
AlwaysMaxTokens bool
// contains filtered or unexported fields
}
PredictableService is an LLM service that returns predictable responses for testing.
To add new test patterns, update the Do() method directly by adding cases to the switch statement or new prefix checks. Do not extend or wrap this service - modify it in place. Available patterns include:
- "echo: <text>" - echoes the text back
- "bash: <command>" - triggers bash tool with command
- "think: <thoughts>" - returns response with extended thinking content
- "subagent: <slug> <prompt>" - triggers subagent tool
- "change_dir: <path>" - triggers change_dir tool
- "delay: <seconds>" - delays response by specified seconds
- See Do() method for complete list of supported patterns
func NewPredictableService ¶
func NewPredictableService() *PredictableService
NewPredictableService creates a new predictable LLM service
func (*PredictableService) ClearRequests ¶
func (s *PredictableService) ClearRequests()
ClearRequests clears the request history
func (*PredictableService) Do ¶
Do processes a request and returns a predictable response based on the input text
func (*PredictableService) GetLastRequest ¶
func (s *PredictableService) GetLastRequest() *llm.Request
GetLastRequest returns the most recent request, or nil if none
func (*PredictableService) GetRecentRequests ¶
func (s *PredictableService) GetRecentRequests() []*llm.Request
GetRecentRequests returns the recent requests made to this service
func (*PredictableService) MaxImageDimension ¶
func (s *PredictableService) MaxImageDimension() int
MaxImageDimension returns the maximum allowed image dimension.
func (*PredictableService) TokenContextWindow ¶
func (s *PredictableService) TokenContextWindow() int
TokenContextWindow returns the maximum token context window size