mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-09-20 00:48:00 +00:00
feat: opencode zen adapter + first-run config generation, fix stats/stream bugs
- adapters/opencode.lua: opencode.ai zen free pool adapter — sends the opencode client User-Agent (zen fingerprints clients by UA; non-official clients hit FreeUsageLimitError); pairs with api_key: public - config: no config file ships in the repo; first run generates a default config at the -config path with a random admin key, loopback listen and a keyless zen source (config.EnsureDefault); remove config.example.yaml - lua: seed bundled adapters from the embedded FS instead of a hardcoded name list - ui: widen model kind select (chat was clipped to 'cha') - phase 5 bugfixes: stats ms/s bucket mixing, cleanScopes nil, ctx.Err guards, direct-path ModelAvailable, empty stream body failure, bestImageModel rewrite, transform failure recording, Core.mu, timer, effective model for tool-calls
This commit is contained in:
@ -2,6 +2,7 @@ package lua
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
@ -28,13 +29,56 @@ func TestLoadBundledAdapters(t *testing.T) {
|
||||
for _, a := range adapters {
|
||||
names[a.Name] = true
|
||||
}
|
||||
for _, want := range []string{"openai", "deepseek", "anthropic", "gemini", "ollama", "kimicode"} {
|
||||
for _, want := range []string{"openai", "deepseek", "anthropic", "gemini", "ollama", "kimicode", "opencode"} {
|
||||
if !names[want] {
|
||||
t.Errorf("missing adapter %s (got %v)", want, names)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFirstRunSeedsEveryBundledAdapter(t *testing.T) {
|
||||
dir := filepath.Join(t.TempDir(), "adapters")
|
||||
vm := NewVM(dir)
|
||||
if err := vm.Start(); err != nil {
|
||||
t.Fatalf("start: %v", err)
|
||||
}
|
||||
defer vm.Stop()
|
||||
// every embedded .lua must have been copied out (and no extra, non-.lua files)
|
||||
embedded, err := bundledAdapters.ReadDir("adapters")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
seeded := 0
|
||||
for _, e := range embedded {
|
||||
if e.IsDir() || !strings.HasSuffix(e.Name(), ".lua") {
|
||||
continue
|
||||
}
|
||||
seeded++
|
||||
if _, err := os.Stat(filepath.Join(dir, e.Name())); err != nil {
|
||||
t.Errorf("embedded adapter %s not seeded", e.Name())
|
||||
}
|
||||
}
|
||||
if seeded == 0 {
|
||||
t.Fatal("no embedded adapters found")
|
||||
}
|
||||
written := 0
|
||||
entries, _ := os.ReadDir(dir)
|
||||
for _, e := range entries {
|
||||
if !e.IsDir() && strings.HasSuffix(e.Name(), ".lua") {
|
||||
written++
|
||||
}
|
||||
}
|
||||
if written != seeded {
|
||||
t.Fatalf("seeded %d files but %d embedded adapters exist", written, seeded)
|
||||
}
|
||||
// re-start with the existing dir: authoritative, never rewritten
|
||||
vm2 := NewVM(dir)
|
||||
if err := vm2.Start(); err != nil {
|
||||
t.Fatalf("second start: %v", err)
|
||||
}
|
||||
defer vm2.Stop()
|
||||
}
|
||||
|
||||
func TestTransformRequest(t *testing.T) {
|
||||
vm := NewVM(freshAdapterDir(t))
|
||||
if err := vm.Start(); err != nil {
|
||||
@ -65,6 +109,32 @@ func TestBuildHeadersFallbackStatic(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenCodeAdapterFingerprint(t *testing.T) {
|
||||
vm := NewVM(freshAdapterDir(t))
|
||||
if err := vm.Start(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer vm.Stop()
|
||||
hdrs, err := vm.BuildHeaders("opencode", map[string]interface{}{"api_key": "public"})
|
||||
if err != nil {
|
||||
t.Fatalf("build headers: %v", err)
|
||||
}
|
||||
if ua := hdrs["User-Agent"]; ua != "opencode/0.1.0" {
|
||||
t.Fatalf("opencode adapter must send the opencode client UA, got %q", ua)
|
||||
}
|
||||
if hdrs["Authorization"] != "" {
|
||||
t.Fatalf("opencode adapter must not hardcode Authorization (config api_key supplies it), got %q", hdrs["Authorization"])
|
||||
}
|
||||
// passthrough behaves like the openai adapter
|
||||
out, err := vm.Transform("opencode", "transform_request", `{"model":"x","disable_thinking":true,"extra_body":{},"messages":[{"role":"user","content":"hi"}]}`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if strings.Contains(out, "disable_thinking") || strings.Contains(out, "extra_body") {
|
||||
t.Fatalf("opencode transform_request must strip provider fields: %s", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildHeadersCustomHook(t *testing.T) {
|
||||
vm := NewVM(freshAdapterDir(t))
|
||||
if err := vm.Start(); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user