Skip to content

Commit 83d67e0

Browse files
EItanyailackarms
andauthored
Add support for cli subcommands (#102)
* add support for cli subcommands * everything seems to be working :fingers_crossed: * oops * fix translation * use matrix to build for speed * update to correct DEFAULT_USER_ID in autogen --------- Co-authored-by: Scott Weiss <[email protected]>
1 parent cdc289e commit 83d67e0

31 files changed

+510
-259
lines changed

.github/workflows/ci.yaml

+10-14
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,20 @@ on:
66
workflow_dispatch:
77

88
jobs:
9-
build-images:
9+
build:
10+
strategy:
11+
matrix:
12+
image:
13+
- controller
14+
- ui
15+
- app
16+
- cli
1017
runs-on: ubuntu-latest
1118

1219
steps:
1320
- name: Checkout repository
1421
uses: actions/checkout@v4
1522

1623
- name: Run make build
17-
run: make build
18-
working-directory: ./
19-
20-
build-cli:
21-
runs-on: ubuntu-latest
22-
23-
steps:
24-
- name: Checkout repository
25-
uses: actions/checkout@v4
26-
27-
- name: Run make build-cli
28-
run: make build-cli
29-
working-directory: ./
24+
run: make build-${{ matrix.image }}
25+
working-directory: ./

Makefile

+15-4
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22
DOCKER_REGISTRY ?= ghcr.io
33
DOCKER_REPO ?= kagent-dev/kagent
44
CONTROLLER_IMAGE_NAME ?= controller
5+
UI_IMAGE_NAME ?= ui
56
APP_IMAGE_NAME ?= app
67
VERSION ?= $(shell git describe --tags --always --dirty)
78
CONTROLLER_IMAGE_TAG ?= $(VERSION)
9+
UI_IMAGE_TAG ?= $(VERSION)
810
APP_IMAGE_TAG ?= $(VERSION)
911
CONTROLLER_IMG ?= $(DOCKER_REGISTRY)/$(DOCKER_REPO)/$(CONTROLLER_IMAGE_NAME):$(CONTROLLER_IMAGE_TAG)
12+
UI_IMG ?= $(DOCKER_REGISTRY)/$(DOCKER_REPO)/$(UI_IMAGE_NAME):$(UI_IMAGE_TAG)
1013
APP_IMG ?= $(DOCKER_REGISTRY)/$(DOCKER_REPO)/$(APP_IMAGE_NAME):$(APP_IMAGE_TAG)
1114

1215
# Check if OPENAI_API_KEY is set
@@ -24,7 +27,7 @@ create-kind-cluster:
2427
kind create cluster --name autogen
2528

2629
.PHONY: build
27-
build: build-controller build-app
30+
build: build-controller build-ui build-app
2831

2932
.PHONY: build-cli
3033
build-cli:
@@ -33,6 +36,7 @@ build-cli:
3336
.PHONY: push
3437
push:
3538
docker push $(CONTROLLER_IMG)
39+
docker push $(UI_IMG)
3640
docker push $(APP_IMG)
3741

3842
.PHONY: controller-manifests
@@ -44,16 +48,22 @@ controller-manifests:
4448
build-controller: controller-manifests
4549
make -C go docker-build
4650

47-
.PHONY: build-app
48-
build-app:
51+
.PHONY: build-ui
52+
build-ui:
4953
# Build the combined UI and backend image
50-
docker build -t $(APP_IMG) .
54+
docker build -t $(UI_IMG) -f ui/Dockerfile ./ui
5155
# Tag with latest for convenience
56+
docker tag $(UI_IMG) $(DOCKER_REGISTRY)/$(DOCKER_REPO)/$(UI_IMAGE_NAME):latest
57+
58+
.PHONY: build-app
59+
build-app:
60+
docker build -t $(APP_IMG) -f python/Dockerfile ./python
5261
docker tag $(APP_IMG) $(DOCKER_REGISTRY)/$(DOCKER_REPO)/$(APP_IMAGE_NAME):latest
5362

5463
.PHONY: kind-load-docker-images
5564
kind-load-docker-images: build
5665
kind load docker-image --name autogen $(CONTROLLER_IMG)
66+
kind load docker-image --name autogen $(UI_IMG)
5767
kind load docker-image --name autogen $(APP_IMG)
5868

5969
.PHONY: helm-version
@@ -67,6 +77,7 @@ helm-install: helm-version check-openai-key kind-load-docker-images
6777
--namespace kagent \
6878
--create-namespace \
6979
--set controller.image.tag=$(CONTROLLER_IMAGE_TAG) \
80+
--set ui.image.tag=$(UI_IMAGE_TAG) \
7081
--set app.image.tag=$(APP_IMAGE_TAG) \
7182
--set openai.apiKey=$(OPENAI_API_KEY)
7283

go/autogen/client/client.go

+20-16
Original file line numberDiff line numberDiff line change
@@ -87,26 +87,30 @@ func (c *Client) doRequest(method, path string, body interface{}, result interfa
8787
return fmt.Errorf("error reading response: %w", err)
8888
}
8989

90-
// Decode into APIResponse first
90+
// Try decoding into APIResponse first
9191
var apiResp APIResponse
92-
if err := json.Unmarshal(b, &apiResp); err != nil {
93-
return fmt.Errorf("error decoding response [%s]: %w", b, err)
94-
}
95-
96-
// Check response status
97-
if !apiResp.Status {
98-
return fmt.Errorf("api error: [%+v]", apiResp)
99-
}
10092

101-
// If caller wants the result, marshal the Data field into their result type
102-
if result != nil {
103-
dataBytes, err := json.Marshal(apiResp.Data)
104-
if err != nil {
105-
return fmt.Errorf("error re-marshaling data: %w", err)
93+
decoder := json.NewDecoder(bytes.NewReader(b))
94+
decoder.DisallowUnknownFields()
95+
if err := decoder.Decode(&apiResp); err != nil {
96+
// Trying the base value
97+
return json.Unmarshal(b, result)
98+
} else {
99+
// Check response status
100+
if !apiResp.Status {
101+
return fmt.Errorf("api error: [%+v]", apiResp)
106102
}
107103

108-
if err := json.Unmarshal(dataBytes, result); err != nil {
109-
return fmt.Errorf("error unmarshaling into result: %w", err)
104+
// If caller wants the result, marshal the Data field into their result type
105+
if result != nil {
106+
dataBytes, err := json.Marshal(apiResp.Data)
107+
if err != nil {
108+
return fmt.Errorf("error re-marshaling data: %w", err)
109+
}
110+
111+
if err := json.Unmarshal(dataBytes, result); err != nil {
112+
return fmt.Errorf("error unmarshaling into result: %w", err)
113+
}
110114
}
111115
}
112116

go/autogen/client/run.go

+2-10
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,11 @@ func (c *Client) ListRuns(userID string) ([]*Run, error) {
2929
// For each session, get the run information
3030
var runs []*Run
3131
for _, session := range sessions {
32-
var sessionRuns SessionRuns
33-
err := c.doRequest("GET", fmt.Sprintf("/sessions/%d/runs/?user_id=%s", session.ID, userID), nil, &sessionRuns)
32+
sessionRuns, err := c.ListSessionRuns(session.ID, userID)
3433
if err != nil {
35-
fmt.Println("Error getting runs for session")
3634
return nil, err
3735
}
38-
for _, run := range sessionRuns.Runs {
39-
run.Messages, err = c.GetRunMessages(run.ID)
40-
if err != nil {
41-
return nil, err
42-
}
43-
runs = append(runs, &run)
44-
}
36+
runs = append(runs, sessionRuns...)
4537
}
4638
return runs, nil
4739
}

go/autogen/client/session.go

+16-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ package client
22

33
import "fmt"
44

5-
func (c *Client) ListSessions(userID string) ([]Session, error) {
6-
var sessions []Session
5+
func (c *Client) ListSessions(userID string) ([]*Session, error) {
6+
var sessions []*Session
77
err := c.doRequest("GET", fmt.Sprintf("/sessions/?user_id=%s", userID), nil, &sessions)
88
return sessions, err
99
}
@@ -23,3 +23,17 @@ func (c *Client) GetSession(sessionID int, userID string) (*Session, error) {
2323
func (c *Client) DeleteSession(sessionID int, userID string) error {
2424
return c.doRequest("DELETE", fmt.Sprintf("/sessions/%d?user_id=%s", sessionID, userID), nil, nil)
2525
}
26+
27+
func (c *Client) ListSessionRuns(sessionID int, userID string) ([]*Run, error) {
28+
var runs SessionRuns
29+
err := c.doRequest("GET", fmt.Sprintf("/sessions/%d/runs/?user_id=%s", sessionID, userID), nil, &runs)
30+
if err != nil {
31+
return nil, err
32+
}
33+
34+
var result []*Run
35+
for _, run := range runs.Runs {
36+
result = append(result, &run)
37+
}
38+
return result, err
39+
}

go/autogen/client/types.go

+10-8
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ type Team struct {
1111
UpdatedAt *string `json:"updated_at,omitempty"`
1212
UserID string `json:"user_id"`
1313
Version *string `json:"version,omitempty"`
14-
Id int `json:"id"`
14+
Id int `json:"id,omitempty"`
1515
}
1616

1717
type ModelsUsage struct {
@@ -32,7 +32,7 @@ type RunMessage struct {
3232
Version *string `json:"version,omitempty"`
3333
SessionID int `json:"session_id"`
3434
MessageMeta map[string]interface{} `json:"message_meta"`
35-
ID uuid.UUID `json:"id"`
35+
ID int `json:"id"`
3636
UserID *string `json:"user_id"`
3737
Config map[string]interface{} `json:"config"`
3838
RunID uuid.UUID `json:"run_id"`
@@ -52,12 +52,14 @@ type SessionRuns struct {
5252
}
5353

5454
type Run struct {
55-
ID uuid.UUID `json:"id"`
56-
CreatedAt string `json:"created_at"`
57-
Status string `json:"status"`
58-
Task Task `json:"task"`
59-
TeamResult TeamResult `json:"team_result"`
60-
Messages []*RunMessage `json:"messages"`
55+
ID uuid.UUID `json:"id"`
56+
SessionID int `json:"session_id"`
57+
CreatedAt string `json:"created_at"`
58+
Status string `json:"status"`
59+
Task Task `json:"task"`
60+
TeamResult TeamResult `json:"team_result"`
61+
Messages []*RunMessage `json:"messages"`
62+
ErrorMessage string `json:"error_message"`
6163
}
6264

6365
type Task struct {

go/cli/cmd/kagent/main.go

+82-12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"os"
66

77
"github.com/abiosoft/ishell/v2"
8+
autogen_client "github.com/kagent-dev/kagent/go/autogen/client"
89
"github.com/kagent-dev/kagent/go/cli/internal/cli"
910
"github.com/kagent-dev/kagent/go/cli/internal/config"
1011
)
@@ -17,31 +18,60 @@ func main() {
1718
os.Exit(1)
1819
}
1920

21+
cfg, err := config.Get()
22+
if err != nil {
23+
fmt.Fprintf(os.Stderr, "Error getting config: %v\n", err)
24+
os.Exit(1)
25+
}
26+
27+
client := autogen_client.New(cfg.APIURL, cfg.WSURL)
28+
2029
// create new shell.
2130
// by default, new shell includes 'exit', 'help' and 'clear' commands.
2231
shell := ishell.New()
32+
cli.SetCfg(shell, cfg)
33+
cli.SetClient(shell, client)
2334

2435
shell.SetPrompt(cli.BoldBlue("kagent >> "))
25-
shell.AddCmd(&ishell.Cmd{
36+
37+
runCmd := &ishell.Cmd{
38+
Name: "run",
39+
Aliases: []string{"r"},
40+
Help: "Run a kagent agent",
41+
LongHelp: `Run a kagent agent.
42+
43+
The available run types are:
44+
- chat: Start a chat with a kagent agent.
45+
46+
Examples:
47+
- run chat [team_name] -s [session_name]
48+
- run chat
49+
`,
50+
}
51+
52+
runCmd.AddCmd(&ishell.Cmd{
2653
Name: "chat",
2754
Aliases: []string{"c"},
2855
Help: "Start a chat with a kagent agent.",
2956
LongHelp: `Start a chat with a kagent agent.
3057
31-
Examples:
32-
chat
33-
chat [team_name]
34-
3558
If no team name is provided, then a list of available teams will be provided to select from.
36-
`,
37-
Func: func(c *ishell.Context) {
59+
If no session name is provided, then a new session will be created and the chat will be associated with it.
3860
61+
Examples:
62+
- chat [team_name] -s [session_name]
63+
- chat [team_name]
64+
- chat
65+
`,
66+
Func: func(c *ishell.Context) {
3967
cli.ChatCmd(c)
4068
c.SetPrompt(cli.BoldBlue("kagent >> "))
4169
},
4270
})
4371

44-
shell.AddCmd(&ishell.Cmd{
72+
shell.AddCmd(runCmd)
73+
74+
getCmd := &ishell.Cmd{
4575
Name: "get",
4676
Aliases: []string{"g"},
4777
Help: "get kagent resources.",
@@ -53,12 +83,52 @@ Examples:
5383
get run
5484
get agents
5585
`,
56-
Func: func(c *ishell.Context) {
57-
cli.GetCmd(c)
58-
c.SetPrompt(cli.BoldBlue("kagent >> "))
59-
},
86+
}
87+
88+
getCmd.AddCmd(&ishell.Cmd{
89+
Name: "session",
90+
Aliases: []string{"s"},
91+
Help: "get a session.",
92+
LongHelp: `get a session.
93+
94+
If no resource name is provided, then a list of available resources will be returned.
95+
Examples:
96+
get session [session_name]
97+
get session
98+
`,
99+
Func: cli.GetSessionCmd,
100+
})
101+
102+
getCmd.AddCmd(&ishell.Cmd{
103+
Name: "run",
104+
Aliases: []string{"r"},
105+
Help: "get a run.",
106+
LongHelp: `get a run.
107+
108+
If no resource name is provided, then a list of available resources will be returned.
109+
Examples:
110+
get run [run_name]
111+
get run
112+
`,
113+
Func: cli.GetRunCmd,
114+
})
115+
116+
getCmd.AddCmd(&ishell.Cmd{
117+
Name: "agent",
118+
Aliases: []string{"a"},
119+
Help: "get an agent.",
120+
LongHelp: `get an agent.
121+
122+
If no resource name is provided, then a list of available resources will be returned.
123+
Examples:
124+
get agent [agent_name]
125+
get agent
126+
`,
127+
Func: cli.GetAgentCmd,
60128
})
61129

130+
shell.AddCmd(getCmd)
131+
62132
shell.NotFound(func(c *ishell.Context) {
63133
// Hidden create command
64134
if len(c.Args) > 0 && c.Args[0] == "create" {

0 commit comments

Comments
 (0)