diff --git a/core/action/custom.go b/core/action/custom.go index bd886e2..3397bff 100644 --- a/core/action/custom.go +++ b/core/action/custom.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" + "github.com/mudler/LocalAgent/core/types" "github.com/mudler/LocalAgent/pkg/xlog" "github.com/sashabaranov/go-openai/jsonschema" "github.com/traefik/yaegi/interp" @@ -79,24 +80,24 @@ func (a *CustomAction) Plannable() bool { return true } -func (a *CustomAction) Run(ctx context.Context, params ActionParams) (ActionResult, error) { +func (a *CustomAction) Run(ctx context.Context, params types.ActionParams) (types.ActionResult, error) { v, err := a.i.Eval(fmt.Sprintf("%s.Run", a.config["name"])) if err != nil { - return ActionResult{}, err + return types.ActionResult{}, err } run := v.Interface().(func(map[string]interface{}) (string, map[string]interface{}, error)) res, meta, err := run(params) - return ActionResult{Result: res, Metadata: meta}, err + return types.ActionResult{Result: res, Metadata: meta}, err } -func (a *CustomAction) Definition() ActionDefinition { +func (a *CustomAction) Definition() types.ActionDefinition { v, err := a.i.Eval(fmt.Sprintf("%s.Definition", a.config["name"])) if err != nil { xlog.Error("Error getting custom action definition", "error", err) - return ActionDefinition{} + return types.ActionDefinition{} } properties := v.Interface().(func() map[string][]string) @@ -104,7 +105,7 @@ func (a *CustomAction) Definition() ActionDefinition { v, err = a.i.Eval(fmt.Sprintf("%s.RequiredFields", a.config["name"])) if err != nil { xlog.Error("Error getting custom action definition", "error", err) - return ActionDefinition{} + return types.ActionDefinition{} } requiredFields := v.Interface().(func() []string) @@ -121,8 +122,8 @@ func (a *CustomAction) Definition() ActionDefinition { Description: v[1], } } - return ActionDefinition{ - Name: ActionDefinitionName(a.config["name"]), + return types.ActionDefinition{ + Name: types.ActionDefinitionName(a.config["name"]), Description: a.config["description"], Properties: prop, Required: requiredFields(), diff --git a/core/action/custom_test.go b/core/action/custom_test.go index 4d780a0..1d0afc5 100644 --- a/core/action/custom_test.go +++ b/core/action/custom_test.go @@ -4,6 +4,7 @@ import ( "context" . "github.com/mudler/LocalAgent/core/action" + "github.com/mudler/LocalAgent/core/types" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -63,7 +64,7 @@ return []string{"foo"} Expect(err).ToNot(HaveOccurred()) definition := customAction.Definition() - Expect(definition).To(Equal(ActionDefinition{ + Expect(definition).To(Equal(types.ActionDefinition{ Properties: map[string]jsonschema.Definition{ "foo": { Type: jsonschema.String, @@ -75,7 +76,7 @@ return []string{"foo"} Description: "A test action", })) - runResult, err := customAction.Run(context.Background(), ActionParams{ + runResult, err := customAction.Run(context.Background(), types.ActionParams{ "Foo": "bar", }) Expect(err).ToNot(HaveOccurred()) diff --git a/core/action/intention.go b/core/action/intention.go index 082efb1..77ef0fa 100644 --- a/core/action/intention.go +++ b/core/action/intention.go @@ -3,6 +3,7 @@ package action import ( "context" + "github.com/mudler/LocalAgent/core/types" "github.com/sashabaranov/go-openai/jsonschema" ) @@ -21,16 +22,16 @@ type IntentResponse struct { Reasoning string `json:"reasoning"` } -func (a *IntentAction) Run(context.Context, ActionParams) (ActionResult, error) { - return ActionResult{}, nil +func (a *IntentAction) Run(context.Context, types.ActionParams) (types.ActionResult, error) { + return types.ActionResult{}, nil } func (a *IntentAction) Plannable() bool { return false } -func (a *IntentAction) Definition() ActionDefinition { - return ActionDefinition{ +func (a *IntentAction) Definition() types.ActionDefinition { + return types.ActionDefinition{ Name: "pick_tool", Description: "Pick a tool", Properties: map[string]jsonschema.Definition{ diff --git a/core/action/newconversation.go b/core/action/newconversation.go index d8c7dc1..50e831a 100644 --- a/core/action/newconversation.go +++ b/core/action/newconversation.go @@ -3,6 +3,7 @@ package action import ( "context" + "github.com/mudler/LocalAgent/core/types" "github.com/sashabaranov/go-openai/jsonschema" ) @@ -18,16 +19,16 @@ type ConversationActionResponse struct { Message string `json:"message"` } -func (a *ConversationAction) Run(context.Context, ActionParams) (ActionResult, error) { - return ActionResult{}, nil +func (a *ConversationAction) Run(context.Context, types.ActionParams) (types.ActionResult, error) { + return types.ActionResult{}, nil } func (a *ConversationAction) Plannable() bool { return false } -func (a *ConversationAction) Definition() ActionDefinition { - return ActionDefinition{ +func (a *ConversationAction) Definition() types.ActionDefinition { + return types.ActionDefinition{ Name: ConversationActionName, Description: "Use this tool to initiate a new conversation or to notify something.", Properties: map[string]jsonschema.Definition{ diff --git a/core/action/noreply.go b/core/action/noreply.go index 4215176..7575b79 100644 --- a/core/action/noreply.go +++ b/core/action/noreply.go @@ -1,6 +1,10 @@ package action -import "context" +import ( + "context" + + "github.com/mudler/LocalAgent/core/types" +) // StopActionName is the name of the action // used by the LLM to stop any further action @@ -12,16 +16,16 @@ func NewStop() *StopAction { type StopAction struct{} -func (a *StopAction) Run(context.Context, ActionParams) (ActionResult, error) { - return ActionResult{}, nil +func (a *StopAction) Run(context.Context, types.ActionParams) (types.ActionResult, error) { + return types.ActionResult{}, nil } func (a *StopAction) Plannable() bool { return false } -func (a *StopAction) Definition() ActionDefinition { - return ActionDefinition{ +func (a *StopAction) Definition() types.ActionDefinition { + return types.ActionDefinition{ Name: StopActionName, Description: "Use this tool to stop any further action and stop the conversation. You must use this when it looks like there is a conclusion to the conversation or the topic diverged too much from the original conversation. For instance if the user offer his help and you already replied with a message, you can use this tool to stop the conversation.", } diff --git a/core/action/plan.go b/core/action/plan.go index 740ea2f..0c3eb96 100644 --- a/core/action/plan.go +++ b/core/action/plan.go @@ -3,6 +3,7 @@ package action import ( "context" + "github.com/mudler/LocalAgent/core/types" "github.com/sashabaranov/go-openai/jsonschema" ) @@ -29,16 +30,16 @@ type PlanSubtask struct { Reasoning string `json:"reasoning"` } -func (a *PlanAction) Run(context.Context, ActionParams) (ActionResult, error) { - return ActionResult{}, nil +func (a *PlanAction) Run(context.Context, types.ActionParams) (types.ActionResult, error) { + return types.ActionResult{}, nil } func (a *PlanAction) Plannable() bool { return false } -func (a *PlanAction) Definition() ActionDefinition { - return ActionDefinition{ +func (a *PlanAction) Definition() types.ActionDefinition { + return types.ActionDefinition{ Name: PlanActionName, Description: "The assistant for solving complex tasks that involves calling more functions in sequence, replies with the action.", Properties: map[string]jsonschema.Definition{ diff --git a/core/action/reasoning.go b/core/action/reasoning.go index f8dc78d..19daaae 100644 --- a/core/action/reasoning.go +++ b/core/action/reasoning.go @@ -3,6 +3,7 @@ package action import ( "context" + "github.com/mudler/LocalAgent/core/types" "github.com/sashabaranov/go-openai/jsonschema" ) @@ -19,16 +20,16 @@ type ReasoningResponse struct { Reasoning string `json:"reasoning"` } -func (a *ReasoningAction) Run(context.Context, ActionParams) (ActionResult, error) { - return ActionResult{}, nil +func (a *ReasoningAction) Run(context.Context, types.ActionParams) (types.ActionResult, error) { + return types.ActionResult{}, nil } func (a *ReasoningAction) Plannable() bool { return false } -func (a *ReasoningAction) Definition() ActionDefinition { - return ActionDefinition{ +func (a *ReasoningAction) Definition() types.ActionDefinition { + return types.ActionDefinition{ Name: "pick_action", Description: "try to understand what's the best thing to do and pick an action with a reasoning", Properties: map[string]jsonschema.Definition{ diff --git a/core/action/reply.go b/core/action/reply.go index 33b6627..62ce86c 100644 --- a/core/action/reply.go +++ b/core/action/reply.go @@ -3,6 +3,7 @@ package action import ( "context" + "github.com/mudler/LocalAgent/core/types" "github.com/sashabaranov/go-openai/jsonschema" ) @@ -21,7 +22,7 @@ type ReplyResponse struct { Message string `json:"message"` } -func (a *ReplyAction) Run(context.Context, ActionParams) (string, error) { +func (a *ReplyAction) Run(context.Context, types.ActionParams) (string, error) { return "no-op", nil } @@ -29,8 +30,8 @@ func (a *ReplyAction) Plannable() bool { return false } -func (a *ReplyAction) Definition() ActionDefinition { - return ActionDefinition{ +func (a *ReplyAction) Definition() types.ActionDefinition { + return types.ActionDefinition{ Name: ReplyActionName, Description: "Use this tool to reply to the user once we have all the informations we need.", Properties: map[string]jsonschema.Definition{ diff --git a/core/action/state.go b/core/action/state.go index d0fe4d0..6121f82 100644 --- a/core/action/state.go +++ b/core/action/state.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/mudler/LocalAgent/core/types" "github.com/sashabaranov/go-openai/jsonschema" ) @@ -33,16 +34,16 @@ type AgentInternalState struct { Goal string `json:"goal"` } -func (a *StateAction) Run(context.Context, ActionParams) (ActionResult, error) { - return ActionResult{Result: "internal state has been updated"}, nil +func (a *StateAction) Run(context.Context, types.ActionParams) (types.ActionResult, error) { + return types.ActionResult{Result: "internal state has been updated"}, nil } func (a *StateAction) Plannable() bool { return false } -func (a *StateAction) Definition() ActionDefinition { - return ActionDefinition{ +func (a *StateAction) Definition() types.ActionDefinition { + return types.ActionDefinition{ Name: StateActionName, Description: "update the agent state (short memory) with the current state of the conversation.", Properties: map[string]jsonschema.Definition{ diff --git a/core/agent/actions.go b/core/agent/actions.go index c3e467d..7b1c191 100644 --- a/core/agent/actions.go +++ b/core/agent/actions.go @@ -7,53 +7,15 @@ import ( "os" "github.com/mudler/LocalAgent/core/action" + "github.com/mudler/LocalAgent/core/types" + "github.com/mudler/LocalAgent/pkg/xlog" "github.com/sashabaranov/go-openai" ) -type ActionState struct { - ActionCurrentState - action.ActionResult -} - -type ActionCurrentState struct { - Action Action - Params action.ActionParams - Reasoning string -} - -// Actions is something the agent can do -type Action interface { - Run(ctx context.Context, action action.ActionParams) (action.ActionResult, error) - Definition() action.ActionDefinition - Plannable() bool -} - -type Actions []Action - -func (a Actions) ToTools() []openai.Tool { - tools := []openai.Tool{} - for _, action := range a { - tools = append(tools, openai.Tool{ - Type: openai.ToolTypeFunction, - Function: action.Definition().ToFunctionDefinition(), - }) - } - return tools -} - -func (a Actions) Find(name string) Action { - for _, action := range a { - if action.Definition().Name.Is(name) { - return action - } - } - return nil -} - type decisionResult struct { - actionParams action.ActionParams + actionParams types.ActionParams message string actioName string } @@ -85,7 +47,7 @@ func (a *Agent) decision( return &decisionResult{message: msg.Content}, nil } - params := action.ActionParams{} + params := types.ActionParams{} if err := params.Read(msg.ToolCalls[0].Function.Arguments); err != nil { return nil, err } @@ -173,7 +135,7 @@ func (m Messages) IsLastMessageFromRole(role string) bool { return m[len(m)-1].Role == role } -func (a *Agent) generateParameters(ctx context.Context, pickTemplate string, act Action, c []openai.ChatCompletionMessage, reasoning string) (*decisionResult, error) { +func (a *Agent) generateParameters(ctx context.Context, pickTemplate string, act types.Action, c []openai.ChatCompletionMessage, reasoning string) (*decisionResult, error) { stateHUD, err := renderTemplate(pickTemplate, a.prepareHUD(), a.availableActions(), reasoning) if err != nil { @@ -212,7 +174,7 @@ func (a *Agent) generateParameters(ctx context.Context, pickTemplate string, act ) } -func (a *Agent) handlePlanning(ctx context.Context, job *Job, chosenAction Action, actionParams action.ActionParams, reasoning string, pickTemplate string) error { +func (a *Agent) handlePlanning(ctx context.Context, job *types.Job, chosenAction types.Action, actionParams types.ActionParams, reasoning string, pickTemplate string) error { // Planning: run all the actions in sequence if !chosenAction.Definition().Name.Is(action.PlanActionName) { xlog.Debug("no plan action") @@ -225,9 +187,17 @@ func (a *Agent) handlePlanning(ctx context.Context, job *Job, chosenAction Actio return fmt.Errorf("error unmarshalling plan result: %w", err) } - stateResult := ActionState{ActionCurrentState{chosenAction, actionParams, reasoning}, action.ActionResult{ - Result: fmt.Sprintf("planning %s, subtasks: %+v", planResult.Goal, planResult.Subtasks), - }} + stateResult := types.ActionState{ + ActionCurrentState: types.ActionCurrentState{ + Job: job, + Action: chosenAction, + Params: actionParams, + Reasoning: reasoning, + }, + ActionResult: types.ActionResult{ + Result: fmt.Sprintf("planning %s, subtasks: %+v", planResult.Goal, planResult.Subtasks), + }, + } job.Result.SetResult(stateResult) job.CallbackWithResult(stateResult) @@ -258,8 +228,23 @@ func (a *Agent) handlePlanning(ctx context.Context, job *Job, chosenAction Actio } actionParams = params.actionParams - if !job.Callback(ActionCurrentState{subTaskAction, actionParams, subTaskReasoning}) { - job.Result.SetResult(ActionState{ActionCurrentState{chosenAction, actionParams, subTaskReasoning}, action.ActionResult{Result: "stopped by callback"}}) + if !job.Callback(types.ActionCurrentState{ + Job: job, + Action: subTaskAction, + Params: actionParams, + Reasoning: subTaskReasoning, + }) { + job.Result.SetResult(types.ActionState{ + ActionCurrentState: types.ActionCurrentState{ + Job: job, + Action: chosenAction, + Params: actionParams, + Reasoning: subTaskReasoning, + }, + ActionResult: types.ActionResult{ + Result: "stopped by callback", + }, + }) job.Result.Conversation = a.currentConversation job.Result.Finish(nil) break @@ -270,7 +255,15 @@ func (a *Agent) handlePlanning(ctx context.Context, job *Job, chosenAction Actio return fmt.Errorf("error running action: %w", err) } - stateResult := ActionState{ActionCurrentState{subTaskAction, actionParams, subTaskReasoning}, result} + stateResult := types.ActionState{ + ActionCurrentState: types.ActionCurrentState{ + Job: job, + Action: subTaskAction, + Params: actionParams, + Reasoning: subTaskReasoning, + }, + ActionResult: result, + } job.Result.SetResult(stateResult) job.CallbackWithResult(stateResult) xlog.Debug("[subtask] Action executed", "agent", a.Character.Name, "action", subTaskAction.Definition().Name, "result", result) @@ -280,10 +273,10 @@ func (a *Agent) handlePlanning(ctx context.Context, job *Job, chosenAction Actio return nil } -func (a *Agent) availableActions() Actions { +func (a *Agent) availableActions() types.Actions { // defaultActions := append(a.options.userActions, action.NewReply()) - addPlanAction := func(actions Actions) Actions { + addPlanAction := func(actions types.Actions) types.Actions { if !a.options.canPlan { return actions } @@ -341,7 +334,7 @@ func (a *Agent) prepareHUD() (promptHUD *PromptHUD) { } // pickAction picks an action based on the conversation -func (a *Agent) pickAction(ctx context.Context, templ string, messages []openai.ChatCompletionMessage) (Action, action.ActionParams, string, error) { +func (a *Agent) pickAction(ctx context.Context, templ string, messages []openai.ChatCompletionMessage) (types.Action, types.ActionParams, string, error) { c := messages if !a.options.forceReasoning { @@ -390,7 +383,7 @@ func (a *Agent) pickAction(ctx context.Context, templ string, messages []openai. // and then use the reply to get the action thought, err := a.decision(ctx, c, - Actions{action.NewReasoning()}.ToTools(), + types.Actions{action.NewReasoning()}.ToTools(), action.NewReasoning().Definition().Name) if err != nil { return nil, nil, "", err @@ -421,7 +414,7 @@ func (a *Agent) pickAction(ctx context.Context, templ string, messages []openai. Role: "system", Content: "Given the assistant thought, pick the relevant action: " + reason, }), - Actions{intentionsTools}.ToTools(), + types.Actions{intentionsTools}.ToTools(), intentionsTools.Definition().Name) if err != nil { return nil, nil, "", fmt.Errorf("failed to get the action tool parameters: %v", err) diff --git a/core/agent/agent.go b/core/agent/agent.go index 08864b6..14c7a1c 100644 --- a/core/agent/agent.go +++ b/core/agent/agent.go @@ -10,6 +10,7 @@ import ( "github.com/mudler/LocalAgent/pkg/xlog" "github.com/mudler/LocalAgent/core/action" + "github.com/mudler/LocalAgent/core/types" "github.com/mudler/LocalAgent/pkg/llm" "github.com/sashabaranov/go-openai" ) @@ -25,21 +26,21 @@ type Agent struct { options *options Character Character client *openai.Client - jobQueue chan *Job - actionContext *action.ActionContext - context *action.ActionContext + jobQueue chan *types.Job + actionContext *types.ActionContext + context *types.ActionContext currentReasoning string currentState *action.AgentInternalState - nextAction Action - nextActionParams *action.ActionParams + nextAction types.Action + nextActionParams *types.ActionParams currentConversation Messages selfEvaluationInProgress bool pause bool newConversations chan openai.ChatCompletionMessage - mcpActions Actions + mcpActions types.Actions } type RAGDB interface { @@ -64,12 +65,12 @@ func New(opts ...Option) (*Agent, error) { ctx, cancel := context.WithCancel(c) a := &Agent{ - jobQueue: make(chan *Job), + jobQueue: make(chan *types.Job), options: options, client: client, Character: options.character, currentState: &action.AgentInternalState{}, - context: action.NewContext(ctx, cancel), + context: types.NewActionContext(ctx, cancel), } if a.options.statefile != "" { @@ -130,18 +131,18 @@ func (a *Agent) ConversationChannel() chan openai.ChatCompletionMessage { // Ask is a pre-emptive, blocking call that returns the response as soon as it's ready. // It discards any other computation. -func (a *Agent) Ask(opts ...JobOption) *JobResult { +func (a *Agent) Ask(opts ...types.JobOption) *types.JobResult { xlog.Debug("Agent Ask()", "agent", a.Character.Name, "model", a.options.LLMAPI.Model) defer func() { xlog.Debug("Agent has finished being asked", "agent", a.Character.Name) }() //a.StopAction() - j := NewJob( + j := types.NewJob( append( opts, - WithReasoningCallback(a.options.reasoningCallback), - WithResultCallback(a.options.resultCallback), + types.WithReasoningCallback(a.options.reasoningCallback), + types.WithResultCallback(a.options.resultCallback), )..., ) a.jobQueue <- j @@ -224,12 +225,12 @@ func (a *Agent) Memory() RAGDB { return a.options.ragdb } -func (a *Agent) runAction(chosenAction Action, params action.ActionParams) (result action.ActionResult, err error) { +func (a *Agent) runAction(chosenAction types.Action, params types.ActionParams) (result types.ActionResult, err error) { for _, act := range a.availableActions() { if act.Definition().Name == chosenAction.Definition().Name { res, err := act.Run(a.actionContext, params) if err != nil { - return action.ActionResult{}, fmt.Errorf("error running action: %w", err) + return types.ActionResult{}, fmt.Errorf("error running action: %w", err) } result = res @@ -244,7 +245,7 @@ func (a *Agent) runAction(chosenAction Action, params action.ActionParams) (resu err = params.Unmarshal(&state) if err != nil { - return action.ActionResult{}, fmt.Errorf("error unmarshalling state of the agent: %w", err) + return types.ActionResult{}, fmt.Errorf("error unmarshalling state of the agent: %w", err) } // update the current state with the one we just got from the action a.currentState = &state @@ -252,7 +253,7 @@ func (a *Agent) runAction(chosenAction Action, params action.ActionParams) (resu // update the state file if a.options.statefile != "" { if err := a.SaveState(a.options.statefile); err != nil { - return action.ActionResult{}, err + return types.ActionResult{}, err } } } @@ -348,7 +349,7 @@ func extractImageContent(message openai.ChatCompletionMessage) (imageURL, text s return } -func (a *Agent) processUserInputs(job *Job, role string) { +func (a *Agent) processUserInputs(job *types.Job, role string) { noNewMessage := job.Text == "" && job.Image == "" onlyText := job.Text != "" && job.Image == "" @@ -434,7 +435,7 @@ func (a *Agent) processUserInputs(job *Job, role string) { } } -func (a *Agent) consumeJob(job *Job, role string) { +func (a *Agent) consumeJob(job *types.Job, role string) { a.Lock() paused := a.pause a.Unlock() @@ -451,10 +452,10 @@ func (a *Agent) consumeJob(job *Job, role string) { a.Lock() // Set the action context ctx, cancel := context.WithCancel(context.Background()) - a.actionContext = action.NewContext(ctx, cancel) + a.actionContext = types.NewActionContext(ctx, cancel) a.selfEvaluationInProgress = selfEvaluation - if len(job.conversationHistory) != 0 { - a.currentConversation = job.conversationHistory + if len(job.ConversationHistory) != 0 { + a.currentConversation = job.ConversationHistory } a.Unlock() @@ -493,9 +494,9 @@ func (a *Agent) consumeJob(job *Job, role string) { } // choose an action first - var chosenAction Action + var chosenAction types.Action var reasoning string - var actionParams action.ActionParams + var actionParams types.ActionParams if a.nextAction != nil { // if we are being re-evaluated, we already have the action @@ -576,8 +577,19 @@ func (a *Agent) consumeJob(job *Job, role string) { return } - if !job.Callback(ActionCurrentState{chosenAction, actionParams, reasoning}) { - job.Result.SetResult(ActionState{ActionCurrentState{chosenAction, actionParams, reasoning}, action.ActionResult{Result: "stopped by callback"}}) + if !job.Callback(types.ActionCurrentState{ + Job: job, + Action: chosenAction, + Params: actionParams, + Reasoning: reasoning}) { + job.Result.SetResult(types.ActionState{ + ActionCurrentState: types.ActionCurrentState{ + Job: job, + Action: chosenAction, + Params: actionParams, + Reasoning: reasoning, + }, + ActionResult: types.ActionResult{Result: "stopped by callback"}}) job.Result.Conversation = a.currentConversation job.Result.Finish(nil) return @@ -622,7 +634,15 @@ func (a *Agent) consumeJob(job *Job, role string) { result.Result = fmt.Sprintf("Error running tool: %v", err) } - stateResult := ActionState{ActionCurrentState{chosenAction, actionParams, reasoning}, result} + stateResult := types.ActionState{ + ActionCurrentState: types.ActionCurrentState{ + Job: job, + Action: chosenAction, + Params: actionParams, + Reasoning: reasoning, + }, + ActionResult: result, + } job.Result.SetResult(stateResult) job.CallbackWithResult(stateResult) xlog.Debug("Action executed", "agent", a.Character.Name, "action", chosenAction.Definition().Name, "result", result) @@ -781,7 +801,7 @@ func (a *Agent) consumeJob(job *Job, role string) { job.Result.Finish(nil) } -func (a *Agent) addFunctionResultToConversation(chosenAction Action, actionParams action.ActionParams, result action.ActionResult) { +func (a *Agent) addFunctionResultToConversation(chosenAction types.Action, actionParams types.ActionParams, result types.ActionResult) { // calling the function a.currentConversation = append(a.currentConversation, openai.ChatCompletionMessage{ Role: "assistant", @@ -847,10 +867,10 @@ func (a *Agent) periodicallyRun(timer *time.Timer) { // - asking the agent to do something else based on the result // whatNext := NewJob(WithText("Decide what to do based on the state")) - whatNext := NewJob( - WithText(innerMonologueTemplate), - WithReasoningCallback(a.options.reasoningCallback), - WithResultCallback(a.options.resultCallback), + whatNext := types.NewJob( + types.WithText(innerMonologueTemplate), + types.WithReasoningCallback(a.options.reasoningCallback), + types.WithResultCallback(a.options.resultCallback), ) a.consumeJob(whatNext, SystemRole) a.ResetConversation() @@ -913,7 +933,7 @@ func (a *Agent) Run() error { } } -func (a *Agent) loop(timer *time.Timer, job *Job) { +func (a *Agent) loop(timer *time.Timer, job *types.Job) { // Remember always to reset the timer - if we don't the agent will stop.. defer timer.Reset(a.options.periodicRuns) // Consume the job and generate a response diff --git a/core/agent/agent_test.go b/core/agent/agent_test.go index 988b893..ebac839 100644 --- a/core/agent/agent_test.go +++ b/core/agent/agent_test.go @@ -8,8 +8,8 @@ import ( "github.com/mudler/LocalAgent/pkg/xlog" "github.com/mudler/LocalAgent/services/actions" - "github.com/mudler/LocalAgent/core/action" . "github.com/mudler/LocalAgent/core/agent" + "github.com/mudler/LocalAgent/core/types" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/sashabaranov/go-openai/jsonschema" @@ -19,14 +19,14 @@ const testActionResult = "In Boston it's 30C today, it's sunny, and humidity is const testActionResult2 = "In milan it's very hot today, it is 45C and the humidity is at 200%" const testActionResult3 = "In paris it's very cold today, it is 2C and the humidity is at 10%" -var _ Action = &TestAction{} +var _ types.Action = &TestAction{} -var debugOptions = []JobOption{ - WithReasoningCallback(func(state ActionCurrentState) bool { +var debugOptions = []types.JobOption{ + types.WithReasoningCallback(func(state types.ActionCurrentState) bool { xlog.Info("Reasoning", state) return true }), - WithResultCallback(func(state ActionState) { + types.WithResultCallback(func(state types.ActionState) { xlog.Info("Reasoning", state.Reasoning) xlog.Info("Action", state.Action) xlog.Info("Result", state.Result) @@ -41,18 +41,18 @@ func (a *TestAction) Plannable() bool { return true } -func (a *TestAction) Run(c context.Context, p action.ActionParams) (action.ActionResult, error) { +func (a *TestAction) Run(c context.Context, p types.ActionParams) (types.ActionResult, error) { for k, r := range a.response { if strings.Contains(strings.ToLower(p.String()), strings.ToLower(k)) { - return action.ActionResult{Result: r}, nil + return types.ActionResult{Result: r}, nil } } - return action.ActionResult{Result: "No match"}, nil + return types.ActionResult{Result: "No match"}, nil } -func (a *TestAction) Definition() action.ActionDefinition { - return action.ActionDefinition{ +func (a *TestAction) Definition() types.ActionDefinition { + return types.ActionDefinition{ Name: "get_weather", Description: "get current weather", Properties: map[string]jsonschema.Definition{ @@ -74,8 +74,8 @@ type FakeStoreResultAction struct { TestAction } -func (a *FakeStoreResultAction) Definition() action.ActionDefinition { - return action.ActionDefinition{ +func (a *FakeStoreResultAction) Definition() types.ActionDefinition { + return types.ActionDefinition{ Name: "store_results", Description: "store results permanently. Use this tool after you have a result you want to keep.", Properties: map[string]jsonschema.Definition{ @@ -93,8 +93,8 @@ type FakeInternetAction struct { TestAction } -func (a *FakeInternetAction) Definition() action.ActionDefinition { - return action.ActionDefinition{ +func (a *FakeInternetAction) Definition() types.ActionDefinition { + return types.ActionDefinition{ Name: "search_internet", Description: "search on internet", Properties: map[string]jsonschema.Definition{ @@ -127,7 +127,7 @@ var _ = Describe("Agent test", func() { res := agent.Ask( append(debugOptions, - WithText("what's the weather in Boston and Milano? Use celsius units"), + types.WithText("what's the weather in Boston and Milano? Use celsius units"), )..., ) Expect(res.Error).ToNot(HaveOccurred()) @@ -142,7 +142,7 @@ var _ = Describe("Agent test", func() { res = agent.Ask( append(debugOptions, - WithText("Now I want to know the weather in Paris, always use celsius units"), + types.WithText("Now I want to know the weather in Paris, always use celsius units"), )...) for _, r := range res.State { @@ -173,7 +173,7 @@ var _ = Describe("Agent test", func() { defer agent.Stop() res := agent.Ask( append(debugOptions, - WithText("can you get the weather in boston? Use celsius units"))..., + types.WithText("can you get the weather in boston? Use celsius units"))..., ) reasons := []string{} for _, r := range res.State { @@ -196,7 +196,7 @@ var _ = Describe("Agent test", func() { defer agent.Stop() result := agent.Ask( - WithText("Update your goals such as you want to learn to play the guitar"), + types.WithText("Update your goals such as you want to learn to play the guitar"), ) fmt.Printf("%+v\n", result) Expect(result.Error).ToNot(HaveOccurred()) @@ -221,7 +221,7 @@ var _ = Describe("Agent test", func() { defer agent.Stop() result := agent.Ask( - WithText("plan a trip to San Francisco from Venice, Italy"), + types.WithText("plan a trip to San Francisco from Venice, Italy"), ) Expect(len(result.State)).To(BeNumerically(">", 1)) diff --git a/core/agent/mcp.go b/core/agent/mcp.go index 82a4350..bb1e178 100644 --- a/core/agent/mcp.go +++ b/core/agent/mcp.go @@ -7,12 +7,12 @@ import ( mcp "github.com/metoro-io/mcp-golang" "github.com/metoro-io/mcp-golang/transport/http" - "github.com/mudler/LocalAgent/core/action" + "github.com/mudler/LocalAgent/core/types" "github.com/mudler/LocalAgent/pkg/xlog" "github.com/sashabaranov/go-openai/jsonschema" ) -var _ Action = &mcpAction{} +var _ types.Action = &mcpAction{} type MCPServer struct { URL string `json:"url"` @@ -30,11 +30,11 @@ func (a *mcpAction) Plannable() bool { return true } -func (m *mcpAction) Run(ctx context.Context, params action.ActionParams) (action.ActionResult, error) { +func (m *mcpAction) Run(ctx context.Context, params types.ActionParams) (types.ActionResult, error) { resp, err := m.mcpClient.CallTool(ctx, m.toolName, params) if err != nil { xlog.Error("Failed to call tool", "error", err.Error()) - return action.ActionResult{}, err + return types.ActionResult{}, err } xlog.Debug("MCP response", "response", resp) @@ -51,12 +51,12 @@ func (m *mcpAction) Run(ctx context.Context, params action.ActionParams) (action } } - return action.ActionResult{ + return types.ActionResult{ Result: textResult, }, nil } -func (m *mcpAction) Definition() action.ActionDefinition { +func (m *mcpAction) Definition() types.ActionDefinition { props := map[string]jsonschema.Definition{} dat, err := json.Marshal(m.inputSchema.Properties) if err != nil { @@ -64,8 +64,8 @@ func (m *mcpAction) Definition() action.ActionDefinition { } json.Unmarshal(dat, &props) - return action.ActionDefinition{ - Name: action.ActionDefinitionName(m.toolName), + return types.ActionDefinition{ + Name: types.ActionDefinitionName(m.toolName), Description: m.toolDescription, Required: m.inputSchema.Required, //Properties: , @@ -84,7 +84,7 @@ func (a *Agent) initMCPActions() error { a.mcpActions = nil var err error - generatedActions := Actions{} + generatedActions := types.Actions{} for _, mcpServer := range a.options.mcpServers { transport := http.NewHTTPClientTransport("/mcp") diff --git a/core/agent/options.go b/core/agent/options.go index da27302..f01f7dd 100644 --- a/core/agent/options.go +++ b/core/agent/options.go @@ -4,6 +4,8 @@ import ( "context" "strings" "time" + + "github.com/mudler/LocalAgent/core/types" ) type Option func(*options) error @@ -20,7 +22,7 @@ type options struct { character Character randomIdentityGuidance string randomIdentity bool - userActions Actions + userActions types.Actions enableHUD, standaloneJob, showCharacter, enableKB, enableSummaryMemory, enableLongTermMemory bool canStopItself bool @@ -41,8 +43,8 @@ type options struct { systemPrompt string // callbacks - reasoningCallback func(ActionCurrentState) bool - resultCallback func(ActionState) + reasoningCallback func(types.ActionCurrentState) bool + resultCallback func(types.ActionState) conversationsPath string @@ -262,14 +264,14 @@ func WithContext(ctx context.Context) Option { } } -func WithAgentReasoningCallback(cb func(ActionCurrentState) bool) Option { +func WithAgentReasoningCallback(cb func(types.ActionCurrentState) bool) Option { return func(o *options) error { o.reasoningCallback = cb return nil } } -func WithAgentResultCallback(cb func(ActionState)) Option { +func WithAgentResultCallback(cb func(types.ActionState)) Option { return func(o *options) error { o.resultCallback = cb return nil @@ -310,7 +312,7 @@ func WithRandomIdentity(guidance ...string) Option { } } -func WithActions(actions ...Action) Option { +func WithActions(actions ...types.Action) Option { return func(o *options) error { o.userActions = actions return nil diff --git a/core/agent/templates.go b/core/agent/templates.go index 00f942d..5c9bf5a 100644 --- a/core/agent/templates.go +++ b/core/agent/templates.go @@ -5,11 +5,11 @@ import ( "html/template" "time" - "github.com/mudler/LocalAgent/core/action" + "github.com/mudler/LocalAgent/core/types" "github.com/sashabaranov/go-openai" ) -func renderTemplate(templ string, hud *PromptHUD, actions Actions, reasoning string) (string, error) { +func renderTemplate(templ string, hud *PromptHUD, actions types.Actions, reasoning string) (string, error) { // prepare the prompt prompt := bytes.NewBuffer([]byte{}) @@ -19,14 +19,14 @@ func renderTemplate(templ string, hud *PromptHUD, actions Actions, reasoning str } // Get all the actions definitions - definitions := []action.ActionDefinition{} + definitions := []types.ActionDefinition{} for _, m := range actions { definitions = append(definitions, m.Definition()) } err = promptTemplate.Execute(prompt, struct { HUD *PromptHUD - Actions []action.ActionDefinition + Actions []types.ActionDefinition Reasoning string Messages []openai.ChatCompletionMessage Time string diff --git a/core/state/config.go b/core/state/config.go index e52588d..2a9084d 100644 --- a/core/state/config.go +++ b/core/state/config.go @@ -4,6 +4,7 @@ import ( "encoding/json" "github.com/mudler/LocalAgent/core/agent" + "github.com/mudler/LocalAgent/core/types" ) type ConnectorConfig struct { @@ -61,7 +62,7 @@ type AgentConfig struct { } type Connector interface { - AgentResultCallback() func(state agent.ActionState) - AgentReasoningCallback() func(state agent.ActionCurrentState) bool + AgentResultCallback() func(state types.ActionState) + AgentReasoningCallback() func(state types.ActionCurrentState) bool Start(a *agent.Agent) } diff --git a/core/state/pool.go b/core/state/pool.go index 5312983..4aac9cc 100644 --- a/core/state/pool.go +++ b/core/state/pool.go @@ -12,9 +12,9 @@ import ( "sync" "time" - "github.com/mudler/LocalAgent/core/agent" . "github.com/mudler/LocalAgent/core/agent" "github.com/mudler/LocalAgent/core/sse" + "github.com/mudler/LocalAgent/core/types" "github.com/mudler/LocalAgent/pkg/llm" "github.com/mudler/LocalAgent/pkg/localrag" "github.com/mudler/LocalAgent/pkg/utils" @@ -26,25 +26,26 @@ import ( type AgentPool struct { sync.Mutex - file string - pooldir string - pool AgentPoolData - agents map[string]*Agent - managers map[string]sse.Manager - agentStatus map[string]*Status - apiURL, defaultModel, defaultMultimodalModel, imageModel, localRAGAPI, localRAGKey, apiKey string - availableActions func(*AgentConfig) func(ctx context.Context, pool *AgentPool) []Action - connectors func(*AgentConfig) []Connector - promptBlocks func(*AgentConfig) []PromptBlock - timeout string - conversationLogs string + file string + pooldir string + pool AgentPoolData + agents map[string]*Agent + managers map[string]sse.Manager + agentStatus map[string]*Status + apiURL, defaultModel, defaultMultimodalModel string + imageModel, localRAGAPI, localRAGKey, apiKey string + availableActions func(*AgentConfig) func(ctx context.Context, pool *AgentPool) []types.Action + connectors func(*AgentConfig) []Connector + promptBlocks func(*AgentConfig) []PromptBlock + timeout string + conversationLogs string } type Status struct { - ActionResults []ActionState + ActionResults []types.ActionState } -func (s *Status) addResult(result ActionState) { +func (s *Status) addResult(result types.ActionState) { // If we have more than 10 results, remove the oldest one if len(s.ActionResults) > 10 { s.ActionResults = s.ActionResults[1:] @@ -53,7 +54,7 @@ func (s *Status) addResult(result ActionState) { s.ActionResults = append(s.ActionResults, result) } -func (s *Status) Results() []ActionState { +func (s *Status) Results() []types.ActionState { return s.ActionResults } @@ -73,7 +74,7 @@ func loadPoolFromFile(path string) (*AgentPoolData, error) { func NewAgentPool( defaultModel, defaultMultimodalModel, imageModel, apiURL, apiKey, directory string, LocalRAGAPI string, - availableActions func(*AgentConfig) func(ctx context.Context, pool *AgentPool) []agent.Action, + availableActions func(*AgentConfig) func(ctx context.Context, pool *AgentPool) []types.Action, connectors func(*AgentConfig) []Connector, promptBlocks func(*AgentConfig) []PromptBlock, timeout string, @@ -158,24 +159,24 @@ func (a *AgentPool) CreateAgent(name string, agentConfig *AgentConfig) error { return err } - go func(ac AgentConfig, pool *AgentPool) { + go func(ac AgentConfig) { // Create the agent avatar - if err := a.createAgentAvatar(ac); err != nil { + if err := createAgentAvatar(a.apiURL, a.apiKey, a.defaultModel, a.imageModel, a.pooldir, ac); err != nil { xlog.Error("Failed to create agent avatar", "error", err) } - }(a.pool[name], a) + }(a.pool[name]) return a.startAgentWithConfig(name, agentConfig) } -func (a *AgentPool) createAgentAvatar(agent AgentConfig) error { - client := llm.NewClient(a.apiKey, a.apiURL+"/v1", "10m") +func createAgentAvatar(APIURL, APIKey, model, imageModel, avatarDir string, agent AgentConfig) error { + client := llm.NewClient(APIKey, APIURL+"/v1", "10m") - if a.imageModel == "" { + if imageModel == "" { return fmt.Errorf("image model not set") } - if a.defaultModel == "" { + if model == "" { return fmt.Errorf("default model not set") } @@ -185,9 +186,9 @@ func (a *AgentPool) createAgentAvatar(agent AgentConfig) error { err := llm.GenerateTypedJSON( context.Background(), - llm.NewClient(a.apiKey, a.apiURL, "10m"), + llm.NewClient(APIKey, APIURL, "10m"), "Generate a prompt that I can use to create a random avatar for the bot '"+agent.Name+"', the description of the bot is: "+agent.Description, - a.defaultModel, + model, jsonschema.Definition{ Type: jsonschema.Object, Properties: map[string]jsonschema.Definition{ @@ -209,7 +210,7 @@ func (a *AgentPool) createAgentAvatar(agent AgentConfig) error { req := openai.ImageRequest{ Prompt: results.ImagePrompt, - Model: a.imageModel, + Model: imageModel, Size: openai.CreateImageSize256x256, ResponseFormat: openai.CreateImageResponseFormatB64JSON, } @@ -228,10 +229,10 @@ func (a *AgentPool) createAgentAvatar(agent AgentConfig) error { imageJson := resp.Data[0].B64JSON - os.MkdirAll(filepath.Join(a.pooldir, "avatars"), 0755) + os.MkdirAll(filepath.Join(avatarDir, "avatars"), 0755) // Save the image to the agent directory - imagePath := filepath.Join(a.pooldir, "avatars", fmt.Sprintf("%s.png", agent.Name)) + imagePath := filepath.Join(avatarDir, "avatars", fmt.Sprintf("%s.png", agent.Name)) imageData, err := base64.StdEncoding.DecodeString(imageJson) if err != nil { return err @@ -343,7 +344,7 @@ func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error WithLLMAPIKey(a.apiKey), WithTimeout(a.timeout), WithRAGDB(localrag.NewWrappedClient(a.localRAGAPI, a.localRAGKey, name)), - WithAgentReasoningCallback(func(state ActionCurrentState) bool { + WithAgentReasoningCallback(func(state types.ActionCurrentState) bool { xlog.Info( "Agent is thinking", "agent", name, @@ -367,7 +368,7 @@ func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error }), WithSystemPrompt(config.SystemPrompt), WithMultimodalModel(multimodalModel), - WithAgentResultCallback(func(state ActionState) { + WithAgentResultCallback(func(state types.ActionState) { a.Lock() if _, ok := a.agentStatus[name]; !ok { a.agentStatus[name] = &Status{} diff --git a/core/action/definition.go b/core/types/actions.go similarity index 63% rename from core/action/definition.go rename to core/types/actions.go index 1473d72..c0452dc 100644 --- a/core/action/definition.go +++ b/core/types/actions.go @@ -1,4 +1,4 @@ -package action +package types import ( "context" @@ -19,7 +19,7 @@ func (ac *ActionContext) Cancel() { } } -func NewContext(ctx context.Context, cancel context.CancelFunc) *ActionContext { +func NewActionContext(ctx context.Context, cancel context.CancelFunc) *ActionContext { return &ActionContext{ Context: ctx, cancelFunc: cancel, @@ -29,6 +29,7 @@ func NewContext(ctx context.Context, cancel context.CancelFunc) *ActionContext { type ActionParams map[string]interface{} type ActionResult struct { + Job *Job Result string Metadata map[string]interface{} } @@ -84,3 +85,44 @@ func (a ActionDefinition) ToFunctionDefinition() openai.FunctionDefinition { }, } } + +// Actions is something the agent can do +type Action interface { + Run(ctx context.Context, action ActionParams) (ActionResult, error) + Definition() ActionDefinition + Plannable() bool +} + +type Actions []Action + +func (a Actions) ToTools() []openai.Tool { + tools := []openai.Tool{} + for _, action := range a { + tools = append(tools, openai.Tool{ + Type: openai.ToolTypeFunction, + Function: action.Definition().ToFunctionDefinition(), + }) + } + return tools +} + +func (a Actions) Find(name string) Action { + for _, action := range a { + if action.Definition().Name.Is(name) { + return action + } + } + return nil +} + +type ActionState struct { + ActionCurrentState + ActionResult +} + +type ActionCurrentState struct { + Job *Job + Action Action + Params ActionParams + Reasoning string +} diff --git a/core/agent/jobs.go b/core/types/job.go similarity index 72% rename from core/agent/jobs.go rename to core/types/job.go index a3b5086..167e2e6 100644 --- a/core/agent/jobs.go +++ b/core/types/job.go @@ -1,8 +1,10 @@ -package agent +package types import ( + "log" "sync" + "github.com/google/uuid" "github.com/sashabaranov/go-openai" ) @@ -16,7 +18,8 @@ type Job struct { Result *JobResult reasoningCallback func(ActionCurrentState) bool resultCallback func(ActionState) - conversationHistory []openai.ChatCompletionMessage + ConversationHistory []openai.ChatCompletionMessage + UUID string } // JobResult is the result of a job @@ -25,17 +28,17 @@ type JobResult struct { // The result of a job State []ActionState Conversation []openai.ChatCompletionMessage - - Response string - Error error - ready chan bool + + Response string + Error error + ready chan bool } type JobOption func(*Job) func WithConversationHistory(history []openai.ChatCompletionMessage) JobOption { return func(j *Job) { - j.conversationHistory = history + j.ConversationHistory = history } } @@ -85,6 +88,19 @@ func WithText(text string) JobOption { } } +func newUUID() string { + // Generate UUID with google/uuid + // https://pkg.go.dev/github.com/google/uuid + + // Generate a Version 4 UUID + u, err := uuid.NewRandom() + if err != nil { + log.Fatalf("failed to generate UUID: %v", err) + } + + return u.String() +} + // NewJob creates a new job // It is a request to the agent to do something // It has a JobResult to get the result asynchronously @@ -92,42 +108,17 @@ func WithText(text string) JobOption { func NewJob(opts ...JobOption) *Job { j := &Job{ Result: NewJobResult(), + UUID: newUUID(), } for _, o := range opts { o(j) } + return j } -// SetResult sets the result of a job -func (j *JobResult) SetResult(text ActionState) { - j.Lock() - defer j.Unlock() - - j.State = append(j.State, text) -} - -// SetResult sets the result of a job -func (j *JobResult) Finish(e error) { - j.Lock() - defer j.Unlock() - - j.Error = e - close(j.ready) -} - -// SetResult sets the result of a job -func (j *JobResult) SetResponse(response string) { - j.Lock() - defer j.Unlock() - - j.Response = response -} - -// WaitResult waits for the result of a job -func (j *JobResult) WaitResult() *JobResult { - <-j.ready - j.Lock() - defer j.Unlock() - return j +func WithUUID(uuid string) JobOption { + return func(j *Job) { + j.UUID = uuid + } } diff --git a/core/types/result.go b/core/types/result.go new file mode 100644 index 0000000..a78c266 --- /dev/null +++ b/core/types/result.go @@ -0,0 +1,34 @@ +package types + +// SetResult sets the result of a job +func (j *JobResult) SetResult(text ActionState) { + j.Lock() + defer j.Unlock() + + j.State = append(j.State, text) +} + +// SetResult sets the result of a job +func (j *JobResult) Finish(e error) { + j.Lock() + defer j.Unlock() + + j.Error = e + close(j.ready) +} + +// SetResult sets the result of a job +func (j *JobResult) SetResponse(response string) { + j.Lock() + defer j.Unlock() + + j.Response = response +} + +// WaitResult waits for the result of a job +func (j *JobResult) WaitResult() *JobResult { + <-j.ready + j.Lock() + defer j.Unlock() + return j +} diff --git a/services/actions.go b/services/actions.go index 735ce2c..1ff5252 100644 --- a/services/actions.go +++ b/services/actions.go @@ -7,9 +7,9 @@ import ( "github.com/mudler/LocalAgent/core/action" "github.com/mudler/LocalAgent/core/state" + "github.com/mudler/LocalAgent/core/types" "github.com/mudler/LocalAgent/pkg/xlog" - "github.com/mudler/LocalAgent/core/agent" "github.com/mudler/LocalAgent/services/actions" ) @@ -60,9 +60,9 @@ var AvailableActions = []string{ ActionShellcommand, } -func Actions(a *state.AgentConfig) func(ctx context.Context, pool *state.AgentPool) []agent.Action { - return func(ctx context.Context, pool *state.AgentPool) []agent.Action { - allActions := []agent.Action{} +func Actions(a *state.AgentConfig) func(ctx context.Context, pool *state.AgentPool) []types.Action { + return func(ctx context.Context, pool *state.AgentPool) []types.Action { + allActions := []types.Action{} for _, a := range a.Actions { var config map[string]string @@ -82,8 +82,8 @@ func Actions(a *state.AgentConfig) func(ctx context.Context, pool *state.AgentPo } } -func Action(name string, config map[string]string, pool *state.AgentPool) (agent.Action, error) { - var a agent.Action +func Action(name string, config map[string]string, pool *state.AgentPool) (types.Action, error) { + var a types.Action var err error switch name { diff --git a/services/actions/browse.go b/services/actions/browse.go index 478ec6d..83ffcae 100644 --- a/services/actions/browse.go +++ b/services/actions/browse.go @@ -6,7 +6,7 @@ import ( "io" "net/http" - "github.com/mudler/LocalAgent/core/action" + "github.com/mudler/LocalAgent/core/types" "github.com/sashabaranov/go-openai/jsonschema" "jaytaylor.com/html2text" ) @@ -18,7 +18,7 @@ func NewBrowse(config map[string]string) *BrowseAction { type BrowseAction struct{} -func (a *BrowseAction) Run(ctx context.Context, params action.ActionParams) (action.ActionResult, error) { +func (a *BrowseAction) Run(ctx context.Context, params types.ActionParams) (types.ActionResult, error) { result := struct { URL string `json:"url"` }{} @@ -26,35 +26,35 @@ func (a *BrowseAction) Run(ctx context.Context, params action.ActionParams) (act if err != nil { fmt.Printf("error: %v", err) - return action.ActionResult{}, err + return types.ActionResult{}, err } // download page with http.Client client := &http.Client{} req, err := http.NewRequest("GET", result.URL, nil) if err != nil { - return action.ActionResult{}, err + return types.ActionResult{}, err } resp, err := client.Do(req) if err != nil { - return action.ActionResult{}, err + return types.ActionResult{}, err } defer resp.Body.Close() pagebyte, err := io.ReadAll(resp.Body) if err != nil { - return action.ActionResult{}, err + return types.ActionResult{}, err } rendered, err := html2text.FromString(string(pagebyte), html2text.Options{PrettyTables: true}) if err != nil { - return action.ActionResult{}, err + return types.ActionResult{}, err } - return action.ActionResult{Result: fmt.Sprintf("The webpage '%s' content is:\n%s", result.URL, rendered)}, nil + return types.ActionResult{Result: fmt.Sprintf("The webpage '%s' content is:\n%s", result.URL, rendered)}, nil } -func (a *BrowseAction) Definition() action.ActionDefinition { - return action.ActionDefinition{ +func (a *BrowseAction) Definition() types.ActionDefinition { + return types.ActionDefinition{ Name: "browse", Description: "Use this tool to visit an URL. It browse a website page and return the text content.", Properties: map[string]jsonschema.Definition{ diff --git a/services/actions/callagents.go b/services/actions/callagents.go index 37a5fc8..71fcc04 100644 --- a/services/actions/callagents.go +++ b/services/actions/callagents.go @@ -4,9 +4,8 @@ import ( "context" "fmt" - "github.com/mudler/LocalAgent/core/action" - "github.com/mudler/LocalAgent/core/agent" "github.com/mudler/LocalAgent/core/state" + "github.com/mudler/LocalAgent/core/types" "github.com/sashabaranov/go-openai" "github.com/sashabaranov/go-openai/jsonschema" ) @@ -21,7 +20,7 @@ type CallAgentAction struct { pool *state.AgentPool } -func (a *CallAgentAction) Run(ctx context.Context, params action.ActionParams) (action.ActionResult, error) { +func (a *CallAgentAction) Run(ctx context.Context, params types.ActionParams) (types.ActionResult, error) { result := struct { AgentName string `json:"agent_name"` Message string `json:"message"` @@ -30,16 +29,16 @@ func (a *CallAgentAction) Run(ctx context.Context, params action.ActionParams) ( if err != nil { fmt.Printf("error: %v", err) - return action.ActionResult{}, err + return types.ActionResult{}, err } ag := a.pool.GetAgent(result.AgentName) if ag == nil { - return action.ActionResult{}, fmt.Errorf("agent '%s' not found", result.AgentName) + return types.ActionResult{}, fmt.Errorf("agent '%s' not found", result.AgentName) } resp := ag.Ask( - agent.WithConversationHistory( + types.WithConversationHistory( []openai.ChatCompletionMessage{ { Role: "user", @@ -49,13 +48,13 @@ func (a *CallAgentAction) Run(ctx context.Context, params action.ActionParams) ( ), ) if resp.Error != nil { - return action.ActionResult{}, err + return types.ActionResult{}, err } - return action.ActionResult{Result: resp.Response}, nil + return types.ActionResult{Result: resp.Response}, nil } -func (a *CallAgentAction) Definition() action.ActionDefinition { +func (a *CallAgentAction) Definition() types.ActionDefinition { allAgents := a.pool.AllAgents() description := "Use this tool to call another agent. Available agents and their roles are:" @@ -68,7 +67,7 @@ func (a *CallAgentAction) Definition() action.ActionDefinition { description += fmt.Sprintf("\n- %s: %s", agent, agentConfig.Description) } - return action.ActionDefinition{ + return types.ActionDefinition{ Name: "call_agent", Description: description, Properties: map[string]jsonschema.Definition{ diff --git a/services/actions/counter.go b/services/actions/counter.go index 22b4397..86dee63 100644 --- a/services/actions/counter.go +++ b/services/actions/counter.go @@ -5,7 +5,7 @@ import ( "fmt" "sync" - "github.com/mudler/LocalAgent/core/action" + "github.com/mudler/LocalAgent/core/types" "github.com/sashabaranov/go-openai/jsonschema" ) @@ -24,7 +24,7 @@ func NewCounter(config map[string]string) *CounterAction { } // Run executes the counter action -func (a *CounterAction) Run(ctx context.Context, params action.ActionParams) (action.ActionResult, error) { +func (a *CounterAction) Run(ctx context.Context, params types.ActionParams) (types.ActionResult, error) { // Parse parameters request := struct { Name string `json:"name"` @@ -32,11 +32,11 @@ func (a *CounterAction) Run(ctx context.Context, params action.ActionParams) (ac }{} if err := params.Unmarshal(&request); err != nil { - return action.ActionResult{}, fmt.Errorf("invalid parameters: %w", err) + return types.ActionResult{}, fmt.Errorf("invalid parameters: %w", err) } if request.Name == "" { - return action.ActionResult{}, fmt.Errorf("counter name cannot be empty") + return types.ActionResult{}, fmt.Errorf("counter name cannot be empty") } a.mutex.Lock() @@ -63,7 +63,7 @@ func (a *CounterAction) Run(ctx context.Context, params action.ActionParams) (ac message = fmt.Sprintf("Current value of counter '%s' is %d", request.Name, newValue) } - return action.ActionResult{ + return types.ActionResult{ Result: message, Metadata: map[string]any{ "counter_name": request.Name, @@ -75,8 +75,8 @@ func (a *CounterAction) Run(ctx context.Context, params action.ActionParams) (ac } // Definition returns the action definition -func (a *CounterAction) Definition() action.ActionDefinition { - return action.ActionDefinition{ +func (a *CounterAction) Definition() types.ActionDefinition { + return types.ActionDefinition{ Name: "counter", Description: "Create, update, or query named counters. Specify a name and an adjustment value (positive to increase, negative to decrease, zero to query).", Properties: map[string]jsonschema.Definition{ diff --git a/services/actions/genimage.go b/services/actions/genimage.go index b395923..bd4c609 100644 --- a/services/actions/genimage.go +++ b/services/actions/genimage.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/mudler/LocalAgent/core/action" + "github.com/mudler/LocalAgent/core/types" "github.com/sashabaranov/go-openai" "github.com/sashabaranov/go-openai/jsonschema" ) @@ -28,18 +28,18 @@ type GenImageAction struct { imageModel string } -func (a *GenImageAction) Run(ctx context.Context, params action.ActionParams) (action.ActionResult, error) { +func (a *GenImageAction) Run(ctx context.Context, params types.ActionParams) (types.ActionResult, error) { result := struct { Prompt string `json:"prompt"` Size string `json:"size"` }{} err := params.Unmarshal(&result) if err != nil { - return action.ActionResult{}, err + return types.ActionResult{}, err } if result.Prompt == "" { - return action.ActionResult{}, fmt.Errorf("prompt is required") + return types.ActionResult{}, fmt.Errorf("prompt is required") } req := openai.ImageRequest{ @@ -60,22 +60,22 @@ func (a *GenImageAction) Run(ctx context.Context, params action.ActionParams) (a resp, err := a.client.CreateImage(ctx, req) if err != nil { - return action.ActionResult{Result: "Failed to generate image " + err.Error()}, err + return types.ActionResult{Result: "Failed to generate image " + err.Error()}, err } if len(resp.Data) == 0 { - return action.ActionResult{Result: "Failed to generate image"}, nil + return types.ActionResult{Result: "Failed to generate image"}, nil } - return action.ActionResult{ + return types.ActionResult{ Result: fmt.Sprintf("The image was generated and available at: %s", resp.Data[0].URL), Metadata: map[string]interface{}{ MetadataImages: []string{resp.Data[0].URL}, }}, nil } -func (a *GenImageAction) Definition() action.ActionDefinition { - return action.ActionDefinition{ +func (a *GenImageAction) Definition() types.ActionDefinition { + return types.ActionDefinition{ Name: "generate_image", Description: "Generate image with.", Properties: map[string]jsonschema.Definition{ diff --git a/services/actions/genimage_test.go b/services/actions/genimage_test.go index f0af8bf..7e3f2cd 100644 --- a/services/actions/genimage_test.go +++ b/services/actions/genimage_test.go @@ -4,7 +4,7 @@ import ( "context" "os" - . "github.com/mudler/LocalAgent/core/action" + "github.com/mudler/LocalAgent/core/types" . "github.com/mudler/LocalAgent/services/actions" . "github.com/onsi/ginkgo/v2" @@ -15,7 +15,7 @@ var _ = Describe("GenImageAction", func() { var ( ctx context.Context action *GenImageAction - params ActionParams + params types.ActionParams config map[string]string ) @@ -37,7 +37,7 @@ var _ = Describe("GenImageAction", func() { Describe("Run", func() { It("should generate an image with valid prompt and size", func() { - params = ActionParams{ + params = types.ActionParams{ "prompt": "test prompt", "size": "256x256", } @@ -48,7 +48,7 @@ var _ = Describe("GenImageAction", func() { }) It("should return an error if the prompt is not provided", func() { - params = ActionParams{ + params = types.ActionParams{ "size": "256x256", } diff --git a/services/actions/githubissuecloser.go b/services/actions/githubissuecloser.go index 0db15d8..fd74df1 100644 --- a/services/actions/githubissuecloser.go +++ b/services/actions/githubissuecloser.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/google/go-github/v69/github" - "github.com/mudler/LocalAgent/core/action" + "github.com/mudler/LocalAgent/core/types" "github.com/sashabaranov/go-openai/jsonschema" ) @@ -27,7 +27,7 @@ func NewGithubIssueCloser(ctx context.Context, config map[string]string) *Github } } -func (g *GithubIssuesCloser) Run(ctx context.Context, params action.ActionParams) (action.ActionResult, error) { +func (g *GithubIssuesCloser) Run(ctx context.Context, params types.ActionParams) (types.ActionResult, error) { result := struct { Repository string `json:"repository"` Owner string `json:"owner"` @@ -37,7 +37,7 @@ func (g *GithubIssuesCloser) Run(ctx context.Context, params action.ActionParams if err != nil { fmt.Printf("error: %v", err) - return action.ActionResult{}, err + return types.ActionResult{}, err } if g.repository != "" { @@ -67,24 +67,24 @@ func (g *GithubIssuesCloser) Run(ctx context.Context, params action.ActionParams if err != nil { fmt.Printf("error: %v", err) - return action.ActionResult{}, err + return types.ActionResult{}, err } resultString := fmt.Sprintf("Closed issue %d in repository %s/%s", result.IssueNumber, result.Owner, result.Repository) if err != nil { resultString = fmt.Sprintf("Error closing issue %d in repository %s/%s: %v", result.IssueNumber, result.Owner, result.Repository, err) } - return action.ActionResult{Result: resultString}, err + return types.ActionResult{Result: resultString}, err } -func (g *GithubIssuesCloser) Definition() action.ActionDefinition { +func (g *GithubIssuesCloser) Definition() types.ActionDefinition { actionName := "close_github_issue" if g.customActionName != "" { actionName = g.customActionName } if g.repository != "" && g.owner != "" { - return action.ActionDefinition{ - Name: action.ActionDefinitionName(actionName), + return types.ActionDefinition{ + Name: types.ActionDefinitionName(actionName), Description: "Closes a Github issue.", Properties: map[string]jsonschema.Definition{ "issue_number": { @@ -96,8 +96,8 @@ func (g *GithubIssuesCloser) Definition() action.ActionDefinition { } } - return action.ActionDefinition{ - Name: action.ActionDefinitionName(actionName), + return types.ActionDefinition{ + Name: types.ActionDefinitionName(actionName), Description: "Closes a Github issue.", Properties: map[string]jsonschema.Definition{ "repository": { diff --git a/services/actions/githubissuecomment.go b/services/actions/githubissuecomment.go index 0c41557..1350a99 100644 --- a/services/actions/githubissuecomment.go +++ b/services/actions/githubissuecomment.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/google/go-github/v69/github" - "github.com/mudler/LocalAgent/core/action" + "github.com/mudler/LocalAgent/core/types" "github.com/sashabaranov/go-openai/jsonschema" ) @@ -28,7 +28,7 @@ func NewGithubIssueCommenter(ctx context.Context, config map[string]string) *Git } } -func (g *GithubIssuesCommenter) Run(ctx context.Context, params action.ActionParams) (action.ActionResult, error) { +func (g *GithubIssuesCommenter) Run(ctx context.Context, params types.ActionParams) (types.ActionResult, error) { result := struct { Repository string `json:"repository"` Owner string `json:"owner"` @@ -37,7 +37,7 @@ func (g *GithubIssuesCommenter) Run(ctx context.Context, params action.ActionPar }{} err := params.Unmarshal(&result) if err != nil { - return action.ActionResult{}, err + return types.ActionResult{}, err } if g.repository != "" && g.owner != "" { @@ -53,18 +53,18 @@ func (g *GithubIssuesCommenter) Run(ctx context.Context, params action.ActionPar if err != nil { resultString = fmt.Sprintf("Error adding comment to issue %d in repository %s/%s: %v", result.IssueNumber, result.Owner, result.Repository, err) } - return action.ActionResult{Result: resultString}, err + return types.ActionResult{Result: resultString}, err } -func (g *GithubIssuesCommenter) Definition() action.ActionDefinition { +func (g *GithubIssuesCommenter) Definition() types.ActionDefinition { actionName := "add_comment_to_github_issue" if g.customActionName != "" { actionName = g.customActionName } description := "Add a comment to a Github issue to a repository." if g.repository != "" && g.owner != "" { - return action.ActionDefinition{ - Name: action.ActionDefinitionName(actionName), + return types.ActionDefinition{ + Name: types.ActionDefinitionName(actionName), Description: description, Properties: map[string]jsonschema.Definition{ "issue_number": { @@ -79,8 +79,8 @@ func (g *GithubIssuesCommenter) Definition() action.ActionDefinition { Required: []string{"issue_number", "comment"}, } } - return action.ActionDefinition{ - Name: action.ActionDefinitionName(actionName), + return types.ActionDefinition{ + Name: types.ActionDefinitionName(actionName), Description: description, Properties: map[string]jsonschema.Definition{ "issue_number": { diff --git a/services/actions/githubissuelabeler.go b/services/actions/githubissuelabeler.go index e3d7fbc..43e8422 100644 --- a/services/actions/githubissuelabeler.go +++ b/services/actions/githubissuelabeler.go @@ -6,7 +6,7 @@ import ( "strings" "github.com/google/go-github/v69/github" - "github.com/mudler/LocalAgent/core/action" + "github.com/mudler/LocalAgent/core/types" "github.com/mudler/LocalAgent/pkg/xlog" "github.com/sashabaranov/go-openai/jsonschema" ) @@ -39,7 +39,7 @@ func NewGithubIssueLabeler(ctx context.Context, config map[string]string) *Githu } } -func (g *GithubIssuesLabeler) Run(ctx context.Context, params action.ActionParams) (action.ActionResult, error) { +func (g *GithubIssuesLabeler) Run(ctx context.Context, params types.ActionParams) (types.ActionResult, error) { result := struct { Repository string `json:"repository"` Owner string `json:"owner"` @@ -48,7 +48,7 @@ func (g *GithubIssuesLabeler) Run(ctx context.Context, params action.ActionParam }{} err := params.Unmarshal(&result) if err != nil { - return action.ActionResult{}, err + return types.ActionResult{}, err } if g.repository != "" && g.owner != "" { @@ -67,17 +67,17 @@ func (g *GithubIssuesLabeler) Run(ctx context.Context, params action.ActionParam if err != nil { resultString = fmt.Sprintf("Error adding label '%s' to issue %d in repository %s/%s: %v", result.Label, result.IssueNumber, result.Owner, result.Repository, err) } - return action.ActionResult{Result: resultString}, err + return types.ActionResult{Result: resultString}, err } -func (g *GithubIssuesLabeler) Definition() action.ActionDefinition { +func (g *GithubIssuesLabeler) Definition() types.ActionDefinition { actionName := "add_label_to_github_issue" if g.customActionName != "" { actionName = g.customActionName } if g.repository != "" && g.owner != "" { - return action.ActionDefinition{ - Name: action.ActionDefinitionName(actionName), + return types.ActionDefinition{ + Name: types.ActionDefinitionName(actionName), Description: "Add a label to a Github issue. You might want to assign labels to issues to categorize them.", Properties: map[string]jsonschema.Definition{ "issue_number": { @@ -93,8 +93,8 @@ func (g *GithubIssuesLabeler) Definition() action.ActionDefinition { Required: []string{"issue_number", "label"}, } } - return action.ActionDefinition{ - Name: action.ActionDefinitionName(actionName), + return types.ActionDefinition{ + Name: types.ActionDefinitionName(actionName), Description: "Add a label to a Github issue. You might want to assign labels to issues to categorize them.", Properties: map[string]jsonschema.Definition{ "issue_number": { diff --git a/services/actions/githubissueopener.go b/services/actions/githubissueopener.go index 2622734..615290a 100644 --- a/services/actions/githubissueopener.go +++ b/services/actions/githubissueopener.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/google/go-github/v69/github" - "github.com/mudler/LocalAgent/core/action" + "github.com/mudler/LocalAgent/core/types" "github.com/sashabaranov/go-openai/jsonschema" ) @@ -28,7 +28,7 @@ func NewGithubIssueOpener(ctx context.Context, config map[string]string) *Github } } -func (g *GithubIssuesOpener) Run(ctx context.Context, params action.ActionParams) (action.ActionResult, error) { +func (g *GithubIssuesOpener) Run(ctx context.Context, params types.ActionParams) (types.ActionResult, error) { result := struct { Title string `json:"title"` Body string `json:"text"` @@ -39,7 +39,7 @@ func (g *GithubIssuesOpener) Run(ctx context.Context, params action.ActionParams if err != nil { fmt.Printf("error: %v", err) - return action.ActionResult{}, err + return types.ActionResult{}, err } if g.repository != "" && g.owner != "" { @@ -60,17 +60,17 @@ func (g *GithubIssuesOpener) Run(ctx context.Context, params action.ActionParams resultString = fmt.Sprintf("Created issue %d in repository %s/%s: %s", createdIssue.GetNumber(), result.Owner, result.Repository, createdIssue.GetURL()) } - return action.ActionResult{Result: resultString}, err + return types.ActionResult{Result: resultString}, err } -func (g *GithubIssuesOpener) Definition() action.ActionDefinition { +func (g *GithubIssuesOpener) Definition() types.ActionDefinition { actionName := "create_github_issue" if g.customActionName != "" { actionName = g.customActionName } if g.repository != "" && g.owner != "" { - return action.ActionDefinition{ - Name: action.ActionDefinitionName(actionName), + return types.ActionDefinition{ + Name: types.ActionDefinitionName(actionName), Description: "Create a new issue on a GitHub repository.", Properties: map[string]jsonschema.Definition{ "text": { @@ -85,8 +85,8 @@ func (g *GithubIssuesOpener) Definition() action.ActionDefinition { Required: []string{"title", "text"}, } } - return action.ActionDefinition{ - Name: action.ActionDefinitionName(actionName), + return types.ActionDefinition{ + Name: types.ActionDefinitionName(actionName), Description: "Create a new issue on a GitHub repository.", Properties: map[string]jsonschema.Definition{ "text": { diff --git a/services/actions/githubissuereader.go b/services/actions/githubissuereader.go index d1515d9..0f8d5ce 100644 --- a/services/actions/githubissuereader.go +++ b/services/actions/githubissuereader.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/google/go-github/v69/github" - "github.com/mudler/LocalAgent/core/action" + "github.com/mudler/LocalAgent/core/types" "github.com/sashabaranov/go-openai/jsonschema" ) @@ -28,7 +28,7 @@ func NewGithubIssueReader(ctx context.Context, config map[string]string) *Github } } -func (g *GithubIssuesReader) Run(ctx context.Context, params action.ActionParams) (action.ActionResult, error) { +func (g *GithubIssuesReader) Run(ctx context.Context, params types.ActionParams) (types.ActionResult, error) { result := struct { Repository string `json:"repository"` Owner string `json:"owner"` @@ -37,7 +37,7 @@ func (g *GithubIssuesReader) Run(ctx context.Context, params action.ActionParams }{} err := params.Unmarshal(&result) if err != nil { - return action.ActionResult{}, err + return types.ActionResult{}, err } if g.repository != "" && g.owner != "" { @@ -47,26 +47,26 @@ func (g *GithubIssuesReader) Run(ctx context.Context, params action.ActionParams issue, _, err := g.client.Issues.Get(g.context, result.Owner, result.Repository, result.IssueNumber) if err == nil && issue != nil { - return action.ActionResult{ + return types.ActionResult{ Result: fmt.Sprintf( "Issue %d Repository: %s\nTitle: %s\nBody: %s", *issue.Number, *issue.Repository.FullName, *issue.Title, *issue.Body)}, nil } if err != nil { - return action.ActionResult{Result: fmt.Sprintf("Error fetching issue: %s", err.Error())}, err + return types.ActionResult{Result: fmt.Sprintf("Error fetching issue: %s", err.Error())}, err } - return action.ActionResult{Result: fmt.Sprintf("No issue found")}, err + return types.ActionResult{Result: fmt.Sprintf("No issue found")}, err } -func (g *GithubIssuesReader) Definition() action.ActionDefinition { +func (g *GithubIssuesReader) Definition() types.ActionDefinition { actionName := "read_github_issue" if g.customActionName != "" { actionName = g.customActionName } description := "Read a Github issue." if g.repository != "" && g.owner != "" { - return action.ActionDefinition{ - Name: action.ActionDefinitionName(actionName), + return types.ActionDefinition{ + Name: types.ActionDefinitionName(actionName), Description: description, Properties: map[string]jsonschema.Definition{ "issue_number": { @@ -77,8 +77,8 @@ func (g *GithubIssuesReader) Definition() action.ActionDefinition { Required: []string{"issue_number"}, } } - return action.ActionDefinition{ - Name: action.ActionDefinitionName(actionName), + return types.ActionDefinition{ + Name: types.ActionDefinitionName(actionName), Description: description, Properties: map[string]jsonschema.Definition{ "issue_number": { diff --git a/services/actions/githubissuesearch.go b/services/actions/githubissuesearch.go index 5b95d81..aecdc9e 100644 --- a/services/actions/githubissuesearch.go +++ b/services/actions/githubissuesearch.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/google/go-github/v69/github" - "github.com/mudler/LocalAgent/core/action" + "github.com/mudler/LocalAgent/core/types" "github.com/mudler/LocalAgent/pkg/xlog" "github.com/sashabaranov/go-openai/jsonschema" ) @@ -29,7 +29,7 @@ func NewGithubIssueSearch(ctx context.Context, config map[string]string) *Github } } -func (g *GithubIssueSearch) Run(ctx context.Context, params action.ActionParams) (action.ActionResult, error) { +func (g *GithubIssueSearch) Run(ctx context.Context, params types.ActionParams) (types.ActionResult, error) { result := struct { Query string `json:"query"` Repository string `json:"repository"` @@ -39,7 +39,7 @@ func (g *GithubIssueSearch) Run(ctx context.Context, params action.ActionParams) if err != nil { fmt.Printf("error: %v", err) - return action.ActionResult{}, err + return types.ActionResult{}, err } if g.repository != "" && g.owner != "" { @@ -56,7 +56,7 @@ func (g *GithubIssueSearch) Run(ctx context.Context, params action.ActionParams) }) if err != nil { resultString = fmt.Sprintf("Error listing issues: %v", err) - return action.ActionResult{Result: resultString}, err + return types.ActionResult{Result: resultString}, err } for _, i := range issues.Issues { xlog.Info("Issue found", "title", i.GetTitle()) @@ -65,17 +65,17 @@ func (g *GithubIssueSearch) Run(ctx context.Context, params action.ActionParams) // resultString += fmt.Sprintf("Body: %s\n", i.GetBody()) } - return action.ActionResult{Result: resultString}, err + return types.ActionResult{Result: resultString}, err } -func (g *GithubIssueSearch) Definition() action.ActionDefinition { +func (g *GithubIssueSearch) Definition() types.ActionDefinition { actionName := "search_github_issue" if g.customActionName != "" { actionName = g.customActionName } if g.repository != "" && g.owner != "" { - return action.ActionDefinition{ - Name: action.ActionDefinitionName(actionName), + return types.ActionDefinition{ + Name: types.ActionDefinitionName(actionName), Description: "Search between github issues", Properties: map[string]jsonschema.Definition{ "query": { @@ -86,8 +86,8 @@ func (g *GithubIssueSearch) Definition() action.ActionDefinition { Required: []string{"query"}, } } - return action.ActionDefinition{ - Name: action.ActionDefinitionName(actionName), + return types.ActionDefinition{ + Name: types.ActionDefinitionName(actionName), Description: "Search between github issues", Properties: map[string]jsonschema.Definition{ "query": { diff --git a/services/actions/githubrepositorycreateupdatecontent.go b/services/actions/githubrepositorycreateupdatecontent.go index 672b700..82fd1a3 100644 --- a/services/actions/githubrepositorycreateupdatecontent.go +++ b/services/actions/githubrepositorycreateupdatecontent.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/google/go-github/v69/github" - "github.com/mudler/LocalAgent/core/action" + "github.com/mudler/LocalAgent/core/types" "github.com/sashabaranov/go-openai/jsonschema" ) @@ -31,7 +31,7 @@ func NewGithubRepositoryCreateOrUpdateContent(ctx context.Context, config map[st } } -func (g *GithubRepositoryCreateOrUpdateContent) Run(ctx context.Context, params action.ActionParams) (action.ActionResult, error) { +func (g *GithubRepositoryCreateOrUpdateContent) Run(ctx context.Context, params types.ActionParams) (types.ActionResult, error) { result := struct { Path string `json:"path"` Repository string `json:"repository"` @@ -44,7 +44,7 @@ func (g *GithubRepositoryCreateOrUpdateContent) Run(ctx context.Context, params if err != nil { fmt.Printf("error: %v", err) - return action.ActionResult{}, err + return types.ActionResult{}, err } if result.Branch == "" { @@ -82,13 +82,13 @@ func (g *GithubRepositoryCreateOrUpdateContent) Run(ctx context.Context, params }) if err != nil { resultString := fmt.Sprintf("Error creating content : %v", err) - return action.ActionResult{Result: resultString}, err + return types.ActionResult{Result: resultString}, err } - return action.ActionResult{Result: fmt.Sprintf("File created/updated: %s\n", fileContent.GetURL())}, err + return types.ActionResult{Result: fmt.Sprintf("File created/updated: %s\n", fileContent.GetURL())}, err } -func (g *GithubRepositoryCreateOrUpdateContent) Definition() action.ActionDefinition { +func (g *GithubRepositoryCreateOrUpdateContent) Definition() types.ActionDefinition { actionName := "github_repository_create_or_update_content" actionDescription := "Create or update a file in a GitHub repository" if g.customActionName != "" { @@ -117,8 +117,8 @@ func (g *GithubRepositoryCreateOrUpdateContent) Definition() action.ActionDefini } if g.repository != "" && g.owner != "" { - return action.ActionDefinition{ - Name: action.ActionDefinitionName(actionName), + return types.ActionDefinition{ + Name: types.ActionDefinitionName(actionName), Description: actionDescription, Properties: properties, Required: []string{"path", "content"}, @@ -135,8 +135,8 @@ func (g *GithubRepositoryCreateOrUpdateContent) Definition() action.ActionDefini Description: "The repository to search in", } - return action.ActionDefinition{ - Name: action.ActionDefinitionName(actionName), + return types.ActionDefinition{ + Name: types.ActionDefinitionName(actionName), Description: actionDescription, Properties: properties, Required: []string{"path", "repository", "owner", "content"}, diff --git a/services/actions/githubrepositorygetcontent.go b/services/actions/githubrepositorygetcontent.go index 4726983..733c145 100644 --- a/services/actions/githubrepositorygetcontent.go +++ b/services/actions/githubrepositorygetcontent.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/google/go-github/v69/github" - "github.com/mudler/LocalAgent/core/action" + "github.com/mudler/LocalAgent/core/types" "github.com/sashabaranov/go-openai/jsonschema" ) @@ -28,7 +28,7 @@ func NewGithubRepositoryGetContent(ctx context.Context, config map[string]string } } -func (g *GithubRepositoryGetContent) Run(ctx context.Context, params action.ActionParams) (action.ActionResult, error) { +func (g *GithubRepositoryGetContent) Run(ctx context.Context, params types.ActionParams) (types.ActionResult, error) { result := struct { Path string `json:"path"` Repository string `json:"repository"` @@ -38,7 +38,7 @@ func (g *GithubRepositoryGetContent) Run(ctx context.Context, params action.Acti if err != nil { fmt.Printf("error: %v", err) - return action.ActionResult{}, err + return types.ActionResult{}, err } if g.repository != "" && g.owner != "" { @@ -49,7 +49,7 @@ func (g *GithubRepositoryGetContent) Run(ctx context.Context, params action.Acti fileContent, directoryContent, _, err := g.client.Repositories.GetContents(g.context, result.Owner, result.Repository, result.Path, nil) if err != nil { resultString := fmt.Sprintf("Error getting content : %v", err) - return action.ActionResult{Result: resultString}, err + return types.ActionResult{Result: resultString}, err } if len(directoryContent) > 0 { @@ -57,26 +57,26 @@ func (g *GithubRepositoryGetContent) Run(ctx context.Context, params action.Acti for _, f := range directoryContent { resultString += fmt.Sprintf("File: %s\n", f.GetName()) } - return action.ActionResult{Result: resultString}, err + return types.ActionResult{Result: resultString}, err } content, err := fileContent.GetContent() if err != nil { - return action.ActionResult{}, err + return types.ActionResult{}, err } - return action.ActionResult{Result: fmt.Sprintf("File %s\nContent:%s\n", result.Path, content)}, err + return types.ActionResult{Result: fmt.Sprintf("File %s\nContent:%s\n", result.Path, content)}, err } -func (g *GithubRepositoryGetContent) Definition() action.ActionDefinition { +func (g *GithubRepositoryGetContent) Definition() types.ActionDefinition { actionName := "get_github_repository_content" actionDescription := "Get content of a file or directory in a github repository" if g.customActionName != "" { actionName = g.customActionName } if g.repository != "" && g.owner != "" { - return action.ActionDefinition{ - Name: action.ActionDefinitionName(actionName), + return types.ActionDefinition{ + Name: types.ActionDefinitionName(actionName), Description: actionDescription, Properties: map[string]jsonschema.Definition{ "path": { @@ -87,8 +87,8 @@ func (g *GithubRepositoryGetContent) Definition() action.ActionDefinition { Required: []string{"path"}, } } - return action.ActionDefinition{ - Name: action.ActionDefinitionName(actionName), + return types.ActionDefinition{ + Name: types.ActionDefinitionName(actionName), Description: actionDescription, Properties: map[string]jsonschema.Definition{ "path": { diff --git a/services/actions/githubrepositoryreadme.go b/services/actions/githubrepositoryreadme.go index f6b3d34..5bd194f 100644 --- a/services/actions/githubrepositoryreadme.go +++ b/services/actions/githubrepositoryreadme.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/google/go-github/v69/github" - "github.com/mudler/LocalAgent/core/action" + "github.com/mudler/LocalAgent/core/types" "github.com/sashabaranov/go-openai/jsonschema" ) @@ -26,7 +26,7 @@ func NewGithubRepositoryREADME(ctx context.Context, config map[string]string) *G } } -func (g *GithubRepositoryREADME) Run(ctx context.Context, params action.ActionParams) (action.ActionResult, error) { +func (g *GithubRepositoryREADME) Run(ctx context.Context, params types.ActionParams) (types.ActionResult, error) { result := struct { Repository string `json:"repository"` Owner string `json:"owner"` @@ -35,30 +35,30 @@ func (g *GithubRepositoryREADME) Run(ctx context.Context, params action.ActionPa if err != nil { fmt.Printf("error: %v", err) - return action.ActionResult{}, err + return types.ActionResult{}, err } fileContent, _, err := g.client.Repositories.GetReadme(g.context, result.Owner, result.Repository, &github.RepositoryContentGetOptions{}) if err != nil { resultString := fmt.Sprintf("Error getting content : %v", err) - return action.ActionResult{Result: resultString}, err + return types.ActionResult{Result: resultString}, err } content, err := fileContent.GetContent() if err != nil { - return action.ActionResult{}, err + return types.ActionResult{}, err } - return action.ActionResult{Result: content}, err + return types.ActionResult{Result: content}, err } -func (g *GithubRepositoryREADME) Definition() action.ActionDefinition { +func (g *GithubRepositoryREADME) Definition() types.ActionDefinition { actionName := "github_readme" actionDescription := "Get the README file of a GitHub repository to have a basic understanding of the project." if g.customActionName != "" { actionName = g.customActionName } - return action.ActionDefinition{ - Name: action.ActionDefinitionName(actionName), + return types.ActionDefinition{ + Name: types.ActionDefinitionName(actionName), Description: actionDescription, Properties: map[string]jsonschema.Definition{ "repository": { diff --git a/services/actions/scrape.go b/services/actions/scrape.go index 678f31a..3bd0e29 100644 --- a/services/actions/scrape.go +++ b/services/actions/scrape.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/mudler/LocalAgent/core/action" + "github.com/mudler/LocalAgent/core/types" "github.com/sashabaranov/go-openai/jsonschema" "github.com/tmc/langchaingo/tools/scraper" ) @@ -16,7 +16,7 @@ func NewScraper(config map[string]string) *ScraperAction { type ScraperAction struct{} -func (a *ScraperAction) Run(ctx context.Context, params action.ActionParams) (action.ActionResult, error) { +func (a *ScraperAction) Run(ctx context.Context, params types.ActionParams) (types.ActionResult, error) { result := struct { URL string `json:"url"` }{} @@ -24,25 +24,25 @@ func (a *ScraperAction) Run(ctx context.Context, params action.ActionParams) (ac if err != nil { fmt.Printf("error: %v", err) - return action.ActionResult{}, err + return types.ActionResult{}, err } scraper, err := scraper.New() if err != nil { fmt.Printf("error: %v", err) - return action.ActionResult{}, err + return types.ActionResult{}, err } res, err := scraper.Call(ctx, result.URL) if err != nil { fmt.Printf("error: %v", err) - return action.ActionResult{}, err + return types.ActionResult{}, err } - return action.ActionResult{Result: res}, nil + return types.ActionResult{Result: res}, nil } -func (a *ScraperAction) Definition() action.ActionDefinition { - return action.ActionDefinition{ +func (a *ScraperAction) Definition() types.ActionDefinition { + return types.ActionDefinition{ Name: "scrape", Description: "Scrapes a full website content and returns the entire site data.", Properties: map[string]jsonschema.Definition{ diff --git a/services/actions/search.go b/services/actions/search.go index 086512b..27130f6 100644 --- a/services/actions/search.go +++ b/services/actions/search.go @@ -6,7 +6,7 @@ import ( "log/slog" "strings" - "github.com/mudler/LocalAgent/core/action" + "github.com/mudler/LocalAgent/core/types" "github.com/sashabaranov/go-openai/jsonschema" "github.com/tmc/langchaingo/tools/duckduckgo" "mvdan.cc/xurls/v2" @@ -34,7 +34,7 @@ func NewSearch(config map[string]string) *SearchAction { type SearchAction struct{ results int } -func (a *SearchAction) Run(ctx context.Context, params action.ActionParams) (action.ActionResult, error) { +func (a *SearchAction) Run(ctx context.Context, params types.ActionParams) (types.ActionResult, error) { result := struct { Query string `json:"query"` }{} @@ -42,19 +42,19 @@ func (a *SearchAction) Run(ctx context.Context, params action.ActionParams) (act if err != nil { fmt.Printf("error: %v", err) - return action.ActionResult{}, err + return types.ActionResult{}, err } ddg, err := duckduckgo.New(a.results, "LocalAgent") if err != nil { fmt.Printf("error: %v", err) - return action.ActionResult{}, err + return types.ActionResult{}, err } res, err := ddg.Call(ctx, result.Query) if err != nil { fmt.Printf("error: %v", err) - return action.ActionResult{}, err + return types.ActionResult{}, err } rxStrict := xurls.Strict() @@ -69,11 +69,11 @@ func (a *SearchAction) Run(ctx context.Context, params action.ActionParams) (act results = append(results, u) } - return action.ActionResult{Result: res, Metadata: map[string]interface{}{MetadataUrls: results}}, nil + return types.ActionResult{Result: res, Metadata: map[string]interface{}{MetadataUrls: results}}, nil } -func (a *SearchAction) Definition() action.ActionDefinition { - return action.ActionDefinition{ +func (a *SearchAction) Definition() types.ActionDefinition { + return types.ActionDefinition{ Name: "search_internet", Description: "Search the internet for something.", Properties: map[string]jsonschema.Definition{ diff --git a/services/actions/sendmail.go b/services/actions/sendmail.go index 3e4b342..bc46a13 100644 --- a/services/actions/sendmail.go +++ b/services/actions/sendmail.go @@ -5,7 +5,7 @@ import ( "fmt" "net/smtp" - "github.com/mudler/LocalAgent/core/action" + "github.com/mudler/LocalAgent/core/types" "github.com/sashabaranov/go-openai/jsonschema" ) @@ -27,7 +27,7 @@ type SendMailAction struct { smtpPort string } -func (a *SendMailAction) Run(ctx context.Context, params action.ActionParams) (action.ActionResult, error) { +func (a *SendMailAction) Run(ctx context.Context, params types.ActionParams) (types.ActionResult, error) { result := struct { Message string `json:"message"` To string `json:"to"` @@ -37,7 +37,7 @@ func (a *SendMailAction) Run(ctx context.Context, params action.ActionParams) (a if err != nil { fmt.Printf("error: %v", err) - return action.ActionResult{}, err + return types.ActionResult{}, err } // Authentication. @@ -50,13 +50,13 @@ func (a *SendMailAction) Run(ctx context.Context, params action.ActionParams) (a result.To, }, []byte(result.Message)) if err != nil { - return action.ActionResult{}, err + return types.ActionResult{}, err } - return action.ActionResult{Result: fmt.Sprintf("Email sent to %s", result.To)}, nil + return types.ActionResult{Result: fmt.Sprintf("Email sent to %s", result.To)}, nil } -func (a *SendMailAction) Definition() action.ActionDefinition { - return action.ActionDefinition{ +func (a *SendMailAction) Definition() types.ActionDefinition { + return types.ActionDefinition{ Name: "send_email", Description: "Send an email.", Properties: map[string]jsonschema.Definition{ diff --git a/services/actions/shell.go b/services/actions/shell.go index fe7956b..9f95c27 100644 --- a/services/actions/shell.go +++ b/services/actions/shell.go @@ -5,7 +5,7 @@ import ( "fmt" "log" - "github.com/mudler/LocalAgent/core/action" + "github.com/mudler/LocalAgent/core/types" "github.com/sashabaranov/go-openai/jsonschema" "golang.org/x/crypto/ssh" ) @@ -27,7 +27,7 @@ type ShellAction struct { customDescription string } -func (a *ShellAction) Run(ctx context.Context, params action.ActionParams) (action.ActionResult, error) { +func (a *ShellAction) Run(ctx context.Context, params types.ActionParams) (types.ActionResult, error) { result := struct { Command string `json:"command"` Host string `json:"host"` @@ -37,7 +37,7 @@ func (a *ShellAction) Run(ctx context.Context, params action.ActionParams) (acti if err != nil { fmt.Printf("error: %v", err) - return action.ActionResult{}, err + return types.ActionResult{}, err } if a.host != "" && a.user != "" { @@ -47,13 +47,13 @@ func (a *ShellAction) Run(ctx context.Context, params action.ActionParams) (acti output, err := sshCommand(a.privateKey, result.Command, result.User, result.Host) if err != nil { - return action.ActionResult{}, err + return types.ActionResult{}, err } - return action.ActionResult{Result: output}, nil + return types.ActionResult{Result: output}, nil } -func (a *ShellAction) Definition() action.ActionDefinition { +func (a *ShellAction) Definition() types.ActionDefinition { name := "shell" description := "Run a shell command on a remote server." if a.customName != "" { @@ -63,8 +63,8 @@ func (a *ShellAction) Definition() action.ActionDefinition { description = a.customDescription } if a.host != "" && a.user != "" { - return action.ActionDefinition{ - Name: action.ActionDefinitionName(name), + return types.ActionDefinition{ + Name: types.ActionDefinitionName(name), Description: description, Properties: map[string]jsonschema.Definition{ "command": { @@ -75,8 +75,8 @@ func (a *ShellAction) Definition() action.ActionDefinition { Required: []string{"command"}, } } - return action.ActionDefinition{ - Name: action.ActionDefinitionName(name), + return types.ActionDefinition{ + Name: types.ActionDefinitionName(name), Description: description, Properties: map[string]jsonschema.Definition{ "command": { diff --git a/services/actions/twitter_post.go b/services/actions/twitter_post.go index d438c0f..d0bd417 100644 --- a/services/actions/twitter_post.go +++ b/services/actions/twitter_post.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/mudler/LocalAgent/core/action" + "github.com/mudler/LocalAgent/core/types" "github.com/mudler/LocalAgent/services/connectors/twitter" "github.com/sashabaranov/go-openai/jsonschema" ) @@ -21,7 +21,7 @@ type PostTweetAction struct { noCharacterLimit bool } -func (a *PostTweetAction) Run(ctx context.Context, params action.ActionParams) (action.ActionResult, error) { +func (a *PostTweetAction) Run(ctx context.Context, params types.ActionParams) (types.ActionResult, error) { result := struct { Text string `json:"text"` }{} @@ -29,24 +29,24 @@ func (a *PostTweetAction) Run(ctx context.Context, params action.ActionParams) ( if err != nil { fmt.Printf("error: %v", err) - return action.ActionResult{}, err + return types.ActionResult{}, err } if !a.noCharacterLimit && len(result.Text) > 280 { - return action.ActionResult{}, fmt.Errorf("tweet is too long, max 280 characters") + return types.ActionResult{}, fmt.Errorf("tweet is too long, max 280 characters") } client := twitter.NewTwitterClient(a.token) if err := client.Post(result.Text); err != nil { - return action.ActionResult{}, err + return types.ActionResult{}, err } - return action.ActionResult{Result: fmt.Sprintf("twitter post created")}, nil + return types.ActionResult{Result: fmt.Sprintf("twitter post created")}, nil } -func (a *PostTweetAction) Definition() action.ActionDefinition { - return action.ActionDefinition{ +func (a *PostTweetAction) Definition() types.ActionDefinition { + return types.ActionDefinition{ Name: "post_tweet", Description: "Post a tweet", Properties: map[string]jsonschema.Definition{ diff --git a/services/actions/wikipedia.go b/services/actions/wikipedia.go index c3af3d5..81b21a1 100644 --- a/services/actions/wikipedia.go +++ b/services/actions/wikipedia.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/mudler/LocalAgent/core/action" + "github.com/mudler/LocalAgent/core/types" "github.com/sashabaranov/go-openai/jsonschema" "github.com/tmc/langchaingo/tools/wikipedia" ) @@ -15,7 +15,7 @@ func NewWikipedia(config map[string]string) *WikipediaAction { type WikipediaAction struct{} -func (a *WikipediaAction) Run(ctx context.Context, params action.ActionParams) (action.ActionResult, error) { +func (a *WikipediaAction) Run(ctx context.Context, params types.ActionParams) (types.ActionResult, error) { result := struct { Query string `json:"query"` }{} @@ -23,20 +23,20 @@ func (a *WikipediaAction) Run(ctx context.Context, params action.ActionParams) ( if err != nil { fmt.Printf("error: %v", err) - return action.ActionResult{}, err + return types.ActionResult{}, err } wiki := wikipedia.New("LocalAgent") res, err := wiki.Call(ctx, result.Query) if err != nil { fmt.Printf("error: %v", err) - return action.ActionResult{}, err + return types.ActionResult{}, err } - return action.ActionResult{Result: res}, nil + return types.ActionResult{Result: res}, nil } -func (a *WikipediaAction) Definition() action.ActionDefinition { - return action.ActionDefinition{ +func (a *WikipediaAction) Definition() types.ActionDefinition { + return types.ActionDefinition{ Name: "wikipedia", Description: "Find wikipedia pages using the wikipedia api", Properties: map[string]jsonschema.Definition{ diff --git a/services/connectors/discord.go b/services/connectors/discord.go index 8365058..d264d08 100644 --- a/services/connectors/discord.go +++ b/services/connectors/discord.go @@ -5,6 +5,7 @@ import ( "github.com/bwmarrin/discordgo" "github.com/mudler/LocalAgent/core/agent" + "github.com/mudler/LocalAgent/core/types" "github.com/mudler/LocalAgent/pkg/xlog" ) @@ -24,14 +25,14 @@ func NewDiscord(config map[string]string) *Discord { } } -func (d *Discord) AgentResultCallback() func(state agent.ActionState) { - return func(state agent.ActionState) { +func (d *Discord) AgentResultCallback() func(state types.ActionState) { + return func(state types.ActionState) { // Send the result to the bot } } -func (d *Discord) AgentReasoningCallback() func(state agent.ActionCurrentState) bool { - return func(state agent.ActionCurrentState) bool { +func (d *Discord) AgentReasoningCallback() func(state types.ActionCurrentState) bool { + return func(state types.ActionCurrentState) bool { // Send the reasoning to the bot return true } @@ -84,7 +85,7 @@ func (d *Discord) messageCreate(a *agent.Agent) func(s *discordgo.Session, m *di content = strings.ReplaceAll(content, "<@"+s.State.User.ID+"> ", "") xlog.Info("Received message", "content", content) job := a.Ask( - agent.WithText( + types.WithText( content, ), ) diff --git a/services/connectors/githubissue.go b/services/connectors/githubissue.go index bd6506e..67c1b75 100644 --- a/services/connectors/githubissue.go +++ b/services/connectors/githubissue.go @@ -7,6 +7,7 @@ import ( "github.com/google/go-github/v69/github" "github.com/mudler/LocalAgent/core/agent" + "github.com/mudler/LocalAgent/core/types" "github.com/mudler/LocalAgent/pkg/xlog" "github.com/sashabaranov/go-openai" @@ -50,14 +51,14 @@ func NewGithubIssueWatcher(config map[string]string) *GithubIssues { } } -func (g *GithubIssues) AgentResultCallback() func(state agent.ActionState) { - return func(state agent.ActionState) { +func (g *GithubIssues) AgentResultCallback() func(state types.ActionState) { + return func(state types.ActionState) { // Send the result to the bot } } -func (g *GithubIssues) AgentReasoningCallback() func(state agent.ActionCurrentState) bool { - return func(state agent.ActionCurrentState) bool { +func (g *GithubIssues) AgentReasoningCallback() func(state types.ActionCurrentState) bool { + return func(state types.ActionCurrentState) bool { // Send the reasoning to the bot return true } @@ -175,7 +176,7 @@ func (g *GithubIssues) issuesService() { } res := g.agent.Ask( - agent.WithConversationHistory(messages), + types.WithConversationHistory(messages), ) if res.Error != nil { xlog.Error("Error asking", "error", res.Error, "agent", g.agent.Character.Name) diff --git a/services/connectors/githubpr.go b/services/connectors/githubpr.go index 3114e01..3041f73 100644 --- a/services/connectors/githubpr.go +++ b/services/connectors/githubpr.go @@ -7,6 +7,7 @@ import ( "github.com/google/go-github/v69/github" "github.com/mudler/LocalAgent/core/agent" + "github.com/mudler/LocalAgent/core/types" "github.com/mudler/LocalAgent/pkg/xlog" "github.com/sashabaranov/go-openai" @@ -50,14 +51,14 @@ func NewGithubPRWatcher(config map[string]string) *GithubPRs { } } -func (g *GithubPRs) AgentResultCallback() func(state agent.ActionState) { - return func(state agent.ActionState) { +func (g *GithubPRs) AgentResultCallback() func(state types.ActionState) { + return func(state types.ActionState) { // Send the result to the bot } } -func (g *GithubPRs) AgentReasoningCallback() func(state agent.ActionCurrentState) bool { - return func(state agent.ActionCurrentState) bool { +func (g *GithubPRs) AgentReasoningCallback() func(state types.ActionCurrentState) bool { + return func(state types.ActionCurrentState) bool { // Send the reasoning to the bot return true } @@ -175,7 +176,7 @@ func (g *GithubPRs) prService() { } res := g.agent.Ask( - agent.WithConversationHistory(messages), + types.WithConversationHistory(messages), ) if res.Error != nil { xlog.Error("Error asking", "error", res.Error, "agent", g.agent.Character.Name) diff --git a/services/connectors/irc.go b/services/connectors/irc.go index b3c1a06..53a2fd2 100644 --- a/services/connectors/irc.go +++ b/services/connectors/irc.go @@ -6,6 +6,7 @@ import ( "time" "github.com/mudler/LocalAgent/core/agent" + "github.com/mudler/LocalAgent/core/types" "github.com/mudler/LocalAgent/pkg/xlog" "github.com/mudler/LocalAgent/services/actions" irc "github.com/thoj/go-ircevent" @@ -30,14 +31,14 @@ func NewIRC(config map[string]string) *IRC { } } -func (i *IRC) AgentResultCallback() func(state agent.ActionState) { - return func(state agent.ActionState) { +func (i *IRC) AgentResultCallback() func(state types.ActionState) { + return func(state types.ActionState) { // Send the result to the bot } } -func (i *IRC) AgentReasoningCallback() func(state agent.ActionCurrentState) bool { - return func(state agent.ActionCurrentState) bool { +func (i *IRC) AgentReasoningCallback() func(state types.ActionCurrentState) bool { + return func(state types.ActionCurrentState) bool { // Send the reasoning to the bot return true } @@ -105,7 +106,7 @@ func (i *IRC) Start(a *agent.Agent) { go func() { res := a.Ask( - agent.WithText(cleanedMessage), + types.WithText(cleanedMessage), ) xlog.Info("Sending message", "message", res.Response, "channel", channel) diff --git a/services/connectors/slack.go b/services/connectors/slack.go index 30af50e..f84e56c 100644 --- a/services/connectors/slack.go +++ b/services/connectors/slack.go @@ -14,6 +14,7 @@ import ( "github.com/sashabaranov/go-openai" "github.com/mudler/LocalAgent/core/agent" + "github.com/mudler/LocalAgent/core/types" "github.com/slack-go/slack/socketmode" @@ -39,14 +40,14 @@ func NewSlack(config map[string]string) *Slack { } } -func (t *Slack) AgentResultCallback() func(state agent.ActionState) { - return func(state agent.ActionState) { +func (t *Slack) AgentResultCallback() func(state types.ActionState) { + return func(state types.ActionState) { // Send the result to the bot } } -func (t *Slack) AgentReasoningCallback() func(state agent.ActionCurrentState) bool { - return func(state agent.ActionCurrentState) bool { +func (t *Slack) AgentReasoningCallback() func(state types.ActionCurrentState) bool { + return func(state types.ActionCurrentState) bool { // Send the reasoning to the bot return true } @@ -71,7 +72,7 @@ func uniqueStringSlice(s []string) []string { return list } -func generateAttachmentsFromJobResponse(j *agent.JobResult) (attachments []slack.Attachment) { +func generateAttachmentsFromJobResponse(j *types.JobResult) (attachments []slack.Attachment) { for _, state := range j.State { // coming from the search action if urls, exists := state.Metadata[actions.MetadataUrls]; exists { @@ -148,7 +149,7 @@ func (t *Slack) handleChannelMessage( } } - agentOptions := []agent.JobOption{agent.WithText(message)} + agentOptions := []types.JobOption{types.WithText(message)} // If the last message has an image, we send it as a multi content message if len(imageBytes.Bytes()) > 0 { @@ -158,7 +159,7 @@ func (t *Slack) handleChannelMessage( if err != nil { xlog.Error(fmt.Sprintf("Error encoding image to base64: %v", err)) } else { - agentOptions = append(agentOptions, agent.WithImage(fmt.Sprintf("data:%s;base64,%s", mimeType, imgBase64))) + agentOptions = append(agentOptions, types.WithImage(fmt.Sprintf("data:%s;base64,%s", mimeType, imgBase64))) } } @@ -350,8 +351,8 @@ func (t *Slack) handleMention( } res := a.Ask( - // agent.WithText(message), - agent.WithConversationHistory(threadMessages), + // types.WithText(message), + types.WithConversationHistory(threadMessages), ) res.Response = githubmarkdownconvertergo.Slack(res.Response) diff --git a/services/connectors/telegram.go b/services/connectors/telegram.go index 37de7dd..ac50616 100644 --- a/services/connectors/telegram.go +++ b/services/connectors/telegram.go @@ -9,6 +9,7 @@ import ( "github.com/go-telegram/bot" "github.com/go-telegram/bot/models" "github.com/mudler/LocalAgent/core/agent" + "github.com/mudler/LocalAgent/core/types" ) type Telegram struct { @@ -20,16 +21,16 @@ type Telegram struct { // Send any text message to the bot after the bot has been started -func (t *Telegram) AgentResultCallback() func(state agent.ActionState) { - return func(state agent.ActionState) { +func (t *Telegram) AgentResultCallback() func(state types.ActionState) { + return func(state types.ActionState) { t.bot.SetMyDescription(t.agent.Context(), &bot.SetMyDescriptionParams{ Description: state.Reasoning, }) } } -func (t *Telegram) AgentReasoningCallback() func(state agent.ActionCurrentState) bool { - return func(state agent.ActionCurrentState) bool { +func (t *Telegram) AgentReasoningCallback() func(state types.ActionCurrentState) bool { + return func(state types.ActionCurrentState) bool { t.bot.SetMyDescription(t.agent.Context(), &bot.SetMyDescriptionParams{ Description: state.Reasoning, }) @@ -45,7 +46,7 @@ func (t *Telegram) Start(a *agent.Agent) { bot.WithDefaultHandler(func(ctx context.Context, b *bot.Bot, update *models.Update) { go func() { res := a.Ask( - agent.WithText( + types.WithText( update.Message.Text, ), ) diff --git a/services/connectors/twitter.go b/services/connectors/twitter.go index 32b9850..adab389 100644 --- a/services/connectors/twitter.go +++ b/services/connectors/twitter.go @@ -7,6 +7,7 @@ import ( "os/signal" "github.com/mudler/LocalAgent/core/agent" + "github.com/mudler/LocalAgent/core/types" "github.com/mudler/LocalAgent/pkg/xlog" "github.com/mudler/LocalAgent/services/connectors/twitter" "github.com/sashabaranov/go-openai" @@ -19,14 +20,14 @@ type Twitter struct { noCharacterLimit bool } -func (t *Twitter) AgentResultCallback() func(state agent.ActionState) { - return func(state agent.ActionState) { +func (t *Twitter) AgentResultCallback() func(state types.ActionState) { + return func(state types.ActionState) { } } -func (t *Twitter) AgentReasoningCallback() func(state agent.ActionCurrentState) bool { - return func(state agent.ActionCurrentState) bool { +func (t *Twitter) AgentReasoningCallback() func(state types.ActionCurrentState) bool { + return func(state types.ActionCurrentState) bool { return true } @@ -98,7 +99,7 @@ func (t *Twitter) run(a *agent.Agent) error { } res := a.Ask( - agent.WithConversationHistory( + types.WithConversationHistory( []openai.ChatCompletionMessage{ { Role: "system", diff --git a/webui/app.go b/webui/app.go index 141755b..c64d15f 100644 --- a/webui/app.go +++ b/webui/app.go @@ -9,14 +9,13 @@ import ( "strings" "time" + coreTypes "github.com/mudler/LocalAgent/core/types" "github.com/mudler/LocalAgent/pkg/llm" "github.com/mudler/LocalAgent/pkg/xlog" "github.com/mudler/LocalAgent/services" "github.com/mudler/LocalAgent/webui/types" "github.com/sashabaranov/go-openai/jsonschema" - "github.com/mudler/LocalAgent/core/action" - "github.com/mudler/LocalAgent/core/agent" "github.com/mudler/LocalAgent/core/sse" "github.com/mudler/LocalAgent/core/state" @@ -72,7 +71,7 @@ func (a *App) Notify(pool *state.AgentPool) func(c *fiber.Ctx) error { a := pool.GetAgent(c.Params("name")) a.Ask( - agent.WithText(query), + coreTypes.WithText(query), ) _, _ = c.Write([]byte("Message sent")) @@ -275,7 +274,7 @@ func (a *App) Chat(pool *state.AgentPool) func(c *fiber.Ctx) error { return } res := a.Ask( - agent.WithText(query), + coreTypes.WithText(query), ) if res.Error != nil { xlog.Error("Error asking agent", "agent", agentName, "error", res.Error) @@ -307,8 +306,8 @@ func (a *App) Chat(pool *state.AgentPool) func(c *fiber.Ctx) error { func (a *App) ExecuteAction(pool *state.AgentPool) func(c *fiber.Ctx) error { return func(c *fiber.Ctx) error { payload := struct { - Config map[string]string `json:"config"` - Params action.ActionParams `json:"params"` + Config map[string]string `json:"config"` + Params coreTypes.ActionParams `json:"params"` }{} if err := c.BodyParser(&payload); err != nil { @@ -365,7 +364,7 @@ func (a *App) Responses(pool *state.AgentPool) func(c *fiber.Ctx) error { } res := a.Ask( - agent.WithConversationHistory(messages), + coreTypes.WithConversationHistory(messages), ) if res.Error != nil { xlog.Error("Error asking agent", "agent", agentName, "error", res.Error) diff --git a/webui/routes.go b/webui/routes.go index 29836fa..81c732a 100644 --- a/webui/routes.go +++ b/webui/routes.go @@ -12,9 +12,9 @@ import ( fiber "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/filesystem" "github.com/gofiber/fiber/v2/middleware/keyauth" - "github.com/mudler/LocalAgent/core/agent" "github.com/mudler/LocalAgent/core/sse" "github.com/mudler/LocalAgent/core/state" + "github.com/mudler/LocalAgent/core/types" "github.com/mudler/LocalAgent/pkg/xlog" "github.com/mudler/LocalAgent/services" ) @@ -95,7 +95,7 @@ func (app *App) registerRoutes(pool *state.AgentPool, webapp *fiber.App) { webapp.Get("/status/:name", func(c *fiber.Ctx) error { history := pool.GetStatusHistory(c.Params("name")) if history == nil { - history = &state.Status{ActionResults: []agent.ActionState{}} + history = &state.Status{ActionResults: []types.ActionState{}} } // reverse history