Skip to content

Commit 0609803

Browse files
committed
Added agent
1 parent 751bc30 commit 0609803

File tree

10 files changed

+134
-129
lines changed

10 files changed

+134
-129
lines changed

agent.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ type Agent interface {
1313
Models(context.Context) ([]Model, error)
1414

1515
// Generate a response from a prompt
16-
Generate(context.Context, Model, Context, ...Opt) (*Response, error)
16+
Generate(context.Context, Model, Context, ...Opt) (Context, error)
1717

1818
// Embedding vector generation
1919
Embedding(context.Context, Model, string, ...Opt) ([]float64, error)
@@ -22,5 +22,5 @@ type Agent interface {
2222
UserPrompt(string, ...Opt) Context
2323

2424
// Create the result of calling a tool
25-
ToolResult(any) Context
25+
ToolResult(id string, opts ...Opt) Context
2626
}

cmd/agent/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ type CLI struct {
4343
Globals
4444

4545
// Agents, Models and Tools
46-
//Agents ListAgentsCmd `cmd:"" help:"Return a list of agents"`
46+
Agents ListAgentsCmd `cmd:"" help:"Return a list of agents"`
4747
Models ListModelsCmd `cmd:"" help:"Return a list of models"`
4848
}
4949

cmd/agent/models.go

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,30 @@ import (
44
"context"
55
"fmt"
66

7+
// Packages
78
llm "github.com/mutablelogic/go-llm"
9+
agent "github.com/mutablelogic/go-llm/pkg/agent"
810
)
911

1012
////////////////////////////////////////////////////////////////////////////////
1113
// TYPES
1214

1315
type ListModelsCmd struct {
16+
Agent []string `help:"Only return models from a specific agent"`
1417
}
1518

19+
type ListAgentsCmd struct{}
20+
1621
////////////////////////////////////////////////////////////////////////////////
1722
// PUBLIC METHODS
1823

19-
func (*ListModelsCmd) Run(globals *Globals) error {
20-
return runagent(globals, func(ctx context.Context, agent llm.Agent) error {
21-
models, err := agent.Models(ctx)
24+
func (cmd *ListModelsCmd) Run(globals *Globals) error {
25+
return runagent(globals, func(ctx context.Context, client llm.Agent) error {
26+
agent, ok := client.(*agent.Agent)
27+
if !ok {
28+
return fmt.Errorf("No agents found")
29+
}
30+
models, err := agent.ListModels(ctx, cmd.Agent...)
2231
if err != nil {
2332
return err
2433
}
@@ -27,6 +36,19 @@ func (*ListModelsCmd) Run(globals *Globals) error {
2736
})
2837
}
2938

39+
func (*ListAgentsCmd) Run(globals *Globals) error {
40+
return runagent(globals, func(ctx context.Context, client llm.Agent) error {
41+
agent, ok := client.(*agent.Agent)
42+
if !ok {
43+
return fmt.Errorf("No agents found")
44+
}
45+
for _, agent := range agent.Agents() {
46+
fmt.Println(agent)
47+
}
48+
return nil
49+
})
50+
}
51+
3052
////////////////////////////////////////////////////////////////////////////////
3153
// PRIVATE METHODS
3254

pkg/agent/agent.go

Lines changed: 62 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"encoding/json"
66
"errors"
7+
"fmt"
78
"slices"
89
"strings"
910

@@ -14,7 +15,7 @@ import (
1415
///////////////////////////////////////////////////////////////////////////////
1516
// TYPES
1617

17-
type agent struct {
18+
type Agent struct {
1819
*opt
1920
}
2021

@@ -23,14 +24,14 @@ type model struct {
2324
llm.Model `json:"model"`
2425
}
2526

26-
var _ llm.Agent = (*agent)(nil)
27+
var _ llm.Agent = (*Agent)(nil)
2728

2829
///////////////////////////////////////////////////////////////////////////////
2930
// LIFECYCLE
3031

31-
// Return a new agent, composed of several different models from different providers
32-
func New(opts ...llm.Opt) (*agent, error) {
33-
agent := new(agent)
32+
// Return a new agent, composed of agents and tools
33+
func New(opts ...llm.Opt) (*Agent, error) {
34+
agent := new(Agent)
3435
opt, err := apply(opts...)
3536
if err != nil {
3637
return nil, err
@@ -57,25 +58,50 @@ func (m model) String() string {
5758
// PUBLIC METHODS
5859

5960
// Return a list of agent names
60-
func (a *agent) Agents() []string {
61+
func (a *Agent) Agents() []string {
6162
var keys []string
6263
for k := range a.agents {
6364
keys = append(keys, k)
6465
}
6566
return keys
6667
}
6768

69+
// Return a list of tool names
70+
func (a *Agent) Tools() []string {
71+
var keys []string
72+
for k := range a.tools {
73+
keys = append(keys, k)
74+
}
75+
return keys
76+
}
77+
6878
// Return a comma-separated list of agent names
69-
func (a *agent) Name() string {
79+
func (a *Agent) Name() string {
7080
return strings.Join(a.Agents(), ",")
7181
}
7282

7383
// Return the models from all agents
74-
func (a *agent) Models(ctx context.Context) ([]llm.Model, error) {
84+
func (a *Agent) Models(ctx context.Context) ([]llm.Model, error) {
85+
return a.ListModels(ctx)
86+
}
87+
88+
// Return the models from list of agents
89+
func (a *Agent) ListModels(ctx context.Context, agents ...string) ([]llm.Model, error) {
7590
var result error
7691

92+
// Ensure all agents are valid
93+
for _, agent := range agents {
94+
if _, exists := a.agents[agent]; !exists {
95+
result = errors.Join(result, llm.ErrNotFound.Withf("agent %q", agent))
96+
}
97+
}
98+
99+
// Gather models from all agents
77100
models := make([]llm.Model, 0, 100)
78101
for _, agent := range a.agents {
102+
if len(agents) > 0 && !slices.Contains(agents, agent.Name()) {
103+
continue
104+
}
79105
agentmodels, err := modelsForAgent(ctx, agent)
80106
if err != nil {
81107
result = errors.Join(result, err)
@@ -91,19 +117,27 @@ func (a *agent) Models(ctx context.Context) ([]llm.Model, error) {
91117

92118
// Return a model by name. If no agents are specified, then all agents are considered.
93119
// If multiple agents are specified, then the first model found is returned.
94-
func (a *agent) Model(ctx context.Context, name string, agent ...string) (llm.Model, error) {
95-
if len(agent) == 0 {
96-
agent = a.Agents()
120+
func (a *Agent) GetModel(ctx context.Context, name string, agents ...string) (llm.Model, error) {
121+
if len(agents) == 0 {
122+
agents = a.Agents()
97123
}
98124

125+
// Ensure all agents are valid
99126
var result error
100-
for _, agent := range agent {
127+
for _, agent := range agents {
128+
if _, exists := a.agents[agent]; !exists {
129+
result = errors.Join(result, llm.ErrNotFound.Withf("agent %q", agent))
130+
}
131+
}
132+
133+
// Gather models from agents
134+
for _, agent := range agents {
101135
models, err := modelsForAgent(ctx, a.agents[agent], name)
102136
if err != nil {
103137
result = errors.Join(result, err)
104138
continue
105139
} else if len(models) > 0 {
106-
return models[0], nil
140+
return models[0], result
107141
}
108142
}
109143

@@ -112,22 +146,33 @@ func (a *agent) Model(ctx context.Context, name string, agent ...string) (llm.Mo
112146
}
113147

114148
// Generate a response from a prompt
115-
func (a *agent) Generate(context.Context, llm.Model, llm.Context, ...llm.Opt) (*llm.Response, error) {
149+
func (a *Agent) Generate(ctx context.Context, m llm.Model, context llm.Context, opts ...llm.Opt) (llm.Context, error) {
150+
// Obtain the agent
151+
var agent llm.Agent
152+
if model, ok := m.(*model); !ok || model == nil {
153+
return nil, llm.ErrBadParameter.With("model")
154+
} else if agent_, exists := a.agents[model.Agent]; !exists {
155+
return nil, llm.ErrNotFound.Withf("agent %q", model.Agent)
156+
} else {
157+
agent = agent_
158+
}
159+
fmt.Println(agent)
160+
116161
return nil, llm.ErrNotImplemented
117162
}
118163

119164
// Embedding vector generation
120-
func (a *agent) Embedding(context.Context, llm.Model, string, ...llm.Opt) ([]float64, error) {
165+
func (a *Agent) Embedding(context.Context, llm.Model, string, ...llm.Opt) ([]float64, error) {
121166
return nil, llm.ErrNotImplemented
122167
}
123168

124169
// Create user message context
125-
func (a *agent) UserPrompt(string, ...llm.Opt) llm.Context {
170+
func (a *Agent) UserPrompt(string, ...llm.Opt) llm.Context {
126171
return nil
127172
}
128173

129174
// Create the result of calling a tool
130-
func (a *agent) ToolResult(any) llm.Context {
175+
func (a *Agent) ToolResult(id string, opts ...llm.Opt) llm.Context {
131176
return nil
132177
}
133178

pkg/agent/opt.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313

1414
type opt struct {
1515
agents map[string]llm.Agent
16+
tools map[string]llm.Tool
1617
}
1718

1819
////////////////////////////////////////////////////////////////////////////////
@@ -54,6 +55,20 @@ func WithAnthropic(key string, opts ...client.ClientOpt) llm.Opt {
5455
}
5556
}
5657

58+
func WithTools(tools ...llm.Tool) llm.Opt {
59+
return func(o any) error {
60+
for _, tool := range tools {
61+
name := tool.Name()
62+
if _, exists := o.(*opt).tools[name]; exists {
63+
return llm.ErrConflict.Withf("Tool %q already exists", name)
64+
}
65+
o.(*opt).tools[name] = tool
66+
}
67+
// Return success
68+
return nil
69+
}
70+
}
71+
5772
////////////////////////////////////////////////////////////////////////////////
5873
// PRIVATE METHODS
5974

pkg/anthropic/message.go

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,30 +29,36 @@ type MessageMeta struct {
2929

3030
type Content struct {
3131
Type string `json:"type"` // image, document, text, tool_use
32-
ContextText
33-
ContextAttachment
34-
ContextTool
32+
ContentText
33+
ContentAttachment
34+
ContentTool
35+
ContentToolResult
3536
CacheControl *cachecontrol `json:"cache_control,omitempty"` // ephemeral
3637
}
3738

38-
type ContextText struct {
39+
type ContentText struct {
3940
Text string `json:"text,omitempty"` // text content
4041
}
4142

42-
type ContextTool struct {
43+
type ContentTool struct {
4344
Id string `json:"id,omitempty"` // tool id
4445
Name string `json:"name,omitempty"` // tool name
4546
Input map[string]any `json:"input,omitempty"` // tool input
4647
InputJson string `json:"partial_json,omitempty"` // partial json input (for streaming)
4748
}
4849

49-
type ContextAttachment struct {
50+
type ContentAttachment struct {
5051
Title string `json:"title,omitempty"` // title of the document
5152
Context string `json:"context,omitempty"` // context of the document
5253
Citations *contentcitation `json:"citations,omitempty"` // citations of the document
5354
Source *contentsource `json:"source,omitempty"` // image or document content
5455
}
5556

57+
type ContentToolResult struct {
58+
Id string `json:"tool_use_id,omitempty"` // tool id
59+
Content []*Content `json:"content,omitempty"`
60+
}
61+
5662
type contentsource struct {
5763
Type string `json:"type"` // base64 or text
5864
MediaType string `json:"media_type"` // image/jpeg, image/png, image/gif, image/webp, application/pdf, text/plain
@@ -74,7 +80,7 @@ type contentcitation struct {
7480
func NewTextContent(v string) *Content {
7581
content := new(Content)
7682
content.Type = "text"
77-
content.ContextText.Text = v
83+
content.ContentText.Text = v
7884
return content
7985
}
8086

@@ -112,7 +118,7 @@ func (*Client) UserPrompt(text string, opts ...llm.Opt) llm.Context {
112118
return nil
113119
}
114120

115-
context := &message{}
121+
context := new(message)
116122
context.MessageMeta.Role = "user"
117123
context.MessageMeta.Content = make([]*Content, 0, len(opt.data)+1)
118124

@@ -129,8 +135,13 @@ func (*Client) UserPrompt(text string, opts ...llm.Opt) llm.Context {
129135
}
130136

131137
// Create the result of calling a tool
132-
func (*Client) ToolResult(any) llm.Context {
133-
return nil
138+
func (*Client) ToolResult(id string, opts ...llm.Opt) llm.Context {
139+
context := new(message)
140+
context.MessageMeta.Role = "user"
141+
context.MessageMeta.Content = make([]*Content, 0, 1)
142+
143+
// Return the context
144+
return context
134145
}
135146

136147
///////////////////////////////////////////////////////////////////////////////

pkg/anthropic/messages.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ func (anthropic *Client) Messages(ctx context.Context, model llm.Model, context
234234
}
235235

236236
// Generate a response from a prompt
237-
func (*Client) Generate(context.Context, llm.Model, llm.Context, ...llm.Opt) (*llm.Response, error) {
237+
func (*Client) Generate(context.Context, llm.Model, llm.Context, ...llm.Opt) (llm.Context, error) {
238238
return nil, llm.ErrNotImplemented
239239
}
240240

pkg/ollama/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,6 @@ func (*Client) Name() string {
5050
}
5151

5252
// Generate a response from a prompt
53-
func (ollama *Client) Generate(ctx context.Context, model llm.Model, context llm.Context, opts ...llm.Opt) (*llm.Response, error) {
53+
func (ollama *Client) Generate(ctx context.Context, model llm.Model, context llm.Context, opts ...llm.Opt) (llm.Context, error) {
5454
return nil, llm.ErrNotImplemented
5555
}

pkg/ollama/message.go

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -63,24 +63,9 @@ func (ollama *Client) UserPrompt(v string, opts ...llm.Opt) llm.Context {
6363
}
6464

6565
// The result of a tool call
66-
func (ollama *Client) ToolResult(v any) llm.Context {
67-
m := new(message)
68-
m.MessageMeta.Role = "tool"
69-
70-
switch v := v.(type) {
71-
case string:
72-
m.MessageMeta.Content = v
73-
default:
74-
// Encode the result into json
75-
data, err := json.Marshal(v)
76-
if err != nil {
77-
return nil
78-
}
79-
m.MessageMeta.Content = string(data)
80-
}
81-
82-
// Return success
83-
return m
66+
func (ollama *Client) ToolResult(id string, opts ...llm.Opt) llm.Context {
67+
// messages.append({'role': 'tool', 'content': str(output), 'name': tool.function.name})
68+
return nil
8469
}
8570

8671
// Return the role of a message

0 commit comments

Comments
 (0)