Skip to content

Commit c8d9dba

Browse files
committed
Updated LLM
1 parent 7641966 commit c8d9dba

File tree

14 files changed

+321
-163
lines changed

14 files changed

+321
-163
lines changed

cmd/agent/generate.go

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ package main
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
7+
"io"
8+
"strings"
69

710
// Packages
811
llm "github.com/mutablelogic/go-llm"
@@ -14,7 +17,6 @@ import (
1417

1518
type GenerateCmd struct {
1619
Model string `arg:"" help:"Model name"`
17-
Text string `arg:"" help:"Text to generate a response for"`
1820
}
1921

2022
////////////////////////////////////////////////////////////////////////////////
@@ -23,7 +25,6 @@ type GenerateCmd struct {
2325
func (cmd *GenerateCmd) Run(globals *Globals) error {
2426
return runagent(globals, func(ctx context.Context, client llm.Agent) error {
2527
// Get the model
26-
// TODO: Model should be cached
2728
agent, ok := client.(*agent.Agent)
2829
if !ok {
2930
return fmt.Errorf("No agents found")
@@ -33,14 +34,38 @@ func (cmd *GenerateCmd) Run(globals *Globals) error {
3334
return err
3435
}
3536

36-
// Generate the content
37-
response, err := agent.Generate(ctx, model, model.UserPrompt(cmd.Text))
38-
if err != nil {
39-
return err
37+
// Create a session
38+
session := model.Context()
39+
40+
// Continue looping until end of input
41+
for {
42+
input, err := globals.term.ReadLine(model.Name() + "> ")
43+
if errors.Is(err, io.EOF) {
44+
return nil
45+
} else if err != nil {
46+
return err
47+
} else if err := session.AppendUserPrompt(strings.TrimSpace(input)); err != nil {
48+
return err
49+
}
50+
51+
// Ignore empty import
52+
if session.Text() == "" {
53+
continue
54+
}
55+
56+
// Feed input into the model
57+
response, err := agent.Generate(ctx, model, session)
58+
if err != nil {
59+
return err
60+
}
61+
fmt.Println("RESPONSE=", response.Text())
4062
}
63+
/*
64+
// Generate the content
65+
4166
42-
// Print the response
43-
fmt.Println("RESPONSE=", response)
44-
return nil
67+
// Print the response
68+
return nil
69+
*/
4570
})
4671
}

cmd/agent/main.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
kong "github.com/alecthomas/kong"
1212
client "github.com/mutablelogic/go-client"
1313
llm "github.com/mutablelogic/go-llm"
14-
"github.com/mutablelogic/go-llm/pkg/agent"
14+
agent "github.com/mutablelogic/go-llm/pkg/agent"
1515
)
1616

1717
////////////////////////////////////////////////////////////////////////////////
@@ -29,6 +29,7 @@ type Globals struct {
2929
// Context
3030
ctx context.Context
3131
agent llm.Agent
32+
term *Term
3233
}
3334

3435
type Ollama struct {
@@ -67,6 +68,15 @@ func main() {
6768
defer cancel()
6869
cli.Globals.ctx = ctx
6970

71+
// Create a terminal
72+
term, err := NewTerm(os.Stdout)
73+
if err != nil {
74+
cmd.FatalIfErrorf(err)
75+
return
76+
} else {
77+
cli.Globals.term = term
78+
}
79+
7080
// Client options
7181
clientopts := []client.ClientOpt{}
7282
if cli.Debug || cli.Verbose {

cmd/agent/term.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package main
2+
3+
import (
4+
"io"
5+
"os"
6+
7+
// Packages
8+
"golang.org/x/term"
9+
)
10+
11+
type Term struct {
12+
r io.Reader
13+
fd int
14+
*term.Terminal
15+
}
16+
17+
func NewTerm(r io.Reader) (*Term, error) {
18+
t := new(Term)
19+
t.r = r
20+
21+
// Set file descriptor
22+
if osf, ok := r.(*os.File); ok {
23+
t.fd = int(osf.Fd())
24+
if term.IsTerminal(t.fd) {
25+
t.Terminal = term.NewTerminal(osf, "")
26+
}
27+
}
28+
29+
// Return success
30+
return t, nil
31+
}
32+
33+
// Returns the width and height of the terminal, or (0,0)
34+
func (t *Term) Size() (int, int) {
35+
if t.Terminal != nil {
36+
if w, h, err := term.GetSize(t.fd); err == nil {
37+
return w, h
38+
}
39+
}
40+
// Unable to get the size
41+
return 0, 0
42+
}
43+
44+
func (t *Term) ReadLine(prompt string) (string, error) {
45+
// Set terminal raw mode
46+
if t.Terminal != nil {
47+
state, err := term.MakeRaw(t.fd)
48+
if err != nil {
49+
return "", err
50+
}
51+
defer term.Restore(t.fd, state)
52+
}
53+
54+
// Set the prompt
55+
if t.Terminal != nil {
56+
t.Terminal.SetPrompt(prompt)
57+
}
58+
59+
// Read the line
60+
if t.Terminal != nil {
61+
return t.Terminal.ReadLine()
62+
} else {
63+
// Don't support non-terminal input yet
64+
return "", io.EOF
65+
}
66+
}

context.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,17 @@ package llm
33
//////////////////////////////////////////////////////////////////
44
// TYPES
55

6-
// Context is fed to the agent to generate a response. Role can be
7-
// assistant, user, tool, tool_result, ...
6+
// Context is fed to the agent to generate a response
87
type Context interface {
8+
// Return the role, which can be assistant, user, tool, tool_result, ...
99
Role() string
10+
11+
// Return the text of the context
12+
Text() string
13+
14+
// Append user prompt (and attachments) to a context
15+
AppendUserPrompt(string, ...Opt) error
16+
17+
// Append the result of calling a tool to a context
18+
AppendToolResult(string, ...Opt) error
1019
}

model.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@ type Model interface {
55
// Return the name of the model
66
Name() string
77

8-
// Create user prompt for a model
9-
UserPrompt(string, ...Opt) Context
10-
11-
// Create the result of calling a tool for a model
12-
ToolResult(id string, opts ...Opt) Context
8+
// Return a context object, and set options
9+
Context(...Opt) Context
1310
}

pkg/agent/agent.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,10 @@ var _ llm.Agent = (*Agent)(nil)
2828
///////////////////////////////////////////////////////////////////////////////
2929
// LIFECYCLE
3030

31-
// Return a new agent, composed of agents and tools
31+
// Return a new agent, composed of a series of agents and tools
3232
func New(opts ...llm.Opt) (*Agent, error) {
3333
agent := new(Agent)
34-
opt, err := apply(opts...)
35-
if err != nil {
34+
if opt, err := apply(opts...); err != nil {
3635
return nil, err
3736
} else {
3837
agent.opt = opt

pkg/agent/generate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func (a *Agent) Generate(ctx context.Context, m llm.Model, context llm.Context,
2929
return nil, err
3030
}
3131

32-
log.Print("agent.Generate:", m, context, opts)
32+
log.Print("agent.Generate =>", context, opts)
3333

3434
// Call Generate for the agent
3535
return agent.Generate(ctx, m, context, opts...)

pkg/anthropic/context.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package anthropic
2+
3+
import (
4+
"encoding/json"
5+
6+
llm "github.com/mutablelogic/go-llm"
7+
)
8+
9+
//////////////////////////////////////////////////////////////////
10+
// TYPES
11+
12+
type session struct {
13+
seq []*MessageMeta
14+
}
15+
16+
var _ llm.Context = (*session)(nil)
17+
18+
///////////////////////////////////////////////////////////////////////////////
19+
// LIFECYCLE
20+
21+
func (*model) Context(...llm.Opt) llm.Context {
22+
// TODO: Currently ignoring options
23+
return &session{}
24+
}
25+
26+
///////////////////////////////////////////////////////////////////////////////
27+
// STRINGIFY
28+
29+
func (session session) String() string {
30+
var data []byte
31+
var err error
32+
if len(session.seq) == 1 {
33+
data, err = json.MarshalIndent(session.seq[0], "", " ")
34+
} else {
35+
data, err = json.MarshalIndent(session.seq, "", " ")
36+
}
37+
if err != nil {
38+
return err.Error()
39+
}
40+
return string(data)
41+
}
42+
43+
//////////////////////////////////////////////////////////////////
44+
// PUBLIC METHODS
45+
46+
// Return the role of the last message
47+
func (session *session) Role() string {
48+
if len(session.seq) == 0 {
49+
return ""
50+
}
51+
return session.seq[len(session.seq)-1].Role
52+
}
53+
54+
// Return the text of the last message
55+
func (session *session) Text() string {
56+
if len(session.seq) == 0 {
57+
return ""
58+
}
59+
meta := session.seq[len(session.seq)-1]
60+
data, err := json.MarshalIndent(meta.Content, "", " ")
61+
if err != nil {
62+
return err.Error()
63+
}
64+
return string(data)
65+
}
66+
67+
// Append user prompt (and attachments) to a context
68+
func (session *session) AppendUserPrompt(text string, opts ...llm.Opt) error {
69+
// Apply attachments
70+
opt, err := apply(opts...)
71+
if err != nil {
72+
return err
73+
}
74+
75+
meta := MessageMeta{
76+
Role: "user",
77+
Content: make([]*Content, 1, len(opt.data)+1),
78+
}
79+
80+
// Append the text
81+
meta.Content[0] = NewTextContent(text)
82+
83+
// Append any additional data
84+
for _, data := range opt.data {
85+
meta.Content = append(meta.Content, data)
86+
}
87+
88+
// Return success
89+
return nil
90+
}
91+
92+
// Append the result of calling a tool to a context
93+
func (session *session) AppendToolResult(string, ...llm.Opt) error {
94+
return llm.ErrNotImplemented
95+
}

0 commit comments

Comments
 (0)