feat: add qwen3.7-plus to Bailian Coding catalog

Closes #169
This commit is contained in:
Duy /zuey/ authored and GitHub committed 2026-06-12 17:57:48 +07:00
1 parent 85fe1b8e81
commit 4f87b93dbd
16 files changed
+451

No files matched your search

+11
View File
@@ -91,6 +91,17 @@ ui/desktop/ Wails v2 desktop app (React frontend + embedded ga
- **Telegram formatting:** LLM output → `SanitizeAssistantContent()` → `markdownToTelegramHTML()` → `chunkHTML()` → `sendHTML()`. Tables rendered as ASCII in `<pre>` tags
- **i18n:** Web UI uses `i18next` with namespace-split locale files in `ui/web/src/i18n/locales/{lang}/`. Backend uses `internal/i18n` message catalog with `i18n.T(locale, key, args...)`. Locale propagated via `store.WithLocale(ctx)` — WS `connect` param `locale`, HTTP `Accept-Language` header. Supported: en (default), vi, zh. New user-facing strings: add key to `internal/i18n/keys.go`, add translations to all 3 catalog files. New UI strings: add key to all 3 locale dirs. Bootstrap templates (SOUL.md, etc.) stay English-only (LLM consumption).
## Cross-Surface Feature/Fix Parity
Every feature implementation or bug fix must audit and update all affected product surfaces before it is marked complete:
- **Gateway server:** handlers, WebSocket methods, stores, migrations, provider/runtime behavior, background jobs.
- **API contract:** request/response structs, `pkg/protocol`, OpenAPI/docs, compatibility shims, tests built from real response shapes.
- **Web UI:** `ui/web` screens, hooks, i18n, validation, loading/error states, and contract consumers.
- **CLI/runtime package:** `cmd` commands, operator/runtime package commands, installers/manifests, response parsers, and runtime package docs.
Do not ship a backend-only change when the web UI, CLI/runtime package, or API contract must also change. If a surface is not affected, state `Surface parity: <surface> N/A because ...` in the plan, PR, or final report. For cross-repo CLI work, verify the current CLI/runtime package repo and release channel before claiming parity.
## Running
```bash
+11
View File
@@ -91,6 +91,17 @@ ui/desktop/ Wails v2 desktop app (React frontend + embedded ga
- **Telegram formatting:** LLM output → `SanitizeAssistantContent()` → `markdownToTelegramHTML()` → `chunkHTML()` → `sendHTML()`. Tables rendered as ASCII in `<pre>` tags
- **i18n:** Web UI uses `i18next` with namespace-split locale files in `ui/web/src/i18n/locales/{lang}/`. Backend uses `internal/i18n` message catalog with `i18n.T(locale, key, args...)`. Locale propagated via `store.WithLocale(ctx)` — WS `connect` param `locale`, HTTP `Accept-Language` header. Supported: en (default), vi, zh. New user-facing strings: add key to `internal/i18n/keys.go`, add translations to all 3 catalog files. New UI strings: add key to all 3 locale dirs. Bootstrap templates (SOUL.md, etc.) stay English-only (LLM consumption).
## Cross-Surface Feature/Fix Parity
Every feature implementation or bug fix must audit and update all affected product surfaces before it is marked complete:
- **Gateway server:** handlers, WebSocket methods, stores, migrations, provider/runtime behavior, background jobs.
- **API contract:** request/response structs, `pkg/protocol`, OpenAPI/docs, compatibility shims, tests built from real response shapes.
- **Web UI:** `ui/web` screens, hooks, i18n, validation, loading/error states, and contract consumers.
- **CLI/runtime package:** `cmd` commands, operator/runtime package commands, installers/manifests, response parsers, and runtime package docs.
Do not ship a backend-only change when the web UI, CLI/runtime package, or API contract must also change. If a surface is not affected, state `Surface parity: <surface> N/A because ...` in the plan, PR, or final report. For cross-repo CLI work, verify the current CLI/runtime package repo and release channel before claiming parity.
## Running
```bash
+5
View File
@@ -351,6 +351,11 @@ Standard OpenAI-compatible provider targeting the Alibaba Coding API.
- **Default model**: `qwen3.5-plus`
- **Base URL**: `https://coding-intl.dashscope.aliyuncs.com/v1`
- **Catalog source**: hardcoded because the Coding API does not expose a standard `/v1/models` endpoint
| Model | Display name | Capabilities |
|-------|--------------|--------------|
| `qwen3.7-plus` | Qwen 3.7 Plus | Text Generation, Deep Thinking, Visual Understanding |
---
+6
View File
@@ -139,6 +139,12 @@ Enables thinking via `enable_thinking: true` plus a `thinking_budget` parameter.
Other models (e.g., `qwen3-plus`, `qwen3-turbo`) silently skip thinking injection to avoid API errors.
**Bailian Coding note**: Bailian is a separate OpenAI-compatible Coding
endpoint. Its model catalog includes `qwen3.7-plus` with Deep Thinking and
Visual Understanding listed for selection, but this DashScope `enable_thinking`
/ `thinking_budget` injection path does not apply to Bailian unless the Coding
endpoint's explicit request controls are verified separately.
**Important limitation**: DashScope does not support streaming when tools are present. When an agent has tools enabled and thinking is active, the provider automatically falls back to non-streaming mode (single `Chat()` call) and synthesizes chunk callbacks to maintain the event flow.
### Codex (ChatGPT OAuth Responses API)
+15
View File
@@ -6,6 +6,21 @@ Significant changes, features, and fixes in reverse chronological order.
## 2026-06-12
### Bailian Coding qwen3.7-plus catalog (issue #169)
**Changes**
- Added `qwen3.7-plus` / `Qwen 3.7 Plus` to the hardcoded Bailian
Coding provider model catalog.
- Documented the model's advertised Text Generation, Deep Thinking, and Visual
Understanding capabilities while keeping Bailian on the existing
OpenAI-compatible provider wrapper.
**Tests**
- Added provider model endpoint coverage to verify Bailian exposes
`qwen3.7-plus` without adding unsupported OpenAI reasoning metadata.
### Multi-attachment outbound delivery (issue #172)
**Changes**
+2
View File
@@ -7,6 +7,8 @@ import "github.com/nextlevelbuilder/goclaw/internal/providers"
// The platform does not expose a /v1/models endpoint.
func bailianModels() []ModelInfo {
return []ModelInfo{
// qwen3.7-plus: Text Generation + Deep Thinking + Visual Understanding.
{ID: "qwen3.7-plus", Name: "Qwen 3.7 Plus"},
{ID: "qwen3.6-plus", Name: "Qwen 3.6 Plus"},
{ID: "qwen3.5-plus", Name: "Qwen 3.5 Plus"},
{ID: "kimi-k2.5", Name: "Kimi K2.5"},
+48
View File
@@ -116,6 +116,54 @@ func TestProvidersHandlerListProviderModelsChatGPTOAuthIncludesReasoningMetadata
}
}
func TestProvidersHandlerListProviderModelsBailianIncludesQwen37Plus(t *testing.T) {
token := setupProvidersAdminToken(t)
providerStore := newMockProviderStore()
provider := &store.LLMProviderData{
BaseModel: store.BaseModel{ID: uuid.New()},
Name: "bailian-coding",
ProviderType: store.ProviderBailian,
APIKey: "token",
Enabled: true,
}
if err := providerStore.CreateProvider(t.Context(), provider); err != nil {
t.Fatalf("CreateProvider() error = %v", err)
}
handler := NewProvidersHandler(providerStore, newMockSecretsStore(), nil, "")
mux := http.NewServeMux()
handler.RegisterRoutes(mux)
req := httptest.NewRequest(http.MethodGet, "/v1/providers/"+provider.ID.String()+"/models", nil)
req.Header.Set("Authorization", "Bearer "+token)
w := httptest.NewRecorder()
mux.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Fatalf("status code = %d, want %d, body=%s", w.Code, http.StatusOK, w.Body.String())
}
var result ProviderModelsResponse
if err := json.NewDecoder(w.Body).Decode(&result); err != nil {
t.Fatalf("Decode() error = %v", err)
}
for _, model := range result.Models {
if model.ID != "qwen3.7-plus" {
continue
}
if model.Name != "Qwen 3.7 Plus" {
t.Fatalf("model name = %q, want Qwen 3.7 Plus", model.Name)
}
if model.Reasoning != nil {
t.Fatalf("model reasoning = %#v, want nil for Bailian OpenAI-compatible catalog entry", model.Reasoning)
}
return
}
t.Fatalf("qwen3.7-plus not found in Bailian model list: %#v", result.Models)
}
func TestProvidersHandlerListProviderModelsOpenAICompatAnnotatesKnownModels(t *testing.T) {
token := setupProvidersAdminToken(t)
upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -0,0 +1,56 @@
---
phase: 1
title: Catalog and capability docs
status: completed
priority: P2
effort: 30m
dependencies: []
---
# Phase 1: Catalog and capability docs
## Overview
Add the new Bailian model to the backend catalog and document its advertised capabilities without changing runtime request-body semantics.
## Requirements
- Functional: `bailianModels()` returns `{ID: "qwen3.7-plus", Name: "Qwen 3.7 Plus"}`.
- Functional: docs state the model capabilities from issue #169: Text Generation, Deep Thinking, Visual Understanding.
- Non-functional: no new API schema fields unless already supported by `ModelInfo`.
- Non-functional: no runtime behavior change for existing Bailian models.
## Architecture
`GET /v1/providers/{id}/models` loads a provider from the store, dispatches `provider_type == "bailian"` to `bailianModels()`, and returns the list. Web and desktop provider model pickers already consume this endpoint, so the backend catalog update is the UI propagation path.
Do not add `qwen3.7-plus` to `internal/providers/reasoning_capability.go`: `OpenAIProvider.buildRequestBody()` uses that registry to decide whether to send OpenAI `reasoning_effort`. Bailian is registered as `NewOpenAIProvider(...)`, not `NewDashScopeProvider(...)`, so Qwen Deep Thinking docs should not imply a wire-level parameter implementation here.
## Related Code Files
- Modify: `internal/http/provider_models_catalog.go`
- Modify: `docs/02-providers.md`
- Modify: `docs/12-extended-thinking.md`
- No UI changes: web and desktop use the existing provider models endpoint.
## Implementation Steps
1. Add `qwen3.7-plus` near the current Qwen Plus entries in `bailianModels()`.
2. Add concise inline catalog grouping that records Bailian capabilities for Qwen Plus models.
3. Update `docs/02-providers.md` Bailian section with the new model and capability row.
4. Update `docs/12-extended-thinking.md` to clarify Bailian advertises Deep Thinking for `qwen3.7-plus`, but GoClaw does not inject DashScope `enable_thinking` controls for Bailian in this change.
## Success Criteria
- [ ] Catalog contains `qwen3.7-plus`.
- [ ] Docs mention Text Generation, Deep Thinking, Visual Understanding.
- [ ] No new provider request-body controls are introduced.
## Risk Assessment
Risk: adding reasoning metadata could make Bailian receive unsupported OpenAI `reasoning_effort`.
Mitigation: leave `LookupReasoningCapability()` unchanged and document the distinction.
## Security Considerations
No new secrets, auth paths, or tenant writes.
@@ -0,0 +1,47 @@
---
phase: 2
title: Tests and verification
status: completed
priority: P2
effort: 30m
dependencies:
- 1
---
# Phase 2: Tests and verification
## Overview
Add regression coverage for Bailian model discovery and run focused validation for the touched backend/docs paths.
## Requirements
- Functional: endpoint-level test proves Bailian returns `qwen3.7-plus`.
- Functional: test verifies no unsupported reasoning metadata is exposed for this OpenAI-compatible Bailian model.
- Non-functional: targeted Go package tests pass with the installed Go binary.
## Implementation Steps
1. Add a provider models test in `internal/http/provider_models_test.go` for `store.ProviderBailian`.
2. Use existing mock provider store and auth token helpers.
3. Assert HTTP 200, model ID/display name, and nil `Reasoning` for `qwen3.7-plus`.
4. Run focused test:
- `PATH=/usr/local/go/bin:$PATH go test ./internal/http -run 'Bailian|ProviderModels' -count=1`
5. Run compile checks when feasible:
- `PATH=/usr/local/go/bin:$PATH go test ./internal/http -count=1`
- broader build/test if the focused package uncovers shared changes.
## Success Criteria
- [ ] Focused test fails before catalog change and passes after.
- [ ] `go test ./internal/http` passes.
- [ ] No generated or unrelated files changed.
## Risk Assessment
Risk: test helper requires an API key for Bailian before returning catalog.
Mitigation: set a dummy API key on the mock provider because the handler checks non-empty `APIKey` before dispatch.
## Security Considerations
Use dummy values only; do not read or write env/secrets.
@@ -0,0 +1,46 @@
---
phase: 3
title: "Ship beta PR"
status: pending
priority: P2
effort: "30m"
dependencies: [2]
---
# Phase 3: Ship beta PR
## Overview
Ship the completed issue #169 work as a beta-targeted PR to `dev`, with labels updated according to `ck:vibe --beta`.
## Requirements
- Functional: GitHub issue gets a plan/update comment and `ready to cook` before implementation.
- Functional: PR targets `dev` and references issue #169.
- Functional: after local and PR review gates pass, source issue and PR get `ready to ship beta`.
- Non-functional: no merge is performed because the user invoked `--beta`, not `--ship --beta`.
## Implementation Steps
1. Ensure GitHub labels exist: `ready to cook`, `ready to ship stable`, `ready to ship beta`.
2. Comment on issue #169 with branch, relative plan path, beta mode, and acceptance criteria.
3. After implementation/tests/review, commit with a focused conventional message.
4. Push `codex/issue-169-qwen37-bailian-model`.
5. Create beta PR against `dev`.
6. Review/fix/reply PR feedback and wait for terminal checks when available.
7. Add `ready to ship beta` to both issue and PR; remove `ready to cook`.
## Success Criteria
- [ ] PR exists and targets `dev`.
- [ ] PR/issue labels reflect ready-to-ship-beta state after review.
- [ ] Merge is skipped.
## Risk Assessment
Risk: GitHub auth lacks label/PR permissions.
Mitigation: stop with exact `gh` error if label or PR creation fails.
## Security Considerations
Do not include secrets or private env output in GitHub comments/PR body.
@@ -0,0 +1,50 @@
---
title: Add qwen3.7-plus to Bailian Coding provider
description: ''
status: pending
priority: P2
issue: 169
branch: codex/issue-169-qwen37-bailian-model
tags: []
blockedBy: []
blocks: []
created: '2026-06-12T10:09:09.353Z'
createdBy: 'ck:plan'
source: skill
---
# Add qwen3.7-plus to Bailian Coding provider
## Overview
Add `qwen3.7-plus` to GoClaw's hardcoded Bailian Coding model catalog so the shared `/v1/providers/{id}/models` endpoint exposes it to both web and desktop model pickers. Keep runtime request behavior backward-compatible: Bailian remains an OpenAI-compatible provider, and no GPT/Codex `reasoning_effort` metadata is added for this Qwen model.
## Phases
| Phase | Name | Status |
|-------|------|--------|
| 1 | [Catalog and capability docs](./phase-01-catalog-and-capability-docs.md) | Completed |
| 2 | [Tests and verification](./phase-02-tests-and-verification.md) | Completed |
| 3 | [Ship beta PR](./phase-03-ship-beta-pr.md) | Pending |
## Dependencies
- Source issue: <https://github.com/digitopvn/goclaw/issues/169>
- Verified code paths:
- `internal/http/provider_models_catalog.go`: `bailianModels()` hardcoded catalog.
- `internal/http/provider_models.go`: Bailian dispatch uses `bailianModels()` then `withReasoningCapabilities()`.
- `internal/http/providers.go`: Bailian runtime registration uses `providers.NewOpenAIProvider(...)`.
- `internal/providers/openai_request.go`: `reasoning_effort` is gated by `LookupReasoningCapability()`, so do not add `qwen3.7-plus` there.
## Acceptance Criteria
- [x] `qwen3.7-plus` appears in the Bailian Coding model catalog.
- [x] Display name is `Qwen 3.7 Plus`.
- [x] Capabilities are documented as Text Generation, Deep Thinking, Visual Understanding.
- [x] Web/Desktop provider model pickers inherit the model through the existing `/v1/providers/{id}/models` API.
- [x] Existing Bailian provider registration remains backward-compatible.
- [x] Tests cover the Bailian model list.
## Unresolved Questions
- Does Bailian expose explicit request parameters for Deep Thinking on `qwen3.7-plus`? Out of scope for this catalog update unless verified separately.
@@ -0,0 +1,27 @@
## Outcome
Add `qwen3.7-plus` to the Bailian Coding provider model catalog so the shared provider model API exposes it to web and desktop model pickers.
## Implementation
- Branch: `codex/issue-169-qwen37-bailian-model`
- Plan: `plans/260612-1709-qwen37-bailian-model/plan.md`
- Mode: `beta`
- PR: pending
## Acceptance Criteria
- [ ] `qwen3.7-plus` appears in Bailian Coding model catalog
- [ ] Display name is `Qwen 3.7 Plus`
- [ ] Capabilities are documented/represented as Text Generation, Deep Thinking, Visual Understanding
- [ ] Web provider model picker can select `qwen3.7-plus` through `/v1/providers/{id}/models`
- [ ] Desktop provider model picker can select `qwen3.7-plus` through the same backend model API
- [ ] Existing Bailian provider registration remains backward-compatible
- [ ] Tests cover the Bailian model catalog
- [ ] Docs updated where provider model support is documented
## Pipeline State
- [x] Worktree and branch created
- [x] TDD plan created
- [x] Plan validated
- [x] Plan red-teamed
- [ ] Cook complete
- [ ] PR reviewed and fixed
- [ ] Merged and CI green (only when --ship)
@@ -0,0 +1,69 @@
## Code Review Summary
### Scope
- Files: `internal/http/provider_models_catalog.go`, `internal/http/provider_models_test.go`, `docs/02-providers.md`, `docs/12-extended-thinking.md`, `docs/project-changelog.md`
- LOC: +76 / -0 pending diff
- Focus: pending changes only, issue #169 Bailian Coding `qwen3.7-plus` catalog
- Scout findings: `/v1/providers/{id}/models` dispatches Bailian to `bailianModels()`; web and desktop picker paths consume this endpoint; Bailian runtime registration remains `providers.NewOpenAIProvider(...)`; OpenAI `reasoning_effort` remains gated by GPT/Codex reasoning metadata and is not added for `qwen3.7-plus`.
### Overall Assessment
Implementation matches acceptance criteria. No blocking production-readiness issues found.
### Critical Issues
None.
### High Priority
None.
### Medium Priority
None.
### Low Priority
None.
### Edge Cases Found by Scout
- Verified Bailian catalog path requires API key before returning hardcoded models; test covers the current contract with a dummy key.
- Verified UI propagation does not require hardcoded web/desktop model list updates because pickers call `/v1/providers/{id}/models`.
- Verified `qwen3.7-plus` is not in `LookupReasoningCapability()`, so selecting it will not trigger unsupported OpenAI `reasoning_effort`.
### Positive Observations
- Test covers endpoint-level behavior, display name, and nil reasoning metadata.
- Docs correctly distinguish advertised Bailian Deep Thinking capability from DashScope `enable_thinking` / `thinking_budget` request injection.
- Existing Bailian provider default model and registration path are unchanged.
### Recommended Actions
1. No code changes required.
2. Plan can mark Phase 2 complete; Phase 3 remains pending.
### Verification
- `PATH=/usr/local/go/bin:$PATH go test ./internal/http -run 'TestProvidersHandlerListProviderModels(BailianIncludesQwen37Plus|ChatGPTOAuthIncludesReasoningMetadata|OpenAICompatAnnotatesKnownModels)'` passed.
- `PATH=/usr/local/go/bin:$PATH go test ./internal/providers -run 'TestLookupReasoningCapability|TestDashScopeModelSupportsThinking|TestDashScopeThinking'` passed.
- `PATH=/usr/local/go/bin:$PATH go test ./internal/http` passed.
- `PATH=/usr/local/go/bin:$PATH go test ./internal/providers` passed.
- `git diff --check -- internal/http/provider_models_catalog.go internal/http/provider_models_test.go docs/02-providers.md docs/12-extended-thinking.md docs/project-changelog.md` passed.
### Metrics
- Type Coverage: N/A for Go; package compile covered by `go test`
- Test Coverage: coverage percentage not collected
- Linting Issues: 0 from `git diff --check`; full lint/vet not run
### Plan Status
- Phase 1: complete by diff evidence.
- Phase 2: appears complete; focused and package tests passed.
- Phase 3: pending, not reviewed as implementation.
### Unresolved Questions
None for this catalog implementation. Bailian-specific Deep Thinking wire controls remain explicitly out of scope.
@@ -0,0 +1,19 @@
# Scout Report
## Findings
- Repo verified: `digitopvn/goclaw`, default branch `dev`.
- Source issue verified: #169 requests `qwen3.7-plus` for Bailian Coding.
- Catalog source: `internal/http/provider_models_catalog.go` -> `bailianModels()`.
- Endpoint dispatch: `internal/http/provider_models.go` uses `bailianModels()` for `provider_type == "bailian"`.
- UI propagation: web and desktop model pickers call `/v1/providers/{id}/models`; no hardcoded Bailian model list found in UI.
- Runtime boundary: `internal/http/providers.go` registers Bailian through `providers.NewOpenAIProvider(...)`.
- Wire guardrail: `internal/providers/openai_request.go` sends OpenAI `reasoning_effort` only when `LookupReasoningCapability()` returns metadata or model is GPT/o-series.
## Decision
Implement catalog entry, endpoint regression test, and docs. Do not add `qwen3.7-plus` to the GPT/Codex reasoning capability registry in this task.
## Unresolved Questions
- Does Bailian expose explicit thinking request parameters for `qwen3.7-plus`? Not needed for the catalog issue.
@@ -0,0 +1,19 @@
# Tester Report - issue #169 Bailian qwen3.7-plus catalog
## Scope
- Verified the Bailian Coding model catalog addition in `internal/http/provider_models_catalog.go`.
- Verified handler coverage in `internal/http/provider_models_test.go`.
- Checked docs updates in `docs/02-providers.md`, `docs/12-extended-thinking.md`, and `docs/project-changelog.md`.
## Results
- `PATH=/usr/local/go/bin:$PATH go test ./internal/http -run TestProvidersHandlerListProviderModelsBailianIncludesQwen37Plus -count=1`
- PASS
- `git diff --check`
- PASS
## Notes
- `GET /v1/providers/{id}/models` is the contract used by the web and desktop pickers; no UI hardcoded model list change was needed.
- Bailian registration remains on the existing OpenAI-compatible provider path with the default model still set to `qwen3.5-plus`.
## Coverage gap
- Catalog is covered by one focused regression only; no additional provider-level or UI-level test was added in this patch.
@@ -0,0 +1,20 @@
# Validation And Red-Team Report
## Validation
- Acceptance criteria are mapped to concrete files and tests.
- Web/Desktop selection is covered through the shared backend model endpoint, not separate UI lists.
- Backward compatibility is explicit: Bailian provider registration remains unchanged.
## Red-Team Findings
- Finding: Adding `qwen3.7-plus` to `LookupReasoningCapability()` would expose advanced reasoning controls and may make Bailian send OpenAI `reasoning_effort`.
- Verdict: Reject for this issue scope. Keep runtime controls unchanged.
- Finding: Vision capability is not a `ModelInfo` field today.
- Verdict: Document capability only; avoid schema expansion for a single catalog entry.
- Finding: Handler rejects providers with empty API keys before model dispatch.
- Verdict: Test must create a Bailian provider with a dummy API key.
## Unresolved Questions
- None blocking implementation.