Skip to content

Commit 495c40e

Browse files
committed
Added agent interface for OpenAI
1 parent 9afc5df commit 495c40e

25 files changed

+1353
-24
lines changed

client.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ const (
6161
PathSeparator = string(os.PathSeparator)
6262
ContentTypeAny = "*/*"
6363
ContentTypeJson = "application/json"
64+
ContentTypeJsonStream = "application/x-ndjson"
6465
ContentTypeTextXml = "text/xml"
6566
ContentTypeApplicationXml = "application/xml"
6667
ContentTypeTextPlain = "text/plain"
@@ -306,7 +307,7 @@ func do(client *http.Client, req *http.Request, accept string, strict bool, out
306307

307308
// Decode the body
308309
switch mimetype {
309-
case ContentTypeJson:
310+
case ContentTypeJson, ContentTypeJsonStream:
310311
// JSON decode is streamable
311312
dec := json.NewDecoder(response.Body)
312313
for {

cmd/agent/chat.go

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
// Packages
8+
agent "github.com/mutablelogic/go-client/pkg/agent"
9+
)
10+
11+
/////////////////////////////////////////////////////////////////////
12+
// TYPES
13+
14+
type ChatCmd struct {
15+
Prompt string `arg:"" help:"The prompt to generate a response for"`
16+
Agent string `flag:"agent" help:"The agent to use"`
17+
Model string `flag:"model" help:"The model to use"`
18+
Stream bool `flag:"stream" help:"Stream the response"`
19+
}
20+
21+
/////////////////////////////////////////////////////////////////////
22+
// PUBLIC METHODS
23+
24+
func (cmd *ChatCmd) Run(globals *Globals) error {
25+
model_agent, model := globals.getModel(globals.ctx, cmd.Agent, cmd.Model)
26+
if model_agent == nil || model == nil {
27+
return fmt.Errorf("model %q not found, or not set on command line", globals.state.Model)
28+
}
29+
30+
// Generate the options
31+
opts := make([]agent.Opt, 0)
32+
if cmd.Stream {
33+
opts = append(opts, agent.OptStream(func(r agent.Response) {
34+
fmt.Println(r)
35+
}))
36+
}
37+
38+
// Add tools
39+
if tools := globals.getTools(); len(tools) > 0 {
40+
opts = append(opts, agent.OptTools(tools...))
41+
}
42+
43+
// Set the initial context
44+
context := []agent.Context{
45+
model_agent.UserPrompt(cmd.Prompt),
46+
}
47+
48+
FOR_LOOP:
49+
for {
50+
// Generate a chat completion
51+
response, err := model_agent.Generate(globals.ctx, model, context, opts...)
52+
if err != nil {
53+
return err
54+
}
55+
56+
// If the response is a tool call, then run the tool
57+
if response.ToolCall != nil {
58+
result, err := globals.runTool(globals.ctx, response.ToolCall)
59+
if err != nil {
60+
return err
61+
}
62+
response.Context = append(response.Context, result)
63+
} else {
64+
fmt.Println(response.Text)
65+
66+
// We're done
67+
break FOR_LOOP
68+
}
69+
70+
// Context comes from the response
71+
context = response.Context
72+
}
73+
74+
// Return success
75+
return nil
76+
}
77+
78+
/////////////////////////////////////////////////////////////////////
79+
// PRIVATE METHODS
80+
81+
// Get the model, either from state or from the command-line flags.
82+
// If the model is not found, or there is another error, return nil
83+
func (globals *Globals) getModel(ctx context.Context, agent, model string) (agent.Agent, agent.Model) {
84+
state := globals.state
85+
if agent != "" {
86+
state.Agent = agent
87+
}
88+
if model != "" {
89+
state.Model = model
90+
}
91+
92+
// Cycle through the agents and models to find the one we want
93+
for _, agent := range globals.agents {
94+
// Filter by agent
95+
if state.Agent != "" && agent.Name() != state.Agent {
96+
continue
97+
}
98+
99+
// Retrieve the models for this agent
100+
models, err := agent.Models(ctx)
101+
if err != nil {
102+
continue
103+
}
104+
105+
// Filter by model
106+
for _, model := range models {
107+
if state.Model != "" && model.Name() != state.Model {
108+
continue
109+
}
110+
111+
// This is the model we're using....
112+
state.Agent = agent.Name()
113+
state.Model = model.Name()
114+
return agent, model
115+
}
116+
}
117+
118+
// No model found
119+
return nil, nil
120+
}
121+
122+
// Get the tools
123+
func (globals *Globals) getTools() []agent.Tool {
124+
return globals.tools
125+
}
126+
127+
// Return a tool by name. If the tool is not found, return nil
128+
func (globals *Globals) getTool(name string) agent.Tool {
129+
for _, tool := range globals.tools {
130+
if tool.Name() == name {
131+
return tool
132+
}
133+
}
134+
return nil
135+
}
136+
137+
// Run a tool from a tool call, and return the result
138+
func (globals *Globals) runTool(ctx context.Context, call *agent.ToolCall) (*agent.ToolResult, error) {
139+
tool := globals.getTool(call.Name)
140+
if tool == nil {
141+
return nil, fmt.Errorf("tool %q not found", call.Name)
142+
}
143+
144+
// Run the tool
145+
return tool.Run(ctx, call)
146+
}

cmd/agent/list_agents.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
)
7+
8+
/////////////////////////////////////////////////////////////////////
9+
// TYPES
10+
11+
type ListAgentsCmd struct {
12+
}
13+
14+
/////////////////////////////////////////////////////////////////////
15+
// METHODS
16+
17+
func (cmd *ListAgentsCmd) Run(ctx *Globals) error {
18+
result := make([]string, 0)
19+
for _, agent := range ctx.agents {
20+
result = append(result, agent.Name())
21+
}
22+
23+
data, err := json.MarshalIndent(result, "", " ")
24+
if err != nil {
25+
return err
26+
}
27+
28+
fmt.Println(string(data))
29+
30+
return nil
31+
}

cmd/agent/list_models.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
)
7+
8+
/////////////////////////////////////////////////////////////////////
9+
// TYPES
10+
11+
type ListModelsCmd struct {
12+
}
13+
14+
type modeljson struct {
15+
Agent string `json:"agent"`
16+
Model string `json:"model"`
17+
}
18+
19+
/////////////////////////////////////////////////////////////////////
20+
// METHODS
21+
22+
func (cmd *ListModelsCmd) Run(ctx *Globals) error {
23+
result := make([]modeljson, 0)
24+
for _, agent := range ctx.agents {
25+
models, err := agent.Models(ctx.ctx)
26+
if err != nil {
27+
return err
28+
}
29+
for _, model := range models {
30+
result = append(result, modeljson{Agent: agent.Name(), Model: model.Name()})
31+
}
32+
}
33+
34+
data, err := json.MarshalIndent(result, "", " ")
35+
if err != nil {
36+
return err
37+
}
38+
39+
fmt.Println(string(data))
40+
41+
return nil
42+
}

cmd/agent/list_tools.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
)
7+
8+
/////////////////////////////////////////////////////////////////////
9+
// TYPES
10+
11+
type ListToolsCmd struct {
12+
}
13+
14+
type tooljson struct {
15+
Provider string `json:"provider"`
16+
Name string `json:"name"`
17+
Description string `json:"description"`
18+
}
19+
20+
/////////////////////////////////////////////////////////////////////
21+
// METHODS
22+
23+
func (cmd *ListToolsCmd) Run(ctx *Globals) error {
24+
result := make([]tooljson, 0)
25+
for _, tool := range ctx.tools {
26+
result = append(result, tooljson{Provider: tool.Provider(), Name: tool.Name(), Description: tool.Description()})
27+
}
28+
29+
data, err := json.MarshalIndent(result, "", " ")
30+
if err != nil {
31+
return err
32+
}
33+
34+
fmt.Println(string(data))
35+
36+
return nil
37+
}

0 commit comments

Comments
 (0)