Skip to content

feat(alibaba): add token-plan routing and cloud discovery - #105

Merged
elkaix merged 12 commits into
mainfrom
feat/alibaba-token-plan-and-cloud
Jun 10, 2026
Merged

feat(alibaba): add token-plan routing and cloud discovery#105
elkaix merged 12 commits into
mainfrom
feat/alibaba-token-plan-and-cloud

Conversation

@elkaix

@elkaix elkaix commented Jun 10, 2026

Copy link
Copy Markdown
Member

Related Issue

Resolve #(issue_number)

Description

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked the related issue, if any.
  • I have added tests that prove my fix is effective or that my feature works.
  • I have run make gen-changelog to update the changelog.
  • I have run make gen-docs to update the user documentation.

Summary by CodeRabbit

  • New Features

    • Token Plan subscription support for Alibaba login with automatic endpoint routing.
    • Broader model discovery: includes all returned chat models and infers capabilities/context sizes.
  • Bug Fixes

    • Fixed subscription key routing to the correct Token Plan endpoint.
    • Quota display now falls back to a local token tally when remote quota APIs are unavailable.
  • Updates

    • Default Alibaba model changed to qwen3.7-plus.
    • Changelog updated to document Alibaba login changes.
  • Tests

    • Updated and added tests covering Token Plan routing and model discovery.

elkaix added 7 commits June 10, 2026 11:05
…ersion

- Auto-populate allowed origins in local mode too: with token auth on,
  the origin check is enforced, and the previously empty allowlist
  rejected every request carrying an Origin header — breaking all
  session-stream WebSocket handshakes with 403/1006. REST GETs worked
  only because browsers omit Origin on same-origin GET fetches.
- Standardize the web/vis startup banners on a shared PYTHINKER
  wordmark in utils/server.py, replacing the legacy upstream art.
- Serve the installed CLI version at runtime via /api/config
  (GlobalConfig.version); the web UI header now prefers it over the
  Vite build-time constant, which goes stale when the CLI is upgraded
  without a frontend rebuild. Rebuilt the bundled static assets.
- Add regression tests for local-mode origin population and the
  empty-allowlist reject-all semantics.
… agent glyphs

Selectively adopted from an earlier TUI iteration, re-based onto the
current design system:

- report.py: detect top-level "Label: body" lines in report-like
  assistant prose and render them as structured sections, with
  conservative guards so ordinary paragraphs stay plain Markdown.
- markdown.py: wide multi-column report tables render as stacked
  records so long paths and prose wrap in one generous value column
  instead of being sliced mid-word across narrow grid cells; compact
  tables keep the bordered grid.
- todo: normalize LLM-supplied status aliases (complete/completed/
  finished -> done, canceled -> cancelled) via a before-validator.
- agent renderer: status glyphs (check/cross/dot) and
  type-first row layout for subagent activity.

Deliberately NOT adopted (superseded by the current standardized
design): the question-marker and markdown-palette recolors, and the
space-separated tool header format with column-grid wrapping, which
conflicts with the pinned parenthesized header style.
GHSA-866g-f22w-33x8 (uncontrolled resource consumption) affects
@ai-sdk/provider-utils <=3.0.97, which every ai@5.x release pins; the
patched 4.x line ships only with ai@6. The web UI imports the ai
package exclusively for types (ChatStatus, FileUIPart, ToolUIPart,
LanguageModelUsage), so the major bump is type-level only — tsc and
biome pass unchanged.

The remaining elliptic advisory (GHSA-848j-6mx2-7j84, low) has no
patched release in any version; it enters via vite-plugin-node-polyfills
-> crypto-browserify at build time only, and the bundle never includes
it because only the path and url polyfills are enabled.
…strap

Three field-found fixes:

- memory/recall: frame the recalled-memory block as background context
  from past sessions, not an instruction. Without the guard the model
  could treat a recalled note or stale todo as the current request
  (e.g. answering a plain "ping" by resuming an old code-review task).
  Open todos are now labelled reference-only.

- background tasks: track tasks spawned during the current interactive
  turn and kill exactly those on ESC. Previously a background subagent
  launched mid-turn survived the interrupt, finished later, and
  re-delivered the abandoned task via its completion notification.
  Earlier turns' tasks are deliberately left running.

- web auth: consume the URL token before React mounts instead of in a
  component effect. Mount-time data fetches fired first and sent a
  stale localStorage token from a previous server run, yielding 401s
  on first load.
- web config API: log get_version() failures instead of swallowing them,
  so an operator can see when the version banner falls back to empty
- usePythinkerVersion: reset the shared promise and log on a failed/empty
  fetch so a transient error no longer permanently disables the backend
  version banner for the session
- test_web_origins: rename unused *args to *_args to signal intent
- CHANGELOG: add the missing ## Unreleased entry for this PR's web fixes
  (unblocks the required changelog-entry-required check)
- AGENTS.md: document the changelog-entry-before-PR requirement as a gotcha
  to stop this check repeatedly blocking PRs
Plan keys (sk-sp-/sk-tok-/sk-ws-) now route to the shared international Token
Plan endpoint instead of coding-intl, fixing a 401 invalid_api_key on every chat.
Generic sk- keys remain pay-as-you-go Cloud (DashScope) with US/China auto-detect.

Model discovery accepts every chat model the live /models returns (excluding
image/audio/embed/rerank/etc), infers capabilities + context window from the id,
and defaults first login to qwen3.7-plus. Removed the dead Coding-Plan endpoint
branches and constant.

Usage: the Token Plan exposes no quota API and its policy forbids automated
balance polling, so /usage on Token Plan/Coding endpoints shows Pythinker's local
token tally plus a console pointer (My Subscriptions / Usage Analysis) instead of
probing a 404 endpoint or scraping the billing console.

CI: split the docs Pages workflow into build + deploy jobs (one artifact per run)
to fix the 'multiple artifacts named github-pages' deploy failure.

Verified live: Token Plan and Cloud endpoints both return /models 200 and a real
chat completion.
Comment thread src/pythinker_code/ui/shell/usage_adapters/alibaba.py Fixed
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 931e9ab5-4dcb-4bf0-a561-fc9e50f482ad

📥 Commits

Reviewing files that changed from the base of the PR and between c85c88b and ff0ba5b.

📒 Files selected for processing (1)
  • tests/auth/test_alibaba_auth.py

📝 Walkthrough

Walkthrough

Refactors Alibaba provider auth: adds a shared Token Plan base URL and default model/context, simplifies base URL normalization, adds regex-based model discovery and capability inference, routes subscription plan keys to the Token Plan endpoint, probes China endpoint on 401/403, and skips quota API calls for certain DashScope hosts; tests and CI/gitignore/changelog updated.

Changes

Alibaba provider routing and model discovery

Layer / File(s) Summary
Auth constants, import, and base URL normalization
src/pythinker_code/auth/alibaba.py
Token Plan endpoint constant (ALIBABA_TOKEN_PLAN_BASE_URL) and defaults (ALIBABA_DEFAULT_MODEL_ALIASqwen3.7-plus, ALIBABA_DEFAULT_CONTEXT = 131_072) are added; re imported; _normalize_alibaba_base_url simplified to a coding-plan-agnostic normalizer.
Model discovery pattern detection and capability inference
src/pythinker_code/auth/alibaba.py
Regex helpers detect non-chat model IDs for exclusion and infer capabilities/context sizes from /models IDs when catalog entries lack that data.
Discovered model parsing with inference
src/pythinker_code/auth/alibaba.py
Parsing skips invalid/non-chat IDs, accepts remaining chat models, enriches known catalog models, and derives alias/display/context/capabilities for unknown chat models using inference helpers.
Login token plan routing and error handling
src/pythinker_code/auth/alibaba.py
Subscription plan keys (sk-sp-, sk-tok-, sk-ws-) are routed to the shared Token Plan base URL by default; user base_url is normalized via the simplified normalizer; on 401/403 from default US endpoint, the China endpoint is probed to detect region-specific keys and adjust OAuthEvent messages/early-return logic.
Usage adapter quota endpoint detection and branching
src/pythinker_code/ui/shell/usage_adapters/alibaba.py
Adds _endpoint_without_quota_api(base_url) to detect DashScope Token/Coding Plan hosts that lack /api/v1/quotas. AlibabaAdapter.fetch skips quota calls and adds a local-tally note for those hosts, otherwise calls the quota API and handles 401/403 and request errors.
Auth test expectations and token plan coverage
tests/auth/test_alibaba_auth.py
Default model assertions updated to alibaba/qwen3.7-plus. Tests refactored to track probed URLs (seen_urls), add tests ensuring Token Plan keys default to the international Token Plan endpoint and that discovered Token Plan models are ingested, plus parametrized plan-key routing tests.
Model discovery parsing test coverage
tests/auth/test_alibaba_auth.py
Malformed-payload parametrization adjusted (empty id → no models; unknown non-empty idalibaba/<id> alias). New tests verify _parse_discovered_models accepts unknown non-image chat models with inferred capabilities and excludes image-generation models.

Workflow and repository configuration

Layer / File(s) Summary
CI workflow, ignore rules, and changelog
.github/workflows/docs-pages.yml, .gitignore, CHANGELOG.md
Docs Pages workflow adds top-level concurrency and makes build an explicit job; deploy now needs: build and declares job-level permissions and steps. .gitignore adds .playwright-mcp/ and .playwright/. CHANGELOG.md adds an "Alibaba login" Unreleased bullet describing these changes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

enhancement

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning Description field is empty except for template placeholders; no issue link, no implementation details, and all checklist items unchecked. Fill in the description field with implementation details, link the related issue, and confirm checklist items where applicable.
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 70.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed Title follows conventional commit format with proper type(scope) structure and accurately describes the main changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/alibaba-token-plan-and-cloud

Comment @coderabbitai help to get the list of available commands and usage tips.

@elkaix elkaix changed the title Feat/alibaba token plan and cloud feat(alibaba): add token-plan routing and cloud discovery Jun 10, 2026
elkaix and others added 2 commits June 10, 2026 15:05
Anchor the maas.aliyuncs.com suffix check on a leading dot so spoofed
hosts (e.g. evilmaas.aliyuncs.com) no longer match the token-plan
quota-skip path, resolving the CodeQL py/incomplete-url-substring-
sanitization alert. Also wrap the long ALIBABA_TOKEN_PLAN_BASE_URL
literal to satisfy ruff format.
@codecov

codecov Bot commented Jun 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 81.35593% with 11 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/pythinker_code/auth/alibaba.py 83.33% 4 Missing and 3 partials ⚠️
.../pythinker_code/ui/shell/usage_adapters/alibaba.py 76.47% 2 Missing and 2 partials ⚠️

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/docs-pages.yml (1)

8-11: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Scope elevated GitHub token permissions to deploy only.

pages: write and id-token: write are still granted at workflow scope, so the build job gets privileges it does not need. Keep only contents: read globally and leave pages/id-token on deploy to enforce least privilege.

Suggested patch
 permissions:
   contents: read
-  pages: write
-  id-token: write

Also applies to: 83-85

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/docs-pages.yml around lines 8 - 11, Top-level workflow
permissions are too permissive—remove the global pages: write and id-token:
write entries from the permissions block and leave only contents: read at
workflow scope; then add pages: write and id-token: write to the permissions for
the deploy job (the job named "deploy") so only deploy has those privileges;
also remove the duplicate elevated entries referenced around lines 83-85 and
ensure any build job keeps only the minimum (contents: read) permission.
♻️ Duplicate comments (1)
src/pythinker_code/ui/shell/usage_adapters/alibaba.py (1)

53-56: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Harden endpoint host matching in _endpoint_without_quota_api.

Line 53 uses netloc, and Line 54 uses a raw substring ("dashscope" in host). This can misclassify hosts (e.g., ports/userinfo in netloc, or non-authoritative substring matches), which flips the quota-probe branch incorrectly.

Suggested patch
 def _endpoint_without_quota_api(base_url: str) -> bool:
@@
-    host = urlparse(base_url).netloc
-    if host.startswith("coding") and "dashscope" in host:
+    host = (urlparse(base_url).hostname or "").lower()
+    labels = host.split(".")
+    if host.startswith("coding") and "dashscope" in labels:
         return True
     return host.startswith("token-plan") and host.endswith(".maas.aliyuncs.com")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pythinker_code/ui/shell/usage_adapters/alibaba.py` around lines 53 - 56,
The host matching in _endpoint_without_quota_api is brittle because it uses
urlparse(...).netloc and substring checks; instead parse the URL with urlparse
and use parsed.hostname (to exclude userinfo/ports), lower-case it, then perform
label-aware checks: for the "dashscope" case split hostname on '.' and check
that one of the labels equals "dashscope" (or check exact label positions if
known) combined with the appropriate leading label check (e.g.,
hostname.startswith("coding.") if intended), and for the aliyun case use
hostname.endswith(".maas.aliyuncs.com") (after lowercasing and using
parsed.hostname) or compare exact labels rather than raw substring; update the
code in _endpoint_without_quota_api to use parsed.hostname and these label-aware
comparisons for robust matching.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/auth/test_alibaba_auth.py`:
- Around line 741-742: The parametrized test
test_login_alibaba_plan_key_routes_to_token_plan is missing the "sk-ws-test-key"
case; update the `@pytest.mark.parametrize` decorator for parameter "key" to
include "sk-ws-test-key" alongside "sk-sp-test-key" and "sk-tok-test-key" so the
websocket-prefixed plan key is asserted to route to the Token Plan in this
consolidated test.

---

Outside diff comments:
In @.github/workflows/docs-pages.yml:
- Around line 8-11: Top-level workflow permissions are too permissive—remove the
global pages: write and id-token: write entries from the permissions block and
leave only contents: read at workflow scope; then add pages: write and id-token:
write to the permissions for the deploy job (the job named "deploy") so only
deploy has those privileges; also remove the duplicate elevated entries
referenced around lines 83-85 and ensure any build job keeps only the minimum
(contents: read) permission.

---

Duplicate comments:
In `@src/pythinker_code/ui/shell/usage_adapters/alibaba.py`:
- Around line 53-56: The host matching in _endpoint_without_quota_api is brittle
because it uses urlparse(...).netloc and substring checks; instead parse the URL
with urlparse and use parsed.hostname (to exclude userinfo/ports), lower-case
it, then perform label-aware checks: for the "dashscope" case split hostname on
'.' and check that one of the labels equals "dashscope" (or check exact label
positions if known) combined with the appropriate leading label check (e.g.,
hostname.startswith("coding.") if intended), and for the aliyun case use
hostname.endswith(".maas.aliyuncs.com") (after lowercasing and using
parsed.hostname) or compare exact labels rather than raw substring; update the
code in _endpoint_without_quota_api to use parsed.hostname and these label-aware
comparisons for robust matching.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9c4b5a53-340d-41f1-8d4c-66e48072124d

📥 Commits

Reviewing files that changed from the base of the PR and between 312dc8c and c85c88b.

📒 Files selected for processing (6)
  • .github/workflows/docs-pages.yml
  • .gitignore
  • CHANGELOG.md
  • src/pythinker_code/auth/alibaba.py
  • src/pythinker_code/ui/shell/usage_adapters/alibaba.py
  • tests/auth/test_alibaba_auth.py

Comment thread tests/auth/test_alibaba_auth.py Outdated
elkaix and others added 3 commits June 10, 2026 15:24
The docstring claims sk-sp-/sk-tok-/sk-ws- all route to the Token Plan
endpoint, but the parametrization only exercised sk-sp- and sk-tok-.
Add sk-ws-test-key so the consolidated assertion matches the docstring
(addresses CodeRabbit nitpick on #105).
@elkaix
elkaix merged commit c819e4b into main Jun 10, 2026
27 checks passed
@elkaix
elkaix deleted the feat/alibaba-token-plan-and-cloud branch July 17, 2026 20:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants