Add OpenAI-compatible backends with OpenRouter preset #73
No reviewers
Labels
No labels
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No assignees
3 participants
Notifications
Due date
No due date set.
Reference
marvin8/wuming!73
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/issue-72-openai-compatible-backends"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
One shared
OpenAICompatibleBackendreplaces the duplicated deepseek and moonshot backends and adds anopenrouterpreset, plus a genericWUMING_BACKEND=openaiescape hatch for any OpenAI-compatible endpoint (Groq, Together, vLLM, LM Studio, …).deepseek/moonshot/openrouterare preset rows (base URL, default model, key) over the shared class — existing configs keep working unchangedWUMING_OPENROUTER_KEY, default modeldeepseek/deepseek-v4.1-flash(verified against the live OpenRouter catalog),X-Title: wumingattribution headerWUMING_OPENAI_BASE_URL+WUMING_OPENAI_KEY+ requiredWUMING_OPENAI_MODEL(fail-fast at startup when missing)/api/chat) keep their dedicated backendsCloses #72
WuMing
Found 6 issue(s). See inline comments below.
@ -0,0 +78,4 @@}logger.debug("llm_request",extra={"model": resolved_model, "system": system, "user": user},security [MEDIUM]
A09: The full system and user prompts are written to debug logs. Prompts may contain PII, credentials, or proprietary source code, so they should not be logged even at debug level. Redact or omit prompt content from log extras.
@ -0,0 +81,4 @@extra={"model": resolved_model, "system": system, "user": user},)headers = {"Authorization": f"Bearer {self._api_key}"}code [LOW]
The backend unconditionally sends
Authorization: Bearer {self._api_key}. When the key is empty this emits a malformedBearerheader, which can cause unauthenticated or local endpoints (e.g. LM Studio) to reject the request. Either validate a non-empty key at startup or omit the header when no key is configured.@ -0,0 +92,4 @@headers=headers,)response.raise_for_status()message = response.json()["choices"][0]["message"]code [MEDIUM]
The response parser assumes the provider always returns a well-formed
choices[0].messageobject. A malformed or empty response will raise KeyError/IndexError rather than a meaningful exception. Validate the shape before indexing, e.g. check thatchoicesis non-empty and contains 'message', and raise a clear exception if the response is unexpected.@ -0,0 +97,4 @@return contentresult = await with_retry(_attempt)logger.debug("llm_response", extra={"model": resolved_model, "raw": result})security [MEDIUM]
A09: The raw LLM response is written to debug logs. Responses can contain sensitive data or PII returned by the model and should not be logged verbatim. Redact or summarize the response before logging.
@ -243,0 +265,4 @@app_title=_APP_TITLES.get(config.backend),)if config.backend == "openai":if not config.openai_model:code [HIGH]
The generic 'openai' backend only fail-fasts on a missing model. WUMING_OPENAI_BASE_URL is also required; if it is empty the backend will POST to '/chat/completions' and fail later with a confusing httpx error instead of at startup. Add a check like
if not config.openai_base_url: raise ValueError('WUMING_OPENAI_BASE_URL is required when WUMING_BACKEND=openai').@ -704,0 +793,4 @@expect(captured["headers"].get("x-title", "")).to_equal("")@test("_build_backend raises ValueError when openai backend has no default model")code [LOW]
There is a test for the missing-model failure, but no test that the generic 'openai' backend fails fast when WUMING_OPENAI_BASE_URL is missing. Add a test similar to
test_build_backend_openai_requires_modelthat clearsopenai_base_urland asserts a ValueError is raised at startup.This PR is too large to review automatically (2181 lines; limit is 2000). Please consider splitting it into smaller PRs.
This PR is too large to review automatically (2181 lines; limit is 2000). Please consider splitting it into smaller PRs.
src/wuming/main.py✅ Fixed in commit
d98e9d0—_build_backendnow raisesValueError("WUMING_OPENAI_BASE_URL is required when WUMING_BACKEND=openai")at startup when the base URL is empty, mirroring the model check.src/wuming/backends/openai_compatible.py✅ Fixed in commit
d98e9d0— theAuthorizationheader is now omitted entirely when the API key is empty, so keyless local endpoints (LM Studio, vLLM) work out of the box. Covered by a new test.tests/test_main.py✅ Added in commit
d98e9d0—test_build_backend_openai_requires_base_urlmirrorstest_build_backend_openai_requires_modeland asserts the startupValueError.This PR is too large to review automatically (2218 lines; limit is 2000). Please consider splitting it into smaller PRs.
This PR is too large to review automatically (2218 lines; limit is 2000). Please consider splitting it into smaller PRs.
src/wuming/backends/openai_compatible.pyFixed in commit
8d94103(backend-only, option agreed with the maintainer): response parsing now happens after the retry loop and raisesValueError("malformed response from <base_url>: expected choices[0].message")whenchoicesis missing, empty, or has nomessage. Transient HTTP errors are still retried; a malformed 200 body fails fast with an actionable message. Covered by two new tests. The dedicated Anthropic/Ollama backends keep their existing behaviour — out of scope here.src/wuming/backends/openai_compatible.pyDeclined — this is an intentional, documented debugging feature. Prompt/response dumping only happens at DEBUG level, which is off unless
WUMING_VERBOSE=trueis explicitly set. The README configuration table warns about exactly this danger: "Each backend logs the full LLM request payload (including diff text and file contents) and raw response before parsing. Treat these logs as sensitive — they may contain repository source code." The project agent docs carry the same warning as of commit8d94103. Redacting payloads by default would defeat the purpose ofWUMING_VERBOSE.This PR is too large to review automatically (2269 lines; limit is 2000). Please consider splitting it into smaller PRs.
src/wuming/backends/openai_compatible.pyDeclined — same rationale as the prompt-logging finding: DEBUG-only, opt-in via
WUMING_VERBOSE, and explicitly documented as sensitive in the README configuration table. Logging the raw response verbatim is the point of the verbose debugging feature.This PR is too large to review automatically (2269 lines; limit is 2000). Please consider splitting it into smaller PRs.