승인된 execution preset을 Edge 조정 경계와 Node workspace/tool 실행 경계로 연결해 단일 요청 수명주기와 관측 계약을 일관되게 처리한다.
2723 lines
77 KiB
Go
2723 lines
77 KiB
Go
package config_test
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"iop/packages/go/config"
|
|
)
|
|
|
|
// validSingleRequestYAML is a baseline valid single-request preset YAML used
|
|
// by multiple sub-tests. It uses an opaque workspace_ref, bounded limits, and
|
|
// the approved plan→work→review stage bindings with high reasoning on selector
|
|
// and plan/review, no high reasoning on work.
|
|
const validSingleRequestYAML = `
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "light"
|
|
routes:
|
|
light:
|
|
stages:
|
|
- role: "plan"
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "work"
|
|
model: "ornith-fast"
|
|
options:
|
|
temperature: 0.2
|
|
- role: "review"
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
single_request:
|
|
workspace_ref: "ws-capability-ref-001"
|
|
limits:
|
|
wall_clock_ms: 1800000
|
|
timeout_ms: 600000
|
|
max_tool_iterations: 32
|
|
max_output_bytes: 8388608
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"
|
|
options:
|
|
temperature: 0.2
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
|
|
// TestLoadEdgeSingleRequestExecutionPreset verifies that a valid single-request
|
|
// preset decodes, normalizes, and survives LoadEdge alongside ordinary presets.
|
|
func TestLoadEdgeSingleRequestExecutionPreset(t *testing.T) {
|
|
dir := t.TempDir()
|
|
f := filepath.Join(dir, "edge.yaml")
|
|
|
|
t.Run("valid single-request preset loads", func(t *testing.T) {
|
|
if err := os.WriteFile(f, []byte(validSingleRequestYAML), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
cfg, err := config.LoadEdge(f)
|
|
if err != nil {
|
|
t.Fatalf("load: %v", err)
|
|
}
|
|
if len(cfg.ExecutionPresets) != 1 {
|
|
t.Fatalf("expected 1 preset, got %d", len(cfg.ExecutionPresets))
|
|
}
|
|
p := cfg.ExecutionPresets[0]
|
|
if p.ID != "fixed-single-request" {
|
|
t.Errorf("preset id = %q, want %q", p.ID, "fixed-single-request")
|
|
}
|
|
if p.SingleRequest == nil {
|
|
t.Fatal("expected SingleRequest to be set")
|
|
}
|
|
sr := p.SingleRequest
|
|
if sr.WorkspaceRef != "ws-capability-ref-001" {
|
|
t.Errorf("workspace_ref = %q, want ws-capability-ref-001", sr.WorkspaceRef)
|
|
}
|
|
if sr.Limits.WallClockMS != 1800000 {
|
|
t.Errorf("wall_clock_ms = %d, want 1800000", sr.Limits.WallClockMS)
|
|
}
|
|
if sr.Limits.StageTimeoutMS != 600000 {
|
|
t.Errorf("timeout_ms = %d, want 600000", sr.Limits.StageTimeoutMS)
|
|
}
|
|
if sr.Limits.MaxToolIterations != 32 {
|
|
t.Errorf("max_tool_iterations = %d, want 32", sr.Limits.MaxToolIterations)
|
|
}
|
|
if sr.Limits.MaxOutputBytes != 8388608 {
|
|
t.Errorf("max_output_bytes = %d, want 8388608", sr.Limits.MaxOutputBytes)
|
|
}
|
|
if sr.Stages.Plan.Model != "gemini-plan" {
|
|
t.Errorf("plan model = %q, want gemini-plan", sr.Stages.Plan.Model)
|
|
}
|
|
if sr.Stages.Work.Model != "ornith-fast" {
|
|
t.Errorf("work model = %q, want ornith-fast", sr.Stages.Work.Model)
|
|
}
|
|
if sr.Stages.Review.Model != "gemini-review" {
|
|
t.Errorf("review model = %q, want gemini-review", sr.Stages.Review.Model)
|
|
}
|
|
// Allowed modes must be exactly ["light"].
|
|
if len(p.AllowedModes) != 1 || p.AllowedModes[0] != "light" {
|
|
t.Errorf("allowed_modes = %v, want [light]", p.AllowedModes)
|
|
}
|
|
// Workspace tools must be nil (rejected for single-request).
|
|
if p.WorkspaceTools != nil {
|
|
t.Errorf("workspace_tools = %v, want nil", p.WorkspaceTools)
|
|
}
|
|
// Route must have exactly plan→work→review.
|
|
route := p.Routes["light"]
|
|
if len(route.Stages) != 3 {
|
|
t.Fatalf("expected 3 route stages, got %d", len(route.Stages))
|
|
}
|
|
if route.Stages[0].Role != "plan" || route.Stages[1].Role != "work" || route.Stages[2].Role != "review" {
|
|
t.Errorf("route stages roles = %v, want [plan, work, review]", routeStagesRoles(route.Stages))
|
|
}
|
|
})
|
|
|
|
t.Run("exact cap values load", func(t *testing.T) {
|
|
yaml := singleRequestYAMLWithLimits(
|
|
config.MaxSingleRequestWallClockMS,
|
|
config.MaxSingleRequestStageTimeoutMS,
|
|
config.MaxSingleRequestToolIterations,
|
|
config.MaxSingleRequestOutputBytes,
|
|
)
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
cfg, err := config.LoadEdge(f)
|
|
if err != nil {
|
|
t.Fatalf("load: %v", err)
|
|
}
|
|
p := cfg.ExecutionPresets[0]
|
|
sr := p.SingleRequest
|
|
if sr.Limits.WallClockMS != config.MaxSingleRequestWallClockMS {
|
|
t.Errorf("wall_clock_ms = %d, want %d", sr.Limits.WallClockMS, config.MaxSingleRequestWallClockMS)
|
|
}
|
|
if sr.Limits.StageTimeoutMS != config.MaxSingleRequestStageTimeoutMS {
|
|
t.Errorf("timeout_ms = %d, want %d", sr.Limits.StageTimeoutMS, config.MaxSingleRequestStageTimeoutMS)
|
|
}
|
|
if sr.Limits.MaxToolIterations != config.MaxSingleRequestToolIterations {
|
|
t.Errorf("max_tool_iterations = %d, want %d", sr.Limits.MaxToolIterations, config.MaxSingleRequestToolIterations)
|
|
}
|
|
if sr.Limits.MaxOutputBytes != config.MaxSingleRequestOutputBytes {
|
|
t.Errorf("max_output_bytes = %d, want %d", sr.Limits.MaxOutputBytes, config.MaxSingleRequestOutputBytes)
|
|
}
|
|
})
|
|
|
|
t.Run("minimum limit values load", func(t *testing.T) {
|
|
yaml := singleRequestYAMLWithLimits(1, 1, 1, 1)
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
cfg, err := config.LoadEdge(f)
|
|
if err != nil {
|
|
t.Fatalf("load: %v", err)
|
|
}
|
|
p := cfg.ExecutionPresets[0]
|
|
sr := p.SingleRequest
|
|
if sr.Limits.WallClockMS != 1 || sr.Limits.StageTimeoutMS != 1 ||
|
|
sr.Limits.MaxToolIterations != 1 || sr.Limits.MaxOutputBytes != 1 {
|
|
t.Errorf("limits = %+v, want all 1", sr.Limits)
|
|
}
|
|
})
|
|
|
|
t.Run("single-request preset coexists with ordinary presets", func(t *testing.T) {
|
|
yaml := `
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "model-a"
|
|
providers:
|
|
prov-a: "model-a"
|
|
- id: "model-b"
|
|
providers:
|
|
prov-a: "model-b"
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "direct-default"
|
|
selector:
|
|
model: "model-a"
|
|
allowed_modes:
|
|
- "direct"
|
|
routes:
|
|
direct:
|
|
stages: []
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "light"
|
|
routes:
|
|
light:
|
|
stages:
|
|
- role: "plan"
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "work"
|
|
model: "ornith-fast"
|
|
- role: "review"
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
single_request:
|
|
workspace_ref: "ws-capability-ref-002"
|
|
limits:
|
|
wall_clock_ms: 300000
|
|
timeout_ms: 120000
|
|
max_tool_iterations: 16
|
|
max_output_bytes: 4194304
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["model-a", "model-b", "gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
cfg, err := config.LoadEdge(f)
|
|
if err != nil {
|
|
t.Fatalf("load: %v", err)
|
|
}
|
|
if len(cfg.ExecutionPresets) != 2 {
|
|
t.Fatalf("expected 2 presets, got %d", len(cfg.ExecutionPresets))
|
|
}
|
|
byID := map[string]config.ExecutionPreset{}
|
|
for _, p := range cfg.ExecutionPresets {
|
|
byID[p.ID] = p
|
|
}
|
|
if _, ok := byID["direct-default"]; !ok {
|
|
t.Fatal("expected direct-default preset")
|
|
}
|
|
if _, ok := byID["fixed-single-request"]; !ok {
|
|
t.Fatal("expected fixed-single-request preset")
|
|
}
|
|
// Ordinary preset must not have SingleRequest set.
|
|
if byID["direct-default"].SingleRequest != nil {
|
|
t.Error("direct-default should not have SingleRequest set")
|
|
}
|
|
// Single-request preset must not have workspace_tools.
|
|
if byID["fixed-single-request"].WorkspaceTools != nil {
|
|
t.Error("fixed-single-request should not have workspace_tools")
|
|
}
|
|
})
|
|
}
|
|
|
|
// TestLoadEdgeSingleRequestExecutionPresetRejectsDivergentEffectiveBindings verifies that every
|
|
// single-request role (plan/work/review) fails closed when route and single_request bindings diverge.
|
|
func TestLoadEdgeSingleRequestExecutionPresetRejectsDivergentEffectiveBindings(t *testing.T) {
|
|
dir := t.TempDir()
|
|
f := filepath.Join(dir, "edge.yaml")
|
|
|
|
const (
|
|
planOptions = "\n options:\n reasoning_effort: \"high\""
|
|
workOptions = ""
|
|
reviewOptions = "\n options:\n reasoning_effort: \"high\""
|
|
planLeakOptions = "\n options:\n reasoning_effort: \"high\"\n temperature: 0.8"
|
|
workRouteLeakModel = "\n options:\n temperature: 0.8"
|
|
workRouteLeakMatch = "\n options:\n temperature: 0.2"
|
|
workReasoningLeak = "\n options:\n reasoning_effort: \"high\""
|
|
)
|
|
|
|
for _, tc := range []struct {
|
|
name string
|
|
yaml string
|
|
wantErrors []string
|
|
}{
|
|
{
|
|
name: "plan route stage model must match single_request plan stage",
|
|
yaml: singleRequestYAMLWithRouteBindingDiff("gemini-review", "ornith-fast", "gemini-review", planOptions, workOptions, reviewOptions, ""),
|
|
wantErrors: []string{"single_request preset light route stage[0] role=\"plan\" binding must match single_request stage"},
|
|
},
|
|
{
|
|
name: "work route stage model must match single_request work stage",
|
|
yaml: singleRequestYAMLWithRouteBindingDiff("gemini-plan", "gemini-review", "gemini-review", planOptions, workOptions, reviewOptions, ""),
|
|
wantErrors: []string{"single_request preset light route stage[1] role=\"work\" binding must match single_request stage"},
|
|
},
|
|
{
|
|
name: "review route stage model must match single_request review stage",
|
|
yaml: singleRequestYAMLWithRouteBindingDiff("gemini-plan", "ornith-fast", "gemini-plan", planOptions, workOptions, reviewOptions, ""),
|
|
wantErrors: []string{"single_request preset light route stage[2] role=\"review\" binding must match single_request stage"},
|
|
},
|
|
{
|
|
name: "plan route option mismatch must be rejected",
|
|
yaml: singleRequestYAMLWithRouteBindingDiff("gemini-plan", "ornith-fast", "gemini-review", planLeakOptions, workOptions, reviewOptions, ""),
|
|
wantErrors: []string{"single_request preset light route stage[0] role=\"plan\" binding must match single_request stage"},
|
|
},
|
|
{
|
|
name: "work route option mismatch must be rejected",
|
|
yaml: singleRequestYAMLWithRouteBindingDiff("gemini-plan", "ornith-fast", "gemini-review", planOptions, workRouteLeakModel, reviewOptions, workRouteLeakMatch),
|
|
wantErrors: []string{"single_request preset light route stage[1] role=\"work\" binding must match single_request stage"},
|
|
},
|
|
{
|
|
name: "review route option mismatch must be rejected",
|
|
yaml: singleRequestYAMLWithRouteBindingDiff("gemini-plan", "ornith-fast", "gemini-review", planOptions, workOptions, planLeakOptions, ""),
|
|
wantErrors: []string{"single_request preset light route stage[2] role=\"review\" binding must match single_request stage"},
|
|
},
|
|
{
|
|
name: "route-only dangling model must be rejected",
|
|
yaml: singleRequestYAMLWithRouteBindingDiff("gemini-plan", "dangling-model-id", "gemini-review", planOptions, workOptions, reviewOptions, ""),
|
|
wantErrors: []string{"single_request preset light route stage[1] role=\"work\" model", "not found in models catalog"},
|
|
},
|
|
{
|
|
name: "route-only work reasoning_effort leakage must be rejected",
|
|
yaml: singleRequestYAMLWithRouteBindingDiff("gemini-plan", "ornith-fast", "gemini-review", planOptions, workReasoningLeak, reviewOptions, ""),
|
|
wantErrors: []string{"single_request preset light route stage[1] role=\"work\" binding must match single_request stage"},
|
|
},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
requireSingleRequestLoadError(t, f, tc.yaml, tc.wantErrors...)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestLoadEdgeSingleRequestExecutionPresetRejectsInvalidShape verifies that
|
|
// invalid single-request shapes fail closed with descriptive errors.
|
|
func TestLoadEdgeSingleRequestExecutionPresetRejectsInvalidShape(t *testing.T) {
|
|
dir := t.TempDir()
|
|
f := filepath.Join(dir, "edge.yaml")
|
|
|
|
t.Run("empty workspace_ref rejected", func(t *testing.T) {
|
|
yaml := srYAMLWithWorkspaceRef("", 300000, 120000, 16, 4194304)
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for empty workspace_ref")
|
|
}
|
|
if !strings.Contains(err.Error(), "workspace_ref must not be empty") {
|
|
t.Fatalf("expected error mentioning workspace_ref must not be empty, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("zero wall_clock_ms rejected", func(t *testing.T) {
|
|
yaml := srYAMLWithWorkspaceRef("ws-ref", 0, 120000, 16, 4194304)
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for zero wall_clock_ms")
|
|
}
|
|
if !strings.Contains(err.Error(), "wall_clock_ms must be in") {
|
|
t.Fatalf("expected error mentioning wall_clock_ms range, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("wall_clock_ms over cap rejected", func(t *testing.T) {
|
|
yaml := srYAMLWithWorkspaceRef("ws-ref", config.MaxSingleRequestWallClockMS+1, 120000, 16, 4194304)
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for wall_clock_ms over cap")
|
|
}
|
|
if !strings.Contains(err.Error(), "wall_clock_ms must be in") {
|
|
t.Fatalf("expected error mentioning wall_clock_ms range, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("zero timeout_ms rejected", func(t *testing.T) {
|
|
yaml := srYAMLWithWorkspaceRef("ws-ref", 300000, 0, 16, 4194304)
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for zero timeout_ms")
|
|
}
|
|
if !strings.Contains(err.Error(), "timeout_ms must be in") {
|
|
t.Fatalf("expected error mentioning timeout_ms range, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("timeout_ms over cap rejected", func(t *testing.T) {
|
|
yaml := srYAMLWithWorkspaceRef("ws-ref", 300000, config.MaxSingleRequestStageTimeoutMS+1, 16, 4194304)
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for timeout_ms over cap")
|
|
}
|
|
if !strings.Contains(err.Error(), "timeout_ms must be in") {
|
|
t.Fatalf("expected error mentioning timeout_ms range, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("timeout_ms greater than wall_clock_ms rejected", func(t *testing.T) {
|
|
yaml := srYAMLWithWorkspaceRef("ws-ref", 100000, 200000, 16, 4194304)
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for timeout_ms > wall_clock_ms")
|
|
}
|
|
if !strings.Contains(err.Error(), "must not exceed wall_clock_ms") {
|
|
t.Fatalf("expected error mentioning must not exceed wall_clock_ms, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("legacy wall_clock_sec key rejected", func(t *testing.T) {
|
|
yaml := `server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "light"
|
|
routes:
|
|
light:
|
|
stages:
|
|
- role: "plan"
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "work"
|
|
model: "ornith-fast"
|
|
- role: "review"
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
single_request:
|
|
workspace_ref: "ws-ref"
|
|
limits:
|
|
wall_clock_sec: 1800
|
|
timeout_ms: 600000
|
|
max_tool_iterations: 16
|
|
max_output_bytes: 4194304
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for legacy wall_clock_sec key")
|
|
}
|
|
if !strings.Contains(err.Error(), "invalid keys") && !strings.Contains(err.Error(), "unknown fields") {
|
|
t.Fatalf("expected error mentioning invalid keys or unknown fields, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("legacy stage_timeout_sec key rejected", func(t *testing.T) {
|
|
yaml := `server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "light"
|
|
routes:
|
|
light:
|
|
stages:
|
|
- role: "plan"
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "work"
|
|
model: "ornith-fast"
|
|
- role: "review"
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
single_request:
|
|
workspace_ref: "ws-ref"
|
|
limits:
|
|
wall_clock_ms: 300000
|
|
stage_timeout_sec: 1800
|
|
timeout_ms: 600000
|
|
max_tool_iterations: 16
|
|
max_output_bytes: 4194304
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for legacy stage_timeout_sec key")
|
|
}
|
|
if !strings.Contains(err.Error(), "invalid keys") && !strings.Contains(err.Error(), "unknown fields") {
|
|
t.Fatalf("expected error mentioning invalid keys or unknown fields, got %v", err)
|
|
}
|
|
if !strings.Contains(err.Error(), "stage_timeout_sec") {
|
|
t.Fatalf("expected error mentioning stage_timeout_sec, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("zero max_tool_iterations rejected", func(t *testing.T) {
|
|
yaml := srYAMLWithWorkspaceRef("ws-ref", 300000, 120000, 0, 4194304)
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for zero max_tool_iterations")
|
|
}
|
|
if !strings.Contains(err.Error(), "max_tool_iterations must be in") {
|
|
t.Fatalf("expected error mentioning max_tool_iterations range, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("max_tool_iterations over cap rejected", func(t *testing.T) {
|
|
yaml := srYAMLWithWorkspaceRef("ws-ref", 300000, 120000, config.MaxSingleRequestToolIterations+1, 4194304)
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for max_tool_iterations over cap")
|
|
}
|
|
if !strings.Contains(err.Error(), "max_tool_iterations must be in") {
|
|
t.Fatalf("expected error mentioning max_tool_iterations range, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("zero max_output_bytes rejected", func(t *testing.T) {
|
|
yaml := srYAMLWithWorkspaceRef("ws-ref", 300000, 120000, 16, 0)
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for zero max_output_bytes")
|
|
}
|
|
if !strings.Contains(err.Error(), "max_output_bytes must be in") {
|
|
t.Fatalf("expected error mentioning max_output_bytes range, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("max_output_bytes over cap rejected", func(t *testing.T) {
|
|
yaml := srYAMLWithWorkspaceRef("ws-ref", 300000, 120000, 16, config.MaxSingleRequestOutputBytes+1)
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for max_output_bytes over cap")
|
|
}
|
|
if !strings.Contains(err.Error(), "max_output_bytes must be in") {
|
|
t.Fatalf("expected error mentioning max_output_bytes range, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("empty plan model rejected", func(t *testing.T) {
|
|
yaml := srYAMLWithPlanModel("", 300000, 120000, 16, 4194304)
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for empty plan model")
|
|
}
|
|
if !strings.Contains(err.Error(), "plan.model must not be empty") {
|
|
t.Fatalf("expected error mentioning plan.model must not be empty, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("empty work model rejected", func(t *testing.T) {
|
|
yaml := srYAMLWithWorkModel("", 300000, 120000, 16, 4194304)
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for empty work model")
|
|
}
|
|
if !strings.Contains(err.Error(), "work.model must not be empty") {
|
|
t.Fatalf("expected error mentioning work.model must not be empty, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("empty review model rejected", func(t *testing.T) {
|
|
yaml := srYAMLWithReviewModel("", 300000, 120000, 16, 4194304)
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for empty review model")
|
|
}
|
|
if !strings.Contains(err.Error(), "review.model must not be empty") {
|
|
t.Fatalf("expected error mentioning review.model must not be empty, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("dangling stage model rejected", func(t *testing.T) {
|
|
yaml := srYAMLWithWorkModel("non-existent-model", 300000, 120000, 16, 4194304)
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for dangling stage model")
|
|
}
|
|
if !strings.Contains(err.Error(), "not found in models catalog") {
|
|
t.Fatalf("expected error mentioning not found in models catalog, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("divergent route stage model rejected", func(t *testing.T) {
|
|
// Route work stage model ("gemini-plan") differs from single_request work stage model ("ornith-fast").
|
|
yaml := `server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "light"
|
|
routes:
|
|
light:
|
|
stages:
|
|
- role: "plan"
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "work"
|
|
model: "gemini-plan"
|
|
- role: "review"
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
single_request:
|
|
workspace_ref: "ws-ref"
|
|
limits:
|
|
wall_clock_ms: 300000
|
|
timeout_ms: 120000
|
|
max_tool_iterations: 16
|
|
max_output_bytes: 4194304
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for divergent route stage model")
|
|
}
|
|
if !strings.Contains(err.Error(), "binding must match single_request stage") {
|
|
t.Fatalf("expected error mentioning binding must match single_request stage, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("divergent route stage options rejected", func(t *testing.T) {
|
|
// Route work stage has temperature 0.8, single_request work stage has temperature 0.2.
|
|
yaml := `server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "light"
|
|
routes:
|
|
light:
|
|
stages:
|
|
- role: "plan"
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "work"
|
|
model: "ornith-fast"
|
|
options:
|
|
temperature: 0.8
|
|
- role: "review"
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
single_request:
|
|
workspace_ref: "ws-ref"
|
|
limits:
|
|
wall_clock_ms: 300000
|
|
timeout_ms: 120000
|
|
max_tool_iterations: 16
|
|
max_output_bytes: 4194304
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"
|
|
options:
|
|
temperature: 0.2
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for divergent route stage options")
|
|
}
|
|
if !strings.Contains(err.Error(), "binding must match single_request stage") {
|
|
t.Fatalf("expected error mentioning binding must match single_request stage, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("dangling route stage model rejected", func(t *testing.T) {
|
|
yaml := `server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "light"
|
|
routes:
|
|
light:
|
|
stages:
|
|
- role: "plan"
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "work"
|
|
model: "dangling-model-id"
|
|
- role: "review"
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
single_request:
|
|
workspace_ref: "ws-ref"
|
|
limits:
|
|
wall_clock_ms: 300000
|
|
timeout_ms: 120000
|
|
max_tool_iterations: 16
|
|
max_output_bytes: 4194304
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "dangling-model-id"
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for dangling route stage model")
|
|
}
|
|
if !strings.Contains(err.Error(), "not found in models catalog") {
|
|
t.Fatalf("expected error mentioning not found in models catalog, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("divergent selector model rejected", func(t *testing.T) {
|
|
yaml := `server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "ornith-fast"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "light"
|
|
routes:
|
|
light:
|
|
stages:
|
|
- role: "plan"
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "work"
|
|
model: "ornith-fast"
|
|
- role: "review"
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
single_request:
|
|
workspace_ref: "ws-ref"
|
|
limits:
|
|
wall_clock_ms: 300000
|
|
timeout_ms: 120000
|
|
max_tool_iterations: 16
|
|
max_output_bytes: 4194304
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for divergent selector model")
|
|
}
|
|
if !strings.Contains(err.Error(), "selector binding must match single_request plan stage") {
|
|
t.Fatalf("expected error mentioning selector binding must match single_request plan stage, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("missing high reasoning on selector rejected", func(t *testing.T) {
|
|
yaml := `server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
allowed_modes:
|
|
- "light"
|
|
routes:
|
|
light:
|
|
stages:
|
|
- role: "plan"
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "work"
|
|
model: "ornith-fast"
|
|
- role: "review"
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
single_request:
|
|
workspace_ref: "ws-ref"
|
|
limits:
|
|
wall_clock_ms: 300000
|
|
timeout_ms: 120000
|
|
max_tool_iterations: 16
|
|
max_output_bytes: 4194304
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for missing high reasoning on selector")
|
|
}
|
|
if !strings.Contains(err.Error(), "selector.options must have reasoning_effort") {
|
|
t.Fatalf("expected error mentioning selector.options reasoning_effort, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("high reasoning on work stage rejected", func(t *testing.T) {
|
|
yaml := `server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "light"
|
|
routes:
|
|
light:
|
|
stages:
|
|
- role: "plan"
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "work"
|
|
model: "ornith-fast"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "review"
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
single_request:
|
|
workspace_ref: "ws-ref"
|
|
limits:
|
|
wall_clock_ms: 300000
|
|
timeout_ms: 120000
|
|
max_tool_iterations: 16
|
|
max_output_bytes: 4194304
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"
|
|
options:
|
|
reasoning_effort: "high"
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for high reasoning on work stage")
|
|
}
|
|
if !strings.Contains(err.Error(), "work.options must not declare reasoning_effort") {
|
|
t.Fatalf("expected error mentioning work.options must not declare reasoning_effort, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("medium reasoning on work stage rejected", func(t *testing.T) {
|
|
yaml := `server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "light"
|
|
routes:
|
|
light:
|
|
stages:
|
|
- role: "plan"
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "work"
|
|
model: "ornith-fast"
|
|
options:
|
|
reasoning_effort: "medium"
|
|
- role: "review"
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
single_request:
|
|
workspace_ref: "ws-ref"
|
|
limits:
|
|
wall_clock_ms: 300000
|
|
timeout_ms: 120000
|
|
max_tool_iterations: 16
|
|
max_output_bytes: 4194304
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"
|
|
options:
|
|
reasoning_effort: "medium"
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for medium reasoning on work stage")
|
|
}
|
|
if !strings.Contains(err.Error(), "work.options must not declare reasoning_effort") {
|
|
t.Fatalf("expected error mentioning work.options must not declare reasoning_effort, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("non-string reasoning on work stage rejected", func(t *testing.T) {
|
|
yaml := `server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "light"
|
|
routes:
|
|
light:
|
|
stages:
|
|
- role: "plan"
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "work"
|
|
model: "ornith-fast"
|
|
options:
|
|
reasoning_effort: 10
|
|
- role: "review"
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
single_request:
|
|
workspace_ref: "ws-ref"
|
|
limits:
|
|
wall_clock_ms: 300000
|
|
timeout_ms: 120000
|
|
max_tool_iterations: 16
|
|
max_output_bytes: 4194304
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"
|
|
options:
|
|
reasoning_effort: 10
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for non-string reasoning on work stage")
|
|
}
|
|
if !strings.Contains(err.Error(), "work.options must not declare reasoning_effort") {
|
|
t.Fatalf("expected error mentioning work.options must not declare reasoning_effort, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("missing high reasoning on review rejected", func(t *testing.T) {
|
|
yaml := `server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "light"
|
|
routes:
|
|
light:
|
|
stages:
|
|
- role: "plan"
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "work"
|
|
model: "ornith-fast"
|
|
- role: "review"
|
|
model: "gemini-review"
|
|
single_request:
|
|
workspace_ref: "ws-ref"
|
|
limits:
|
|
wall_clock_ms: 300000
|
|
timeout_ms: 120000
|
|
max_tool_iterations: 16
|
|
max_output_bytes: 4194304
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"
|
|
review:
|
|
model: "gemini-review"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for missing high reasoning on review")
|
|
}
|
|
if !strings.Contains(err.Error(), "review.options must have reasoning_effort") {
|
|
t.Fatalf("expected error mentioning review.options reasoning_effort, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("non-light allowed mode rejected", func(t *testing.T) {
|
|
// Use allowed_modes=["direct"] with a direct route; single_request validation
|
|
// rejects non-light allowed_modes before route checks complete.
|
|
yaml := `server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "direct"
|
|
routes:
|
|
direct:
|
|
stages: []
|
|
single_request:
|
|
workspace_ref: "ws-ref"
|
|
limits:
|
|
wall_clock_ms: 300000
|
|
timeout_ms: 120000
|
|
max_tool_iterations: 16
|
|
max_output_bytes: 4194304
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for non-light allowed mode")
|
|
}
|
|
if !strings.Contains(err.Error(), "allowed_modes must be exactly") {
|
|
t.Fatalf("expected error mentioning allowed_modes must be exactly, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("direct+light allowed modes rejected", func(t *testing.T) {
|
|
// allowed_modes=["direct", "light"] with both routes; single_request rejects
|
|
// because allowed_modes is not exactly ["light"].
|
|
yaml := `server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "direct"
|
|
- "light"
|
|
routes:
|
|
direct:
|
|
stages: []
|
|
light:
|
|
stages:
|
|
- role: "plan"
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "work"
|
|
model: "ornith-fast"
|
|
- role: "review"
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
single_request:
|
|
workspace_ref: "ws-ref"
|
|
limits:
|
|
wall_clock_ms: 300000
|
|
timeout_ms: 120000
|
|
max_tool_iterations: 16
|
|
max_output_bytes: 4194304
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for direct+light allowed modes")
|
|
}
|
|
if !strings.Contains(err.Error(), "allowed_modes must be exactly") {
|
|
t.Fatalf("expected error mentioning allowed_modes must be exactly, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("workspace_tools with single_request rejected", func(t *testing.T) {
|
|
yaml := `server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "light"
|
|
routes:
|
|
light:
|
|
stages:
|
|
- role: "plan"
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "work"
|
|
model: "ornith-fast"
|
|
- role: "review"
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
single_request:
|
|
workspace_ref: "ws-ref"
|
|
limits:
|
|
wall_clock_ms: 300000
|
|
timeout_ms: 120000
|
|
max_tool_iterations: 16
|
|
max_output_bytes: 4194304
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
workspace_tools:
|
|
- name: "ws1"
|
|
operations:
|
|
read:
|
|
tool_name: "cat"
|
|
schema_matcher:
|
|
type: "object"
|
|
argument_map:
|
|
path: "path"
|
|
result_matcher:
|
|
status: "ok"
|
|
write:
|
|
tool_name: "tee"
|
|
creates_parents: true
|
|
schema_matcher:
|
|
type: "object"
|
|
argument_map:
|
|
path: "path"
|
|
result_matcher:
|
|
status: "ok"
|
|
delete:
|
|
tool_name: "rm"
|
|
schema_matcher:
|
|
type: "object"
|
|
argument_map:
|
|
path: "path"
|
|
result_matcher:
|
|
status: "ok"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for workspace_tools with single_request")
|
|
}
|
|
if !strings.Contains(err.Error(), "workspace_tools") && !strings.Contains(err.Error(), "single_request") {
|
|
t.Fatalf("expected error mentioning workspace_tools or single_request, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("wrong route stage order rejected", func(t *testing.T) {
|
|
// Swap plan and review roles in the light route stages only.
|
|
// The single_request stages still have correct roles, but the route is wrong.
|
|
yaml := `server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "light"
|
|
routes:
|
|
light:
|
|
stages:
|
|
- role: "review"
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "work"
|
|
model: "ornith-fast"
|
|
- role: "plan"
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
single_request:
|
|
workspace_ref: "ws-ref"
|
|
limits:
|
|
wall_clock_ms: 300000
|
|
timeout_ms: 120000
|
|
max_tool_iterations: 16
|
|
max_output_bytes: 4194304
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for wrong route stage order")
|
|
}
|
|
if !strings.Contains(err.Error(), "stage[0] role is") {
|
|
t.Fatalf("expected error mentioning stage role mismatch, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("extra route stage rejected", func(t *testing.T) {
|
|
yaml := `server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "light"
|
|
routes:
|
|
light:
|
|
stages:
|
|
- role: "plan"
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "work"
|
|
model: "ornith-fast"
|
|
- role: "review"
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "extra"
|
|
model: "gemini-plan"
|
|
single_request:
|
|
workspace_ref: "ws-ref"
|
|
limits:
|
|
wall_clock_ms: 300000
|
|
timeout_ms: 120000
|
|
max_tool_iterations: 16
|
|
max_output_bytes: 4194304
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for extra route stage")
|
|
}
|
|
if !strings.Contains(err.Error(), "exactly 3 stages") {
|
|
t.Fatalf("expected error mentioning exactly 3 stages, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("missing light route rejected", func(t *testing.T) {
|
|
// No routes section at all; single_request requires a light route.
|
|
yaml := `server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "light"
|
|
routes: {}
|
|
single_request:
|
|
workspace_ref: "ws-ref"
|
|
limits:
|
|
wall_clock_ms: 300000
|
|
timeout_ms: 120000
|
|
max_tool_iterations: 16
|
|
max_output_bytes: 4194304
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for missing light route")
|
|
}
|
|
if !strings.Contains(err.Error(), "missing route for allowed mode") && !strings.Contains(err.Error(), "must declare a route for mode") {
|
|
t.Fatalf("expected error mentioning missing route or must declare a route for mode, got %v", err)
|
|
}
|
|
})
|
|
}
|
|
|
|
// TestLoadEdgeSingleRequestExecutionPresetRejectsUnknownNestedFields verifies
|
|
// that unknown fields fail strict decode at every new policy boundary (R3).
|
|
func TestLoadEdgeSingleRequestExecutionPresetRejectsUnknownNestedFields(t *testing.T) {
|
|
dir := t.TempDir()
|
|
f := filepath.Join(dir, "edge.yaml")
|
|
|
|
t.Run("unknown field on single_request root rejected", func(t *testing.T) {
|
|
yaml := `server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "light"
|
|
routes:
|
|
light:
|
|
stages:
|
|
- role: "plan"
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "work"
|
|
model: "ornith-fast"
|
|
- role: "review"
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
single_request:
|
|
workspace_ref: "ws-ref"
|
|
unknown_root_field: "unexpected"
|
|
limits:
|
|
wall_clock_ms: 300000
|
|
timeout_ms: 120000
|
|
max_tool_iterations: 16
|
|
max_output_bytes: 4194304
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for unknown field on single_request root")
|
|
}
|
|
if !strings.Contains(err.Error(), "invalid keys") && !strings.Contains(err.Error(), "unknown fields") {
|
|
t.Fatalf("expected error mentioning invalid keys or unknown fields, got %v", err)
|
|
}
|
|
if !strings.Contains(err.Error(), "unknown_root_field") {
|
|
t.Fatalf("expected error mentioning unknown_root_field, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("unknown field on single_request.limits rejected", func(t *testing.T) {
|
|
yaml := `server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "light"
|
|
routes:
|
|
light:
|
|
stages:
|
|
- role: "plan"
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "work"
|
|
model: "ornith-fast"
|
|
- role: "review"
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
single_request:
|
|
workspace_ref: "ws-ref"
|
|
limits:
|
|
wall_clock_ms: 300000
|
|
timeout_ms: 120000
|
|
max_tool_iterations: 16
|
|
max_output_bytes: 4194304
|
|
unknown_limit_field: 123
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for unknown field on limits")
|
|
}
|
|
if !strings.Contains(err.Error(), "invalid keys") && !strings.Contains(err.Error(), "unknown fields") {
|
|
t.Fatalf("expected error mentioning invalid keys or unknown fields, got %v", err)
|
|
}
|
|
if !strings.Contains(err.Error(), "unknown_limit_field") {
|
|
t.Fatalf("expected error mentioning unknown_limit_field, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("unknown field on single_request.stages rejected", func(t *testing.T) {
|
|
yaml := `server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "light"
|
|
routes:
|
|
light:
|
|
stages:
|
|
- role: "plan"
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "work"
|
|
model: "ornith-fast"
|
|
- role: "review"
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
single_request:
|
|
workspace_ref: "ws-ref"
|
|
limits:
|
|
wall_clock_ms: 300000
|
|
timeout_ms: 120000
|
|
max_tool_iterations: 16
|
|
max_output_bytes: 4194304
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
unknown_stage_field: {}
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for unknown field on stages")
|
|
}
|
|
if !strings.Contains(err.Error(), "invalid keys") && !strings.Contains(err.Error(), "unknown fields") {
|
|
t.Fatalf("expected error mentioning invalid keys or unknown fields, got %v", err)
|
|
}
|
|
if !strings.Contains(err.Error(), "unknown_stage_field") {
|
|
t.Fatalf("expected error mentioning unknown_stage_field, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("unknown field on single_request stage binding rejected", func(t *testing.T) {
|
|
yaml := `server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "light"
|
|
routes:
|
|
light:
|
|
stages:
|
|
- role: "plan"
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "work"
|
|
model: "ornith-fast"
|
|
- role: "review"
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
single_request:
|
|
workspace_ref: "ws-ref"
|
|
limits:
|
|
wall_clock_ms: 300000
|
|
timeout_ms: 120000
|
|
max_tool_iterations: 16
|
|
max_output_bytes: 4194304
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"
|
|
unknown_stage_config_field: "invalid"
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(f)
|
|
if err == nil {
|
|
t.Fatal("expected error for unknown field on stage config")
|
|
}
|
|
if !strings.Contains(err.Error(), "invalid keys") && !strings.Contains(err.Error(), "unknown fields") {
|
|
t.Fatalf("expected error mentioning invalid keys or unknown fields, got %v", err)
|
|
}
|
|
if !strings.Contains(err.Error(), "unknown_stage_config_field") {
|
|
t.Fatalf("expected error mentioning unknown_stage_config_field, got %v", err)
|
|
}
|
|
})
|
|
}
|
|
|
|
// TestCloneExecutionPresetSingleRequestIsolation verifies that deep-cloning a
|
|
// single-request preset produces an isolated copy: mutating the clone must not
|
|
// affect the source.
|
|
func TestCloneExecutionPresetSingleRequestIsolation(t *testing.T) {
|
|
dir := t.TempDir()
|
|
f := filepath.Join(dir, "edge.yaml")
|
|
|
|
if err := os.WriteFile(f, []byte(validSingleRequestYAML), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
cfg, err := config.LoadEdge(f)
|
|
if err != nil {
|
|
t.Fatalf("load: %v", err)
|
|
}
|
|
original := cfg.ExecutionPresets[0]
|
|
if original.SingleRequest == nil {
|
|
t.Fatal("expected SingleRequest to be set")
|
|
}
|
|
|
|
t.Run("clone isolates workspace_ref", func(t *testing.T) {
|
|
clone := original.Clone()
|
|
if clone.SingleRequest == nil {
|
|
t.Fatal("clone: SingleRequest is nil")
|
|
}
|
|
clone.SingleRequest.WorkspaceRef = "mutated"
|
|
if original.SingleRequest.WorkspaceRef == "mutated" {
|
|
t.Error("source SingleRequest.WorkspaceRef was mutated by clone")
|
|
}
|
|
if clone.SingleRequest.WorkspaceRef != "mutated" {
|
|
t.Error("clone SingleRequest.WorkspaceRef was not set")
|
|
}
|
|
})
|
|
|
|
t.Run("clone isolates limits", func(t *testing.T) {
|
|
clone := original.Clone()
|
|
if clone.SingleRequest == nil {
|
|
t.Fatal("clone: SingleRequest is nil")
|
|
}
|
|
clone.SingleRequest.Limits.WallClockMS = 999999
|
|
if original.SingleRequest.Limits.WallClockMS == 999999 {
|
|
t.Error("source SingleRequest.Limits.WallClockMS was mutated by clone")
|
|
}
|
|
})
|
|
|
|
t.Run("clone isolates stage plan options", func(t *testing.T) {
|
|
clone := original.Clone()
|
|
if clone.SingleRequest == nil {
|
|
t.Fatal("clone: SingleRequest is nil")
|
|
}
|
|
clone.SingleRequest.Stages.Plan.Options["reasoning_effort"] = "mutated"
|
|
origVal, _ := original.SingleRequest.Stages.Plan.Options["reasoning_effort"]
|
|
if origVal == "mutated" {
|
|
t.Error("source plan options were mutated by clone")
|
|
}
|
|
cloneVal, _ := clone.SingleRequest.Stages.Plan.Options["reasoning_effort"]
|
|
if cloneVal != "mutated" {
|
|
t.Error("clone plan options were not set")
|
|
}
|
|
})
|
|
|
|
t.Run("clone isolates stage work options", func(t *testing.T) {
|
|
clone := original.Clone()
|
|
if clone.SingleRequest == nil {
|
|
t.Fatal("clone: SingleRequest is nil")
|
|
}
|
|
clone.SingleRequest.Stages.Work.Options["temperature"] = 99.0
|
|
origVal, hasOrig := original.SingleRequest.Stages.Work.Options["temperature"]
|
|
if hasOrig && origVal == 99.0 {
|
|
t.Error("source work options were mutated by clone")
|
|
}
|
|
cloneVal, hasClone := clone.SingleRequest.Stages.Work.Options["temperature"]
|
|
if !hasClone || cloneVal != 99.0 {
|
|
t.Error("clone work options were not set")
|
|
}
|
|
})
|
|
|
|
t.Run("clone isolates stage review options", func(t *testing.T) {
|
|
clone := original.Clone()
|
|
if clone.SingleRequest == nil {
|
|
t.Fatal("clone: SingleRequest is nil")
|
|
}
|
|
clone.SingleRequest.Stages.Review.Options["max_retries"] = 99
|
|
origVal, hasOrig := original.SingleRequest.Stages.Review.Options["max_retries"]
|
|
if hasOrig && origVal == 99 {
|
|
t.Error("source review options were mutated by clone")
|
|
}
|
|
cloneVal, hasClone := clone.SingleRequest.Stages.Review.Options["max_retries"]
|
|
if !hasClone || cloneVal != 99 {
|
|
t.Error("clone review options were not set")
|
|
}
|
|
})
|
|
|
|
t.Run("clone isolates route stages", func(t *testing.T) {
|
|
clone := original.Clone()
|
|
if len(clone.Routes["light"].Stages) != 3 {
|
|
t.Fatalf("clone route stages = %d, want 3", len(clone.Routes["light"].Stages))
|
|
}
|
|
clone.Routes["light"].Stages[0].Model = "mutated-model"
|
|
if original.Routes["light"].Stages[0].Model == "mutated-model" {
|
|
t.Error("source route stages were mutated by clone")
|
|
}
|
|
if clone.Routes["light"].Stages[0].Model != "mutated-model" {
|
|
t.Error("clone route stages were not set")
|
|
}
|
|
})
|
|
|
|
t.Run("clone isolates allowed_modes", func(t *testing.T) {
|
|
clone := original.Clone()
|
|
clone.AllowedModes[0] = "mutated"
|
|
if original.AllowedModes[0] == "mutated" {
|
|
t.Error("source AllowedModes was mutated by clone")
|
|
}
|
|
})
|
|
|
|
t.Run("nil SingleRequest clone returns nil", func(t *testing.T) {
|
|
noSR := original
|
|
noSR.SingleRequest = nil
|
|
clone := noSR.Clone()
|
|
if clone.SingleRequest != nil {
|
|
t.Error("expected nil SingleRequest on clone of preset without SingleRequest")
|
|
}
|
|
})
|
|
|
|
t.Run("CloneExecutionPresetCatalog isolates single-request presets", func(t *testing.T) {
|
|
catalog := config.CloneExecutionPresetCatalog(cfg.ExecutionPresets)
|
|
if len(catalog) != 1 {
|
|
t.Fatalf("expected 1 preset in catalog, got %d", len(catalog))
|
|
}
|
|
clone := catalog[0]
|
|
if clone.SingleRequest == nil {
|
|
t.Fatal("clone catalog: SingleRequest is nil")
|
|
}
|
|
clone.SingleRequest.Limits.MaxToolIterations = 0
|
|
if cfg.ExecutionPresets[0].SingleRequest.Limits.MaxToolIterations == 0 {
|
|
t.Error("source catalog preset was mutated by clone")
|
|
}
|
|
})
|
|
}
|
|
|
|
// routeStagesRoles extracts role names from a slice of ExecutionRouteStage.
|
|
func routeStagesRoles(stages []config.ExecutionRouteStage) []string {
|
|
roles := make([]string, len(stages))
|
|
for i, s := range stages {
|
|
roles[i] = s.Role
|
|
}
|
|
return roles
|
|
}
|
|
|
|
func requireSingleRequestLoadError(t *testing.T, path, yaml string, wantFragments ...string) {
|
|
t.Helper()
|
|
if err := os.WriteFile(path, []byte(yaml), 0o600); err != nil {
|
|
t.Fatalf("write yaml: %v", err)
|
|
}
|
|
_, err := config.LoadEdge(path)
|
|
if err == nil {
|
|
t.Fatal("expected error for malformed single-request binding")
|
|
}
|
|
for _, want := range wantFragments {
|
|
if !strings.Contains(err.Error(), want) {
|
|
t.Fatalf("expected error mentioning %q, got %v", want, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func singleRequestYAMLWithRouteBindingDiff(
|
|
routePlanModel, routeWorkModel, routeReviewModel string,
|
|
routePlanOptions, routeWorkOptions, routeReviewOptions, singleWorkOptions string,
|
|
) string {
|
|
return `server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "light"
|
|
routes:
|
|
light:
|
|
stages:
|
|
- role: "plan"
|
|
model: "` + routePlanModel + `"` + routePlanOptions + `
|
|
- role: "work"
|
|
model: "` + routeWorkModel + `"` + routeWorkOptions + `
|
|
- role: "review"
|
|
model: "` + routeReviewModel + `"` + routeReviewOptions + `
|
|
single_request:
|
|
workspace_ref: "ws-ref"
|
|
limits:
|
|
wall_clock_ms: 300000
|
|
timeout_ms: 120000
|
|
max_tool_iterations: 16
|
|
max_output_bytes: 4194304
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"` + singleWorkOptions + `
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
}
|
|
|
|
// srYAMLWithWorkspaceRef produces a valid single-request preset YAML with the
|
|
// given workspace_ref and limits, using standard plan/work/review stage bindings.
|
|
func srYAMLWithWorkspaceRef(workspaceRef string, wallClock, stageTimeout, toolIters, outputBytes int) string {
|
|
return `server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "light"
|
|
routes:
|
|
light:
|
|
stages:
|
|
- role: "plan"
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "work"
|
|
model: "ornith-fast"
|
|
- role: "review"
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
single_request:
|
|
workspace_ref: "` + workspaceRef + `"
|
|
limits:
|
|
wall_clock_ms: ` + itoa(wallClock) + `
|
|
timeout_ms: ` + itoa(stageTimeout) + `
|
|
max_tool_iterations: ` + itoa(toolIters) + `
|
|
max_output_bytes: ` + itoa(outputBytes) + `
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
}
|
|
|
|
// srYAMLWithPlanModel produces a valid single-request preset YAML with the
|
|
// given plan stage model and standard limits.
|
|
func srYAMLWithPlanModel(planModel string, wallClock, stageTimeout, toolIters, outputBytes int) string {
|
|
return `server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "light"
|
|
routes:
|
|
light:
|
|
stages:
|
|
- role: "plan"
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "work"
|
|
model: "ornith-fast"
|
|
- role: "review"
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
single_request:
|
|
workspace_ref: "ws-ref"
|
|
limits:
|
|
wall_clock_ms: ` + itoa(wallClock) + `
|
|
timeout_ms: ` + itoa(stageTimeout) + `
|
|
max_tool_iterations: ` + itoa(toolIters) + `
|
|
max_output_bytes: ` + itoa(outputBytes) + `
|
|
stages:
|
|
plan:
|
|
model: "` + planModel + `"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
}
|
|
|
|
// srYAMLWithWorkModel produces a valid single-request preset YAML with the
|
|
// given work stage model and standard limits.
|
|
func srYAMLWithWorkModel(workModel string, wallClock, stageTimeout, toolIters, outputBytes int) string {
|
|
return `server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "light"
|
|
routes:
|
|
light:
|
|
stages:
|
|
- role: "plan"
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "work"
|
|
model: "` + workModel + `"
|
|
- role: "review"
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
single_request:
|
|
workspace_ref: "ws-ref"
|
|
limits:
|
|
wall_clock_ms: ` + itoa(wallClock) + `
|
|
timeout_ms: ` + itoa(stageTimeout) + `
|
|
max_tool_iterations: ` + itoa(toolIters) + `
|
|
max_output_bytes: ` + itoa(outputBytes) + `
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "` + workModel + `"
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
}
|
|
|
|
// srYAMLWithReviewModel produces a valid single-request preset YAML with the
|
|
// given review stage model and standard limits.
|
|
func srYAMLWithReviewModel(reviewModel string, wallClock, stageTimeout, toolIters, outputBytes int) string {
|
|
return `server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "light"
|
|
routes:
|
|
light:
|
|
stages:
|
|
- role: "plan"
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "work"
|
|
model: "ornith-fast"
|
|
- role: "review"
|
|
model: "` + reviewModel + `"
|
|
options:
|
|
reasoning_effort: "high"
|
|
single_request:
|
|
workspace_ref: "ws-ref"
|
|
limits:
|
|
wall_clock_ms: ` + itoa(wallClock) + `
|
|
timeout_ms: ` + itoa(stageTimeout) + `
|
|
max_tool_iterations: ` + itoa(toolIters) + `
|
|
max_output_bytes: ` + itoa(outputBytes) + `
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"
|
|
review:
|
|
model: "` + reviewModel + `"
|
|
options:
|
|
reasoning_effort: "high"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
}
|
|
|
|
// singleRequestYAMLWithLimits produces a valid single-request preset YAML with
|
|
// the given limits and the standard plan/work/review stage bindings.
|
|
func singleRequestYAMLWithLimits(wallClock, stageTimeout, toolIters, outputBytes int) string {
|
|
return `
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
models:
|
|
- id: "gemini-plan"
|
|
providers:
|
|
prov-a: "gemini-plan"
|
|
- id: "ornith-fast"
|
|
providers:
|
|
prov-a: "ornith-fast"
|
|
- id: "gemini-review"
|
|
providers:
|
|
prov-a: "gemini-review"
|
|
execution_presets:
|
|
- id: "fixed-single-request"
|
|
selector:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
allowed_modes:
|
|
- "light"
|
|
routes:
|
|
light:
|
|
stages:
|
|
- role: "plan"
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
- role: "work"
|
|
model: "ornith-fast"
|
|
options:
|
|
temperature: 0.2
|
|
- role: "review"
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
single_request:
|
|
workspace_ref: "ws-capability-ref-001"
|
|
limits:
|
|
wall_clock_ms: ` + itoa(wallClock) + `
|
|
timeout_ms: ` + itoa(stageTimeout) + `
|
|
max_tool_iterations: ` + itoa(toolIters) + `
|
|
max_output_bytes: ` + itoa(outputBytes) + `
|
|
stages:
|
|
plan:
|
|
model: "gemini-plan"
|
|
options:
|
|
reasoning_effort: "high"
|
|
work:
|
|
model: "ornith-fast"
|
|
options:
|
|
temperature: 0.2
|
|
review:
|
|
model: "gemini-review"
|
|
options:
|
|
reasoning_effort: "high"
|
|
nodes:
|
|
- id: "node-01"
|
|
providers:
|
|
- id: "prov-a"
|
|
type: "ollama"
|
|
category: "local_inference"
|
|
models: ["gemini-plan", "ornith-fast", "gemini-review"]
|
|
capacity: 2
|
|
`
|
|
}
|
|
|
|
func itoa(v int) string {
|
|
return fmt.Sprintf("%d", v)
|
|
}
|