Repository navigation
Conversation
Frankfurt is the one Model Studio region without a shared DashScope
domain: neither dashscope-eu nor dashscope-de resolves, and it is served
only by the workspace-dedicated {WorkspaceId}.eu-central-1.maas host.
Region endpoints can therefore carry a {workspaceId} placeholder that the
credential fills in, supplied per provider key or via
LLM_ALIBABA_WORKSPACE_ID(__EU_FRANKFURT).
A region that needs a workspace id is not routable from an API key
alone, so platform-credential availability requires both before the
gateway will select it, and the workspace id is validated as a bare
hostname label before it is interpolated.
Adds Frankfurt mappings with Alibaba's published Frankfurt prices for
qwen3.7-max, qwen3.7-plus and qwen3.6-plus.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
WalkthroughThis change adds Alibaba Cloud EU Frankfurt support with workspace-specific endpoints. Provider schemas, environment resolution, endpoint construction, model metadata, tests, and provider-key UI configuration now support ChangesAlibaba workspace routing
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Admin
participant ProviderKeyDialog
participant API
participant getProviderEndpoint
participant Alibaba
Admin->>ProviderKeyDialog: Select Alibaba EU Frankfurt
ProviderKeyDialog->>ProviderKeyDialog: Validate workspace ID
ProviderKeyDialog->>API: Submit region and workspace ID
API->>getProviderEndpoint: Resolve provider endpoint
getProviderEndpoint->>Alibaba: Build workspace-specific URL
Alibaba-->>getProviderEndpoint: Return routed endpoint
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 `@apps/api/src/routes/keys-provider.ts`:
- Around line 337-340: Update the create-request schema containing
alibaba_region and alibaba_workspace_id with a conditional refinement that
requires a workspace ID when the region is eu-frankfurt and validates it as 1–64
alphanumeric-or-hyphen characters. Add create-route tests covering a missing
workspace ID and an invalid workspace ID, while preserving existing validation
for other regions.
In `@packages/models/src/provider.ts`:
- Around line 380-389: Update the regional lookup in the provider
environment-value resolver to generate variant-specific names using variant and
region, then apply the same comma-separated list selection by configIndex as
getProviderEnvValue before returning. Preserve fallback to getProviderEnvValue
when no matching regional value exists, and add endpoint tests covering
variant-specific and indexed regional workspace IDs.
🪄 Autofix
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: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 2475ecb4-ae89-4cd4-af93-8548f83f7697
📒 Files selected for processing (12)
apps/api/src/routes/admin-provider-credentials.spec.tsapps/api/src/routes/keys-provider.tsapps/docs/content/features/routing.mdxapps/ui/src/components/provider-keys/create-provider-key-dialog.tsxapps/ui/src/components/provider-keys/provider-keys-list.tsxpackages/actions/src/get-provider-endpoint.spec.tspackages/actions/src/get-provider-endpoint.tspackages/db/src/schema.tspackages/models/src/models/alibaba.tspackages/models/src/provider.spec.tspackages/models/src/provider.tspackages/models/src/providers.ts
|
add Also, |
Frankfurt is the one Model Studio region with no shared DashScope domain.
It is served by the workspace-dedicated
`{WorkspaceId}.eu-central-1.maas.aliyuncs.com` host, and by a shared entry
point that resolves the workspace from the API key.
The workspace id is therefore optional: without one the region falls back to
the shared host, so an API key alone keeps working. Alibaba caps that host at
1000 RPM with no SLA and advises against production use, so a credential that
supplies a workspace id gets its own endpoint instead.
Also addresses review feedback: regional env lookups now honour the
`__ENTERPRISE__`/`__PLANS__` variant names and comma-separated configIndex
selection, and the workspace id is format-validated server-side rather than
in the dialog alone.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 `@apps/api/src/routes/keys-provider.spec.ts`:
- Around line 260-263: Update the acceptance tests for both shared-host Alibaba
regions, including the test around postAlibabaKey for eu-frankfurt and the
corresponding test near it, to assert an exact 200 response instead of merely
checking the status is not 400. Preserve the existing request setup and region
coverage.
In `@apps/gateway/src/chat-helpers.e2e.ts`:
- Around line 1275-1285: Update the Alibaba branch in the credential-seeding
helper to read the Frankfurt-specific value via
getRegionSpecificEnvValue("alibaba", "eu-frankfurt") while retaining the generic
LLM_ALIBABA_API_KEY token flow. When the workspace is available, persist
alibaba_region as "eu-frankfurt" together with alibaba_workspace_id; preserve
the undefined result when no workspace is configured.
🪄 Autofix
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: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 5f7bf337-14df-4f15-9801-e28f9b254894
📒 Files selected for processing (12)
apps/api/src/routes/keys-provider.e2e.tsapps/api/src/routes/keys-provider.spec.tsapps/api/src/routes/keys-provider.tsapps/docs/content/features/routing.mdxapps/gateway/src/chat-helpers.e2e.tsapps/ui/src/components/provider-keys/create-provider-key-dialog.tsxpackages/actions/src/get-provider-endpoint.spec.tspackages/actions/src/get-provider-endpoint.tspackages/models/src/models/alibaba.tspackages/models/src/provider.spec.tspackages/models/src/provider.tspackages/models/src/providers.ts
💤 Files with no reviewable changes (1)
- packages/models/src/models/alibaba.ts
🚧 Files skipped from review as they are similar to previous changes (6)
- apps/docs/content/features/routing.mdx
- packages/models/src/provider.spec.ts
- packages/actions/src/get-provider-endpoint.spec.ts
- packages/actions/src/get-provider-endpoint.ts
- packages/models/src/provider.ts
- apps/ui/src/components/provider-keys/create-provider-key-dialog.tsx
| test("accepts eu-frankfurt without a workspace id", async () => { | ||
| const res = await postAlibabaKey({ alibaba_region: "eu-frankfurt" }); | ||
| expect(res.status).not.toBe(400); | ||
| }); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Assert successful creation in the acceptance tests.
A 401, 403, or 500 response satisfies not.toBe(400). The tests can therefore pass when provider-key creation fails. Assert 200 for both shared-host regions.
Proposed assertion fix
- expect(res.status).not.toBe(400);
+ expect(res.status).toBe(200);Also applies to: 273-276
🤖 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 `@apps/api/src/routes/keys-provider.spec.ts` around lines 260 - 263, Update the
acceptance tests for both shared-host Alibaba regions, including the test around
postAlibabaKey for eu-frankfurt and the corresponding test near it, to assert an
exact 200 response instead of merely checking the status is not 400. Preserve
the existing request setup and region coverage.
| if (providerId === "alibaba") { | ||
| // BYOK keys never read env vars at request time, so a workspace-scoped | ||
| // region (Frankfurt) is only reachable when the seeded key carries the | ||
| // workspace id the way a real credential would. | ||
| const workspaceId = getRegionScopedProviderEnvValue( | ||
| "alibaba", | ||
| "workspaceId", | ||
| "eu-frankfurt", | ||
| ); | ||
| return workspaceId ? { alibaba_workspace_id: workspaceId } : undefined; | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
ast-grep outline apps/gateway/src/chat-helpers.e2e.ts --items all
rg -n -C 8 'createProviderKey\s*\(' apps/gateway/src/chat-helpers.e2e.ts
rg -n -C 8 'alibaba_region|alibaba_workspace_id|getRegionScopedProviderEnvValue' \
apps/gateway/src/chat-helpers.e2e.ts \
packages/actions/src/get-provider-endpoint.tsRepository: theopenco/llmgateway
Length of output: 10132
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- createProviderKey implementation ---'
sed -n '1121,1155p' apps/gateway/src/chat-helpers.e2e.ts
printf '%s\n' '--- Alibaba endpoint resolution context ---'
sed -n '360,465p' packages/actions/src/get-provider-endpoint.ts
printf '%s\n' '--- Alibaba option/type and region definitions ---'
rg -n -C 5 'alibaba_region|alibaba_workspace_id|workspaceId|region.*alibaba|alibaba.*region' \
packages apps -g '*.ts' -g '*.tsx' | head -n 260
printf '%s\n' '--- changed-file summary and Qwen references ---'
git diff --stat
rg -n -C 3 'qwen|Qwen|TEST_MODELS' apps/gateway packages -g '*.e2e.ts' -g '*.ts' | head -n 260Repository: theopenco/llmgateway
Length of output: 41562
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- endpoint function signature and region selection ---'
rg -n -C 12 'export (async )?function getProviderEndpoint|function getProviderEndpoint|regionBaseUrl|const region =' \
packages/actions/src/get-provider-endpoint.ts | head -n 220
printf '%s\n' '--- Alibaba provider catalogue and environment configuration ---'
rg -n -C 12 'id: "alibaba"|workspaceId|apiKey|LLM_ALIBABA|eu-frankfurt|qwen' \
packages/models apps/gateway/src/chat-helpers.e2e.ts \
-g '*.ts' -g '*.tsx' | head -n 320
printf '%s\n' '--- provider-key options at all Alibaba call sites ---'
rg -n -C 10 'providerKeyOptions|credentialConfig|createProviderKey\(' \
packages/actions/src apps/gateway/src -g '*.ts' | head -n 320
printf '%s\n' '--- repository state ---'
git status --short
git diff --name-onlyRepository: theopenco/llmgateway
Length of output: 50376
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- Alibaba provider definition ---'
line=$(rg -n '^(\s*)id: "alibaba"' packages/models/src/providers.ts | cut -d: -f1 | head -n1)
echo "line=$line"
sed -n "$((line-8)),$((line+70))p" packages/models/src/providers.ts
printf '%s\n' '--- Alibaba environment helper implementation ---'
rg -n -C 8 'function getRegionScopedProviderEnvValue|export function getRegionScopedProviderEnvValue|getRegionEnvVarSuffix' \
packages/models/src -g '*.ts'
printf '%s\n' '--- Alibaba region mappings and Qwen model entries ---'
rg -n -C 8 'providerId: "alibaba"|provider: "alibaba"|eu-frankfurt|qwen' \
packages/models/src -g '*.ts' | grep -E -B8 -A8 'alibaba|eu-frankfurt|qwen' | head -n 260
printf '%s\n' '--- Runtime uses of alibaba_region ---'
rg -n -C 5 'alibaba_region' packages apps -g '*.ts' -g '*.tsx'Repository: theopenco/llmgateway
Length of output: 26796
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- provider environment filtering ---'
sed -n '140,185p' apps/gateway/src/chat-helpers.e2e.ts
sed -n '430,490p' packages/models/src/provider.ts
printf '%s\n' '--- Alibaba Qwen model mappings ---'
rg -n -C 12 'providerId: "alibaba"|regions:|qwen' packages/models/src/models -g '*.ts' \
| awk 'BEGIN{p=0} /qwen|Qwen/{p=1} p{print} p&&NR>0{c++} c>220{exit}'
printf '%s\n' '--- regional API-key helper and provider-key seeding helpers ---'
rg -n -C 10 'getRegionSpecificEnvValue|getRegionSpecificEnvVarName|getProviderEnvVar\(|hasAllRequiredProviderEnvVars|providerEnvOptionsForTests|beforeAllHook' \
packages/models/src apps/gateway/src/chat-helpers.e2e.ts -g '*.ts'
printf '%s\n' '--- E2E scripts and model-selection documentation ---'
rg -n -C 4 'TEST_MODELS|vitest.*e2e|gateway.*e2e' package.json apps/gateway/package.json README.md .github -g '*.json' -g '*.md' -g '*.yml' -g '*.yaml' 2>/dev/null | head -n 220Repository: theopenco/llmgateway
Length of output: 50376
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- request call sites that supply the Alibaba region ---'
rg -n -C 14 'getProviderEndpoint\(' packages apps -g '*.ts' | grep -E -B14 -A14 'getProviderEndpoint|region' | head -n 360
printf '%s\n' '--- endpoint fallback implementation ---'
sed -n '49,90p' packages/actions/src/get-provider-endpoint.ts
rg -n -C 8 'regionConfig\.optionsKey|optionsKey|providerKeyOptions.*options|options.*providerKey' \
packages apps -g '*.ts' | head -n 240
printf '%s\n' '--- read-only behavioral verifier ---'
python3 - <<'PY'
from pathlib import Path
import re
helper = Path("apps/gateway/src/chat-helpers.e2e.ts").read_text()
endpoint = Path("packages/actions/src/get-provider-endpoint.ts").read_text()
provider = Path("packages/models/src/providers.ts").read_text()
models = Path("packages/models/src/provider.ts").read_text()
before_all = helper[helper.index("export async function beforeAllHook"):helper.index("function providerEnvOptionsForTests")]
options_fn = helper[helper.index("function providerEnvOptionsForTests"):helper.index("export async function beforeEachHook")]
create_fn = helper[helper.index("export async function createProviderKey"):helper.index("export function validateResponse")]
assert 'const envVarName = getProviderEnvVar(provider.id);' in before_all
assert 'getRegionSpecificEnvValue(' not in before_all
assert 'getRegionScopedProviderEnvValue(' in options_fn
assert 'alibaba_workspace_id: workspaceId' in options_fn
assert 'alibaba_region' not in options_fn
assert re.search(r'options,', create_fn)
assert 'providerKeyOptions?.alibaba_workspace_id' in endpoint
assert 'getRegionScopedProviderEnvValue(' in endpoint
assert '"eu-frankfurt": `https://${REGION_WORKSPACE_ID_PLACEHOLDER}.eu-central-1.maas.aliyuncs.com`' in provider
assert '"eu-frankfurt": "https://trial.eu-central-1.maas.aliyuncs.com"' in provider
# Emulate the relevant credential selection:
def seeded_token(env):
return env.get("LLM_ALIBABA_API_KEY")
def seeded_options(env):
ws = env.get("LLM_ALIBABA_WORKSPACE_ID__EU_FRANKFURT")
return {"alibaba_workspace_id": ws} if ws else None
env = {
"LLM_ALIBABA_API_KEY": "generic-token",
"LLM_ALIBABA_API_KEY__EU_FRANKFURT": "frankfurt-token",
"LLM_ALIBABA_WORKSPACE_ID__EU_FRANKFURT": "workspace-id",
}
assert seeded_token(env) == "generic-token"
assert seeded_options(env) == {"alibaba_workspace_id": "workspace-id"}
assert "alibaba_region" not in seeded_options(env)
print("beforeAllHook seeds the base Alibaba token, not the regional token.")
print("providerEnvOptionsForTests carries the Frankfurt workspace ID only.")
print("Alibaba endpoint routing receives region separately and uses the workspace option for Frankfurt host construction.")
print("The original concern about missing alibaba_region is not supported by the endpoint call shape.")
PYRepository: theopenco/llmgateway
Length of output: 29696
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- selected-region resolver ---'
rg -n -C 18 'function resolveSelectedRegion|export function resolveSelectedRegion|resolveSelectedRegion\(' \
packages/actions/src apps -g '*.ts' | head -n 220
printf '%s\n' '--- non-validation gateway endpoint calls with region ---'
rg -l 'getProviderEndpoint\(' apps/gateway packages/actions/src -g '*.ts' \
| while read -r file; do
if [ "$file" != "packages/actions/src/get-provider-endpoint.ts" ] &&
[ "$file" != "packages/actions/src/validate-provider-key.ts" ]; then
echo "--- $file"
rg -n -A 28 -B 5 'getProviderEndpoint\(' "$file" | head -n 90
fi
done
printf '%s\n' '--- focused behavioral verifier ---'
python3 - <<'PY'
from pathlib import Path
import re
helper = Path("apps/gateway/src/chat-helpers.e2e.ts").read_text()
endpoint = Path("packages/actions/src/get-provider-endpoint.ts").read_text()
provider = Path("packages/models/src/providers.ts").read_text()
before_all = helper[helper.index("export async function beforeAllHook"):helper.index("function providerEnvOptionsForTests")]
options_fn = helper[helper.index("function providerEnvOptionsForTests"):helper.index("export async function beforeEachHook")]
create_fn = helper[helper.index("export async function createProviderKey"):helper.index("export function validateResponse")]
checks = {
"beforeAllHook reads only the base API-key env name": 'const envVarName = getProviderEnvVar(provider.id);' in before_all,
"beforeAllHook does not read a regional API key": 'getRegionSpecificEnvValue(' not in before_all,
"Alibaba options include the workspace ID": 'alibaba_workspace_id: workspaceId' in options_fn,
"Alibaba options omit the region": 'alibaba_region' not in options_fn,
"createProviderKey persists the options argument": bool(re.search(r'options,', create_fn)),
"endpoint uses the separately supplied region": 'region?: string' in endpoint,
"endpoint uses the workspace option": 'providerKeyOptions?.alibaba_workspace_id' in endpoint,
"Frankfurt has a workspace endpoint": '"eu-frankfurt": `https://${REGION_WORKSPACE_ID_PLACEHOLDER}.eu-central-1.maas.aliyuncs.com`' in provider,
}
for name, ok in checks.items():
print(f"{'PASS' if ok else 'FAIL'}: {name}")
assert all(checks.values())
def seeded_credential(env):
# This mirrors beforeAllHook's two relevant assignments.
token = env.get("LLM_ALIBABA_API_KEY")
workspace = env.get("LLM_ALIBABA_WORKSPACE_ID__EU_FRANKFURT")
options = {"alibaba_workspace_id": workspace} if workspace else None
return token, options
env = {
"LLM_ALIBABA_API_KEY": "generic-token",
"LLM_ALIBABA_API_KEY__EU_FRANKFURT": "frankfurt-token",
"LLM_ALIBABA_WORKSPACE_ID__EU_FRANKFURT": "workspace-id",
}
token, options = seeded_credential(env)
assert token == "generic-token"
assert options == {"alibaba_workspace_id": "workspace-id"}
assert "alibaba_region" not in options
print("PASS: regional API-key env is present but is not selected by beforeAllHook.")
print("PASS: Frankfurt routing metadata is supplied through the request region plus workspace option, not alibaba_region.")
PYRepository: theopenco/llmgateway
Length of output: 17892
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import re
helper = Path("apps/gateway/src/chat-helpers.e2e.ts").read_text()
endpoint = Path("packages/actions/src/get-provider-endpoint.ts").read_text()
provider = Path("packages/models/src/providers.ts").read_text()
validator = Path("packages/actions/src/validate-provider-key.ts").read_text()
before_all = helper[helper.index("export async function beforeAllHook"):helper.index("function providerEnvOptionsForTests")]
options_fn = helper[helper.index("function providerEnvOptionsForTests"):helper.index("export async function beforeEachHook")]
create_fn = helper[helper.index("export async function createProviderKey"):helper.index("export function validateResponse")]
checks = {
"beforeAllHook reads the base Alibaba API-key env name": 'const envVarName = getProviderEnvVar(provider.id);' in before_all,
"beforeAllHook does not read a regional Alibaba API key": 'getRegionSpecificEnvValue(' not in before_all,
"Alibaba test options include the Frankfurt workspace ID": 'alibaba_workspace_id: workspaceId' in options_fn,
"Alibaba test options omit alibaba_region": 'alibaba_region' not in options_fn,
"createProviderKey persists its options argument": bool(re.search(r'options,', create_fn)),
"request endpoint accepts region separately": 'region?: string' in endpoint,
"Alibaba endpoint uses the workspace option": 'providerKeyOptions?.alibaba_workspace_id' in endpoint,
"provider validation resolves alibaba_region from key options": 'regionKey = providerDef?.regionConfig?.optionsKey' in validator,
}
for name, ok in checks.items():
print(f"{'PASS' if ok else 'FAIL'}: {name}")
assert all(checks.values())
env = {
"LLM_ALIBABA_API_KEY": "generic-token",
"LLM_ALIBABA_API_KEY__EU_FRANKFURT": "frankfurt-token",
"LLM_ALIBABA_WORKSPACE_ID__EU_FRANKFURT": "workspace-id",
}
# Model the assignments made by beforeAllHook/providerEnvOptionsForTests.
seeded_token = env["LLM_ALIBABA_API_KEY"]
seeded_options = {"alibaba_workspace_id": env["LLM_ALIBABA_WORKSPACE_ID__EU_FRANKFURT"]}
assert seeded_token == "generic-token"
assert seeded_options == {"alibaba_workspace_id": "workspace-id"}
assert "alibaba_region" not in seeded_options
print("PASS: with both keys configured, the seeded Alibaba credential uses the generic token.")
print("PASS: the seeded options contain the Frankfurt workspace but no alibaba_region.")
print("PASS: the endpoint region is a separate request argument; alibaba_region is used by provider-key validation.")
PYRepository: theopenco/llmgateway
Length of output: 883
Seed the Frankfurt Alibaba credential. beforeAllHook always reads LLM_ALIBABA_API_KEY, so it can combine the generic token with the Frankfurt workspace. Read LLM_ALIBABA_API_KEY__EU_FRANKFURT through getRegionSpecificEnvValue("alibaba", "eu-frankfurt") and persist alibaba_region: "eu-frankfurt" with alibaba_workspace_id. Run the complete filtered E2E suite with TEST_MODELS set to the changed Qwen models.
🤖 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 `@apps/gateway/src/chat-helpers.e2e.ts` around lines 1275 - 1285, Update the
Alibaba branch in the credential-seeding helper to read the Frankfurt-specific
value via getRegionSpecificEnvValue("alibaba", "eu-frankfurt") while retaining
the generic LLM_ALIBABA_API_KEY token flow. When the workspace is available,
persist alibaba_region as "eu-frankfurt" together with alibaba_workspace_id;
preserve the undefined result when no workspace is configured.
Source: Coding guidelines
LLM_ALIBABA_REGION had no reader anywhere: the region comes from the model's `:region` suffix or the credential's own region binding. Declaring it only rendered a dead text field on the managed-credential form, next to the workspace id field that does matter. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
## Problem `qwen3.8-max` had no `regions` array at all, so the only reachable Alibaba endpoint for it was the default Singapore one — even though Model Studio serves it from every region, including Frankfurt (which #3525 just wired up). `qwen3.7-flash` was likewise limited to Singapore and Beijing. ## Change Both mappings now declare all four regions: `singapore`, `eu-frankfurt`, `us-virginia`, `cn-beijing`. `qwen3.7-max` and `qwen3.7-plus` already had all four, so the Qwen 3.7/3.8 line is now uniform. Availability was established by probing every model in the Alibaba catalogue against all four regional endpoints with that region's real key, not from the docs — the OpenAI-compatible `/models` listing is unreliable here (it omits `qwen3.8-max` in *every* region, including Singapore where it demonstrably works). ## Pricing The new region entries inherit the mapping's global (Singapore) prices, matching how every other unverified Alibaba region entry in the catalogue is expressed (e.g. `qwen3.7-max:us-virginia`, `qwen3.7-plus:cn-beijing`). Alibaba's regional list prices are never *above* the global one, so no entry can bill below cost; the effect is that cheapest-region routing simply won't prefer these regions over Singapore until real regional prices are filled in. ## Verification `pnpm format`, `pnpm build`, and the models/pricing unit specs (304 tests) pass. Real e2e per region — the seeded BYOK provider key always takes its token from `LLM_ALIBABA_API_KEY`, so one run can only exercise one region's credentials; the suite was run four times, each with that variable pointed at the corresponding regional key: | region | qwen 3.7/3.8 e2e cases | | --- | --- | | `singapore` | 68 passed / 0 failed | | `eu-frankfurt` | 68 passed / 0 failed | | `us-virginia` | 68 passed / 0 failed | | `cn-beijing` | 68 passed / 0 failed | Each run covers `qwen3.7-max`, `qwen3.7-plus`, `qwen3.7-flash` and `qwen3.8-max` pinned to that region, across basic completion, streaming, reasoning (all seven efforts), reasoning + streaming, JSON output, and tool calls. The remaining red in those runs is unrelated to this change: six provider-key registration cases for providers with no local key (AtlasCloud, AWS Mantle, Azure, Azure AI Foundry, ElevenLabs, Sakana AI), plus — in the three non-Singapore runs — the two Alibaba key-registration cases, which validate `LLM_ALIBABA_API_KEY` against the Singapore host and therefore fail while that variable holds another region's key. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Expanded regional availability for the `qwen3.7-flash` model to include Singapore, Frankfurt, Virginia, and Beijing. * Added support for the same regions to the `qwen3.8-max` model. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds Alibaba Cloud's EU (Frankfurt) region (
eu-frankfurt).Why this needs more than an
endpointMapentryFrankfurt is the one Model Studio region with no shared DashScope domain —
dashscope-eu,dashscope-deandeu-central-1.dashscopedo not resolve at all. It is reachable two ways, both verified live against a real Frankfurt key:{WorkspaceId}.eu-central-1.maas.aliyuncs.comtrial.eu-central-1.maas.aliyuncs.comfoo.,dashscope.,api., …)Workspace endpoint is invalidAn earlier attempt (48d088f, never merged) pointed
frankfurtatdashscope-intl, which silently executed EU-designated traffic in Singapore. This does not alias any host.Design
The workspace ID is optional. Without one, Frankfurt uses the shared entry point, so an API key alone keeps working. With one, requests go to the credential's own endpoint — Alibaba caps the shared host at 1000 RPM with no SLA and documents it as "not recommended for production environments".
endpointMapentries may carry a{workspaceId}placeholder;endpointFallbackMapsupplies the shared host to use when no ID is configured.LLM_ALIBABA_WORKSPACE_ID__EU_FRANKFURT→ base var → shared host. The ID is validated as a bare hostname label before interpolation.__ENTERPRISE__/__PLANS__variant names and comma-separatedconfigIndexselection.Other regions are untouched: they keep their plain DashScope hosts, which accept both legacy and workspace-scoped (
sk-ws-…) keys.Pricing
Alibaba's published Frankfurt (Global-scope) list prices; all three models are Global-scope only there, so there is no deployment-scope ambiguity. Cache rates follow the documented model-level ratios (implicit 20%, explicit hit 10%, write 125%), which are region-independent.
qwen3.7-maxqwen3.7-plusqwen3.6-plusqwen3.8-maxis not offered in Frankfurt and is excluded.Testing
Real e2e against Frankfurt, both endpoint paths,
144 passed / 0 failedeach:27 named
:eu-frankfurtcases ran (reasoning effortsnone→max, reasoning + streaming) withrequestedRegion: "eu-frankfurt".Provider-key registration verified through the real
POST /keys/providerroute with live keys: legacy key on a shared-host region, workspace-scoped key with its workspace ID, workspace-scoped key without one, and an invalid workspace ID rejected.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation