Skip to content

Commit 749d8a8

Browse files
committed
interp(text/template): init text/template interpreter
1 parent fbb8f5d commit 749d8a8

File tree

4 files changed

+113
-4
lines changed

4 files changed

+113
-4
lines changed

pkg/builtin/builtin.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ var SafeTools = map[string]struct{}{
3535
"sys.time.now": {},
3636
"sys.context": {},
3737
"sys.model.provider.credential": {},
38+
"sys.interp.template.text": {},
3839
}
3940

4041
var tools = map[string]types.Tool{

pkg/engine/engine.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,10 @@ func (e *Engine) Start(ctx Context, input string) (ret *Return, _ error) {
299299
return e.runOpenAPI(tool, input)
300300
} else if tool.IsEcho() {
301301
return e.runEcho(tool)
302+
} else if tool.IsInterpTemplateText() {
303+
return e.runInterpTemplateText(ctx, tool, input)
302304
}
305+
303306
s, err := e.runCommand(ctx, tool, input, ctx.ToolCategory)
304307
if err != nil {
305308
return nil, err

pkg/engine/interpreter_template.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package engine
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"strings"
7+
"text/template"
8+
9+
"github.com/gptscript-ai/gptscript/pkg/counter"
10+
"github.com/gptscript-ai/gptscript/pkg/types"
11+
)
12+
13+
func (e *Engine) runInterpTemplateText(ctx Context, tool types.Tool, input string) (*Return, error) {
14+
// get code to eval
15+
_, script, _ := strings.Cut(ctx.Tool.Instructions, "\n")
16+
if script == "" {
17+
return nil, nil
18+
}
19+
20+
// setup environment for the interpreter
21+
tmplData, err := prepareData(&ctx, input)
22+
if err != nil {
23+
return nil, err
24+
}
25+
26+
tt, err := template.New(tool.Name).Funcs(prepareFuncMap()).Parse(script)
27+
if err != nil {
28+
return nil, err
29+
}
30+
31+
// eval code
32+
var tplOut bytes.Buffer
33+
err = tt.Execute(&tplOut, tmplData)
34+
if err != nil {
35+
return nil, err
36+
}
37+
out := tplOut.String()
38+
39+
e.Progress <- types.CompletionStatus{
40+
CompletionID: counter.Next(),
41+
Response: map[string]any{
42+
"output": out,
43+
"err": nil,
44+
},
45+
}
46+
47+
return &Return{
48+
Result: &out,
49+
}, nil
50+
}
51+
52+
func prepareData(engineContext *Context, input string) (any, error) {
53+
// get input
54+
IN := map[string]any{
55+
"input": "",
56+
}
57+
if input != "" {
58+
err := json.Unmarshal([]byte(input), &IN)
59+
if err != nil {
60+
return nil, err
61+
}
62+
}
63+
64+
// names kept uppercase just for testing to match (kind of) env variables
65+
return map[string]any{
66+
"INPUT": IN["input"],
67+
"GPTSCRIPT_INPUT": input,
68+
"GPTSCRIPT_CONTEXT": engineContext, // full engine context just to test atm
69+
}, nil
70+
}
71+
72+
// test helper functions
73+
func prepareFuncMap() template.FuncMap {
74+
return template.FuncMap{
75+
"join": strings.Join,
76+
"split": strings.Split,
77+
"fields": strings.Fields,
78+
"trim": strings.Trim,
79+
"trimSpace": strings.TrimSpace,
80+
"trimPrefix": strings.TrimPrefix,
81+
"cutPrefix": func(s string, prefix string) string {
82+
after, _ := strings.CutPrefix(s, prefix)
83+
return after
84+
},
85+
"cut": func(s string, sep string) map[string]string {
86+
before, after, _ := strings.Cut(s, sep)
87+
return map[string]string{"before": before, "after": after}
88+
},
89+
"hasPrefix": strings.HasPrefix,
90+
"append": func(slice []string, elems ...string) []string {
91+
return append(slice, elems...)
92+
},
93+
"newSlice": func(args ...string) []string {
94+
if len(args) == 0 {
95+
return []string{}
96+
}
97+
return args
98+
},
99+
}
100+
}

pkg/types/tool.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ import (
1616
)
1717

1818
const (
19-
DaemonPrefix = "#!sys.daemon"
20-
OpenAPIPrefix = "#!sys.openapi"
21-
EchoPrefix = "#!sys.echo"
22-
CommandPrefix = "#!"
19+
InterpTemplateTextPrefix = "#!sys.interp.template.text"
20+
DaemonPrefix = "#!sys.daemon"
21+
OpenAPIPrefix = "#!sys.openapi"
22+
EchoPrefix = "#!sys.echo"
23+
CommandPrefix = "#!"
2324
)
2425

2526
var (
@@ -898,6 +899,10 @@ func (t Tool) IsHTTP() bool {
898899
strings.HasPrefix(t.Instructions, "#!https://")
899900
}
900901

902+
func (t Tool) IsInterpTemplateText() bool {
903+
return strings.HasPrefix(t.Instructions, InterpTemplateTextPrefix)
904+
}
905+
901906
func FirstSet[T comparable](in ...T) (result T) {
902907
for _, i := range in {
903908
if i != result {

0 commit comments

Comments
 (0)