Fix Qwen session tool calls and expose thinking compatibility
Publish documentation / publish (push) Successful in 30s

This commit is contained in:
Codex
2026-09-21 16:23:51 +02:00
parent efd7d788d9
commit 84084bba37
10 changed files with 220 additions and 4 deletions
@@ -28,6 +28,38 @@ func TestLoadAcceptsInstallationModelCatalog(t *testing.T) {
}
}
func TestLoadQwenThinkingCompatibility(t *testing.T) {
for _, test := range []struct {
name, format string
reasoning bool
wantError bool
}{
{"vllm template", "qwen-chat-template", true, false},
{"top level Qwen", "qwen", true, false},
{"unknown format", "qwen-typo", true, true},
{"thinking disabled in model capabilities", "qwen-chat-template", false, true},
} {
t.Run(test.name, func(t *testing.T) {
path, _, _, _ := writeInstallation(t, "local")
reasoning := "false"
if test.reasoning {
reasoning = "true"
}
catalog := strings.Replace(validModelCatalogYAML(), " contextWindow: 32768",
" reasoning: "+reasoning+"\n compatibility:\n thinkingFormat: "+test.format+"\n contextWindow: 32768", 1)
rewriteInstallationCatalog(t, path, catalog)
_, err := Load(path)
if test.wantError {
if err == nil || !strings.Contains(err.Error(), "thinkingFormat") {
t.Fatalf("Load() error = %v, want thinkingFormat error", err)
}
} else if err != nil {
t.Fatalf("Load() error = %v", err)
}
})
}
}
func TestLoadAcceptsSharedDeepSeekBuiltinWithBundleAuthentication(t *testing.T) {
path, _, envFile, _ := writeInstallation(t, "local")
catalog := strings.Replace(validModelCatalogYAML(), "interaction: zai/glm-5.3", "interaction: deepseek/deepseek-v4-pro", 1)
@@ -90,6 +90,7 @@ type ModelCompatibility struct {
SupportsReasoningEffort bool `yaml:"supportsReasoningEffort" json:"supportsReasoningEffort"`
SupportsStore bool `yaml:"supportsStore" json:"supportsStore"`
MaxTokensField string `yaml:"maxTokensField" json:"maxTokensField,omitempty"`
ThinkingFormat string `yaml:"thinkingFormat,omitempty" json:"thinkingFormat,omitempty"`
}
type SessionModel struct {
@@ -270,6 +271,15 @@ func validateCatalogProvider(id string, provider ModelProvider, hasSession, hasM
if model.Session != nil && (model.Session.ContextWindow <= 0 || model.Session.MaxTokens <= 0) {
return fmt.Errorf("modelCatalog model %q/%q requires contextWindow and maxTokens", id, modelID)
}
if model.Session != nil && model.Session.Compatibility != nil {
format := model.Session.Compatibility.ThinkingFormat
if format != "" && format != "qwen" && format != "qwen-chat-template" {
return fmt.Errorf("modelCatalog model %q/%q compatibility.thinkingFormat is invalid", id, modelID)
}
if format != "" && !model.Session.Reasoning {
return fmt.Errorf("modelCatalog model %q/%q compatibility.thinkingFormat requires reasoning: true", id, modelID)
}
}
}
}
} else if provider.Session != nil {
@@ -97,6 +97,30 @@ func TestDeepSeekBuiltinAndMetadataShareCatalogIdentityAndSecretReference(t *tes
}
}
func TestQwenThinkingFormatSurvivesCatalogAndPiProjection(t *testing.T) {
i := projectionFixture(t)
model := i.ModelCatalog.Providers["local"].Models["qwen"]
if err := json.Unmarshal([]byte(`{"reasoning":true,"contextWindow":32768,"maxTokens":8192,"compatibility":{"supportsDeveloperRole":false,"supportsReasoningEffort":false,"supportsStore":false,"maxTokensField":"max_tokens","thinkingFormat":"qwen-chat-template"}}`), model.Session); err != nil {
t.Fatal(err)
}
files, err := Render(i)
if err != nil {
t.Fatal(err)
}
for _, path := range []string{CatalogFile, PiModelsFile} {
if !bytes.Contains(files[path], []byte(`"thinkingFormat": "qwen-chat-template"`)) {
t.Fatalf("%s dropped Qwen thinking compatibility", path)
}
}
var models piModels
if err := json.Unmarshal(files[PiModelsFile], &models); err != nil {
t.Fatal(err)
}
if !models.Providers["local"].Models[0].Reasoning {
t.Fatal("Pi must know the model supports explicit thinking control")
}
}
func TestProjectionKeepsSingleUseInventoryOutOfSharedPiSelection(t *testing.T) {
i := projectionFixture(t)
p := i.ModelCatalog.Providers["zai"]