wips
This commit is contained in:
@@ -59,12 +59,6 @@ func New(opts ...Option) (*Agent, error) {
|
|||||||
context: action.NewContext(ctx, cancel),
|
context: action.NewContext(ctx, cancel),
|
||||||
}
|
}
|
||||||
|
|
||||||
if a.options.randomIdentity {
|
|
||||||
if err = a.generateIdentity(a.options.randomIdentityGuidance); err != nil {
|
|
||||||
return a, fmt.Errorf("failed to generate identity: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.options.statefile != "" {
|
if a.options.statefile != "" {
|
||||||
if _, err := os.Stat(a.options.statefile); err == nil {
|
if _, err := os.Stat(a.options.statefile); err == nil {
|
||||||
if err = a.LoadState(a.options.statefile); err != nil {
|
if err = a.LoadState(a.options.statefile); err != nil {
|
||||||
@@ -73,20 +67,6 @@ func New(opts ...Option) (*Agent, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if a.options.characterfile != "" {
|
|
||||||
if _, err := os.Stat(a.options.characterfile); err == nil {
|
|
||||||
// if there is a file, load the character back
|
|
||||||
if err = a.LoadCharacter(a.options.characterfile); err != nil {
|
|
||||||
return a, fmt.Errorf("failed to load character: %v", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// otherwise save it for next time
|
|
||||||
if err = a.SaveCharacter(a.options.characterfile); err != nil {
|
|
||||||
return a, fmt.Errorf("failed to save character: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.options.debugMode {
|
if a.options.debugMode {
|
||||||
fmt.Println("=== Agent in Debug mode ===")
|
fmt.Println("=== Agent in Debug mode ===")
|
||||||
fmt.Println(a.Character.String())
|
fmt.Println(a.Character.String())
|
||||||
@@ -520,11 +500,44 @@ func (a *Agent) periodicallyRun() {
|
|||||||
// a.ResetConversation()
|
// a.ResetConversation()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Agent) prepareIdentity() error {
|
||||||
|
|
||||||
|
if a.options.characterfile != "" {
|
||||||
|
if _, err := os.Stat(a.options.characterfile); err == nil {
|
||||||
|
// if there is a file, load the character back
|
||||||
|
if err = a.LoadCharacter(a.options.characterfile); err != nil {
|
||||||
|
return fmt.Errorf("failed to load character: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if a.options.randomIdentity {
|
||||||
|
if err = a.generateIdentity(a.options.randomIdentityGuidance); err != nil {
|
||||||
|
return fmt.Errorf("failed to generate identity: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise save it for next time
|
||||||
|
if err = a.SaveCharacter(a.options.characterfile); err != nil {
|
||||||
|
return fmt.Errorf("failed to save character: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := a.generateIdentity(a.options.randomIdentityGuidance); err != nil {
|
||||||
|
return fmt.Errorf("failed to generate identity: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (a *Agent) Run() error {
|
func (a *Agent) Run() error {
|
||||||
// The agent run does two things:
|
// The agent run does two things:
|
||||||
// picks up requests from a queue
|
// picks up requests from a queue
|
||||||
// and generates a response/perform actions
|
// and generates a response/perform actions
|
||||||
|
|
||||||
|
if err := a.prepareIdentity(); err != nil {
|
||||||
|
return fmt.Errorf("failed to prepare identity: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// It is also preemptive.
|
// It is also preemptive.
|
||||||
// That is, it can interrupt the current action
|
// That is, it can interrupt the current action
|
||||||
// if another one comes in.
|
// if another one comes in.
|
||||||
|
|||||||
@@ -19,17 +19,17 @@ type ConnectorConfig struct {
|
|||||||
type ActionsConfig string
|
type ActionsConfig string
|
||||||
|
|
||||||
type AgentConfig struct {
|
type AgentConfig struct {
|
||||||
Connector []ConnectorConfig `json:"connector"`
|
Connector []ConnectorConfig `json:"connector" form:"connector" `
|
||||||
Actions []ActionsConfig `json:"actions"`
|
Actions []ActionsConfig `json:"actions" form:"actions"`
|
||||||
// This is what needs to be part of ActionsConfig
|
// This is what needs to be part of ActionsConfig
|
||||||
Model string `json:"model"`
|
Model string `json:"model" form:"model"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name" form:"name"`
|
||||||
HUD bool `json:"hud"`
|
HUD bool `json:"hud" form:"hud"`
|
||||||
Debug bool `json:"debug"`
|
Debug bool `json:"debug" form:"debug"`
|
||||||
StandaloneJob bool `json:"standalone_job"`
|
StandaloneJob bool `json:"standalone_job" form:"standalone_job"`
|
||||||
RandomIdentity bool `json:"random_identity"`
|
RandomIdentity bool `json:"random_identity" form:"random_identity"`
|
||||||
IdentityGuidance string `json:"identity_guidance"`
|
IdentityGuidance string `json:"identity_guidance" form:"identity_guidance"`
|
||||||
PeriodicRuns string `json:"periodic_runs"`
|
PeriodicRuns string `json:"periodic_runs" form:"periodic_runs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AgentPool struct {
|
type AgentPool struct {
|
||||||
@@ -104,13 +104,17 @@ func (a *AgentPool) List() []string {
|
|||||||
return agents
|
return agents
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var AvailableActions = []string{"search"}
|
||||||
|
|
||||||
func (a *AgentConfig) availableActions() []Action {
|
func (a *AgentConfig) availableActions() []Action {
|
||||||
actions := []Action{}
|
actions := []Action{}
|
||||||
|
|
||||||
if len(a.Actions) == 0 {
|
if len(a.Actions) == 0 {
|
||||||
// Return search as default
|
// Return search as default
|
||||||
return []Action{external.NewSearch(3)}
|
return []Action{external.NewSearch(3)}
|
||||||
}
|
}
|
||||||
for _, action := range a.Actions {
|
for _, action := range a.Actions {
|
||||||
|
fmt.Println("Set Action", action)
|
||||||
switch action {
|
switch action {
|
||||||
case "search":
|
case "search":
|
||||||
actions = append(actions, external.NewSearch(3))
|
actions = append(actions, external.NewSearch(3))
|
||||||
@@ -134,6 +138,10 @@ func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error
|
|||||||
fmt.Println("API URL", a.apiURL)
|
fmt.Println("API URL", a.apiURL)
|
||||||
|
|
||||||
actions := config.availableActions()
|
actions := config.availableActions()
|
||||||
|
|
||||||
|
stateFile, characterFile := a.stateFiles(name)
|
||||||
|
|
||||||
|
fmt.Println("Actions", actions)
|
||||||
opts := []Option{
|
opts := []Option{
|
||||||
WithModel(model),
|
WithModel(model),
|
||||||
WithLLMAPIURL(a.apiURL),
|
WithLLMAPIURL(a.apiURL),
|
||||||
@@ -141,8 +149,8 @@ func (a *AgentPool) startAgentWithConfig(name string, config *AgentConfig) error
|
|||||||
WithActions(
|
WithActions(
|
||||||
actions...,
|
actions...,
|
||||||
),
|
),
|
||||||
WithStateFile(filepath.Join(a.pooldir, fmt.Sprintf("%s.state.json", name))),
|
WithStateFile(stateFile),
|
||||||
WithCharacterFile(filepath.Join(a.pooldir, fmt.Sprintf("%s.character.json", name))),
|
WithCharacterFile(characterFile),
|
||||||
WithAgentReasoningCallback(func(state ActionCurrentState) bool {
|
WithAgentReasoningCallback(func(state ActionCurrentState) bool {
|
||||||
fmt.Println("Reasoning", state.Reasoning)
|
fmt.Println("Reasoning", state.Reasoning)
|
||||||
manager.Send(
|
manager.Send(
|
||||||
@@ -201,7 +209,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 {
|
||||||
panic(err)
|
fmt.Println("Agent stop: ", err.Error())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -253,7 +261,21 @@ func (a *AgentPool) Start(name string) error {
|
|||||||
return fmt.Errorf("agent %s not found", name)
|
return fmt.Errorf("agent %s not found", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *AgentPool) stateFiles(name string) (string, string) {
|
||||||
|
stateFile := filepath.Join(a.pooldir, fmt.Sprintf("%s.state.json", name))
|
||||||
|
characterFile := filepath.Join(a.pooldir, fmt.Sprintf("%s.character.json", name))
|
||||||
|
|
||||||
|
return stateFile, characterFile
|
||||||
|
}
|
||||||
|
|
||||||
func (a *AgentPool) Remove(name string) error {
|
func (a *AgentPool) Remove(name string) error {
|
||||||
|
|
||||||
|
// Cleanup character and state
|
||||||
|
stateFile, characterFile := a.stateFiles(name)
|
||||||
|
|
||||||
|
os.Remove(stateFile)
|
||||||
|
os.Remove(characterFile)
|
||||||
|
|
||||||
a.Stop(name)
|
a.Stop(name)
|
||||||
delete(a.agents, name)
|
delete(a.agents, name)
|
||||||
delete(a.pool, name)
|
delete(a.pool, name)
|
||||||
|
|||||||
@@ -4,25 +4,38 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Create New Agent</title>
|
<title>Create New Agent</title>
|
||||||
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
|
||||||
|
<script src="https://unpkg.com/htmx.org"></script>
|
||||||
|
<script src="https://unpkg.com/htmx.org/dist/ext/sse.js"></script>
|
||||||
|
<script src="https://unpkg.com/hyperscript.org@0.9.12"></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-gray-900 p-4 text-white">
|
<body class="bg-gray-900 p-4 text-white">
|
||||||
<div class="max-w-md mx-auto mt-10">
|
<div class="max-w-md mx-auto mt-10">
|
||||||
<h1 class="text-2xl font-bold text-center mb-6">Create New Agent</h1>
|
<h1 class="text-2xl font-bold text-center mb-6">Create New Agent</h1>
|
||||||
<form action="/create" method="POST">
|
<form action="/create" method="POST">
|
||||||
<div class="mb-4">
|
|
||||||
<label for="connector" class="block text-sm font-medium text-gray-400">Connector Config (JSON)</label>
|
|
||||||
<textarea id="connector" name="connector" class="mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md bg-gray-700 text-white" placeholder='[{"...":"..."}]'></textarea>
|
|
||||||
</div>
|
|
||||||
<div class="mb-4">
|
|
||||||
<label for="actions" class="block text-sm font-medium text-gray-400">Actions Config (JSON)</label>
|
|
||||||
<textarea id="actions" name="actions" class="mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md bg-gray-700 text-white" placeholder='[{"...":"..."}]'></textarea>
|
|
||||||
</div>
|
|
||||||
<!-- Adding fields for properties in ActionsConfig -->
|
|
||||||
|
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="name" class="block text-sm font-medium text-gray-400">Name</label>
|
<label for="name" class="block text-sm font-medium text-gray-400">Name</label>
|
||||||
<input type="text" name="name" id="name" class="mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md bg-gray-700 text-white" placeholder="Name">
|
<input type="text" name="name" id="name" class="mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md bg-gray-700 text-white" placeholder="Name">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<div class="mb-4">
|
||||||
|
<label for="connector" class="block text-sm font-medium text-gray-400">Connector Config (JSON)</label>
|
||||||
|
<textarea id="connector" name="connector" class="mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md bg-gray-700 text-white" placeholder='[{"...":"..."}]'></textarea>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
<div class="mb-4" id="action_box">
|
||||||
|
<label for="actions" class="block text-sm font-medium text-gray-400">Agent Actions</label>
|
||||||
|
<select style="display: none" name="actions" id="actions" class="mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md bg-gray-700 text-white">
|
||||||
|
{{ range .Actions }}
|
||||||
|
<option value="{{.}}">{{.}}</option>
|
||||||
|
{{ end }}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-500 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" hx-on:click="let actionbox=document.getElementById('actions'); let clone=actionbox.cloneNode(true); clone.style.display ='block'; actionbox.after(clone)">Add action</button>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="hud" class="block text-sm font-medium text-gray-400">HUD</label>
|
<label for="hud" class="block text-sm font-medium text-gray-400">HUD</label>
|
||||||
<input type="checkbox" name="hud" id="hud" class="mt-1 focus:ring-blue-500 focus:border-blue-500 block shadow-sm sm:text-sm border-gray-300 rounded bg-gray-700">
|
<input type="checkbox" name="hud" id="hud" class="mt-1 focus:ring-blue-500 focus:border-blue-500 block shadow-sm sm:text-sm border-gray-300 rounded bg-gray-700">
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ func loader() string {
|
|||||||
return `<div class="loader"></div>`
|
return `<div class="loader"></div>`
|
||||||
}
|
}
|
||||||
|
|
||||||
func inputMessageDisabled(disabled bool) string {
|
func disabledElement(id string, disabled bool) string {
|
||||||
if disabled {
|
if disabled {
|
||||||
return `<script> document.getElementById('inputMessage').disabled = true;</script>`
|
return `<script> document.getElementById('` + id + `').disabled = true;</script>`
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<script> document.getElementById('inputMessage').disabled = false;</script>`
|
return `<script> document.getElementById('` + id + `').disabled = false;</script>`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Agent List</title>
|
<title>Agent List</title>
|
||||||
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-gray-900 p-4 text-white">
|
<body class="bg-gray-900 p-4 text-white">
|
||||||
<div class="max-w-6xl mx-auto">
|
<div class="max-w-6xl mx-auto">
|
||||||
@@ -24,7 +25,10 @@
|
|||||||
Status
|
Status
|
||||||
</th>
|
</th>
|
||||||
<th scope="col" class="relative px-6 py-3">
|
<th scope="col" class="relative px-6 py-3">
|
||||||
<span class="sr-only">Edit</span>
|
<span class="sr-only">Talk</span>
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="relative px-6 py-3">
|
||||||
|
<span class="sr-only">Delete</span>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -37,6 +41,9 @@
|
|||||||
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||||
<a href="/talk/{{.}}" class="text-blue-500 hover:text-blue-400">Talk</a>
|
<a href="/talk/{{.}}" class="text-blue-500 hover:text-blue-400">Talk</a>
|
||||||
</td>
|
</td>
|
||||||
|
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||||
|
<a href="/delete/{{.}}" class="text-blue-500 hover:text-blue-400">Delete</a>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<!-- Repeat for each agent -->
|
<!-- Repeat for each agent -->
|
||||||
|
|||||||
@@ -88,7 +88,8 @@ func main() {
|
|||||||
|
|
||||||
webapp.Get("/create", func(c *fiber.Ctx) error {
|
webapp.Get("/create", func(c *fiber.Ctx) error {
|
||||||
return c.Render("create.html", fiber.Map{
|
return c.Render("create.html", fiber.Map{
|
||||||
"Title": "Hello, World!",
|
"Title": "Hello, World!",
|
||||||
|
"Actions": AvailableActions,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -107,6 +108,7 @@ func main() {
|
|||||||
webapp.Get("/notify/:name", app.Notify(pool))
|
webapp.Get("/notify/:name", app.Notify(pool))
|
||||||
webapp.Post("/chat/:name", app.Chat(pool))
|
webapp.Post("/chat/:name", app.Chat(pool))
|
||||||
webapp.Post("/create", app.Create(pool))
|
webapp.Post("/create", app.Create(pool))
|
||||||
|
webapp.Get("/delete/:name", app.Delete(pool))
|
||||||
|
|
||||||
webapp.Get("/talk/:name", func(c *fiber.Ctx) error {
|
webapp.Get("/talk/:name", func(c *fiber.Ctx) error {
|
||||||
return c.Render("chat.html", fiber.Map{
|
return c.Render("chat.html", fiber.Map{
|
||||||
@@ -166,6 +168,16 @@ func (a *App) Notify(pool *AgentPool) func(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) Delete(pool *AgentPool) func(c *fiber.Ctx) error {
|
||||||
|
return func(c *fiber.Ctx) error {
|
||||||
|
if err := pool.Remove(c.Params("name")); err != nil {
|
||||||
|
fmt.Println("Error removing agent", err)
|
||||||
|
return c.Status(http.StatusInternalServerError).SendString(err.Error())
|
||||||
|
}
|
||||||
|
return c.Redirect("/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) Create(pool *AgentPool) func(c *fiber.Ctx) error {
|
func (a *App) Create(pool *AgentPool) func(c *fiber.Ctx) error {
|
||||||
return func(c *fiber.Ctx) error {
|
return func(c *fiber.Ctx) error {
|
||||||
config := AgentConfig{}
|
config := AgentConfig{}
|
||||||
@@ -173,6 +185,8 @@ func (a *App) Create(pool *AgentPool) func(c *fiber.Ctx) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Agent configuration: %+v\n", config)
|
||||||
|
|
||||||
if config.Name == "" {
|
if config.Name == "" {
|
||||||
c.Status(http.StatusBadRequest).SendString("Name is required")
|
c.Status(http.StatusBadRequest).SendString("Name is required")
|
||||||
return nil
|
return nil
|
||||||
@@ -223,7 +237,7 @@ func (a *App) Chat(pool *AgentPool) func(c *fiber.Ctx) error {
|
|||||||
).WithEvent("messages"))
|
).WithEvent("messages"))
|
||||||
manager.Send(
|
manager.Send(
|
||||||
NewMessage(
|
NewMessage(
|
||||||
inputMessageDisabled(false), // show again the input
|
disabledElement("inputMessage", false), // show again the input
|
||||||
).WithEvent("message_status"))
|
).WithEvent("message_status"))
|
||||||
|
|
||||||
//result := `<i>done</i>`
|
//result := `<i>done</i>`
|
||||||
@@ -232,7 +246,7 @@ func (a *App) Chat(pool *AgentPool) func(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
manager.Send(
|
manager.Send(
|
||||||
NewMessage(
|
NewMessage(
|
||||||
loader() + inputMessageDisabled(true),
|
loader() + disabledElement("inputMessage", true),
|
||||||
).WithEvent("message_status"))
|
).WithEvent("message_status"))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
2
external/search.go
vendored
2
external/search.go
vendored
@@ -30,7 +30,7 @@ func (a *SearchAction) Run(params action.ActionParams) (string, error) {
|
|||||||
ddg := client.NewDuckDuckGoSearchClient()
|
ddg := client.NewDuckDuckGoSearchClient()
|
||||||
res, err := ddg.SearchLimited(result.Query, a.results)
|
res, err := ddg.SearchLimited(result.Query, a.results)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg := fmt.Sprintf("error: %v", err)
|
msg := fmt.Sprintf("error duckduckgo: %v", err)
|
||||||
fmt.Printf(msg)
|
fmt.Printf(msg)
|
||||||
return msg, err
|
return msg, err
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user