v0.50.0 c4ec31f

rpc_reasoning_test.go RawHistoryFolder

1package agent
2
3import (
4	"bytes"
5	"strings"
6	"testing"
7
8	"github.com/bnema/zut/packages/core"
9	"github.com/bnema/zut/packages/provider"
10)
11
12func TestRPCSetModelRejectsBusyTurn(t *testing.T) {
13	var out bytes.Buffer
14	s := &rpcServer{
15		provider: provider.ProviderOpenCodeGo,
16		model:    "old-model",
17		agent:    &core.Agent{Model: "old-model", ContextWindow: 111, MaxTokens: 222},
18		out:      &out,
19	}
20	s.turnMu.Lock()
21	s.dispatch("set_model", "1", []byte(`{"model":"new-model"}`))
22	s.turnMu.Unlock()
23	if s.agent.Model != "old-model" || s.model != "old-model" {
24		t.Fatalf("busy set_model changed active model: agent=%q server=%q", s.agent.Model, s.model)
25	}
26	if !strings.Contains(out.String(), `"success":false`) {
27		t.Fatalf("busy set_model response = %q", out.String())
28	}
29}
30
31func TestRPCSetModelAllowsUnlistedOpenCodeGoModel(t *testing.T) {
32	preserveProviderCatalog(t)
33	var out bytes.Buffer
34	const model = "muse-spark-1.2-contributor"
35	s := &rpcServer{
36		provider: "opencode-go",
37		model:    "kimi-k2.6",
38		agent:    &core.Agent{Model: "kimi-k2.6"},
39		out:      &out,
40	}
41	s.dispatch("set_model", "1", []byte(`{"model":"  `+model+`  "}`))
42
43	if s.agent.Model != model || s.model != model {
44		t.Fatalf("model = agent:%q server:%q, want trimmed %q", s.agent.Model, s.model, model)
45	}
46	if s.agent.ContextWindow != 128000 || s.agent.MaxTokens != 16384 {
47		t.Fatalf("bootstrap limits = context %d output %d, want 128000/16384", s.agent.ContextWindow, s.agent.MaxTokens)
48	}
49	if !strings.Contains(out.String(), `"success":true`) || !strings.Contains(out.String(), `"model":"`+model+`"`) {
50		t.Fatalf("response = %q", out.String())
51	}
52}
53
54func TestRPCSetModelUsesKnownModelMetadata(t *testing.T) {
55	preserveProviderCatalog(t)
56	provider.SetLiveModels([]provider.Model{{
57		Provider:      provider.ProviderOpenCodeGo,
58		ID:            "served-model",
59		ContextWindow: 300000,
60		MaxOutput:     60000,
61	}})
62
63	var out bytes.Buffer
64	s := &rpcServer{
65		provider: provider.ProviderOpenCodeGo,
66		model:    "old-model",
67		agent:    &core.Agent{Model: "old-model", ContextWindow: 1, MaxTokens: 2},
68		out:      &out,
69	}
70	s.dispatch("set_model", "1", []byte(`{"model":"served-model"}`))
71	if s.agent.ContextWindow != 300000 || s.agent.MaxTokens != 60000 {
72		t.Fatalf("known limits = context %d output %d, want 300000/60000", s.agent.ContextWindow, s.agent.MaxTokens)
73	}
74}
75
76func TestRPCSetModelPreservesExistingLimitsWhenMetadataIsMissing(t *testing.T) {
77	preserveProviderCatalog(t)
78	provider.SetLiveModels([]provider.Model{{
79		Provider: provider.ProviderOpenCodeGo,
80		ID:       "metadata-without-limits",
81	}})
82
83	var out bytes.Buffer
84	s := &rpcServer{
85		provider: provider.ProviderOpenCodeGo,
86		model:    "old-model",
87		agent:    &core.Agent{Model: "old-model", ContextWindow: 111, MaxTokens: 222},
88		out:      &out,
89	}
90	s.dispatch("set_model", "1", []byte(`{"model":"metadata-without-limits"}`))
91	if s.agent.ContextWindow != 111 || s.agent.MaxTokens != 222 {
92		t.Fatalf("missing limits changed agent values = context %d output %d, want 111/222", s.agent.ContextWindow, s.agent.MaxTokens)
93	}
94}
95
96func TestRPCSetModelRejectsBlankOpenCodeGoModel(t *testing.T) {
97	for _, model := range []string{"", "   "} {
98		var out bytes.Buffer
99		s := &rpcServer{
100			provider: "opencode-go",
101			model:    "kimi-k2.6",
102			agent:    &core.Agent{Model: "kimi-k2.6"},
103			out:      &out,
104		}
105		s.dispatch("set_model", "1", []byte(`{"model":"`+model+`"}`))
106		if s.agent.Model != "kimi-k2.6" || s.model != "kimi-k2.6" {
107			t.Fatalf("blank model %q changed active model: agent=%q server=%q", model, s.agent.Model, s.model)
108		}
109		if !strings.Contains(out.String(), `"success":false`) {
110			t.Fatalf("blank model %q response = %q", model, out.String())
111		}
112	}
113}
114
115func TestRPCSetReasoningMax(t *testing.T) {
116	var out bytes.Buffer
117	s := &rpcServer{agent: &core.Agent{}, out: &out}
118	s.dispatch("set_reasoning", "1", []byte(`{"reasoning":"max"}`))
119
120	if s.agent.Reasoning != "max" {
121		t.Fatalf("reasoning = %q, want max", s.agent.Reasoning)
122	}
123	if !strings.Contains(out.String(), `"reasoning":"max"`) {
124		t.Fatalf("response = %q", out.String())
125	}
126}
127
128func TestRPCSetReasoningRejectsUnknownLevel(t *testing.T) {
129	var out bytes.Buffer
130	s := &rpcServer{agent: &core.Agent{}, out: &out}
131	s.dispatch("set_reasoning", "1", []byte(`{"reasoning":"extreme"}`))
132
133	if s.agent.Reasoning != "" {
134		t.Fatalf("reasoning = %q, want unchanged", s.agent.Reasoning)
135	}
136	if !strings.Contains(out.String(), `"success":false`) {
137		t.Fatalf("response = %q", out.String())
138	}
139}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 476

↑↓ moveenter openesc close