-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathagent.go
More file actions
163 lines (139 loc) · 5.45 KB
/
Copy pathagent.go
File metadata and controls
163 lines (139 loc) · 5.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package main
import (
"fmt"
"os"
"github.com/BurntSushi/toml"
)
type Agent struct {
Name string `toml:"name"`
Description string `toml:"description"`
Functions []FunctionConfig `toml:"functions"`
Ask string `toml:"ask"`
SystemPrompt string `toml:"system_prompt"`
InitialMessage string `toml:"initial_message"`
DefaultModel string `toml:"default_model"`
}
type FunctionConfig struct {
Name string `toml:"name"`
Description string `toml:"description"`
Command string `toml:"command"`
Parameters []ParameterConfig `toml:"parameters"`
Safe bool `toml:"safe"`
Stdin string `toml:"stdin,omitempty"`
Output string `toml:"output"`
OutputType string `toml:"output_type,omitempty"` // e.g. "image/png", "image/jpeg"
Pwd string `toml:"pwd,omitempty"`
Timeout int `toml:"timeout"`
}
type ParameterConfig struct {
Name string `toml:"name"`
Type string `toml:"type"`
Description string `toml:"description"`
Required bool `toml:"required"`
Format string `toml:"format,omitempty"`
Options []string `toml:"options,omitempty"`
Default any `toml:"default,omitempty"`
}
func loadAgent(agentPath string) (Agent, error) {
var agent Agent
_, err := toml.DecodeFile(agentPath, &agent)
if err != nil {
return agent, err
}
return validateAgent(agent)
}
// validateAgent performs validation on an agent configuration
// to ensure all required fields are present and properly formatted.
func validateAgent(agent Agent) (Agent, error) {
var err error
// Validate ask level
validAskLevels := map[string]bool{"": true, "none": true, "unsafe": true, "all": true}
if !validAskLevels[agent.Ask] {
return agent, fmt.Errorf("agent '%s' has invalid ask level: %q (must be one of: none, unsafe, all)", agent.Name, agent.Ask)
}
// Check function name uniqueness
funcNames := make(map[string]bool)
// Validate each function configuration
for i, fc := range agent.Functions {
if fc.Name == "" {
return agent, fmt.Errorf("function %d in agent '%s' has no name", i+1, agent.Name)
}
if funcNames[fc.Name] {
return agent, fmt.Errorf("duplicate function name '%s' in agent '%s'", fc.Name, agent.Name)
}
funcNames[fc.Name] = true
if fc.Command == "" {
return agent, fmt.Errorf("function %s in agent '%s' has no command defined", fc.Name, agent.Name)
}
if fc.Timeout < 0 || fc.Timeout > 3600 {
return agent, fmt.Errorf("function '%s' in agent '%s' has invalid timeout %d (must be 0-3600)", fc.Name, agent.Name, fc.Timeout)
}
agent.Functions[i].Description, err = processShellBlocks(fc.Description)
if err != nil {
return agent, fmt.Errorf("error processing shell blocks in function %s: %v", fc.Name, err)
}
// Validate parameters
paramNames := make(map[string]bool)
for j, param := range fc.Parameters {
if param.Name == "" {
return agent, fmt.Errorf("parameter %d in function '%s' has no name", j+1, fc.Name)
}
if paramNames[param.Name] {
return agent, fmt.Errorf("duplicate parameter name '%s' in function '%s'", param.Name, fc.Name)
}
paramNames[param.Name] = true
if param.Type == "" {
return agent, fmt.Errorf("parameter %s in function '%s' has no type defined", param.Name, fc.Name)
}
// Validate parameter type
validTypes := map[string]bool{
"string": true,
"number": true,
"boolean": true,
"array": true,
"object": true,
}
if !validTypes[param.Type] {
return agent, fmt.Errorf("parameter %s in function '%s' has invalid type: %s", param.Name, fc.Name, param.Type)
}
agent.Functions[i].Parameters[j].Description, err = processShellBlocks(param.Description)
if err != nil {
return agent, fmt.Errorf("error processing shell blocks in parameter %s of function %s: %v",
param.Name, fc.Name, err)
}
}
}
return agent, nil
}
func loadConfiguration(opts *CLIOptions) (Agent, error) {
if conf, exists := builtinAgents[opts.AgentName]; exists {
var agent Agent
if _, err := toml.Decode(conf, &agent); err != nil {
return Agent{}, fmt.Errorf("error loading embedded '%s' agent config: %v", opts.AgentName, err)
}
return agent, nil
}
agentPath := expandHomePath(opts.AgentPath)
_, err := os.Stat(agentPath)
if err != nil {
if os.IsNotExist(err) && opts.AgentName == "" && opts.AgentPath == DefaultAgentPath {
var agent Agent
if _, err := toml.Decode(defaultAgentToml, &agent); err != nil {
return Agent{}, fmt.Errorf("error loading embedded new agent config: %v", err)
}
return agent, nil
}
}
return loadAgent(agentPath)
}
const systemPrompt = `You are Esa, a professional assistant capable of performing various tasks. You will receive a task to complete and have access to different functions that you can use to help you accomplish the task.
When responding to tasks:
1. Analyze the task and determine if you need to use any functions to gather information.
2. If needed, make function calls to gather necessary information.
3. Process the information and formulate your response.
4. Provide only concise responses that directly address the task.
Other information:
- Date: {{$date '+%Y-%m-%d %A'}}
- OS: {{$uname}}
- Current directory: {{$pwd}}
Remember to keep your responses brief and to the point. Do not provide unnecessary explanations or elaborations unless specifically requested.`