Automatically save all conversations
This commit is contained in:
@@ -2,7 +2,9 @@ package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/mudler/LocalAgent/core/action"
|
||||
"github.com/mudler/LocalAgent/pkg/xlog"
|
||||
@@ -87,6 +89,10 @@ func (a *Agent) decision(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := a.saveConversation(append(conversation, msg), "decision"); err != nil {
|
||||
xlog.Error("Error saving conversation", "error", err)
|
||||
}
|
||||
|
||||
return &decisionResult{actionParams: params, actioName: msg.ToolCalls[0].Function.Name, message: msg.Content}, nil
|
||||
}
|
||||
|
||||
@@ -113,6 +119,26 @@ func (m Messages) Exist(content string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (m Messages) Save(path string) error {
|
||||
content, err := json.MarshalIndent(m, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
if _, err := f.Write(content); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Agent) generateParameters(ctx context.Context, pickTemplate string, act Action, c []openai.ChatCompletionMessage, reasoning string) (*decisionResult, error) {
|
||||
|
||||
stateHUD, err := renderTemplate(pickTemplate, a.prepareHUD(), a.systemInternalActions(), reasoning)
|
||||
|
||||
@@ -177,7 +177,7 @@ func (a *Agent) ResetConversation() {
|
||||
// store into memory the conversation before pruning it
|
||||
// TODO: Shall we summarize the conversation into a bullet list of highlights
|
||||
// using the LLM instead?
|
||||
a.saveCurrentConversationInMemory()
|
||||
a.saveCurrentConversation()
|
||||
|
||||
a.currentConversation = []openai.ChatCompletionMessage{}
|
||||
}
|
||||
@@ -382,7 +382,7 @@ func (a *Agent) consumeJob(job *Job, role string) {
|
||||
Content: reasoning,
|
||||
})
|
||||
job.Result.Conversation = a.currentConversation
|
||||
a.saveCurrentConversationInMemory()
|
||||
a.saveCurrentConversation()
|
||||
job.Result.SetResponse(reasoning)
|
||||
job.Result.Finish(nil)
|
||||
return
|
||||
@@ -532,7 +532,7 @@ func (a *Agent) consumeJob(job *Job, role string) {
|
||||
}
|
||||
|
||||
a.currentConversation = append(a.currentConversation, msg)
|
||||
a.saveCurrentConversationInMemory()
|
||||
a.saveCurrentConversation()
|
||||
job.Result.SetResponse(msg.Content)
|
||||
job.Result.Conversation = a.currentConversation
|
||||
job.Result.Finish(nil)
|
||||
@@ -612,7 +612,7 @@ func (a *Agent) consumeJob(job *Job, role string) {
|
||||
a.currentConversation = append(a.currentConversation, msg)
|
||||
job.Result.Conversation = a.currentConversation
|
||||
job.Result.SetResponse(msg.Content)
|
||||
a.saveCurrentConversationInMemory()
|
||||
a.saveCurrentConversation()
|
||||
job.Result.Finish(nil)
|
||||
return
|
||||
}
|
||||
@@ -642,7 +642,7 @@ func (a *Agent) consumeJob(job *Job, role string) {
|
||||
job.Result.SetResponse(msg.Content)
|
||||
xlog.Info("Response from LLM", "response", msg.Content, "agent", a.Character.Name)
|
||||
job.Result.Conversation = a.currentConversation
|
||||
a.saveCurrentConversationInMemory()
|
||||
a.saveCurrentConversation()
|
||||
job.Result.Finish(nil)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@ package agent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/mudler/LocalAgent/pkg/xlog"
|
||||
"github.com/sashabaranov/go-openai"
|
||||
@@ -60,8 +63,24 @@ func (a *Agent) knowledgeBaseLookup() {
|
||||
}}, a.currentConversation...)
|
||||
}
|
||||
|
||||
func (a *Agent) saveCurrentConversationInMemory() {
|
||||
func (a *Agent) saveConversation(m Messages, prefix string) error {
|
||||
if a.options.conversationsPath == "" {
|
||||
return nil
|
||||
}
|
||||
dateTime := time.Now().Format("2006-01-02-15-04-05")
|
||||
fileName := a.Character.Name + "-" + dateTime + ".json"
|
||||
if prefix != "" {
|
||||
fileName = prefix + "-" + fileName
|
||||
}
|
||||
os.MkdirAll(a.options.conversationsPath, os.ModePerm)
|
||||
return m.Save(filepath.Join(a.options.conversationsPath, fileName))
|
||||
}
|
||||
|
||||
func (a *Agent) saveCurrentConversation() {
|
||||
|
||||
if err := a.saveConversation(a.currentConversation, ""); err != nil {
|
||||
xlog.Error("Error saving conversation", "error", err)
|
||||
}
|
||||
|
||||
if !a.options.enableLongTermMemory && !a.options.enableSummaryMemory {
|
||||
xlog.Debug("Long term memory is disabled", "agent", a.Character.Name)
|
||||
|
||||
@@ -40,6 +40,8 @@ type options struct {
|
||||
// callbacks
|
||||
reasoningCallback func(ActionCurrentState) bool
|
||||
resultCallback func(ActionState)
|
||||
|
||||
conversationsPath string
|
||||
}
|
||||
|
||||
func defaultOptions() *options {
|
||||
@@ -97,6 +99,13 @@ func WithTimeout(timeout string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
func WithConversationsPath(path string) Option {
|
||||
return func(o *options) error {
|
||||
o.conversationsPath = path
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func EnableKnowledgeBaseWithResults(results int) Option {
|
||||
return func(o *options) error {
|
||||
o.enableKB = true
|
||||
|
||||
@@ -32,6 +32,7 @@ type AgentPool struct {
|
||||
connectors func(*AgentConfig) []Connector
|
||||
promptBlocks func(*AgentConfig) []PromptBlock
|
||||
timeout string
|
||||
conversationLogs string
|
||||
}
|
||||
|
||||
type Status struct {
|
||||
@@ -94,6 +95,7 @@ func NewAgentPool(
|
||||
availableActions: availableActions,
|
||||
promptBlocks: promptBlocks,
|
||||
timeout: timeout,
|
||||
conversationLogs: filepath.Join(directory, "conversations"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -116,6 +118,7 @@ func NewAgentPool(
|
||||
promptBlocks: promptBlocks,
|
||||
availableActions: availableActions,
|
||||
timeout: timeout,
|
||||
conversationLogs: filepath.Join(directory, "conversations"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -272,6 +275,10 @@ func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error
|
||||
opts = append(opts, EnableHUD)
|
||||
}
|
||||
|
||||
if a.conversationLogs != "" {
|
||||
opts = append(opts, WithConversationsPath(a.conversationLogs))
|
||||
}
|
||||
|
||||
if config.StandaloneJob {
|
||||
opts = append(opts, EnableStandaloneJob)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/mudler/LocalAgent/core/action"
|
||||
"github.com/sashabaranov/go-openai/jsonschema"
|
||||
@@ -59,7 +60,16 @@ func (a *SearchAction) Run(ctx context.Context, params action.ActionParams) (act
|
||||
rxStrict := xurls.Strict()
|
||||
urls := rxStrict.FindAllString(res, -1)
|
||||
|
||||
return action.ActionResult{Result: res, Metadata: map[string]interface{}{MetadataUrls: urls}}, nil
|
||||
results := []string{}
|
||||
for _, u := range urls {
|
||||
// remove //duckduckgo.com/l/?uddg= from the url
|
||||
u = strings.ReplaceAll(u, "//duckduckgo.com/l/?uddg=", "")
|
||||
// remove everything with &rut=.... at the end
|
||||
u = strings.Split(u, "&rut=")[0]
|
||||
results = append(results, u)
|
||||
}
|
||||
|
||||
return action.ActionResult{Result: res, Metadata: map[string]interface{}{MetadataUrls: results}}, nil
|
||||
}
|
||||
|
||||
func (a *SearchAction) Definition() action.ActionDefinition {
|
||||
|
||||
Reference in New Issue
Block a user