diff --git a/action/newconversation.go b/action/newconversation.go new file mode 100644 index 0000000..b8439bc --- /dev/null +++ b/action/newconversation.go @@ -0,0 +1,35 @@ +package action + +import ( + "github.com/sashabaranov/go-openai/jsonschema" +) + +const ConversationActionName = "new_conversation" + +func NewConversation() *ConversationAction { + return &ConversationAction{} +} + +type ConversationAction struct{} + +type ConversationActionResponse struct { + Message string `json:"message"` +} + +func (a *ConversationAction) Run(ActionParams) (string, error) { + return "no-op", nil +} + +func (a *ConversationAction) Definition() ActionDefinition { + return ActionDefinition{ + Name: ConversationActionName, + Description: "Use this tool to initiate a new conversation or to notify something.", + Properties: map[string]jsonschema.Definition{ + "message": { + Type: jsonschema.String, + Description: "The message to start the conversation", + }, + }, + Required: []string{"message"}, + } +} diff --git a/agent/actions.go b/agent/actions.go index 0c50127..2d20a3c 100644 --- a/agent/actions.go +++ b/agent/actions.go @@ -137,10 +137,17 @@ func (a *Agent) generateParameters(ctx context.Context, pickTemplate string, act func (a *Agent) systemInternalActions() Actions { if a.options.enableHUD { - return append(a.options.userActions, action.NewState(), action.NewReply()) + return append(a.options.userActions, + action.NewState(), action.NewReply()) } - return append(a.options.userActions, action.NewReply()) + if a.options.initiateConversations && a.selfEvaluationInProgress { // && self-evaluation.. + return append(a.options.userActions, + action.NewState(), action.NewReply(), action.NewConversation()) + } + + return append(a.options.userActions, + action.NewReply()) } func (a *Agent) prepareHUD() PromptHUD { diff --git a/agent/agent.go b/agent/agent.go index 3aa3aa6..d86d279 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -27,10 +27,13 @@ type Agent struct { actionContext *action.ActionContext context *action.ActionContext - currentReasoning string - currentState *action.StateResult - nextAction Action - currentConversation []openai.ChatCompletionMessage + currentReasoning string + currentState *action.StateResult + nextAction Action + currentConversation []openai.ChatCompletionMessage + selfEvaluationInProgress bool + + newConversations chan openai.ChatCompletionMessage } func New(opts ...Option) (*Agent, error) { @@ -180,13 +183,21 @@ func (a *Agent) consumeJob(job *Job, role string) { // Set the action context ctx, cancel := context.WithCancel(context.Background()) a.actionContext = action.NewContext(ctx, cancel) + a.selfEvaluationInProgress = selfEvaluation a.Unlock() - if job.Image != "" { - // TODO: Use llava to explain the image content - + if selfEvaluation { + defer func() { + a.Lock() + a.selfEvaluationInProgress = false + a.Unlock() + }() } + //if job.Image != "" { + // TODO: Use llava to explain the image content + //} + if job.Text != "" { a.currentConversation = append(a.currentConversation, openai.ChatCompletionMessage{ Role: role, @@ -265,6 +276,32 @@ func (a *Agent) consumeJob(job *Job, role string) { return } + if selfEvaluation && a.options.initiateConversations && + chosenAction.Definition().Name.Is(action.ConversationActionName) { + + message := action.ConversationActionResponse{} + if err := params.actionParams.Unmarshal(&message); err != nil { + job.Result.Finish(fmt.Errorf("error unmarshalling conversation response: %w", err)) + return + } + + a.currentConversation = []openai.ChatCompletionMessage{ + { + Role: "assistant", + Content: message.Message, + }, + } + go func() { + a.newConversations <- openai.ChatCompletionMessage{ + Role: "assistant", + Content: message.Message, + } + }() + job.Result.SetResponse("decided to initiate a new conversation") + job.Result.Finish(nil) + return + } + // If we don't have to reply , run the action! if !chosenAction.Definition().Name.Is(action.ReplyActionName) { result, err := a.runAction(chosenAction, params) @@ -411,6 +448,13 @@ func (a *Agent) consumeJob(job *Job, role string) { } func (a *Agent) periodicallyRun() { + // This is running in the background. + + // TODO: Would be nice if we have a special action to + // contact the user. This would actually make sure that + // if the agent wants to initiate a conversation, it can do so. + // This would be a special action that would be picked up by the agent + // and would be used to contact the user. if a.options.debugMode { fmt.Println("START -- Periodically run is starting") diff --git a/agent/options.go b/agent/options.go index 0f8f75a..ca7f528 100644 --- a/agent/options.go +++ b/agent/options.go @@ -21,6 +21,7 @@ type options struct { userActions Actions enableHUD, standaloneJob, showCharacter bool debugMode bool + initiateConversations bool characterfile string statefile string context context.Context @@ -64,6 +65,11 @@ var EnableHUD = func(o *options) error { return nil } +var EnableInitiateConversations = func(o *options) error { + o.initiateConversations = true + return nil +} + var DebugMode = func(o *options) error { o.debugMode = true return nil