dsh-provider-qoder 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -5
- package/README_CN.md +10 -5
- package/lib/client.js +189 -84
- package/lib/index.d.ts +60 -341
- package/lib/index.js +1666 -831
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
English | [简体中文](./README_CN.md)
|
|
4
4
|
|
|
5
|
-
`dsh-provider-qoder` integrates your Qoder subscription into DeepSeek Harness (DSH), supporting streaming text, reasoning content, and tool calls
|
|
5
|
+
`dsh-provider-qoder` integrates your Qoder subscription into DeepSeek Harness (DSH), supporting text and image input, streaming text, reasoning content, and tool calls across Global and China Qoder services.
|
|
6
6
|
|
|
7
7
|
The plugin handles authentication and model communication, while tool execution, workspace operations, and permission management are handled by DSH.
|
|
8
8
|
This project is a community adapter plugin.
|
|
@@ -12,7 +12,8 @@ This project is a community adapter plugin.
|
|
|
12
12
|
- Configure subscription access via Qoder Personal Access Token (PAT).
|
|
13
13
|
- Supports both Global (`global`) and China (`china`) services.
|
|
14
14
|
- Discovers available models for your account, allows selecting enabled models, and displays pricing multipliers and reasoning effort options when reported by the service.
|
|
15
|
-
-
|
|
15
|
+
- Sends DSH image attachments to models advertised by Qoder as vision-language models, including images returned by tools. Images are published to the Qoder center service and referenced by URL to keep requests small; if publication fails, the image is sent inline instead so the turn still completes.
|
|
16
|
+
- Supports streaming responses, reasoning content, tool calls, and multi-turn interactions.
|
|
16
17
|
- View account information, personal quota, organization resource packs, and reset dates in Settings.
|
|
17
18
|
|
|
18
19
|
## Installation
|
|
@@ -51,7 +52,7 @@ Select the region matching your account, enter your Qoder PAT into the input box
|
|
|
51
52
|
3. Select the models you want to enable (keep at least one), then click **Save**.
|
|
52
53
|
4. Select a Qoder model in the DSH model picker to start chatting.
|
|
53
54
|
|
|
54
|
-
Fetching models uses the **saved PAT and service region**. If you switch accounts or regions, save your changes first before fetching models again. The initial model catalog is only a candidate reference; actual available models are subject to account query results. Model multipliers
|
|
55
|
+
Fetching models uses the **saved PAT and service region**. If you switch accounts or regions, save your changes first before fetching models again. The initial model catalog is only a candidate reference; actual available models are subject to account query results. Model multipliers, reasoning effort options, and image-input capability are provided by Qoder and may not be available for all models.
|
|
55
56
|
|
|
56
57
|
### 3. View Account & Quota
|
|
57
58
|
|
|
@@ -69,11 +70,11 @@ Verify that the PAT is valid and matches the saved service region. If modified,
|
|
|
69
70
|
|
|
70
71
|
### Models remain unavailable after switching regions
|
|
71
72
|
|
|
72
|
-
Switching regions does not automatically update your PAT or refetch models
|
|
73
|
+
Global and China model catalogs are stored separately. Switching regions does not automatically update your PAT or refetch that region's models; after saving the matching PAT, fetch and save the models for the selected region.
|
|
73
74
|
|
|
74
75
|
### Are requests automatically retried?
|
|
75
76
|
|
|
76
|
-
|
|
77
|
+
Model-generation retries are managed by DSH. To enable them, the running profile should include `@deepseek-ai/dsh-llm-retry`. The plugin reports empty responses, rate limits, server errors, timeouts, and transport errors using DSH's default retry policy; authentication, invalid requests, cancellations, quota issues, and protocol formatting errors are not retried by default. The Qoder transport may independently retry an idempotent model-catalog, subscriber-profile, or quota read once, but never retries a model-generation POST internally.
|
|
77
78
|
|
|
78
79
|
## Install from Source & Development
|
|
79
80
|
|
|
@@ -108,6 +109,10 @@ pnpm run check
|
|
|
108
109
|
|
|
109
110
|
This command runs non-UI mocked tests, builds the ESM package and type declarations, checks build artifacts, and previews the package tarball without sending real Qoder model requests or publishing npm packages.
|
|
110
111
|
|
|
112
|
+
## References
|
|
113
|
+
|
|
114
|
+
- [pi-provider-qoder](https://github.com/simonsmh/pi-provider-qoder) - Reference implementation for Qoder authentication, API communication, and protocol handling.
|
|
115
|
+
|
|
111
116
|
## Feedback
|
|
112
117
|
|
|
113
118
|
Please report issues via [GitHub Issues](https://github.com/mo-n/dsh-provider-qoder/issues) with the plugin and DSH versions, selected service region, reproduction steps, and sanitized error messages. Do not submit PATs, short-lived tokens, or personal account information.
|
package/README_CN.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[English](./README.md) | 简体中文
|
|
4
4
|
|
|
5
|
-
`dsh-provider-qoder` 将你的 Qoder 订阅接入 DeepSeek Harness(DSH
|
|
5
|
+
`dsh-provider-qoder` 将你的 Qoder 订阅接入 DeepSeek Harness(DSH),支持文本与图片输入、流式文本、推理内容和工具调用,可选择国际版或中国区 Qoder 服务。
|
|
6
6
|
|
|
7
7
|
插件负责认证和模型通信,工具执行、工作区操作及权限管理由 DSH 负责。
|
|
8
8
|
本项目是社区适配插件。
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
- 通过 Qoder Personal Access Token(PAT,个人访问令牌)配置订阅访问。
|
|
13
13
|
- 支持国际版(`global`)和中国区(`china`)服务。
|
|
14
14
|
- 获取账号可用模型,选择启用的模型,并在服务返回相关信息时显示价格倍率和推理强度选项。
|
|
15
|
-
-
|
|
15
|
+
- 将 DSH 图片附件发送给 Qoder 标记为视觉语言模型的模型,同时支持工具返回的图片。图片会先上传到 Qoder 中心端服务并以 URL 引用,从而避免请求体膨胀;若上传失败,则自动改为内联发送,保证本轮对话仍可完成。
|
|
16
|
+
- 支持流式回答、推理内容、工具调用及多轮交互。
|
|
16
17
|
- 在设置中查看账号信息、个人额度、组织资源包和重置时间。
|
|
17
18
|
|
|
18
19
|
## 安装
|
|
@@ -51,7 +52,7 @@ dsh plugin --profile web add dsh-provider-qoder
|
|
|
51
52
|
3. 选择需要启用的模型,至少保留一个,然后点击 **保存**。
|
|
52
53
|
4. 在 DSH 的模型选择器中选择 Qoder 模型,开始对话。
|
|
53
54
|
|
|
54
|
-
获取模型使用的是**已保存的 PAT
|
|
55
|
+
获取模型使用的是**已保存的 PAT 和服务区域**。更换账号或区域后,请先保存,再重新获取模型。初始模型目录仅供候选参考,实际可用模型以账号查询结果为准。模型倍率、推理强度选项及图片输入能力由 Qoder 提供,并非所有模型都有这些信息。
|
|
55
56
|
|
|
56
57
|
### 3. 查看账号与额度
|
|
57
58
|
|
|
@@ -69,11 +70,11 @@ dsh plugin --profile web add dsh-provider-qoder
|
|
|
69
70
|
|
|
70
71
|
### 切换区域后,模型仍不可用
|
|
71
72
|
|
|
72
|
-
|
|
73
|
+
Global 与 China 的模型目录会分别保存。切换区域不会自动替换 PAT,也不会自动重新获取该区域的模型;请先保存匹配区域的 PAT,再获取并保存所选区域的模型。
|
|
73
74
|
|
|
74
75
|
### 请求是否会自动重试?
|
|
75
76
|
|
|
76
|
-
|
|
77
|
+
模型生成重试由 DSH 管理。需要启用时,运行中的 profile 应包含 `@deepseek-ai/dsh-llm-retry`。插件使用 DSH 默认重试策略报告空响应、限流、服务端、超时和传输错误;认证、无效请求、取消、额度及协议格式错误默认不重试。Qoder transport 可以独立重试一次幂等的模型目录、订阅者资料或配额读取,但绝不会在内部重试模型生成 POST。
|
|
77
78
|
|
|
78
79
|
## 从源码安装与开发
|
|
79
80
|
|
|
@@ -108,6 +109,10 @@ pnpm run check
|
|
|
108
109
|
|
|
109
110
|
该命令运行模拟网络的非 UI 测试、构建 ESM 包与类型声明、检查构建产物并预览打包内容,不会发起真实 Qoder 模型请求或发布 npm 包。
|
|
110
111
|
|
|
112
|
+
## 参考项目
|
|
113
|
+
|
|
114
|
+
- [pi-provider-qoder](https://github.com/simonsmh/pi-provider-qoder) - Qoder 认证、API 通信及协议处理的参考实现。
|
|
115
|
+
|
|
111
116
|
## 反馈
|
|
112
117
|
|
|
113
118
|
请通过 [GitHub Issues](https://github.com/mo-n/dsh-provider-qoder/issues) 提交问题,附上插件和 DSH 版本、所选服务区域、复现步骤及脱敏后的错误信息。不要提交 PAT、短期令牌或个人账号信息。
|
package/lib/client.js
CHANGED
|
@@ -6,16 +6,16 @@ window.__ModuleLoader__.load({
|
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
7
|
let react = require("react");
|
|
8
8
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
9
|
-
//#region src/credential-contract.ts
|
|
10
|
-
/** Shared
|
|
9
|
+
//#region src/dsh/credential-contract.ts
|
|
10
|
+
/** Shared DSH host/browser contract for the one managed Qoder credential. */
|
|
11
11
|
const qoderCredentialRef = "QODER_MANAGED_CREDENTIAL";
|
|
12
12
|
/** DSH's local provider uses `env` and `*-env` for ambient credential layers. */
|
|
13
13
|
function isEnvironmentCredentialSource(source) {
|
|
14
14
|
return source === "env" || source?.endsWith("-env") === true;
|
|
15
15
|
}
|
|
16
16
|
//#endregion
|
|
17
|
-
//#region \0dsh-css
|
|
18
|
-
const css = ".
|
|
17
|
+
//#region \0dsh-css:/home/runner/work/dsh-provider-qoder/dsh-provider-qoder/src/client/QoderCredentialCard.module.css.mjs
|
|
18
|
+
const css = ".SRqrhW_credential{color:var(--dsw-alias-label-primary);border-radius:10px;margin-top:12px}.SRqrhW_credential p{margin:8px 0 0}.SRqrhW_credentialEditor{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;flex-direction:column;gap:12px;padding:12px 14px;display:flex}.SRqrhW_credentialRowHead{align-items:center;gap:10px;display:flex}.SRqrhW_credentialIdentity{align-items:center;gap:6px;min-width:0;display:inline-flex}.SRqrhW_credentialName{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.SRqrhW_credentialDot{box-sizing:border-box;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.SRqrhW_credentialDotConfigured{background:var(--dsw-alias-state-success-primary)}.SRqrhW_credentialDotMissing{background:var(--dsw-alias-state-error-primary)}.SRqrhW_regionBadge{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary);border-radius:4px;align-items:center;padding:1px 6px;font-size:11px;font-weight:500;line-height:16px;display:inline-flex}.SRqrhW_regionSelector{flex-direction:column;gap:8px;margin-bottom:14px;display:flex}.SRqrhW_regionGroup{gap:8px;display:flex}.SRqrhW_regionOption{border:1px solid var(--dsw-alias-border-l2);cursor:pointer;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);user-select:none;border-radius:8px;align-items:center;gap:6px;padding:6px 12px;font-size:12px;line-height:18px;transition:all .12s;display:inline-flex}.SRqrhW_regionOption:hover:not(.SRqrhW_regionOptionDisabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.SRqrhW_credential button.SRqrhW_regionOptionActive{border-color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-interactive-bg-selected);color:var(--dsw-alias-brand-primary);font-weight:500}.SRqrhW_regionOptionDisabled{cursor:not-allowed;opacity:.5}.SRqrhW_editButton{box-sizing:border-box;font:inherit;background:0 0;margin-left:auto;font-size:12px;line-height:18px;border:1px solid var(--dsw-alias-border-l2)!important;border-radius:14px!important;height:28px!important;padding:0 10px!important}.SRqrhW_editButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.SRqrhW_editor{background:var(--dsw-alias-bg-module-platform);border-radius:12px;padding:14px 16px}.SRqrhW_customized{border-top:1px solid var(--dsw-alias-border-l2);margin-top:14px;padding-top:10px}.SRqrhW_customizedSummary{cursor:pointer;width:fit-content;color:var(--dsw-alias-label-secondary);border-radius:6px;align-items:center;gap:6px;margin-left:-4px;padding:2px 4px;font-size:12px;font-weight:500;line-height:18px;list-style:none;display:flex}.SRqrhW_customizedSummary::-webkit-details-marker{display:none}.SRqrhW_customizedSummary:before{content:\"\";border-bottom:1.5px solid;border-right:1.5px solid;width:5px;height:5px;transition:transform .12s;transform:rotate(-45deg)translate(-1px,-1px)}.SRqrhW_customized[open]>.SRqrhW_customizedSummary:before{transform:rotate(45deg)translate(-1px,-1px)}.SRqrhW_customizedBody{padding-top:12px}.SRqrhW_modelCatalog,.SRqrhW_modelList{flex-direction:column;gap:10px;display:flex}.SRqrhW_modelCatalogHead{justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.SRqrhW_modelCatalogTitle{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px}.SRqrhW_modelCatalogMeta{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;margin:2px 0 0!important}.SRqrhW_linkButton{box-sizing:border-box;font:inherit;background:0 0;font-size:12px;line-height:18px;border:none!important;border-radius:14px!important;height:28px!important;padding:0 10px!important}.SRqrhW_linkButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.SRqrhW_modelChoice{cursor:pointer;border-radius:6px;grid-template-columns:auto minmax(0,1fr) auto auto auto;align-items:center;gap:8px;padding:6px 8px;font-size:13px;display:grid}.SRqrhW_modelChoice:hover{background:var(--dsw-alias-interactive-bg-hover)}.SRqrhW_modelChoiceName{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.SRqrhW_modelChoice code{color:var(--dsw-alias-label-tertiary);font-size:11px}.SRqrhW_modelRate{min-width:34px;color:var(--dsw-alias-brand-primary);text-align:right;font-size:12px;font-weight:600}.SRqrhW_modelUnavailable{cursor:not-allowed;opacity:.55}.SRqrhW_modelBadge{color:var(--dsw-alias-state-error-primary);font-size:11px}@media (prefers-reduced-motion:reduce){.SRqrhW_customizedSummary:before{transition:none}}.SRqrhW_head{justify-content:space-between;align-items:center;gap:12px;margin-bottom:20px;display:flex}.SRqrhW_heading{margin:0;font-size:18px;font-weight:600}.SRqrhW_status{color:var(--dsw-alias-label-secondary);font-size:12px}.SRqrhW_configured,.SRqrhW_success{color:var(--dsw-alias-state-success-primary)}.SRqrhW_label{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px;display:block}.SRqrhW_input{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);width:100%;height:32px;color:var(--dsw-alias-label-primary);border-radius:8px;outline:none;margin-top:6px;padding:0 10px}.SRqrhW_input:focus{border-color:var(--dsw-alias-brand-primary)}.SRqrhW_input::placeholder{color:var(--dsw-alias-label-dimmed)}.SRqrhW_input:disabled{cursor:not-allowed;opacity:.4}.SRqrhW_hint{color:var(--dsw-alias-label-secondary);font-size:12px}.SRqrhW_actions{justify-content:flex-end;gap:8px;margin-top:14px;display:flex}.SRqrhW_credential button{cursor:pointer;height:32px;color:var(--dsw-alias-label-primary);border:1px solid #0000;border-radius:16px;padding:0 12px}.SRqrhW_credential button:disabled{cursor:not-allowed;opacity:.4}.SRqrhW_primary{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)!important}.SRqrhW_primary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}.SRqrhW_secondary{background:0 0;border-color:var(--dsw-alias-border-l2)!important}.SRqrhW_secondary:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.SRqrhW_danger{background:0 0;border-color:var(--dsw-alias-state-error-primary)!important;color:var(--dsw-alias-state-error-primary)!important}.SRqrhW_danger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.SRqrhW_error{color:var(--dsw-alias-state-error-primary);font-size:12px}.SRqrhW_success{font-size:12px}.SRqrhW_accountSection{background:var(--dsw-alias-bg-module-platform);border-radius:8px;flex-direction:column;gap:10px;margin-top:12px;padding:12px;display:flex}.SRqrhW_accountHead{justify-content:space-between;align-items:center;gap:8px;display:flex}.SRqrhW_userInfo{align-items:center;gap:10px;min-width:0;display:flex}.SRqrhW_avatar{background:var(--dsw-alias-brand-primary);width:32px;height:32px;color:var(--dsw-alias-label-primary-foreground);text-transform:uppercase;border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;font-size:14px;font-weight:600;display:flex}.SRqrhW_userDetails{flex-direction:column;gap:2px;min-width:0;display:flex}.SRqrhW_userName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:600;overflow:hidden}.SRqrhW_userEmail{color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;font-size:11px;overflow:hidden}.SRqrhW_refreshBtn{border:1px solid var(--dsw-alias-border-l2)!important;height:26px!important;color:var(--dsw-alias-label-secondary)!important;background:0 0!important;border-radius:13px!important;padding:0 8px!important;font-size:12px!important}.SRqrhW_refreshBtn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)!important}.SRqrhW_quotaBlock{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:6px;padding-top:8px;display:flex}.SRqrhW_quotaHeader{justify-content:space-between;align-items:center;gap:8px;display:flex}.SRqrhW_quotaLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600}.SRqrhW_quotaTop{justify-content:space-between;align-items:baseline;gap:8px;font-size:12px;display:flex}.SRqrhW_quotaTop strong{color:var(--dsw-alias-brand-primary);font-size:13px}.SRqrhW_quotaProgress{background:var(--dsw-alias-border-l3);width:100%;height:6px;accent-color:var(--dsw-alias-brand-primary);-webkit-appearance:none;appearance:none;border:0;border-radius:999px;overflow:hidden}.SRqrhW_quotaProgress::-webkit-progress-bar{background:var(--dsw-alias-border-l3);border-radius:999px}.SRqrhW_quotaProgress::-webkit-progress-value{background:var(--dsw-alias-brand-primary);border-radius:999px}.SRqrhW_quotaProgress::-moz-progress-bar{background:var(--dsw-alias-brand-primary);border-radius:999px}.SRqrhW_quotaMeta{color:var(--dsw-alias-label-tertiary);justify-content:space-between;align-items:center;gap:8px;font-size:11px;display:flex}.SRqrhW_accountLoadingText,.SRqrhW_accountEmptyText{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px}.SRqrhW_userNameRow{align-items:center;gap:8px;display:flex}.SRqrhW_planBadge{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-brand-primary);border-radius:4px;align-items:center;padding:1px 6px;font-size:11px;font-weight:600;line-height:16px;display:inline-flex}.SRqrhW_orgTag{color:var(--dsw-alias-label-tertiary);font-size:11px}.SRqrhW_suspendedBanner{background:var(--dsw-alias-state-error-subtle,#ff4d4f1a);color:var(--dsw-alias-state-error-primary);border-radius:6px;align-items:center;gap:6px;margin-bottom:8px;padding:6px 10px;font-size:12px;display:flex}";
|
|
19
19
|
const tagId = "dsh-provider-qoder/QoderCredentialCard.module.css";
|
|
20
20
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
21
21
|
const tag = document.createElement("style");
|
|
@@ -25,65 +25,69 @@ window.__ModuleLoader__.load({
|
|
|
25
25
|
document.head.appendChild(tag);
|
|
26
26
|
}
|
|
27
27
|
var QoderCredentialCard_module_css_default = {
|
|
28
|
-
"accountEmptyText": "
|
|
29
|
-
"accountHead": "
|
|
30
|
-
"accountLoadingText": "
|
|
31
|
-
"accountSection": "
|
|
32
|
-
"actions": "
|
|
33
|
-
"avatar": "
|
|
34
|
-
"configured": "
|
|
35
|
-
"credential": "
|
|
36
|
-
"credentialDot": "
|
|
37
|
-
"credentialDotConfigured": "
|
|
38
|
-
"credentialDotMissing": "
|
|
39
|
-
"credentialEditor": "
|
|
40
|
-
"credentialIdentity": "
|
|
41
|
-
"credentialName": "
|
|
42
|
-
"credentialRowHead": "
|
|
43
|
-
"customized": "
|
|
44
|
-
"customizedBody": "
|
|
45
|
-
"customizedSummary": "
|
|
46
|
-
"danger": "
|
|
47
|
-
"editButton": "
|
|
48
|
-
"editor": "
|
|
49
|
-
"error": "
|
|
50
|
-
"head": "
|
|
51
|
-
"heading": "
|
|
52
|
-
"hint": "
|
|
53
|
-
"input": "
|
|
54
|
-
"label": "
|
|
55
|
-
"linkButton": "
|
|
56
|
-
"modelBadge": "
|
|
57
|
-
"modelCatalog": "
|
|
58
|
-
"modelCatalogHead": "
|
|
59
|
-
"modelCatalogMeta": "
|
|
60
|
-
"modelCatalogTitle": "
|
|
61
|
-
"modelChoice": "
|
|
62
|
-
"modelChoiceName": "
|
|
63
|
-
"modelList": "
|
|
64
|
-
"modelRate": "
|
|
65
|
-
"modelUnavailable": "
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"
|
|
28
|
+
"accountEmptyText": "SRqrhW_accountEmptyText",
|
|
29
|
+
"accountHead": "SRqrhW_accountHead",
|
|
30
|
+
"accountLoadingText": "SRqrhW_accountLoadingText",
|
|
31
|
+
"accountSection": "SRqrhW_accountSection",
|
|
32
|
+
"actions": "SRqrhW_actions",
|
|
33
|
+
"avatar": "SRqrhW_avatar",
|
|
34
|
+
"configured": "SRqrhW_configured",
|
|
35
|
+
"credential": "SRqrhW_credential",
|
|
36
|
+
"credentialDot": "SRqrhW_credentialDot",
|
|
37
|
+
"credentialDotConfigured": "SRqrhW_credentialDotConfigured",
|
|
38
|
+
"credentialDotMissing": "SRqrhW_credentialDotMissing",
|
|
39
|
+
"credentialEditor": "SRqrhW_credentialEditor",
|
|
40
|
+
"credentialIdentity": "SRqrhW_credentialIdentity",
|
|
41
|
+
"credentialName": "SRqrhW_credentialName",
|
|
42
|
+
"credentialRowHead": "SRqrhW_credentialRowHead",
|
|
43
|
+
"customized": "SRqrhW_customized",
|
|
44
|
+
"customizedBody": "SRqrhW_customizedBody",
|
|
45
|
+
"customizedSummary": "SRqrhW_customizedSummary",
|
|
46
|
+
"danger": "SRqrhW_danger",
|
|
47
|
+
"editButton": "SRqrhW_editButton",
|
|
48
|
+
"editor": "SRqrhW_editor",
|
|
49
|
+
"error": "SRqrhW_error",
|
|
50
|
+
"head": "SRqrhW_head",
|
|
51
|
+
"heading": "SRqrhW_heading",
|
|
52
|
+
"hint": "SRqrhW_hint",
|
|
53
|
+
"input": "SRqrhW_input",
|
|
54
|
+
"label": "SRqrhW_label",
|
|
55
|
+
"linkButton": "SRqrhW_linkButton",
|
|
56
|
+
"modelBadge": "SRqrhW_modelBadge",
|
|
57
|
+
"modelCatalog": "SRqrhW_modelCatalog",
|
|
58
|
+
"modelCatalogHead": "SRqrhW_modelCatalogHead",
|
|
59
|
+
"modelCatalogMeta": "SRqrhW_modelCatalogMeta",
|
|
60
|
+
"modelCatalogTitle": "SRqrhW_modelCatalogTitle",
|
|
61
|
+
"modelChoice": "SRqrhW_modelChoice",
|
|
62
|
+
"modelChoiceName": "SRqrhW_modelChoiceName",
|
|
63
|
+
"modelList": "SRqrhW_modelList",
|
|
64
|
+
"modelRate": "SRqrhW_modelRate",
|
|
65
|
+
"modelUnavailable": "SRqrhW_modelUnavailable",
|
|
66
|
+
"orgTag": "SRqrhW_orgTag",
|
|
67
|
+
"planBadge": "SRqrhW_planBadge",
|
|
68
|
+
"primary": "SRqrhW_primary",
|
|
69
|
+
"quotaBlock": "SRqrhW_quotaBlock",
|
|
70
|
+
"quotaHeader": "SRqrhW_quotaHeader",
|
|
71
|
+
"quotaLabel": "SRqrhW_quotaLabel",
|
|
72
|
+
"quotaMeta": "SRqrhW_quotaMeta",
|
|
73
|
+
"quotaProgress": "SRqrhW_quotaProgress",
|
|
74
|
+
"quotaTop": "SRqrhW_quotaTop",
|
|
75
|
+
"refreshBtn": "SRqrhW_refreshBtn",
|
|
76
|
+
"regionBadge": "SRqrhW_regionBadge",
|
|
77
|
+
"regionGroup": "SRqrhW_regionGroup",
|
|
78
|
+
"regionOption": "SRqrhW_regionOption",
|
|
79
|
+
"regionOptionActive": "SRqrhW_regionOptionActive",
|
|
80
|
+
"regionOptionDisabled": "SRqrhW_regionOptionDisabled",
|
|
81
|
+
"regionSelector": "SRqrhW_regionSelector",
|
|
82
|
+
"secondary": "SRqrhW_secondary",
|
|
83
|
+
"status": "SRqrhW_status",
|
|
84
|
+
"success": "SRqrhW_success",
|
|
85
|
+
"suspendedBanner": "SRqrhW_suspendedBanner",
|
|
86
|
+
"userDetails": "SRqrhW_userDetails",
|
|
87
|
+
"userEmail": "SRqrhW_userEmail",
|
|
88
|
+
"userInfo": "SRqrhW_userInfo",
|
|
89
|
+
"userName": "SRqrhW_userName",
|
|
90
|
+
"userNameRow": "SRqrhW_userNameRow"
|
|
87
91
|
};
|
|
88
92
|
//#endregion
|
|
89
93
|
//#region src/client/QoderAccountCard.tsx
|
|
@@ -242,14 +246,21 @@ window.__ModuleLoader__.load({
|
|
|
242
246
|
});
|
|
243
247
|
}
|
|
244
248
|
if (accountState.status !== "ready") return null;
|
|
245
|
-
const { profile, usage } = accountState.account;
|
|
249
|
+
const { profile, usage, plan } = accountState.account;
|
|
246
250
|
const userQuota = usage?.userQuota;
|
|
247
251
|
const orgPackage = usage?.orgResourcePackage;
|
|
248
252
|
const avatarInitial = (profile.name || profile.email || "Q").charAt(0).toUpperCase();
|
|
249
253
|
const resetDate = formatResetDate(usage?.expiresAt);
|
|
254
|
+
const planExpiry = plan?.endDate ? formatResetDate(plan.endDate) : void 0;
|
|
255
|
+
const isSuspended = plan?.organization?.isSuspended === true;
|
|
250
256
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
251
257
|
className: QoderCredentialCard_module_css_default.accountSection,
|
|
252
258
|
children: [
|
|
259
|
+
isSuspended ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
260
|
+
className: QoderCredentialCard_module_css_default.suspendedBanner,
|
|
261
|
+
role: "alert",
|
|
262
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("accountSuspended") })
|
|
263
|
+
}) : null,
|
|
253
264
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
254
265
|
className: QoderCredentialCard_module_css_default.accountHead,
|
|
255
266
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -260,13 +271,27 @@ window.__ModuleLoader__.load({
|
|
|
260
271
|
children: avatarInitial
|
|
261
272
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
262
273
|
className: QoderCredentialCard_module_css_default.userDetails,
|
|
263
|
-
children: [
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
274
|
+
children: [
|
|
275
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
276
|
+
className: QoderCredentialCard_module_css_default.userNameRow,
|
|
277
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
278
|
+
className: QoderCredentialCard_module_css_default.userName,
|
|
279
|
+
children: profile.name || "Qoder Subscriber"
|
|
280
|
+
}), plan?.planTierName ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
281
|
+
className: QoderCredentialCard_module_css_default.planBadge,
|
|
282
|
+
title: planExpiry ? t("planExpiresAt", { value: planExpiry }) : void 0,
|
|
283
|
+
children: plan.planTierName
|
|
284
|
+
}) : null]
|
|
285
|
+
}),
|
|
286
|
+
profile.email ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
287
|
+
className: QoderCredentialCard_module_css_default.userEmail,
|
|
288
|
+
children: profile.email
|
|
289
|
+
}) : null,
|
|
290
|
+
plan?.organization?.orgName ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
291
|
+
className: QoderCredentialCard_module_css_default.orgTag,
|
|
292
|
+
children: t("organization", { name: plan.organization.orgName })
|
|
293
|
+
}) : null
|
|
294
|
+
]
|
|
270
295
|
})]
|
|
271
296
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
272
297
|
type: "button",
|
|
@@ -307,13 +332,73 @@ window.__ModuleLoader__.load({
|
|
|
307
332
|
});
|
|
308
333
|
}
|
|
309
334
|
//#endregion
|
|
335
|
+
//#region src/qoder/catalog.ts
|
|
336
|
+
const defaultMaxTokens = 32768;
|
|
337
|
+
const defaultModels = [
|
|
338
|
+
{
|
|
339
|
+
id: "cmodel",
|
|
340
|
+
name: "Cantus (Qoder)",
|
|
341
|
+
description: "Default Global Qoder subscription model for quick validation",
|
|
342
|
+
contextWindow: 1e6,
|
|
343
|
+
maxTokens: defaultMaxTokens,
|
|
344
|
+
supportsImages: true
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
id: "auto",
|
|
348
|
+
name: "Qoder Auto",
|
|
349
|
+
description: "Server-routed Global Qoder model pool",
|
|
350
|
+
contextWindow: 18e4,
|
|
351
|
+
maxTokens: defaultMaxTokens,
|
|
352
|
+
supportsImages: true
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
id: "ultimate",
|
|
356
|
+
name: "Qoder Ultimate",
|
|
357
|
+
description: "Highest-capability Global Qoder model pool",
|
|
358
|
+
contextWindow: 1e6,
|
|
359
|
+
maxTokens: defaultMaxTokens,
|
|
360
|
+
supportsImages: true
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
id: "performance",
|
|
364
|
+
name: "Qoder Performance",
|
|
365
|
+
description: "Performance-oriented Global Qoder model pool",
|
|
366
|
+
contextWindow: 1e6,
|
|
367
|
+
maxTokens: defaultMaxTokens,
|
|
368
|
+
supportsImages: true
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
id: "efficient",
|
|
372
|
+
name: "Qoder Efficient",
|
|
373
|
+
description: "Efficiency-oriented Global Qoder model pool",
|
|
374
|
+
contextWindow: 18e4,
|
|
375
|
+
maxTokens: defaultMaxTokens,
|
|
376
|
+
supportsImages: true
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
id: "lite",
|
|
380
|
+
name: "Qoder Lite",
|
|
381
|
+
description: "Basic Global Qoder model pool",
|
|
382
|
+
contextWindow: 18e4,
|
|
383
|
+
maxTokens: defaultMaxTokens
|
|
384
|
+
}
|
|
385
|
+
];
|
|
386
|
+
//#endregion
|
|
310
387
|
//#region src/client/credential-operations.ts
|
|
311
388
|
function reconcileQoderModels(known, discovered) {
|
|
312
389
|
const availableIds = new Set(discovered.map((model) => model.id));
|
|
313
390
|
const unavailable = known.filter((model) => !availableIds.has(model.id));
|
|
391
|
+
const previous = new Map(known.map((model) => [model.id, model]));
|
|
392
|
+
const reconciled = discovered.map((model) => {
|
|
393
|
+
const budget = previous.get(model.id)?.contextWindow;
|
|
394
|
+
return budget === void 0 || model.contextWindow === void 0 ? model : {
|
|
395
|
+
...model,
|
|
396
|
+
contextWindow: Math.min(budget, model.contextWindow)
|
|
397
|
+
};
|
|
398
|
+
});
|
|
314
399
|
return {
|
|
315
|
-
catalog: [...
|
|
316
|
-
selected:
|
|
400
|
+
catalog: [...reconciled, ...unavailable],
|
|
401
|
+
selected: reconciled,
|
|
317
402
|
unavailableIds: new Set(unavailable.map((model) => model.id))
|
|
318
403
|
};
|
|
319
404
|
}
|
|
@@ -417,10 +502,13 @@ window.__ModuleLoader__.load({
|
|
|
417
502
|
}
|
|
418
503
|
//#endregion
|
|
419
504
|
//#region src/client/QoderCredentialCard.tsx
|
|
420
|
-
function modelsOf(value) {
|
|
505
|
+
function modelsOf(value, selectedRegion) {
|
|
421
506
|
if (typeof value !== "object" || value === null) return [];
|
|
422
|
-
const
|
|
423
|
-
|
|
507
|
+
const section = value;
|
|
508
|
+
const region = selectedRegion ?? (section.region === "china" ? "china" : "global");
|
|
509
|
+
const scoped = section.modelsByRegion?.[region];
|
|
510
|
+
if (Array.isArray(scoped)) return scoped;
|
|
511
|
+
return region === (section.region === "china" ? "china" : "global") && Array.isArray(section.models) ? section.models : defaultModels.map((model) => ({ ...model }));
|
|
424
512
|
}
|
|
425
513
|
function regionOf(value) {
|
|
426
514
|
if (typeof value !== "object" || value === null) return "global";
|
|
@@ -439,13 +527,14 @@ window.__ModuleLoader__.load({
|
|
|
439
527
|
const modelSnapshot = (0, react.useSyncExternalStore)(operations.subscribeModels, operations.getModelSnapshot, operations.getModelSnapshot);
|
|
440
528
|
(0, react.useEffect)(() => {
|
|
441
529
|
if (modelSnapshot.status === "ready") {
|
|
442
|
-
if (!modelsDirty) setModelDraft(modelsOf(modelSnapshot.value));
|
|
530
|
+
if (!modelsDirty) setModelDraft(modelsOf(modelSnapshot.value, regionDraft));
|
|
443
531
|
if (!regionDirty) setRegionDraft(regionOf(modelSnapshot.value));
|
|
444
532
|
}
|
|
445
533
|
}, [
|
|
446
534
|
modelSnapshot,
|
|
447
535
|
modelsDirty,
|
|
448
|
-
regionDirty
|
|
536
|
+
regionDirty,
|
|
537
|
+
regionDraft
|
|
449
538
|
]);
|
|
450
539
|
const load = (0, react.useCallback)(async () => {
|
|
451
540
|
const info = await operations.describe();
|
|
@@ -488,7 +577,7 @@ window.__ModuleLoader__.load({
|
|
|
488
577
|
const currentRegion = regionOf(modelSnapshot.value);
|
|
489
578
|
const closeEditor = () => {
|
|
490
579
|
setDraft("");
|
|
491
|
-
setModelDraft(modelsOf(modelSnapshot.value));
|
|
580
|
+
setModelDraft(modelsOf(modelSnapshot.value, regionOf(modelSnapshot.value)));
|
|
492
581
|
setRegionDraft(regionOf(modelSnapshot.value));
|
|
493
582
|
setModelsDirty(false);
|
|
494
583
|
setRegionDirty(false);
|
|
@@ -508,7 +597,7 @@ window.__ModuleLoader__.load({
|
|
|
508
597
|
setRegionDirty(false);
|
|
509
598
|
}
|
|
510
599
|
if (modelsDirty) {
|
|
511
|
-
if (!await operations.storeModels(modelDraft)) {
|
|
600
|
+
if (!await operations.storeModels(regionDraft, modelDraft)) {
|
|
512
601
|
setNotice("saveFailed");
|
|
513
602
|
setMutation(void 0);
|
|
514
603
|
return;
|
|
@@ -598,6 +687,8 @@ window.__ModuleLoader__.load({
|
|
|
598
687
|
disabled: !modelWritable || busy,
|
|
599
688
|
onClick: () => {
|
|
600
689
|
setRegionDraft("global");
|
|
690
|
+
setModelDraft(modelsOf(modelSnapshot.value, "global"));
|
|
691
|
+
setModelsDirty(false);
|
|
601
692
|
setRegionDirty(true);
|
|
602
693
|
},
|
|
603
694
|
children: t("regionGlobal")
|
|
@@ -609,6 +700,8 @@ window.__ModuleLoader__.load({
|
|
|
609
700
|
disabled: !modelWritable || busy,
|
|
610
701
|
onClick: () => {
|
|
611
702
|
setRegionDraft("china");
|
|
703
|
+
setModelDraft(modelsOf(modelSnapshot.value, "china"));
|
|
704
|
+
setModelsDirty(false);
|
|
612
705
|
setRegionDirty(true);
|
|
613
706
|
},
|
|
614
707
|
children: t("regionChina")
|
|
@@ -750,7 +843,11 @@ window.__ModuleLoader__.load({
|
|
|
750
843
|
quotaUsed: "已用 {used} / {total} {unit}",
|
|
751
844
|
resetsAt: "重置时间:{value}",
|
|
752
845
|
noQuota: "暂无可用额度信息",
|
|
753
|
-
quotaExceeded: "当前订阅额度已用尽。"
|
|
846
|
+
quotaExceeded: "当前订阅额度已用尽。",
|
|
847
|
+
plan: "套餐计划",
|
|
848
|
+
planExpiresAt: "有效期至:{value}",
|
|
849
|
+
organization: "组织:{name}",
|
|
850
|
+
accountSuspended: "当前账号或组织已被暂停/冻结。"
|
|
754
851
|
};
|
|
755
852
|
const en = {
|
|
756
853
|
nav: "Qoder",
|
|
@@ -802,7 +899,11 @@ window.__ModuleLoader__.load({
|
|
|
802
899
|
quotaUsed: "Used {used} / {total} {unit}",
|
|
803
900
|
resetsAt: "Resets: {value}",
|
|
804
901
|
noQuota: "No quota metrics available",
|
|
805
|
-
quotaExceeded: "The current subscription quota is exhausted."
|
|
902
|
+
quotaExceeded: "The current subscription quota is exhausted.",
|
|
903
|
+
plan: "Plan",
|
|
904
|
+
planExpiresAt: "Expires at: {value}",
|
|
905
|
+
organization: "Org: {name}",
|
|
906
|
+
accountSuspended: "Account or organization is currently suspended."
|
|
806
907
|
};
|
|
807
908
|
//#endregion
|
|
808
909
|
//#region src/client/index.ts
|
|
@@ -872,9 +973,13 @@ window.__ModuleLoader__.load({
|
|
|
872
973
|
},
|
|
873
974
|
getModelSnapshot: () => modelScope.getSnapshot(),
|
|
874
975
|
subscribeModels: (listener) => modelScope.subscribe(listener),
|
|
875
|
-
storeModels: async (models) => {
|
|
976
|
+
storeModels: async (region, models) => {
|
|
876
977
|
try {
|
|
877
|
-
|
|
978
|
+
const current = modelScope.getSnapshot().value;
|
|
979
|
+
await modelScope.set("modelsByRegion", {
|
|
980
|
+
...current?.modelsByRegion,
|
|
981
|
+
[region]: models
|
|
982
|
+
});
|
|
878
983
|
return true;
|
|
879
984
|
} catch {
|
|
880
985
|
return false;
|