Skip to content

Commit c359a10

Browse files
fixing more bugs for deepseek
1 parent af947eb commit c359a10

2 files changed

Lines changed: 134 additions & 1 deletion

File tree

eval.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -877,7 +877,7 @@ func (e *evaluator) evalCallExpr(n *callExpr) (Value, error) {
877877
}
878878

879879
if !fn.IsCallable() {
880-
return Undefined(), fmt.Errorf("value is not callable: %s", fn.String())
880+
return Undefined(), fmt.Errorf("%s is not callable: %s", callableName(n.fn), fn.String())
881881
}
882882

883883
args := make([]Value, len(n.args))
@@ -904,6 +904,22 @@ func (e *evaluator) evalCallExpr(n *callExpr) (Value, error) {
904904
return fn.AsCallable().Fn(args, kwargs)
905905
}
906906

907+
func callableName(ex expr) string {
908+
switch n := ex.(type) {
909+
case *nameExpr:
910+
return n.name
911+
912+
case *attrExpr:
913+
return callableName(n.obj) + "." + n.attr
914+
915+
case *itemExpr:
916+
return callableName(n.obj) + "[item]"
917+
918+
default:
919+
return ex.nodeType()
920+
}
921+
}
922+
907923
func (e *evaluator) evalFilter(n *filterExpr) (Value, error) {
908924
input, err := e.evalExpr(n.expr)
909925
if err != nil {

jinja_test.go

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,23 @@ func TestFromJSONFilter(t *testing.T) {
113113
}
114114
}
115115

116+
func TestUndefinedCallableErrorNamesExpression(t *testing.T) {
117+
tmpl, err := jinja.Compile(`{{ args.items() }}`)
118+
if err != nil {
119+
t.Fatalf("compile: %v", err)
120+
}
121+
122+
_, err = tmpl.Render(nil)
123+
if err == nil {
124+
t.Fatal("render: expected error")
125+
}
126+
127+
const want = "args.items is not callable: Undefined"
128+
if err.Error() != want {
129+
t.Errorf("error: got %q, want %q", err, want)
130+
}
131+
}
132+
116133
func TestNamespace(t *testing.T) {
117134
source := `{%- set ns = namespace(found=false) -%}
118135
{%- for item in items -%}
@@ -192,6 +209,106 @@ func TestDictMethods(t *testing.T) {
192209
}
193210
}
194211

212+
func TestFromJSONWithDictItems(t *testing.T) {
213+
source := `{%- set func = tool['function'] -%}
214+
{%- set args = func['arguments'] -%}
215+
{%- if args is string -%}
216+
{%- set args = args | from_json -%}
217+
{%- endif -%}
218+
{%- for key, val in args.items() -%}{{ key }}={{ val }}{%- endfor -%}`
219+
220+
tests := []struct {
221+
name string
222+
arguments any
223+
want string
224+
}{
225+
{
226+
name: "json string",
227+
arguments: `{"location":"New York City, NY"}`,
228+
want: "location=New York City, NY",
229+
},
230+
{
231+
name: "map",
232+
arguments: map[string]any{"location": "New York City, NY"},
233+
want: "location=New York City, NY",
234+
},
235+
}
236+
237+
for _, tt := range tests {
238+
t.Run(tt.name, func(t *testing.T) {
239+
tmpl, err := jinja.Compile(source)
240+
if err != nil {
241+
t.Fatalf("compile: %v", err)
242+
}
243+
244+
result, err := tmpl.Render(map[string]any{
245+
"tool": map[string]any{
246+
"function": map[string]any{
247+
"name": "get_weather",
248+
"arguments": tt.arguments,
249+
},
250+
},
251+
})
252+
if err != nil {
253+
t.Fatalf("render: %v", err)
254+
}
255+
256+
if result != tt.want {
257+
t.Errorf("result: got %q, want %q", result, tt.want)
258+
}
259+
})
260+
}
261+
}
262+
263+
func TestNestedRangePreservesOuterLoop(t *testing.T) {
264+
source := `{%- for message in messages -%}
265+
{%- if message['role'] == 'assistant' -%}
266+
{%- set ep = namespace(idx=(loop.index0 - 1), done=false) -%}
267+
{%- for _i in range(loop.index0) -%}
268+
{%- if not ep.done and ep.idx >= 0 -%}
269+
{%- set ep.done = true -%}
270+
{%- endif -%}
271+
{%- endfor -%}
272+
{%- for tool in message['tool_calls'] -%}
273+
{%- set args = tool['function']['arguments'] -%}
274+
{%- if args is string -%}{%- set args = args | from_json -%}{%- endif -%}
275+
{%- for key, val in args.items() -%}{{ key }}={{ val }}{%- endfor -%}
276+
{%- endfor -%}
277+
{%- endif -%}
278+
{%- endfor -%}`
279+
280+
tmpl, err := jinja.Compile(source)
281+
if err != nil {
282+
t.Fatalf("compile: %v", err)
283+
}
284+
285+
result, err := tmpl.Render(map[string]any{
286+
"messages": []map[string]any{
287+
{"role": "user", "content": "weather"},
288+
{
289+
"role": "assistant",
290+
"tool_calls": []map[string]any{
291+
{
292+
"function": map[string]any{
293+
"name": "get_weather",
294+
"arguments": map[string]any{"location": "New York City, NY"},
295+
},
296+
},
297+
},
298+
},
299+
{"role": "tool", "content": `{"temperature":"72°F"}`},
300+
},
301+
})
302+
if err != nil {
303+
t.Fatalf("render: %v", err)
304+
}
305+
306+
const want = "location=New York City, NY"
307+
if result != want {
308+
t.Errorf("result: got %q, want %q", result, want)
309+
}
310+
}
311+
195312
func TestInlineIf(t *testing.T) {
196313
source := `{{ "yes" if x else "no" }}`
197314
tmpl, err := jinja.Compile(source)

0 commit comments

Comments
 (0)