diff --git a/action/intention.go b/action/intention.go
index 8f778dd..eb99145 100644
--- a/action/intention.go
+++ b/action/intention.go
@@ -1,6 +1,8 @@
package action
import (
+ "context"
+
"github.com/sashabaranov/go-openai/jsonschema"
)
@@ -19,7 +21,7 @@ type IntentResponse struct {
Reasoning string `json:"reasoning"`
}
-func (a *IntentAction) Run(ActionParams) (string, error) {
+func (a *IntentAction) Run(context.Context, ActionParams) (string, error) {
return "no-op", nil
}
diff --git a/action/newconversation.go b/action/newconversation.go
index b8439bc..5d0f94b 100644
--- a/action/newconversation.go
+++ b/action/newconversation.go
@@ -1,6 +1,8 @@
package action
import (
+ "context"
+
"github.com/sashabaranov/go-openai/jsonschema"
)
@@ -16,7 +18,7 @@ type ConversationActionResponse struct {
Message string `json:"message"`
}
-func (a *ConversationAction) Run(ActionParams) (string, error) {
+func (a *ConversationAction) Run(context.Context, ActionParams) (string, error) {
return "no-op", nil
}
diff --git a/action/noreply.go b/action/noreply.go
index a8ebb50..8f999bd 100644
--- a/action/noreply.go
+++ b/action/noreply.go
@@ -1,5 +1,7 @@
package action
+import "context"
+
// StopActionName is the name of the action
// used by the LLM to stop any further action
const StopActionName = "stop"
@@ -10,13 +12,13 @@ func NewStop() *StopAction {
type StopAction struct{}
-func (a *StopAction) Run(ActionParams) (string, error) {
+func (a *StopAction) Run(context.Context, ActionParams) (string, error) {
return "no-op", nil
}
func (a *StopAction) Definition() ActionDefinition {
return ActionDefinition{
Name: StopActionName,
- Description: "Use this tool to stop any further action and stop the conversation.",
+ Description: "Use this tool to stop any further action and stop the conversation. You must use this when: the user wants to stop the conversation, it seems that the user does not need any additional answer, it looks like there is already a conclusion to the conversation or the topic diverged too much from the original conversation.",
}
}
diff --git a/action/plan.go b/action/plan.go
index 11d74da..6c6cfb5 100644
--- a/action/plan.go
+++ b/action/plan.go
@@ -1,6 +1,8 @@
package action
import (
+ "context"
+
"github.com/sashabaranov/go-openai/jsonschema"
)
@@ -22,7 +24,7 @@ type PlanSubtask struct {
Reasoning string `json:"reasoning"`
}
-func (a *PlanAction) Run(ActionParams) (string, error) {
+func (a *PlanAction) Run(context.Context, ActionParams) (string, error) {
return "no-op", nil
}
diff --git a/action/reasoning.go b/action/reasoning.go
index 22f53ee..f6a5e9a 100644
--- a/action/reasoning.go
+++ b/action/reasoning.go
@@ -1,6 +1,8 @@
package action
import (
+ "context"
+
"github.com/sashabaranov/go-openai/jsonschema"
)
@@ -17,7 +19,7 @@ type ReasoningResponse struct {
Reasoning string `json:"reasoning"`
}
-func (a *ReasoningAction) Run(ActionParams) (string, error) {
+func (a *ReasoningAction) Run(context.Context, ActionParams) (string, error) {
return "no-op", nil
}
diff --git a/action/reply.go b/action/reply.go
index a23b8e0..db47898 100644
--- a/action/reply.go
+++ b/action/reply.go
@@ -1,6 +1,8 @@
package action
import (
+ "context"
+
"github.com/sashabaranov/go-openai/jsonschema"
)
@@ -19,7 +21,7 @@ type ReplyResponse struct {
Message string `json:"message"`
}
-func (a *ReplyAction) Run(ActionParams) (string, error) {
+func (a *ReplyAction) Run(context.Context, ActionParams) (string, error) {
return "no-op", nil
}
diff --git a/action/state.go b/action/state.go
index 9deb194..f4d8901 100644
--- a/action/state.go
+++ b/action/state.go
@@ -1,6 +1,7 @@
package action
import (
+ "context"
"fmt"
"github.com/sashabaranov/go-openai/jsonschema"
@@ -32,7 +33,7 @@ type StateResult struct {
Goal string `json:"goal"`
}
-func (a *StateAction) Run(ActionParams) (string, error) {
+func (a *StateAction) Run(context.Context, ActionParams) (string, error) {
return "internal state has been updated", nil
}
diff --git a/agent/actions.go b/agent/actions.go
index 7d90af0..018a517 100644
--- a/agent/actions.go
+++ b/agent/actions.go
@@ -3,6 +3,7 @@ package agent
import (
"context"
"fmt"
+ "time"
"github.com/mudler/local-agent-framework/action"
@@ -22,7 +23,7 @@ type ActionCurrentState struct {
// Actions is something the agent can do
type Action interface {
- Run(action.ActionParams) (string, error)
+ Run(ctx context.Context, action action.ActionParams) (string, error)
Definition() action.ActionDefinition
}
@@ -143,9 +144,9 @@ func (a *Agent) systemInternalActions() Actions {
if a.options.enableHUD {
acts = append(acts, action.NewState())
}
- if a.options.canStopItself {
- acts = append(acts, action.NewStop())
- }
+ //if a.options.canStopItself {
+ // acts = append(acts, action.NewStop())
+ // }
return acts
}
@@ -171,6 +172,7 @@ func (a *Agent) prepareHUD() PromptHUD {
CurrentState: *a.currentState,
PermanentGoal: a.options.permanentGoal,
ShowCharacter: a.options.showCharacter,
+ Time: time.Now().Format(time.RFC3339),
}
}
diff --git a/agent/agent.go b/agent/agent.go
index c82abff..e9f792f 100644
--- a/agent/agent.go
+++ b/agent/agent.go
@@ -164,7 +164,7 @@ func (a *Agent) Paused() bool {
func (a *Agent) runAction(chosenAction Action, decisionResult *decisionResult) (result string, err error) {
for _, action := range a.systemInternalActions() {
if action.Definition().Name == chosenAction.Definition().Name {
- if result, err = action.Run(decisionResult.actionParams); err != nil {
+ if result, err = action.Run(a.context, decisionResult.actionParams); err != nil {
return "", fmt.Errorf("error running action: %w", err)
}
}
@@ -337,12 +337,6 @@ func (a *Agent) consumeJob(job *Job, role string) {
}
}
- if chosenAction.Definition().Name.Is(action.StopActionName) {
- a.logger.Info("LLM decided to stop")
- job.Result.Finish(nil)
- return
- }
-
if chosenAction == nil {
// If no action was picked up, the reasoning is the message returned by the assistant
// so we can consume it as if it was a reply.
@@ -357,6 +351,12 @@ func (a *Agent) consumeJob(job *Job, role string) {
return
}
+ if chosenAction.Definition().Name.Is(action.StopActionName) {
+ a.logger.Info("LLM decided to stop")
+ job.Result.Finish(nil)
+ return
+ }
+
a.logger.Info("===> Generating parameters for", "action", chosenAction.Definition().Name)
params, err := a.generateParameters(ctx, pickTemplate, chosenAction, a.currentConversation, reasoning)
diff --git a/agent/agent_test.go b/agent/agent_test.go
index c699e1c..a5a19bd 100644
--- a/agent/agent_test.go
+++ b/agent/agent_test.go
@@ -1,6 +1,7 @@
package agent_test
import (
+ "context"
"fmt"
"log/slog"
@@ -34,7 +35,7 @@ type TestAction struct {
responseN int
}
-func (a *TestAction) Run(action.ActionParams) (string, error) {
+func (a *TestAction) Run(context.Context, action.ActionParams) (string, error) {
res := a.response[a.responseN]
a.responseN++
diff --git a/agent/state.go b/agent/state.go
index 8cdf369..2d8ce1b 100644
--- a/agent/state.go
+++ b/agent/state.go
@@ -18,6 +18,7 @@ type PromptHUD struct {
CurrentState action.StateResult `json:"current_state"`
PermanentGoal string `json:"permanent_goal"`
ShowCharacter bool `json:"show_character"`
+ Time string `json:"time"`
}
type Character struct {
diff --git a/agent/templates.go b/agent/templates.go
index 224abae..15dc1fa 100644
--- a/agent/templates.go
+++ b/agent/templates.go
@@ -54,6 +54,7 @@ const hudTemplate = `{{with .HUD }}{{if .ShowCharacter}}The assistant acts like
{{end}}
This is your current state:
+Current time: {{.Time}}
NowDoing: {{if .CurrentState.NowDoing}}{{.CurrentState.NowDoing}}{{else}}Nothing{{end}}
DoingNext: {{if .CurrentState.DoingNext}}{{.CurrentState.DoingNext}}{{else}}Nothing{{end}}
Your permanent goal is: {{if .PermanentGoal}}{{.PermanentGoal}}{{else}}Nothing{{end}}
diff --git a/example/webui/actions.go b/example/webui/actions.go
new file mode 100644
index 0000000..10ddb04
--- /dev/null
+++ b/example/webui/actions.go
@@ -0,0 +1,65 @@
+package main
+
+import (
+ "context"
+ "encoding/json"
+ "log/slog"
+
+ . "github.com/mudler/local-agent-framework/agent"
+ "github.com/mudler/local-agent-framework/external"
+)
+
+const (
+ // Actions
+ ActionSearch = "search"
+ ActionGithubIssueLabeler = "github-issue-labeler"
+ ActionGithubIssueOpener = "github-issue-opener"
+ ActionGithubIssueCloser = "github-issue-closer"
+ ActionGithubIssueSearcher = "github-issue-searcher"
+ ActionScraper = "scraper"
+ ActionWikipedia = "wikipedia"
+)
+
+var AvailableActions = []string{
+ ActionSearch,
+ ActionGithubIssueLabeler,
+ ActionGithubIssueOpener,
+ ActionGithubIssueCloser,
+ ActionGithubIssueSearcher,
+ ActionScraper,
+ ActionWikipedia,
+}
+
+func (a *AgentConfig) availableActions(ctx context.Context) []Action {
+ actions := []Action{}
+
+ for _, action := range a.Actions {
+ slog.Info("Set Action", action)
+
+ var config map[string]string
+ if err := json.Unmarshal([]byte(action.Config), &config); err != nil {
+ slog.Info("Error unmarshalling action config", err)
+ continue
+ }
+ slog.Info("Config", config)
+
+ switch action.Name {
+ case ActionSearch:
+ actions = append(actions, external.NewSearch(config))
+ case ActionGithubIssueLabeler:
+ actions = append(actions, external.NewGithubIssueLabeler(ctx, config))
+ case ActionGithubIssueOpener:
+ actions = append(actions, external.NewGithubIssueOpener(ctx, config))
+ case ActionGithubIssueCloser:
+ actions = append(actions, external.NewGithubIssueCloser(ctx, config))
+ case ActionGithubIssueSearcher:
+ actions = append(actions, external.NewGithubIssueSearch(ctx, config))
+ case ActionScraper:
+ actions = append(actions, external.NewScraper(config))
+ case ActionWikipedia:
+ actions = append(actions, external.NewWikipedia(config))
+ }
+ }
+
+ return actions
+}
diff --git a/example/webui/agentconfig.go b/example/webui/agentconfig.go
new file mode 100644
index 0000000..35a3175
--- /dev/null
+++ b/example/webui/agentconfig.go
@@ -0,0 +1,30 @@
+package main
+
+type ConnectorConfig struct {
+ Type string `json:"type"` // e.g. Slack
+ Config string `json:"config"`
+}
+
+type ActionsConfig struct {
+ Name string `json:"name"` // e.g. search
+ Config string `json:"config"`
+}
+
+type AgentConfig struct {
+ Connector []ConnectorConfig `json:"connectors" form:"connectors" `
+ Actions []ActionsConfig `json:"actions" form:"actions"`
+ // This is what needs to be part of ActionsConfig
+ Model string `json:"model" form:"model"`
+ Name string `json:"name" form:"name"`
+ HUD bool `json:"hud" form:"hud"`
+ StandaloneJob bool `json:"standalone_job" form:"standalone_job"`
+ RandomIdentity bool `json:"random_identity" form:"random_identity"`
+ InitiateConversations bool `json:"initiate_conversations" form:"initiate_conversations"`
+ IdentityGuidance string `json:"identity_guidance" form:"identity_guidance"`
+ PeriodicRuns string `json:"periodic_runs" form:"periodic_runs"`
+ PermanentGoal string `json:"permanent_goal" form:"permanent_goal"`
+ EnableKnowledgeBase bool `json:"enable_kb" form:"enable_kb"`
+ KnowledgeBaseResults int `json:"kb_results" form:"kb_results"`
+ CanStopItself bool `json:"can_stop_itself" form:"can_stop_itself"`
+ SystemPrompt string `json:"system_prompt" form:"system_prompt"`
+}
diff --git a/example/webui/agentpool.go b/example/webui/agentpool.go
index 318e5ed..d3c4339 100644
--- a/example/webui/agentpool.go
+++ b/example/webui/agentpool.go
@@ -10,41 +10,9 @@ import (
"sync"
"time"
- "github.com/mudler/local-agent-framework/example/webui/connector"
-
. "github.com/mudler/local-agent-framework/agent"
- "github.com/mudler/local-agent-framework/external"
)
-type ConnectorConfig struct {
- Type string `json:"type"` // e.g. Slack
- Config string `json:"config"`
-}
-
-type ActionsConfig struct {
- Name string `json:"name"` // e.g. search
- Config string `json:"config"`
-}
-
-type AgentConfig struct {
- Connector []ConnectorConfig `json:"connectors" form:"connectors" `
- Actions []ActionsConfig `json:"actions" form:"actions"`
- // This is what needs to be part of ActionsConfig
- Model string `json:"model" form:"model"`
- Name string `json:"name" form:"name"`
- HUD bool `json:"hud" form:"hud"`
- StandaloneJob bool `json:"standalone_job" form:"standalone_job"`
- RandomIdentity bool `json:"random_identity" form:"random_identity"`
- InitiateConversations bool `json:"initiate_conversations" form:"initiate_conversations"`
- IdentityGuidance string `json:"identity_guidance" form:"identity_guidance"`
- PeriodicRuns string `json:"periodic_runs" form:"periodic_runs"`
- PermanentGoal string `json:"permanent_goal" form:"permanent_goal"`
- EnableKnowledgeBase bool `json:"enable_kb" form:"enable_kb"`
- KnowledgeBaseResults int `json:"kb_results" form:"kb_results"`
- CanStopItself bool `json:"can_stop_itself" form:"can_stop_itself"`
- SystemPrompt string `json:"system_prompt" form:"system_prompt"`
-}
-
type AgentPool struct {
sync.Mutex
file string
@@ -148,105 +116,6 @@ func (a *AgentPool) List() []string {
return agents
}
-const (
- // Connectors
- ConnectorTelegram = "telegram"
- ConnectorSlack = "slack"
- ConnectorDiscord = "discord"
- ConnectorGithubIssues = "github-issues"
-
- // Actions
- ActionSearch = "search"
- ActionGithubIssueLabeler = "github-issue-labeler"
- ActionGithubIssueOpener = "github-issue-opener"
- ActionGithubIssueCloser = "github-issue-closer"
- ActionGithubIssueSearcher = "github-issue-searcher"
-)
-
-var AvailableActions = []string{
- ActionSearch,
- ActionGithubIssueLabeler,
- ActionGithubIssueOpener,
- ActionGithubIssueCloser,
- ActionGithubIssueSearcher,
-}
-
-func (a *AgentConfig) availableActions(ctx context.Context) []Action {
- actions := []Action{}
-
- for _, action := range a.Actions {
- slog.Info("Set Action", action)
-
- var config map[string]string
- if err := json.Unmarshal([]byte(action.Config), &config); err != nil {
- slog.Info("Error unmarshalling action config", err)
- continue
- }
- slog.Info("Config", config)
-
- switch action.Name {
- case ActionSearch:
- actions = append(actions, external.NewSearch(config))
- case ActionGithubIssueLabeler:
- actions = append(actions, external.NewGithubIssueLabeler(ctx, config))
- case ActionGithubIssueOpener:
- actions = append(actions, external.NewGithubIssueOpener(ctx, config))
- case ActionGithubIssueCloser:
- actions = append(actions, external.NewGithubIssueCloser(ctx, config))
- case ActionGithubIssueSearcher:
- actions = append(actions, external.NewGithubIssueSearch(ctx, config))
- }
- }
-
- return actions
-}
-
-type Connector interface {
- AgentResultCallback() func(state ActionState)
- AgentReasoningCallback() func(state ActionCurrentState) bool
- Start(a *Agent)
-}
-
-var AvailableConnectors = []string{
- ConnectorTelegram,
- ConnectorSlack,
- ConnectorDiscord,
- ConnectorGithubIssues,
-}
-
-func (a *AgentConfig) availableConnectors() []Connector {
- connectors := []Connector{}
-
- for _, c := range a.Connector {
- slog.Info("Set Connector", c)
-
- var config map[string]string
- if err := json.Unmarshal([]byte(c.Config), &config); err != nil {
- slog.Info("Error unmarshalling connector config", err)
- continue
- }
- slog.Info("Config", config)
-
- switch c.Type {
- case ConnectorTelegram:
- cc, err := connector.NewTelegramConnector(config)
- if err != nil {
- slog.Info("Error creating telegram connector", err)
- continue
- }
-
- connectors = append(connectors, cc)
- case ConnectorSlack:
- connectors = append(connectors, connector.NewSlack(config))
- case ConnectorDiscord:
- connectors = append(connectors, connector.NewDiscord(config))
- case ConnectorGithubIssues:
- connectors = append(connectors, connector.NewGithubIssueWatcher(config))
- }
- }
- return connectors
-}
-
func (a *AgentPool) GetStatusHistory(name string) *Status {
a.Lock()
defer a.Unlock()
diff --git a/example/webui/app.go b/example/webui/app.go
index 180fb10..d2d9e72 100644
--- a/example/webui/app.go
+++ b/example/webui/app.go
@@ -264,7 +264,11 @@ func (a *App) Chat(pool *AgentPool) func(c *fiber.Ctx) error {
res := agent.Ask(
WithText(query),
)
- slog.Info("response is", res.Response)
+ if res.Error != nil {
+ slog.Error("Error asking agent", "agent", agentName, "error", res.Error)
+ } else {
+ slog.Info("we got a response from the agent", "agent", agentName, "response", res.Response)
+ }
manager.Send(
NewMessage(
chatDiv(res.Response, "blue"),
diff --git a/example/webui/connectors.go b/example/webui/connectors.go
new file mode 100644
index 0000000..7667d33
--- /dev/null
+++ b/example/webui/connectors.go
@@ -0,0 +1,64 @@
+package main
+
+import (
+ "encoding/json"
+ "log/slog"
+
+ . "github.com/mudler/local-agent-framework/agent"
+
+ "github.com/mudler/local-agent-framework/example/webui/connector"
+)
+
+const (
+ // Connectors
+ ConnectorTelegram = "telegram"
+ ConnectorSlack = "slack"
+ ConnectorDiscord = "discord"
+ ConnectorGithubIssues = "github-issues"
+)
+
+type Connector interface {
+ AgentResultCallback() func(state ActionState)
+ AgentReasoningCallback() func(state ActionCurrentState) bool
+ Start(a *Agent)
+}
+
+var AvailableConnectors = []string{
+ ConnectorTelegram,
+ ConnectorSlack,
+ ConnectorDiscord,
+ ConnectorGithubIssues,
+}
+
+func (a *AgentConfig) availableConnectors() []Connector {
+ connectors := []Connector{}
+
+ for _, c := range a.Connector {
+ slog.Info("Set Connector", c)
+
+ var config map[string]string
+ if err := json.Unmarshal([]byte(c.Config), &config); err != nil {
+ slog.Info("Error unmarshalling connector config", err)
+ continue
+ }
+ slog.Info("Config", config)
+
+ switch c.Type {
+ case ConnectorTelegram:
+ cc, err := connector.NewTelegramConnector(config)
+ if err != nil {
+ slog.Info("Error creating telegram connector", err)
+ continue
+ }
+
+ connectors = append(connectors, cc)
+ case ConnectorSlack:
+ connectors = append(connectors, connector.NewSlack(config))
+ case ConnectorDiscord:
+ connectors = append(connectors, connector.NewDiscord(config))
+ case ConnectorGithubIssues:
+ connectors = append(connectors, connector.NewGithubIssueWatcher(config))
+ }
+ }
+ return connectors
+}
diff --git a/example/webui/views/create.html b/example/webui/views/create.html
index c90bb44..87f7799 100644
--- a/example/webui/views/create.html
+++ b/example/webui/views/create.html
@@ -105,7 +105,7 @@
-
+
@@ -113,13 +113,13 @@
-
+
-
+
-
+