uniform logging

This commit is contained in:
Ettore Di Giacinto
2024-04-19 00:02:00 +02:00
parent 2cba2eafe6
commit 08563c3286
13 changed files with 178 additions and 108 deletions

View File

@@ -3,12 +3,13 @@ package agent
import ( import (
"context" "context"
"fmt" "fmt"
"log/slog"
"os" "os"
"strings" "strings"
"sync" "sync"
"time" "time"
"github.com/mudler/local-agent-framework/xlog"
"github.com/mudler/local-agent-framework/action" "github.com/mudler/local-agent-framework/action"
"github.com/mudler/local-agent-framework/llm" "github.com/mudler/local-agent-framework/llm"
"github.com/sashabaranov/go-openai" "github.com/sashabaranov/go-openai"
@@ -36,7 +37,6 @@ type Agent struct {
selfEvaluationInProgress bool selfEvaluationInProgress bool
pause bool pause bool
logger *slog.Logger
newConversations chan openai.ChatCompletionMessage newConversations chan openai.ChatCompletionMessage
} }
@@ -77,15 +77,15 @@ func New(opts ...Option) (*Agent, error) {
} }
} }
var programLevel = new(slog.LevelVar) // Info by default // var programLevel = new(xlog.LevelVar) // Info by default
h := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: programLevel}) // h := xlog.NewTextHandler(os.Stdout, &xlog.HandlerOptions{Level: programLevel})
a.logger = slog.New(h) // xlog = xlog.New(h)
programLevel.Set(a.options.logLevel) //programLevel.Set(a.options.logLevel)
a.logger.Info("Agent in Debug mode", "agent", a.Character.Name) xlog.Info("Agent in Debug mode", "agent", a.Character.Name)
a.logger.Info("Character", "agent", a.Character.Name, "character", a.Character.String()) xlog.Info("Character", "agent", a.Character.Name, "character", a.Character.String())
a.logger.Info("State", "agent", a.Character.Name, "state", a.State().String()) xlog.Info("State", "agent", a.Character.Name, "state", a.State().String())
a.logger.Info("Permanent goal", "agent", a.Character.Name, "goal", a.options.permanentGoal) xlog.Info("Permanent goal", "agent", a.Character.Name, "goal", a.options.permanentGoal)
return a, nil return a, nil
} }
@@ -123,7 +123,7 @@ func (a *Agent) Ask(opts ...JobOption) *JobResult {
WithResultCallback(a.options.resultCallback), WithResultCallback(a.options.resultCallback),
)..., )...,
) )
// slog.Info("Job created", text) // xlog.Info("Job created", text)
a.jobQueue <- j a.jobQueue <- j
return j.Result.WaitResult() return j.Result.WaitResult()
} }
@@ -181,7 +181,7 @@ func (a *Agent) runAction(chosenAction Action, decisionResult *decisionResult) (
} }
} }
a.logger.Info("Running action", "action", chosenAction.Definition().Name, "agent", a.Character.Name) xlog.Info("Running action", "action", chosenAction.Definition().Name, "agent", a.Character.Name)
if chosenAction.Definition().Name.Is(action.StateActionName) { if chosenAction.Definition().Name.Is(action.StateActionName) {
// We need to store the result in the state // We need to store the result in the state
@@ -211,7 +211,7 @@ func (a *Agent) consumeJob(job *Job, role string) {
a.Unlock() a.Unlock()
if paused { if paused {
a.logger.Info("Agent is paused, skipping job", "agent", a.Character.Name) xlog.Info("Agent is paused, skipping job", "agent", a.Character.Name)
job.Result.Finish(fmt.Errorf("agent is paused")) job.Result.Finish(fmt.Errorf("agent is paused"))
return return
} }
@@ -281,7 +281,7 @@ func (a *Agent) consumeJob(job *Job, role string) {
if userMessage != "" { if userMessage != "" {
results, err := a.options.ragdb.Search(userMessage, a.options.kbResults) results, err := a.options.ragdb.Search(userMessage, a.options.kbResults)
if err != nil { if err != nil {
a.logger.Info("Error finding similar strings inside KB:", "error", err) xlog.Info("Error finding similar strings inside KB:", "error", err)
// job.Result.Finish(fmt.Errorf("error finding similar strings inside KB: %w", err)) // job.Result.Finish(fmt.Errorf("error finding similar strings inside KB: %w", err))
// return // return
@@ -293,7 +293,7 @@ func (a *Agent) consumeJob(job *Job, role string) {
for _, r := range results { for _, r := range results {
formatResults += fmt.Sprintf("- %s \n", r) formatResults += fmt.Sprintf("- %s \n", r)
} }
a.logger.Info("Found similar strings in KB", "agent", a.Character.Name, "results", formatResults) xlog.Info("Found similar strings in KB", "agent", a.Character.Name, "results", formatResults)
// a.currentConversation = append(a.currentConversation, // a.currentConversation = append(a.currentConversation,
// openai.ChatCompletionMessage{ // openai.ChatCompletionMessage{
@@ -363,12 +363,12 @@ func (a *Agent) consumeJob(job *Job, role string) {
} }
if chosenAction.Definition().Name.Is(action.StopActionName) { if chosenAction.Definition().Name.Is(action.StopActionName) {
a.logger.Info("LLM decided to stop") xlog.Info("LLM decided to stop")
job.Result.Finish(nil) job.Result.Finish(nil)
return return
} }
a.logger.Info("Generating parameters", xlog.Info("Generating parameters",
"agent", a.Character.Name, "agent", a.Character.Name,
"action", chosenAction.Definition().Name, "action", chosenAction.Definition().Name,
"reasoning", reasoning, "reasoning", reasoning,
@@ -380,7 +380,7 @@ func (a *Agent) consumeJob(job *Job, role string) {
return return
} }
a.logger.Info( xlog.Info(
"Generated parameters", "Generated parameters",
"agent", a.Character.Name, "agent", a.Character.Name,
"action", chosenAction.Definition().Name, "action", chosenAction.Definition().Name,
@@ -572,7 +572,7 @@ func (a *Agent) consumeJob(job *Job, role string) {
// If we didn't got any message, we can use the response from the action // If we didn't got any message, we can use the response from the action
if chosenAction.Definition().Name.Is(action.ReplyActionName) && msg.Content == "" || if chosenAction.Definition().Name.Is(action.ReplyActionName) && msg.Content == "" ||
strings.Contains(msg.Content, "<tool_call>") { strings.Contains(msg.Content, "<tool_call>") {
a.logger.Info("No output returned from conversation, using the action response as a reply " + replyResponse.Message) xlog.Info("No output returned from conversation, using the action response as a reply " + replyResponse.Message)
msg = openai.ChatCompletionMessage{ msg = openai.ChatCompletionMessage{
Role: "assistant", Role: "assistant",
@@ -594,7 +594,7 @@ func (a *Agent) periodicallyRun() {
// This would be a special action that would be picked up by the agent // This would be a special action that would be picked up by the agent
// and would be used to contact the user. // and would be used to contact the user.
a.logger.Info("START -- Periodically run is starting") xlog.Info("START -- Periodically run is starting")
if len(a.CurrentConversation()) != 0 { if len(a.CurrentConversation()) != 0 {
// Here the LLM could decide to store some part of the conversation too in the memory // Here the LLM could decide to store some part of the conversation too in the memory
@@ -624,7 +624,7 @@ func (a *Agent) periodicallyRun() {
a.consumeJob(whatNext, SystemRole) a.consumeJob(whatNext, SystemRole)
a.ResetConversation() a.ResetConversation()
a.logger.Info("STOP -- Periodically run is done") xlog.Info("STOP -- Periodically run is done")
// Save results from state // Save results from state

View File

@@ -3,7 +3,8 @@ package agent_test
import ( import (
"context" "context"
"fmt" "fmt"
"log/slog"
"github.com/mudler/local-agent-framework/xlog"
"github.com/mudler/local-agent-framework/action" "github.com/mudler/local-agent-framework/action"
. "github.com/mudler/local-agent-framework/agent" . "github.com/mudler/local-agent-framework/agent"
@@ -20,13 +21,13 @@ var _ Action = &TestAction{}
var debugOptions = []JobOption{ var debugOptions = []JobOption{
WithReasoningCallback(func(state ActionCurrentState) bool { WithReasoningCallback(func(state ActionCurrentState) bool {
slog.Info("Reasoning", state) xlog.Info("Reasoning", state)
return true return true
}), }),
WithResultCallback(func(state ActionState) { WithResultCallback(func(state ActionState) {
slog.Info("Reasoning", state.Reasoning) xlog.Info("Reasoning", state.Reasoning)
slog.Info("Action", state.Action) xlog.Info("Action", state.Action)
slog.Info("Result", state.Result) xlog.Info("Result", state.Result)
}), }),
} }
@@ -197,13 +198,13 @@ var _ = Describe("Agent test", func() {
EnableHUD, EnableHUD,
EnableStandaloneJob, EnableStandaloneJob,
WithAgentReasoningCallback(func(state ActionCurrentState) bool { WithAgentReasoningCallback(func(state ActionCurrentState) bool {
slog.Info("Reasoning", state) xlog.Info("Reasoning", state)
return true return true
}), }),
WithAgentResultCallback(func(state ActionState) { WithAgentResultCallback(func(state ActionState) {
slog.Info("Reasoning", state.Reasoning) xlog.Info("Reasoning", state.Reasoning)
slog.Info("Action", state.Action) xlog.Info("Action", state.Action)
slog.Info("Result", state.Result) xlog.Info("Result", state.Result)
}), }),
WithActions( WithActions(
&FakeInternetAction{ &FakeInternetAction{

View File

@@ -2,7 +2,6 @@ package agent
import ( import (
"context" "context"
"log/slog"
"strings" "strings"
"time" "time"
) )
@@ -22,7 +21,6 @@ type options struct {
userActions Actions userActions Actions
enableHUD, standaloneJob, showCharacter, enableKB bool enableHUD, standaloneJob, showCharacter, enableKB bool
logLevel slog.Level
canStopItself bool canStopItself bool
initiateConversations bool initiateConversations bool
characterfile string characterfile string
@@ -55,7 +53,6 @@ func defaultOptions() *options {
APIURL: "http://localhost:8080", APIURL: "http://localhost:8080",
Model: "echidna", Model: "echidna",
}, },
logLevel: slog.LevelInfo,
character: Character{ character: Character{
Name: "John Doe", Name: "John Doe",
Age: "", Age: "",
@@ -92,13 +89,6 @@ var CanStopItself = func(o *options) error {
return nil return nil
} }
func LogLevel(level slog.Level) Option {
return func(o *options) error {
o.logLevel = level
return nil
}
}
func WithTimeout(timeout string) Option { func WithTimeout(timeout string) Option {
return func(o *options) error { return func(o *options) error {
o.timeout = timeout o.timeout = timeout

View File

@@ -3,7 +3,8 @@ package main
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"log/slog"
"github.com/mudler/local-agent-framework/xlog"
. "github.com/mudler/local-agent-framework/agent" . "github.com/mudler/local-agent-framework/agent"
"github.com/mudler/local-agent-framework/external" "github.com/mudler/local-agent-framework/external"
@@ -36,14 +37,14 @@ func (a *AgentConfig) availableActions(ctx context.Context) []Action {
actions := []Action{} actions := []Action{}
for _, action := range a.Actions { for _, action := range a.Actions {
slog.Info("Set Action", action) xlog.Info("Set Action", action)
var config map[string]string var config map[string]string
if err := json.Unmarshal([]byte(action.Config), &config); err != nil { if err := json.Unmarshal([]byte(action.Config), &config); err != nil {
slog.Info("Error unmarshalling action config", err) xlog.Info("Error unmarshalling action config", err)
continue continue
} }
slog.Info("Config", config) xlog.Info("Config", config)
switch action.Name { switch action.Name {
case ActionSearch: case ActionSearch:

View File

@@ -4,13 +4,14 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"log/slog"
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
"sync" "sync"
"time" "time"
"github.com/mudler/local-agent-framework/xlog"
. "github.com/mudler/local-agent-framework/agent" . "github.com/mudler/local-agent-framework/agent"
) )
@@ -140,15 +141,15 @@ func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error
connectors := config.availableConnectors() connectors := config.availableConnectors()
slog.Info("Creating agent", name) xlog.Info("Creating agent", name)
slog.Info("Model", model) xlog.Info("Model", model)
slog.Info("API URL", a.apiURL) xlog.Info("API URL", a.apiURL)
actions := config.availableActions(ctx) actions := config.availableActions(ctx)
stateFile, characterFile := a.stateFiles(name) stateFile, characterFile := a.stateFiles(name)
slog.Info("Actions", actions) xlog.Info("Actions", actions)
opts := []Option{ opts := []Option{
WithModel(model), WithModel(model),
WithLLMAPIURL(a.apiURL), WithLLMAPIURL(a.apiURL),
@@ -163,7 +164,7 @@ func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error
WithTimeout(timeout), WithTimeout(timeout),
WithRAGDB(a.ragDB), WithRAGDB(a.ragDB),
WithAgentReasoningCallback(func(state ActionCurrentState) bool { WithAgentReasoningCallback(func(state ActionCurrentState) bool {
slog.Info("Reasoning", state.Reasoning) xlog.Info("Reasoning", state.Reasoning)
manager.Send( manager.Send(
NewMessage( NewMessage(
fmt.Sprintf(`Thinking: %s`, htmlIfy(state.Reasoning)), fmt.Sprintf(`Thinking: %s`, htmlIfy(state.Reasoning)),
@@ -186,7 +187,7 @@ func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error
a.agentStatus[name].addResult(state) a.agentStatus[name].addResult(state)
a.Unlock() a.Unlock()
slog.Info("Reasoning", state.Reasoning) xlog.Info("Reasoning", state.Reasoning)
text := fmt.Sprintf(`Reasoning: %s text := fmt.Sprintf(`Reasoning: %s
Action taken: %+v Action taken: %+v
@@ -212,9 +213,7 @@ func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error
if config.HUD { if config.HUD {
opts = append(opts, EnableHUD) opts = append(opts, EnableHUD)
} }
if os.Getenv("DEBUG") != "" {
opts = append(opts, LogLevel(slog.LevelDebug))
}
if config.StandaloneJob { if config.StandaloneJob {
opts = append(opts, EnableStandaloneJob) opts = append(opts, EnableStandaloneJob)
} }
@@ -240,7 +239,7 @@ func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error
opts = append(opts, EnableKnowledgeBaseWithResults(config.KnowledgeBaseResults)) opts = append(opts, EnableKnowledgeBaseWithResults(config.KnowledgeBaseResults))
} }
slog.Info("Starting agent", "name", name, "config", config) xlog.Info("Starting agent", "name", name, "config", config)
agent, err := New(opts...) agent, err := New(opts...)
if err != nil { if err != nil {
return err return err
@@ -251,7 +250,7 @@ func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error
go func() { go func() {
if err := agent.Run(); err != nil { if err := agent.Run(); err != nil {
slog.Info("Agent stop: ", err.Error()) xlog.Info("Agent stop: ", err.Error())
} }
}() }()
@@ -302,7 +301,7 @@ func (a *AgentPool) Start(name string) error {
if err != nil { if err != nil {
return fmt.Errorf("agent %s failed to start: %w", name, err) return fmt.Errorf("agent %s failed to start: %w", name, err)
} }
slog.Info("Agent started", "name", name) xlog.Info("Agent started", "name", name)
return nil return nil
} }
if config, ok := a.pool[name]; ok { if config, ok := a.pool[name]; ok {

View File

@@ -4,10 +4,11 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"log/slog"
"net/http" "net/http"
"os" "os"
"github.com/mudler/local-agent-framework/xlog"
. "github.com/mudler/local-agent-framework/agent" . "github.com/mudler/local-agent-framework/agent"
"github.com/donseba/go-htmx" "github.com/donseba/go-htmx"
@@ -54,7 +55,7 @@ func (a *App) KnowledgeBaseFile(db *InMemoryDatabase) func(c *fiber.Ctx) error {
return err return err
} }
slog.Info("File uploaded to: " + destination) xlog.Info("File uploaded to: " + destination)
fmt.Printf("Payload: %+v\n", payload) fmt.Printf("Payload: %+v\n", payload)
content, err := readPdf(destination) // Read local pdf file content, err := readPdf(destination) // Read local pdf file
@@ -62,7 +63,7 @@ func (a *App) KnowledgeBaseFile(db *InMemoryDatabase) func(c *fiber.Ctx) error {
panic(err) panic(err)
} }
slog.Info("Content is", content) xlog.Info("Content is", content)
chunkSize := defaultChunkSize chunkSize := defaultChunkSize
if payload.ChunkSize > 0 { if payload.ChunkSize > 0 {
chunkSize = payload.ChunkSize chunkSize = payload.ChunkSize
@@ -131,7 +132,7 @@ func (a *App) Notify(pool *AgentPool) func(c *fiber.Ctx) error {
func (a *App) Delete(pool *AgentPool) func(c *fiber.Ctx) error { func (a *App) Delete(pool *AgentPool) func(c *fiber.Ctx) error {
return func(c *fiber.Ctx) error { return func(c *fiber.Ctx) error {
if err := pool.Remove(c.Params("name")); err != nil { if err := pool.Remove(c.Params("name")); err != nil {
slog.Info("Error removing agent", err) xlog.Info("Error removing agent", err)
return c.Status(http.StatusInternalServerError).SendString(err.Error()) return c.Status(http.StatusInternalServerError).SendString(err.Error())
} }
return c.Redirect("/agents") return c.Redirect("/agents")
@@ -140,7 +141,7 @@ func (a *App) Delete(pool *AgentPool) func(c *fiber.Ctx) error {
func (a *App) Pause(pool *AgentPool) func(c *fiber.Ctx) error { func (a *App) Pause(pool *AgentPool) func(c *fiber.Ctx) error {
return func(c *fiber.Ctx) error { return func(c *fiber.Ctx) error {
slog.Info("Pausing agent", c.Params("name")) xlog.Info("Pausing agent", c.Params("name"))
agent := pool.GetAgent(c.Params("name")) agent := pool.GetAgent(c.Params("name"))
if agent != nil { if agent != nil {
agent.Pause() agent.Pause()
@@ -218,7 +219,7 @@ func (a *App) ImportAgent(pool *AgentPool) func(c *fiber.Ctx) error {
return err return err
} }
slog.Info("Importing agent", config.Name) xlog.Info("Importing agent", config.Name)
if config.Name == "" { if config.Name == "" {
c.Status(http.StatusBadRequest).SendString("Name is required") c.Status(http.StatusBadRequest).SendString("Name is required")
@@ -258,16 +259,16 @@ func (a *App) Chat(pool *AgentPool) func(c *fiber.Ctx) error {
go func() { go func() {
agent := pool.GetAgent(agentName) agent := pool.GetAgent(agentName)
if agent == nil { if agent == nil {
slog.Info("Agent not found in pool", c.Params("name")) xlog.Info("Agent not found in pool", c.Params("name"))
return return
} }
res := agent.Ask( res := agent.Ask(
WithText(query), WithText(query),
) )
if res.Error != nil { if res.Error != nil {
slog.Error("Error asking agent", "agent", agentName, "error", res.Error) xlog.Error("Error asking agent", "agent", agentName, "error", res.Error)
} else { } else {
slog.Info("we got a response from the agent", "agent", agentName, "response", res.Response) xlog.Info("we got a response from the agent", "agent", agentName, "response", res.Response)
} }
manager.Send( manager.Send(
NewMessage( NewMessage(

View File

@@ -1,11 +1,11 @@
package connector package connector
import ( import (
"log/slog"
"strings" "strings"
"github.com/bwmarrin/discordgo" "github.com/bwmarrin/discordgo"
"github.com/mudler/local-agent-framework/agent" "github.com/mudler/local-agent-framework/agent"
"github.com/mudler/local-agent-framework/xlog"
) )
type Discord struct { type Discord struct {
@@ -39,7 +39,7 @@ func (d *Discord) Start(a *agent.Agent) {
// Create a new Discord session using the provided bot token. // Create a new Discord session using the provided bot token.
dg, err := discordgo.New(Token) dg, err := discordgo.New(Token)
if err != nil { if err != nil {
slog.Info("error creating Discord session,", err) xlog.Info("error creating Discord session,", err)
return return
} }
@@ -52,15 +52,15 @@ func (d *Discord) Start(a *agent.Agent) {
// Open a websocket connection to Discord and begin listening. // Open a websocket connection to Discord and begin listening.
err = dg.Open() err = dg.Open()
if err != nil { if err != nil {
slog.Info("error opening connection,", err) xlog.Info("error opening connection,", err)
return return
} }
go func() { go func() {
slog.Info("Discord bot is now running. Press CTRL-C to exit.") xlog.Info("Discord bot is now running. Press CTRL-C to exit.")
<-a.Context().Done() <-a.Context().Done()
dg.Close() dg.Close()
slog.Info("Discord bot is now stopped.") xlog.Info("Discord bot is now stopped.")
}() }()
} }
@@ -78,21 +78,21 @@ func (d *Discord) messageCreate(a *agent.Agent) func(s *discordgo.Session, m *di
content := m.Content content := m.Content
content = strings.ReplaceAll(content, "<@"+s.State.User.ID+"> ", "") content = strings.ReplaceAll(content, "<@"+s.State.User.ID+"> ", "")
slog.Info("Received message", "content", content) xlog.Info("Received message", "content", content)
job := a.Ask( job := a.Ask(
agent.WithText( agent.WithText(
content, content,
), ),
) )
if job.Error != nil { if job.Error != nil {
slog.Info("error asking agent,", job.Error) xlog.Info("error asking agent,", job.Error)
return return
} }
slog.Info("Response", "response", job.Response) xlog.Info("Response", "response", job.Response)
_, err := s.ChannelMessageSend(m.ChannelID, job.Response) _, err := s.ChannelMessageSend(m.ChannelID, job.Response)
if err != nil { if err != nil {
slog.Info("error sending message,", err) xlog.Info("error sending message,", err)
} }
} }

View File

@@ -2,12 +2,13 @@ package connector
import ( import (
"fmt" "fmt"
"log/slog"
"strings" "strings"
"time" "time"
"github.com/google/go-github/v61/github" "github.com/google/go-github/v61/github"
"github.com/mudler/local-agent-framework/agent" "github.com/mudler/local-agent-framework/agent"
"github.com/mudler/local-agent-framework/xlog"
"github.com/sashabaranov/go-openai" "github.com/sashabaranov/go-openai"
) )
@@ -58,10 +59,10 @@ func (g *GithubIssues) Start(a *agent.Agent) {
for { for {
select { select {
case <-ticker.C: case <-ticker.C:
slog.Info("Looking into github issues...") xlog.Info("Looking into github issues...")
g.issuesService() g.issuesService()
case <-a.Context().Done(): case <-a.Context().Done():
slog.Info("GithubIssues connector is now stopping") xlog.Info("GithubIssues connector is now stopping")
return return
} }
} }
@@ -81,7 +82,7 @@ func (g *GithubIssues) issuesService() {
g.repository, g.repository,
&github.IssueListByRepoOptions{}) &github.IssueListByRepoOptions{})
if err != nil { if err != nil {
slog.Info("Error listing issues", err) xlog.Info("Error listing issues", err)
} }
for _, issue := range issues { for _, issue := range issues {
// Do something with the issue // Do something with the issue
@@ -101,7 +102,7 @@ func (g *GithubIssues) issuesService() {
} }
if userName == user.GetLogin() { if userName == user.GetLogin() {
slog.Info("Ignoring issue opened by the bot") xlog.Info("Ignoring issue opened by the bot")
continue continue
} }
messages := []openai.ChatCompletionMessage{ messages := []openai.ChatCompletionMessage{
@@ -135,7 +136,7 @@ func (g *GithubIssues) issuesService() {
// if last comment is from the user and mentions the bot username, we must answer // if last comment is from the user and mentions the bot username, we must answer
if comment.User.GetName() != user.GetLogin() && len(comments)-1 == i { if comment.User.GetName() != user.GetLogin() && len(comments)-1 == i {
if strings.Contains(comment.GetBody(), fmt.Sprintf("@%s", user.GetLogin())) { if strings.Contains(comment.GetBody(), fmt.Sprintf("@%s", user.GetLogin())) {
slog.Info("Bot was mentioned in the last comment") xlog.Info("Bot was mentioned in the last comment")
mustAnswer = true mustAnswer = true
} }
} }
@@ -143,9 +144,9 @@ func (g *GithubIssues) issuesService() {
if len(comments) == 0 || !botAnsweredAlready { if len(comments) == 0 || !botAnsweredAlready {
// if no comments, or bot didn't answer yet, we must answer // if no comments, or bot didn't answer yet, we must answer
slog.Info("No comments, or bot didn't answer yet") xlog.Info("No comments, or bot didn't answer yet")
slog.Info("Comments:", len(comments)) xlog.Info("Comments:", len(comments))
slog.Info("Bot answered already", botAnsweredAlready) xlog.Info("Bot answered already", botAnsweredAlready)
mustAnswer = true mustAnswer = true
} }
@@ -157,7 +158,7 @@ func (g *GithubIssues) issuesService() {
agent.WithConversationHistory(messages), agent.WithConversationHistory(messages),
) )
if res.Error != nil { if res.Error != nil {
slog.Error("Error asking", "error", res.Error) xlog.Error("Error asking", "error", res.Error)
return return
} }
@@ -169,7 +170,7 @@ func (g *GithubIssues) issuesService() {
}, },
) )
if err != nil { if err != nil {
slog.Error("Error creating comment", "error", err) xlog.Error("Error creating comment", "error", err)
} }
} }
} }

View File

@@ -3,10 +3,11 @@ package connector
import ( import (
"fmt" "fmt"
"log" "log"
"log/slog"
"os" "os"
"strings" "strings"
"github.com/mudler/local-agent-framework/xlog"
"github.com/mudler/local-agent-framework/agent" "github.com/mudler/local-agent-framework/agent"
"github.com/slack-go/slack/socketmode" "github.com/slack-go/slack/socketmode"
@@ -61,11 +62,11 @@ func (t *Slack) Start(a *agent.Agent) {
for evt := range client.Events { for evt := range client.Events {
switch evt.Type { switch evt.Type {
case socketmode.EventTypeConnecting: case socketmode.EventTypeConnecting:
slog.Info("Connecting to Slack with Socket Mode...") xlog.Info("Connecting to Slack with Socket Mode...")
case socketmode.EventTypeConnectionError: case socketmode.EventTypeConnectionError:
slog.Info("Connection failed. Retrying later...") xlog.Info("Connection failed. Retrying later...")
case socketmode.EventTypeConnected: case socketmode.EventTypeConnected:
slog.Info("Connected to Slack with Socket Mode.") xlog.Info("Connected to Slack with Socket Mode.")
case socketmode.EventTypeEventsAPI: case socketmode.EventTypeEventsAPI:
eventsAPIEvent, ok := evt.Data.(slackevents.EventsAPIEvent) eventsAPIEvent, ok := evt.Data.(slackevents.EventsAPIEvent)
if !ok { if !ok {
@@ -92,7 +93,7 @@ func (t *Slack) Start(a *agent.Agent) {
if t.channelID == "" && !t.alwaysReply || // If we have set alwaysReply and no channelID if t.channelID == "" && !t.alwaysReply || // If we have set alwaysReply and no channelID
t.channelID != ev.Channel { // If we have a channelID and it's not the same as the event channel t.channelID != ev.Channel { // If we have a channelID and it's not the same as the event channel
// Skip messages from other channels // Skip messages from other channels
slog.Info("Skipping reply to channel", ev.Channel, t.channelID) xlog.Info("Skipping reply to channel", ev.Channel, t.channelID)
continue continue
} }
@@ -124,7 +125,7 @@ func (t *Slack) Start(a *agent.Agent) {
// strip our id from the message // strip our id from the message
message = strings.ReplaceAll(message, "<@"+b.UserID+"> ", "") message = strings.ReplaceAll(message, "<@"+b.UserID+"> ", "")
slog.Info("Message", message) xlog.Info("Message", message)
go func() { go func() {
res := a.Ask( res := a.Ask(

View File

@@ -2,7 +2,8 @@ package main
import ( import (
"encoding/json" "encoding/json"
"log/slog"
"github.com/mudler/local-agent-framework/xlog"
. "github.com/mudler/local-agent-framework/agent" . "github.com/mudler/local-agent-framework/agent"
@@ -34,20 +35,20 @@ func (a *AgentConfig) availableConnectors() []Connector {
connectors := []Connector{} connectors := []Connector{}
for _, c := range a.Connector { for _, c := range a.Connector {
slog.Info("Set Connector", c) xlog.Info("Set Connector", c)
var config map[string]string var config map[string]string
if err := json.Unmarshal([]byte(c.Config), &config); err != nil { if err := json.Unmarshal([]byte(c.Config), &config); err != nil {
slog.Info("Error unmarshalling connector config", err) xlog.Info("Error unmarshalling connector config", err)
continue continue
} }
slog.Info("Config", config) xlog.Info("Config", config)
switch c.Type { switch c.Type {
case ConnectorTelegram: case ConnectorTelegram:
cc, err := connector.NewTelegramConnector(config) cc, err := connector.NewTelegramConnector(config)
if err != nil { if err != nil {
slog.Info("Error creating telegram connector", err) xlog.Info("Error creating telegram connector", err)
continue continue
} }

View File

@@ -3,10 +3,11 @@ package main
import ( import (
"embed" "embed"
"log" "log"
"log/slog"
"net/http" "net/http"
"os" "os"
"github.com/mudler/local-agent-framework/xlog"
"github.com/donseba/go-htmx" "github.com/donseba/go-htmx"
fiber "github.com/gofiber/fiber/v2" fiber "github.com/gofiber/fiber/v2"
"github.com/gofiber/template/html/v2" "github.com/gofiber/template/html/v2"
@@ -79,9 +80,9 @@ func main() {
} }
if len(db.Database) > 0 && kbdisableIndexing != "true" { if len(db.Database) > 0 && kbdisableIndexing != "true" {
slog.Info("Loading knowledgebase from disk, to skip run with KBDISABLEINDEX=true") xlog.Info("Loading knowledgebase from disk, to skip run with KBDISABLEINDEX=true")
if err := db.SaveToStore(); err != nil { if err := db.SaveToStore(); err != nil {
slog.Info("Error storing in the KB", err) xlog.Info("Error storing in the KB", err)
} }
} }

View File

@@ -4,7 +4,9 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"log/slog"
"github.com/mudler/local-agent-framework/xlog"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
@@ -121,7 +123,7 @@ func getWebPage(url string) (string, error) {
func Sitemap(url string) (res []string, err error) { func Sitemap(url string) (res []string, err error) {
err = sitemap.ParseFromSite(url, func(e sitemap.Entry) error { err = sitemap.ParseFromSite(url, func(e sitemap.Entry) error {
slog.Info("Sitemap page: " + e.GetLocation()) xlog.Info("Sitemap page: " + e.GetLocation())
content, err := getWebPage(e.GetLocation()) content, err := getWebPage(e.GetLocation())
if err == nil { if err == nil {
res = append(res, content) res = append(res, content)
@@ -134,10 +136,10 @@ func Sitemap(url string) (res []string, err error) {
func WebsiteToKB(website string, chunkSize int, db *InMemoryDatabase) { func WebsiteToKB(website string, chunkSize int, db *InMemoryDatabase) {
content, err := Sitemap(website) content, err := Sitemap(website)
if err != nil { if err != nil {
slog.Info("Error walking sitemap for website", err) xlog.Info("Error walking sitemap for website", err)
} }
slog.Info("Found pages: ", len(content)) xlog.Info("Found pages: ", len(content))
slog.Info("ChunkSize: ", chunkSize) xlog.Info("ChunkSize: ", chunkSize)
StringsToKB(db, chunkSize, content...) StringsToKB(db, chunkSize, content...)
} }
@@ -145,9 +147,9 @@ func WebsiteToKB(website string, chunkSize int, db *InMemoryDatabase) {
func StringsToKB(db *InMemoryDatabase, chunkSize int, content ...string) { func StringsToKB(db *InMemoryDatabase, chunkSize int, content ...string) {
for _, c := range content { for _, c := range content {
chunks := splitParagraphIntoChunks(c, chunkSize) chunks := splitParagraphIntoChunks(c, chunkSize)
slog.Info("chunks: ", len(chunks)) xlog.Info("chunks: ", len(chunks))
for _, chunk := range chunks { for _, chunk := range chunks {
slog.Info("Chunk size: ", len(chunk)) xlog.Info("Chunk size: ", len(chunk))
db.AddEntry(chunk) db.AddEntry(chunk)
} }
@@ -155,7 +157,7 @@ func StringsToKB(db *InMemoryDatabase, chunkSize int, content ...string) {
} }
if err := db.SaveToStore(); err != nil { if err := db.SaveToStore(); err != nil {
slog.Info("Error storing in the KB", err) xlog.Info("Error storing in the KB", err)
} }
} }

72
xlog/xlog.go Normal file
View File

@@ -0,0 +1,72 @@
package xlog
import (
"context"
"log/slog"
"os"
"runtime"
)
var logger *slog.Logger
func init() {
var level = slog.LevelDebug
switch os.Getenv("LOG_LEVEL") {
case "info":
level = slog.LevelInfo
case "warn":
level = slog.LevelWarn
case "error":
level = slog.LevelError
case "debug":
level = slog.LevelDebug
}
var opts = &slog.HandlerOptions{
Level: level,
}
var handler slog.Handler
if os.Getenv("LOG_FORMAT") == "json" {
handler = slog.NewJSONHandler(os.Stdout, opts)
} else {
handler = slog.NewTextHandler(os.Stdout, opts)
}
logger = slog.New(handler)
}
func _log(level slog.Level, msg string, args ...any) {
_, f, l, _ := runtime.Caller(2)
group := slog.Group(
"source",
slog.Attr{
Key: "file",
Value: slog.AnyValue(f),
},
slog.Attr{
Key: "L",
Value: slog.AnyValue(l),
},
)
args = append(args, group)
logger.Log(context.Background(), level, msg, args...)
}
func Info(msg string, args ...any) {
_log(slog.LevelInfo, msg, args...)
}
func Debug(msg string, args ...any) {
_log(slog.LevelDebug, msg, args...)
logger.Debug(msg, args...)
}
func Error(msg string, args ...any) {
_log(slog.LevelError, msg, args...)
}
func Warn(msg string, args ...any) {
_log(slog.LevelWarn, msg, args...)
}