package agent import ( "context" "strings" "time" "github.com/mudler/LocalAGI/core/types" "github.com/sashabaranov/go-openai" ) type Option func(*options) error type llmOptions struct { APIURL string APIKey string Model string MultimodalModel string } type options struct { LLMAPI llmOptions character Character randomIdentityGuidance string randomIdentity bool userActions types.Actions enableHUD, standaloneJob, showCharacter, enableKB, enableSummaryMemory, enableLongTermMemory bool stripThinkingTags bool canStopItself bool initiateConversations bool loopDetectionSteps int forceReasoning bool canPlan bool characterfile string statefile string context context.Context permanentGoal string timeout string periodicRuns time.Duration kbResults int ragdb RAGDB prompts []DynamicPrompt systemPrompt string // callbacks reasoningCallback func(types.ActionCurrentState) bool resultCallback func(types.ActionState) conversationsPath string mcpServers []MCPServer mcpStdioServers []MCPSTDIOServer mcpBoxURL string mcpPrepareScript string newConversationsSubscribers []func(openai.ChatCompletionMessage) observer Observer parallelJobs int } func (o *options) SeparatedMultimodalModel() bool { return o.LLMAPI.MultimodalModel != "" && o.LLMAPI.Model != o.LLMAPI.MultimodalModel } func defaultOptions() *options { return &options{ parallelJobs: 1, periodicRuns: 15 * time.Minute, loopDetectionSteps: 10, LLMAPI: llmOptions{ APIURL: "http://localhost:8080", Model: "gpt-4", }, character: Character{ Name: "", Age: "", Occupation: "", Hobbies: []string{}, MusicTaste: []string{}, }, } } func newOptions(opts ...Option) (*options, error) { options := defaultOptions() for _, o := range opts { if err := o(options); err != nil { return nil, err } } return options, nil } var EnableHUD = func(o *options) error { o.enableHUD = true return nil } var EnableForceReasoning = func(o *options) error { o.forceReasoning = true return nil } var EnableKnowledgeBase = func(o *options) error { o.enableKB = true o.kbResults = 5 return nil } var CanStopItself = func(o *options) error { o.canStopItself = true return nil } func WithTimeout(timeout string) Option { return func(o *options) error { o.timeout = timeout return nil } } func WithLoopDetectionSteps(steps int) Option { return func(o *options) error { o.loopDetectionSteps = steps return nil } } func WithConversationsPath(path string) Option { return func(o *options) error { o.conversationsPath = path return nil } } func EnableKnowledgeBaseWithResults(results int) Option { return func(o *options) error { o.enableKB = true o.kbResults = results return nil } } func WithParallelJobs(jobs int) Option { return func(o *options) error { o.parallelJobs = jobs return nil } } func WithNewConversationSubscriber(sub func(openai.ChatCompletionMessage)) Option { return func(o *options) error { o.newConversationsSubscribers = append(o.newConversationsSubscribers, sub) return nil } } var EnableInitiateConversations = func(o *options) error { o.initiateConversations = true return nil } var EnablePlanning = func(o *options) error { o.canPlan = true return nil } // EnableStandaloneJob is an option to enable the agent // to run jobs in the background automatically var EnableStandaloneJob = func(o *options) error { o.standaloneJob = true return nil } var EnablePersonality = func(o *options) error { o.showCharacter = true return nil } var EnableSummaryMemory = func(o *options) error { o.enableSummaryMemory = true return nil } var EnableLongTermMemory = func(o *options) error { o.enableLongTermMemory = true return nil } func WithRAGDB(db RAGDB) Option { return func(o *options) error { o.ragdb = db return nil } } func WithSystemPrompt(prompt string) Option { return func(o *options) error { o.systemPrompt = prompt return nil } } func WithMCPServers(servers ...MCPServer) Option { return func(o *options) error { o.mcpServers = servers return nil } } func WithMCPSTDIOServers(servers ...MCPSTDIOServer) Option { return func(o *options) error { o.mcpStdioServers = servers return nil } } func WithMCPBoxURL(url string) Option { return func(o *options) error { o.mcpBoxURL = url return nil } } func WithMCPPrepareScript(script string) Option { return func(o *options) error { o.mcpPrepareScript = script return nil } } func WithLLMAPIURL(url string) Option { return func(o *options) error { o.LLMAPI.APIURL = url return nil } } func WithStateFile(path string) Option { return func(o *options) error { o.statefile = path return nil } } func WithCharacterFile(path string) Option { return func(o *options) error { o.characterfile = path return nil } } // WithPrompts adds additional block prompts to the agent // to be rendered internally in the conversation // when processing the conversation to the LLM func WithPrompts(prompts ...DynamicPrompt) Option { return func(o *options) error { o.prompts = prompts return nil } } // WithDynamicPrompts is a helper function to create dynamic prompts // Dynamic prompts contains golang code which is executed dynamically // // to render a prompt to the LLM // func WithDynamicPrompts(prompts ...map[string]string) Option { // return func(o *options) error { // for _, p := range prompts { // prompt, err := NewDynamicPrompt(p, "") // if err != nil { // return err // } // o.prompts = append(o.prompts, prompt) // } // return nil // } // } func WithLLMAPIKey(key string) Option { return func(o *options) error { o.LLMAPI.APIKey = key return nil } } func WithMultimodalModel(model string) Option { return func(o *options) error { o.LLMAPI.MultimodalModel = model return nil } } func WithPermanentGoal(goal string) Option { return func(o *options) error { o.permanentGoal = goal return nil } } func WithPeriodicRuns(duration string) Option { return func(o *options) error { t, err := time.ParseDuration(duration) if err != nil { o.periodicRuns, _ = time.ParseDuration("10m") } o.periodicRuns = t return nil } } func WithContext(ctx context.Context) Option { return func(o *options) error { o.context = ctx return nil } } func WithAgentReasoningCallback(cb func(types.ActionCurrentState) bool) Option { return func(o *options) error { o.reasoningCallback = cb return nil } } func WithAgentResultCallback(cb func(types.ActionState)) Option { return func(o *options) error { o.resultCallback = cb return nil } } func WithModel(model string) Option { return func(o *options) error { o.LLMAPI.Model = model return nil } } func WithCharacter(c Character) Option { return func(o *options) error { o.character = c return nil } } func FromFile(path string) Option { return func(o *options) error { c, err := Load(path) if err != nil { return err } o.character = *c return nil } } func WithRandomIdentity(guidance ...string) Option { return func(o *options) error { o.randomIdentityGuidance = strings.Join(guidance, "") o.randomIdentity = true o.showCharacter = true return nil } } func WithActions(actions ...types.Action) Option { return func(o *options) error { o.userActions = actions return nil } } func WithObserver(observer Observer) Option { return func(o *options) error { o.observer = observer return nil } } var EnableStripThinkingTags = func(o *options) error { o.stripThinkingTags = true return nil }