Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 5c18177

Browse files
authoredMar 11, 2024
Merge pull request #142 from ibuildthecloud/distribution
Add keep-alive stdin for daemons
2 parents 6518854 + 5d00052 commit 5c18177

File tree

9 files changed

+153
-64
lines changed

9 files changed

+153
-64
lines changed
 

‎pkg/debugcmd/debug.go

Lines changed: 87 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,99 @@ package debugcmd
22

33
import (
44
"context"
5+
"fmt"
56
"os"
67
"os/exec"
8+
"strings"
9+
"sync"
710
)
811

9-
func New(ctx context.Context, arg string, args ...string) *exec.Cmd {
10-
cmd := exec.CommandContext(ctx, arg, args...)
11-
SetupDebug(cmd)
12-
return cmd
12+
type WrappedCmd struct {
13+
c *exec.Cmd
14+
r recorder
15+
Env []string
16+
Dir string
1317
}
1418

15-
func SetupDebug(cmd *exec.Cmd) {
19+
func (w *WrappedCmd) Run() error {
20+
if len(w.Env) > 0 {
21+
w.c.Env = w.Env
22+
}
23+
if w.Dir != "" {
24+
w.c.Dir = w.Dir
25+
}
26+
if err := w.c.Run(); err != nil {
27+
msg := w.r.dump()
28+
if msg != "" {
29+
return fmt.Errorf("%w: %s", err, msg)
30+
}
31+
return err
32+
}
33+
return nil
34+
}
35+
36+
func New(ctx context.Context, arg string, args ...string) *WrappedCmd {
37+
w := &WrappedCmd{
38+
c: exec.CommandContext(ctx, arg, args...),
39+
}
40+
setupDebug(w)
41+
return w
42+
}
43+
44+
type entry struct {
45+
err bool
46+
data []byte
47+
}
48+
49+
type recorder struct {
50+
lock sync.Mutex
51+
entries []entry
52+
}
53+
54+
func (r *recorder) dump() string {
55+
var errMessage strings.Builder
56+
for _, entry := range r.entries {
57+
if entry.err {
58+
errMessage.Write(entry.data)
59+
_, _ = os.Stderr.Write(entry.data)
60+
} else {
61+
_, _ = os.Stdout.Write(entry.data)
62+
}
63+
}
64+
return errMessage.String()
65+
}
66+
67+
type writer struct {
68+
err bool
69+
r *recorder
70+
}
71+
72+
func (w *writer) Write(data []byte) (int, error) {
73+
w.r.lock.Lock()
74+
defer w.r.lock.Unlock()
75+
76+
cp := make([]byte, len(data))
77+
copy(cp, data)
78+
79+
w.r.entries = append(w.r.entries, entry{
80+
err: w.err,
81+
data: cp,
82+
})
83+
84+
return len(data), nil
85+
}
86+
87+
func setupDebug(w *WrappedCmd) {
1688
if log.IsDebug() {
17-
cmd.Stdout = os.Stdout
89+
w.c.Stdout = os.Stdout
90+
w.c.Stderr = os.Stderr
91+
} else {
92+
w.c.Stdout = &writer{
93+
r: &w.r,
94+
}
95+
w.c.Stderr = &writer{
96+
err: true,
97+
r: &w.r,
98+
}
1899
}
19-
cmd.Stderr = os.Stderr
20100
}

‎pkg/engine/daemon.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,12 @@ func (e *Engine) startDaemon(_ context.Context, tool types.Tool) (string, error)
100100
return url, err
101101
}
102102

103+
r, w, err := os.Pipe()
104+
if err != nil {
105+
return "", err
106+
}
107+
108+
cmd.Stdin = r
103109
cmd.Stderr = os.Stderr
104110
cmd.Stdout = os.Stdout
105111
log.Infof("launched [%s][%s] port [%d] %v", tool.Parameters.Name, tool.ID, port, cmd.Args)
@@ -121,6 +127,8 @@ func (e *Engine) startDaemon(_ context.Context, tool types.Tool) (string, error)
121127
if err != nil {
122128
log.Errorf("daemon exited tool [%s] %v: %v", tool.Parameters.Name, cmd.Args, err)
123129
}
130+
_ = r.Close()
131+
_ = w.Close()
124132

125133
cancel(err)
126134
stop()
@@ -133,7 +141,7 @@ func (e *Engine) startDaemon(_ context.Context, tool types.Tool) (string, error)
133141
daemonWG.Add(1)
134142
context.AfterFunc(ctx, func() {
135143
if err := cmd.Process.Kill(); err != nil {
136-
log.Errorf("daemon failed to kill tool [%s] process: %v", tool.Parameters.Name, err)
144+
log.Debugf("daemon failed to kill tool [%s] process: %v", tool.Parameters.Name, err)
137145
}
138146
daemonWG.Done()
139147
})

‎pkg/engine/engine.go

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -155,14 +155,18 @@ func (c *Context) appendTool(completion *types.CompletionRequest, parentTool typ
155155
c.toolNames = map[string]struct{}{}
156156
}
157157

158-
completion.Tools = append(completion.Tools, types.CompletionTool{
159-
Function: types.CompletionFunctionDefinition{
160-
ToolID: subTool.ID,
161-
Name: PickToolName(subToolName, c.toolNames),
162-
Description: subTool.Parameters.Description,
163-
Parameters: args,
164-
},
165-
})
158+
if subTool.Instructions == "" {
159+
log.Debugf("Skipping zero instruction tool %s (%s)", subToolName, subTool.ID)
160+
} else {
161+
completion.Tools = append(completion.Tools, types.CompletionTool{
162+
Function: types.CompletionFunctionDefinition{
163+
ToolID: subTool.ID,
164+
Name: PickToolName(subToolName, c.toolNames),
165+
Description: subTool.Parameters.Description,
166+
Parameters: args,
167+
},
168+
})
169+
}
166170

167171
for _, export := range subTool.Export {
168172
if err := c.appendTool(completion, subTool, export); err != nil {

‎pkg/repos/git/cmd.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,12 @@ package git
22

33
import (
44
"context"
5-
"os/exec"
65

76
"github.com/gptscript-ai/gptscript/pkg/debugcmd"
87
)
98

10-
func newGitCommand(ctx context.Context, args ...string) *exec.Cmd {
11-
cmd := exec.CommandContext(ctx, "git", args...)
12-
debugcmd.SetupDebug(cmd)
9+
func newGitCommand(ctx context.Context, args ...string) *debugcmd.WrappedCmd {
10+
cmd := debugcmd.New(ctx, "git", args...)
1311
return cmd
1412
}
1513

‎pkg/tests/testdata/TestExport/call1.golden

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"Tools": [
55
{
66
"function": {
7-
"toolID": "testdata/TestExport/parent.gpt:4",
7+
"toolID": "testdata/TestExport/parent.gpt:5",
88
"name": "frommain",
99
"parameters": {
1010
"type": "object",
@@ -22,7 +22,7 @@
2222
},
2323
{
2424
"function": {
25-
"toolID": "testdata/TestExport/parent.gpt:8",
25+
"toolID": "testdata/TestExport/parent.gpt:11",
2626
"name": "parent-local",
2727
"parameters": {
2828
"type": "object",
@@ -38,24 +38,6 @@
3838
}
3939
}
4040
},
41-
{
42-
"function": {
43-
"toolID": "testdata/TestExport/sub/child.gpt:4",
44-
"name": "child",
45-
"parameters": {
46-
"type": "object",
47-
"properties": {
48-
"defaultPromptParameter": {
49-
"description": "Prompt to send to the tool or assistant. This may be instructions or question.",
50-
"type": "string"
51-
}
52-
},
53-
"required": [
54-
"defaultPromptParameter"
55-
]
56-
}
57-
}
58-
},
5941
{
6042
"function": {
6143
"toolID": "testdata/TestExport/sub/child.gpt:8",
@@ -75,7 +57,16 @@
7557
}
7658
}
7759
],
78-
"Messages": null,
60+
"Messages": [
61+
{
62+
"role": "system",
63+
"content": [
64+
{
65+
"text": "the default"
66+
}
67+
]
68+
}
69+
],
7970
"MaxTokens": 0,
8071
"Temperature": null,
8172
"JSONResponse": false,

‎pkg/tests/testdata/TestExport/call2.golden

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,16 @@
22
"Model": "gpt-4-turbo-preview",
33
"InternalSystemPrompt": null,
44
"Tools": null,
5-
"Messages": null,
5+
"Messages": [
6+
{
7+
"role": "system",
8+
"content": [
9+
{
10+
"text": "the transient"
11+
}
12+
]
13+
}
14+
],
615
"MaxTokens": 0,
716
"Temperature": null,
817
"JSONResponse": false,

‎pkg/tests/testdata/TestExport/call3.golden

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"Tools": [
55
{
66
"function": {
7-
"toolID": "testdata/TestExport/parent.gpt:4",
7+
"toolID": "testdata/TestExport/parent.gpt:5",
88
"name": "frommain",
99
"parameters": {
1010
"type": "object",
@@ -22,7 +22,7 @@
2222
},
2323
{
2424
"function": {
25-
"toolID": "testdata/TestExport/parent.gpt:8",
25+
"toolID": "testdata/TestExport/parent.gpt:11",
2626
"name": "parent-local",
2727
"parameters": {
2828
"type": "object",
@@ -38,24 +38,6 @@
3838
}
3939
}
4040
},
41-
{
42-
"function": {
43-
"toolID": "testdata/TestExport/sub/child.gpt:4",
44-
"name": "child",
45-
"parameters": {
46-
"type": "object",
47-
"properties": {
48-
"defaultPromptParameter": {
49-
"description": "Prompt to send to the tool or assistant. This may be instructions or question.",
50-
"type": "string"
51-
}
52-
},
53-
"required": [
54-
"defaultPromptParameter"
55-
]
56-
}
57-
}
58-
},
5941
{
6042
"function": {
6143
"toolID": "testdata/TestExport/sub/child.gpt:8",
@@ -76,12 +58,20 @@
7658
}
7759
],
7860
"Messages": [
61+
{
62+
"role": "system",
63+
"content": [
64+
{
65+
"text": "the default"
66+
}
67+
]
68+
},
7969
{
8070
"role": "assistant",
8171
"content": [
8272
{
8373
"toolCall": {
84-
"index": 3,
74+
"index": 2,
8575
"id": "call_1",
8676
"function": {
8777
"name": "transient"
@@ -98,7 +88,7 @@
9888
}
9989
],
10090
"toolCall": {
101-
"index": 3,
91+
"index": 2,
10292
"id": "call_1",
10393
"function": {
10494
"name": "transient"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
tools: frommain, sub/child.gpt
22

3+
the default
34
---
45
name: frommain
56
export: parent-local, fromchild from sub/child.gpt
67

8+
the frommain
9+
710
---
811
name: parent-local
12+
13+
the parent-local

‎pkg/tests/testdata/TestExport/sub/child.gpt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,9 @@ export: transient
77
---
88
name: transient
99

10+
the transient
11+
1012
---
1113
name: miss
14+
15+
the miss

0 commit comments

Comments
 (0)
Please sign in to comment.