Finish moving types
This commit is contained in:
committed by
Ettore Di Giacinto
parent
f0b8bfb4f4
commit
75a8d63e83
@@ -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(),
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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.",
|
||||
}
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
34
core/types/result.go
Normal file
34
core/types/result.go
Normal file
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user