Skip to content

Commit 25a30d0

Browse files
committed
Updates
1 parent a5499c6 commit 25a30d0

26 files changed

+513
-351
lines changed

.gitignore

+2-18
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,12 @@
1-
# If you prefer the allow list template instead of the deny list, see community template:
2-
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
3-
#
4-
# Binaries for programs and plugins
51
*.exe
62
*.exe~
73
*.dll
84
*.so
95
*.dylib
10-
11-
# Test binary, built with `go test -c`
126
*.test
13-
14-
# Output of the go coverage tool, specifically when used with LiteIDE
157
*.out
16-
17-
# Dependency directories (remove the comment below to include it)
18-
# vendor/
19-
20-
# Go workspace file
218
go.work
229
go.work.sum
23-
24-
# env file
25-
.env
26-
27-
# build output
10+
vendor/
2811
build/
12+
.DS_Store

agent.go

-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,4 @@ type Agent interface {
1111

1212
// Return the models
1313
Models(context.Context) ([]Model, error)
14-
15-
// Embedding vector generation
16-
Embedding(context.Context, Model, string, ...Opt) ([]float64, error)
1714
}

attachment.go

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package llm
2+
3+
import (
4+
"io"
5+
"os"
6+
)
7+
8+
///////////////////////////////////////////////////////////////////////////////
9+
// TYPES
10+
11+
// Attachment for messages
12+
type Attachment struct {
13+
filename string
14+
data []byte
15+
}
16+
17+
////////////////////////////////////////////////////////////////////////////////
18+
// LIFECYCLE
19+
20+
// ReadAttachment returns an attachment from a reader object.
21+
// It is the responsibility of the caller to close the reader.
22+
func ReadAttachment(r io.Reader) (*Attachment, error) {
23+
var filename string
24+
data, err := io.ReadAll(r)
25+
if err != nil {
26+
return nil, err
27+
}
28+
if f, ok := r.(*os.File); ok {
29+
filename = f.Name()
30+
}
31+
return &Attachment{filename: filename, data: data}, nil
32+
}
33+
34+
////////////////////////////////////////////////////////////////////////////////
35+
// PUBLIC METHODS
36+
37+
func (a *Attachment) Filename() string {
38+
return a.filename
39+
}
40+
41+
func (a *Attachment) Data() []byte {
42+
return a.data
43+
}

cmd/agent/generate.go renamed to cmd/agent/chat.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func (cmd *GenerateCmd) Run(globals *Globals) error {
3636
}
3737

3838
// Create a session
39-
session := model.Context(agent.WithStream(!cmd.NoStream))
39+
session := model.Context()
4040

4141
// Continue looping until end of input
4242
for {

cmd/agent/main.go

+15-8
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,10 @@ type Globals struct {
3232
NewsAPI `embed:"" help:"NewsAPI configuration"`
3333

3434
// Context
35-
ctx context.Context
36-
agent llm.Agent
37-
term *Term
35+
ctx context.Context
36+
agent llm.Agent
37+
toolkit *tool.ToolKit
38+
term *Term
3839
}
3940

4041
type Ollama struct {
@@ -53,10 +54,13 @@ type CLI struct {
5354
Globals
5455

5556
// Agents, Models and Tools
56-
Agents ListAgentsCmd `cmd:"" help:"Return a list of agents"`
57-
Models ListModelsCmd `cmd:"" help:"Return a list of models"`
57+
Agents ListAgentsCmd `cmd:"" help:"Return a list of agents"`
58+
Models ListModelsCmd `cmd:"" help:"Return a list of models"`
59+
Tools ListToolsCmd `cmd:"" help:"Return a list of tools"`
60+
61+
// Commands
5862
Download DownloadModelCmd `cmd:"" help:"Download a model"`
59-
Generate GenerateCmd `cmd:"" help:"Generate a response"`
63+
Generate GenerateCmd `cmd:"chat" help:"Start a chat session"`
6064
}
6165

6266
////////////////////////////////////////////////////////////////////////////////
@@ -104,9 +108,9 @@ func main() {
104108

105109
// Make a toolkit
106110
toolkit := tool.NewToolKit()
107-
opts = append(opts, agent.WithToolKit(toolkit))
111+
cli.Globals.toolkit = toolkit
108112

109-
// NewsAPI
113+
// Register NewsAPI
110114
if cli.NewsKey != "" {
111115
if client, err := newsapi.New(cli.NewsKey, clientopts...); err != nil {
112116
cmd.FatalIfErrorf(err)
@@ -115,6 +119,9 @@ func main() {
115119
}
116120
}
117121

122+
// Append the toolkit
123+
opts = append(opts, llm.WithToolKit(toolkit))
124+
118125
// Create the agent
119126
agent, err := agent.New(opts...)
120127
cmd.FatalIfErrorf(err)

cmd/agent/models.go

+10
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ type ListModelsCmd struct {
2020

2121
type ListAgentsCmd struct{}
2222

23+
type ListToolsCmd struct{}
24+
2325
type DownloadModelCmd struct {
2426
Agent string `arg:"" help:"Agent name"`
2527
Model string `arg:"" help:"Model name"`
@@ -28,6 +30,14 @@ type DownloadModelCmd struct {
2830
////////////////////////////////////////////////////////////////////////////////
2931
// PUBLIC METHODS
3032

33+
func (cmd *ListToolsCmd) Run(globals *Globals) error {
34+
return runagent(globals, func(ctx context.Context, client llm.Agent) error {
35+
tools := globals.toolkit.Tools(client)
36+
fmt.Println(tools)
37+
return nil
38+
})
39+
}
40+
3141
func (cmd *ListModelsCmd) Run(globals *Globals) error {
3242
return runagent(globals, func(ctx context.Context, client llm.Agent) error {
3343
agent, ok := client.(*agent.Agent)

model.go

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package llm
22

3+
import "context"
4+
35
// An Model can be used to generate a response to a user prompt,
46
// which is passed to an agent. The interaction occurs through
57
// a session context object.
@@ -14,4 +16,7 @@ type Model interface {
1416
// Convenience method to create a session context object
1517
// with a user prompt
1618
UserPrompt(string, ...Opt) Context
19+
20+
// Embedding vector generation
21+
Embedding(context.Context, string, ...Opt) ([]float64, error)
1722
}

opt.go

+192-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,198 @@
11
package llm
22

3+
import (
4+
"io"
5+
"time"
6+
)
7+
38
///////////////////////////////////////////////////////////////////////////////
49
// TYPES
510

611
// A generic option type, which can set options on an agent or session
7-
type Opt func(any) error
12+
type Opt func(*Opts) error
13+
14+
// set of options
15+
type Opts struct {
16+
agents map[string]Agent // Set of agents
17+
toolkit ToolKit // Toolkit for tools
18+
callback func(Context) // Streaming callback
19+
attachments []*Attachment // Attachments
20+
options map[string]any // Additional options
21+
}
22+
23+
////////////////////////////////////////////////////////////////////////////////
24+
// LIFECYCLE
25+
26+
// ApplyOpts returns a structure of options
27+
func ApplyOpts(opts ...Opt) (*Opts, error) {
28+
o := new(Opts)
29+
o.agents = make(map[string]Agent)
30+
o.options = make(map[string]any)
31+
for _, opt := range opts {
32+
if err := opt(o); err != nil {
33+
return nil, err
34+
}
35+
}
36+
return o, nil
37+
}
38+
39+
///////////////////////////////////////////////////////////////////////////////
40+
// PUBLIC METHODS - PROPERTIES
41+
42+
// Return the set of tools
43+
func (o *Opts) ToolKit() ToolKit {
44+
return o.toolkit
45+
}
46+
47+
// Return the stream function
48+
func (o *Opts) StreamFn() func(Context) {
49+
return o.callback
50+
}
51+
52+
// Return the array of registered agents
53+
func (o *Opts) Agents() []Agent {
54+
result := make([]Agent, 0, len(o.agents))
55+
for _, agent := range o.agents {
56+
result = append(result, agent)
57+
}
58+
return result
59+
}
60+
61+
// Return attachments
62+
func (o *Opts) Attachments() []*Attachment {
63+
return o.attachments
64+
}
65+
66+
// Set an option value
67+
func (o *Opts) Set(key string, value any) {
68+
o.options[key] = value
69+
}
70+
71+
// Get an option value
72+
func (o *Opts) Get(key string) any {
73+
if value, exists := o.options[key]; exists {
74+
return value
75+
}
76+
return nil
77+
}
78+
79+
// Has an option value
80+
func (o *Opts) Has(key string) bool {
81+
_, exists := o.options[key]
82+
return exists
83+
}
84+
85+
// Get an option value as a string
86+
func (o *Opts) GetString(key string) string {
87+
if value, exists := o.options[key]; exists {
88+
if v, ok := value.(string); ok {
89+
return v
90+
}
91+
}
92+
return ""
93+
}
94+
95+
// Get an option value as a boolean
96+
func (o *Opts) GetBool(key string) bool {
97+
if value, exists := o.options[key]; exists {
98+
if v, ok := value.(bool); ok {
99+
return v
100+
}
101+
}
102+
return false
103+
}
104+
105+
// Get an option value as a duration
106+
func (o *Opts) GetDuration(key string) time.Duration {
107+
if value, exists := o.options[key]; exists {
108+
if v, ok := value.(time.Duration); ok {
109+
return v
110+
}
111+
}
112+
return 0
113+
}
114+
115+
///////////////////////////////////////////////////////////////////////////////
116+
// PUBLIC METHODS - SET OPTIONS
117+
118+
// Set toolkit of tools
119+
func WithToolKit(toolkit ToolKit) Opt {
120+
return func(o *Opts) error {
121+
o.toolkit = toolkit
122+
return nil
123+
}
124+
}
125+
126+
// Set chat streaming function
127+
func WithStream(fn func(Context)) Opt {
128+
return func(o *Opts) error {
129+
o.callback = fn
130+
return nil
131+
}
132+
}
133+
134+
// Set agent
135+
func WithAgent(agent Agent) Opt {
136+
return func(o *Opts) error {
137+
// Check parameters
138+
if agent == nil {
139+
return ErrBadParameter.With("withAgent")
140+
}
141+
142+
// Add agent
143+
name := agent.Name()
144+
if _, exists := o.agents[name]; exists {
145+
return ErrConflict.Withf("Agent %q already exists", name)
146+
} else {
147+
o.agents[name] = agent
148+
}
149+
150+
// Return success
151+
return nil
152+
}
153+
154+
}
155+
156+
// Create an attachment
157+
func WithAttachment(r io.Reader) Opt {
158+
return func(o *Opts) error {
159+
if attachment, err := ReadAttachment(r); err != nil {
160+
return err
161+
} else {
162+
o.attachments = append(o.attachments, attachment)
163+
}
164+
return nil
165+
}
166+
}
167+
168+
// The temperature of the model. Increasing the temperature will make the model answer more creatively.
169+
func WithTemperature(v float64) Opt {
170+
return func(o *Opts) error {
171+
if v < 0.0 || v > 1.0 {
172+
return ErrBadParameter.With("temperature must be between 0.0 and 1.0")
173+
}
174+
o.Set("temperature", v)
175+
return nil
176+
}
177+
}
178+
179+
// Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while
180+
// a lower value (e.g., 0.5) will generate more focused and conservative text.
181+
func WithTopP(v float64) Opt {
182+
return func(o *Opts) error {
183+
if v < 0.0 || v > 1.0 {
184+
return ErrBadParameter.With("top_p must be between 0.0 and 1.0")
185+
}
186+
o.Set("top_p", v)
187+
return nil
188+
}
189+
}
190+
191+
// Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more
192+
// diverse answers, while a lower value (e.g. 10) will be more conservative.
193+
func WithTopK(v uint) Opt {
194+
return func(o *Opts) error {
195+
o.Set("top_k", v)
196+
return nil
197+
}
198+
}

0 commit comments

Comments
 (0)