dsh-cost-meter 1.6.12 → 1.6.13
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.en.md +5 -5
- package/README.md +5 -5
- package/lib/client.js +2 -2
- package/lib/coding-plans.js +81 -0
- package/lib/index.js +21 -1
- package/lib/plan-billing.js +1 -0
- package/lib/store.js +37 -0
- package/lib/typert.host.js +14 -0
- package/package.json +4 -2
package/lib/coding-plans.js
CHANGED
|
@@ -89,6 +89,14 @@ export const CODING_PLAN_PROVIDERS = {
|
|
|
89
89
|
credentialEnvsSecret: ['VOLC_SECRETKEY', 'VOLCENGINE_SECRET_ACCESS_KEY', 'VOLCENGINE_SECRET_KEY', 'ARK_SECRET_ACCESS_KEY'],
|
|
90
90
|
keyHint: '火山引擎访问密钥 AccessKeyID + SecretAccessKey(控制台 IAM→用户→密钥,需 ArkReadOnlyAccess + BillingCenterReadOnlyAccess;或导出 VOLC_ACCESSKEY / VOLC_SECRETKEY)',
|
|
91
91
|
},
|
|
92
|
+
qwen: {
|
|
93
|
+
label: '千问 Qwen Token Plan',
|
|
94
|
+
credentialEnvs: [],
|
|
95
|
+
// 千问 Token Plan(个人版,platform.qianwenai.com)未提供 API-Key 化的额度查询端点:
|
|
96
|
+
// 额度仅控制台可见(网关 cookie + sec_token 会话),不走网络。与 SCNet 同模式:
|
|
97
|
+
// 按官方 Credits 抵扣率由本地账本估算,无需任何凭据(issue #78)。
|
|
98
|
+
keyHint: '无需凭据:按官方 Credits 抵扣率由本地账本估算月度用量(抵扣率可在设置中修改)',
|
|
99
|
+
},
|
|
92
100
|
}
|
|
93
101
|
|
|
94
102
|
export const CODING_PLAN_PROVIDER_IDS = Object.keys(CODING_PLAN_PROVIDERS)
|
|
@@ -859,6 +867,77 @@ export function scnetTokenPlanWindows(days, entry, nowMs) {
|
|
|
859
867
|
}
|
|
860
868
|
}
|
|
861
869
|
|
|
870
|
+
/**
|
|
871
|
+
* 千问 Token Plan(个人版)Credits 抵扣率(issue #78)。
|
|
872
|
+
*
|
|
873
|
+
* 平台未提供 API-Key 化的额度查询端点(额度仅控制台可见,需 cookie+sec_token
|
|
874
|
+
* 网关会话),与 SCNet 同模式走本地账本估算。抵扣率取自平台定价页
|
|
875
|
+
* (platform.qianwenai.com,单位:每百万 token 抵扣的 Credits;个人版为 Credits
|
|
876
|
+
* 计费,5 小时 + 7 天滚动窗口,本表按官方页 2026-08 数值内置,可在设置中覆盖)。
|
|
877
|
+
* cachedInput 为缓存读抵扣率;cacheWrite 计入未命中输入。
|
|
878
|
+
*/
|
|
879
|
+
export const QWEN_CREDIT_RATES = {
|
|
880
|
+
'qwen3.8-max-preview': { input: 60, cachedInput: 6, output: 60 },
|
|
881
|
+
'qwen3.7-max': { input: 60, cachedInput: 6, output: 60 },
|
|
882
|
+
'qwen3.7-plus': { input: 15, cachedInput: 1.5, output: 60 },
|
|
883
|
+
'qwen3.6-flash': { input: 3, cachedInput: 0.3, output: 12 },
|
|
884
|
+
'glm-5.2': { input: 45, cachedInput: 4.5, output: 120 },
|
|
885
|
+
'deepseek-v4-pro': { input: 45, cachedInput: 4.5, output: 90 },
|
|
886
|
+
'deepseek-v4-flash': { input: 3, cachedInput: 0.3, output: 12 },
|
|
887
|
+
'kimi-k2.7-code': { input: 30, cachedInput: 3, output: 60 },
|
|
888
|
+
'kimi-k2.6': { input: 30, cachedInput: 3, output: 60 },
|
|
889
|
+
'minimax-m2.5': { input: 30, cachedInput: 3, output: 60 },
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
const QWEN_CREDIT_RATES_BY_CANON = Object.fromEntries(
|
|
893
|
+
Object.entries(QWEN_CREDIT_RATES).map(([id, rate]) => [scnetCanonModelId(id), rate]),
|
|
894
|
+
)
|
|
895
|
+
|
|
896
|
+
/**
|
|
897
|
+
* 汇总本地账本当前计费周期(自然月,与 SCNet 同款 scnetPlanPeriod)的千问
|
|
898
|
+
* Credits 用量。entry 为 codingPlans.qwen 配置:{ planCredits(必填,月度总额),
|
|
899
|
+
* planStart(可选,计费周期锚日), rates(可选,模型名 → 抵扣率覆盖,键为归一模型名) }。
|
|
900
|
+
* 返回 null(planCredits 非法)或 { used, total, percent, resetsAt, byModel, windows }。
|
|
901
|
+
*/
|
|
902
|
+
export function qwenTokenPlanWindows(days, entry, nowMs) {
|
|
903
|
+
const total = Number(entry?.planCredits)
|
|
904
|
+
if (!Number.isFinite(total) || total <= 0) return null
|
|
905
|
+
// 用户覆盖的抵扣率优先,内置表兜底;键与模型名均做字母数字归一比较。
|
|
906
|
+
const overrides = entry?.rates !== null && typeof entry?.rates === 'object' && !Array.isArray(entry?.rates) ? entry.rates : {}
|
|
907
|
+
const rateOf = canon => {
|
|
908
|
+
if (overrides[canon] !== undefined && overrides[canon] !== null && typeof overrides[canon] === 'object') return overrides[canon]
|
|
909
|
+
return QWEN_CREDIT_RATES_BY_CANON[canon]
|
|
910
|
+
}
|
|
911
|
+
const period = scnetPlanPeriod(nowMs, entry?.planStart)
|
|
912
|
+
const byModel = {}
|
|
913
|
+
let used = 0
|
|
914
|
+
for (const [date, day] of Object.entries(days ?? {})) {
|
|
915
|
+
if (typeof date !== 'string' || date < period.fromKey || date > period.toKeyInclusive) continue
|
|
916
|
+
for (const [pmKey, buckets] of Object.entries(day?.byProviderModel ?? {})) {
|
|
917
|
+
const model = pmKey.includes(':') ? pmKey.slice(pmKey.indexOf(':') + 1) : pmKey
|
|
918
|
+
const canon = scnetCanonModelId(model)
|
|
919
|
+
const rate = rateOf(canon)
|
|
920
|
+
if (rate === undefined || buckets === null || typeof buckets !== 'object') continue
|
|
921
|
+
const credits = scnetModelCredits(buckets, rate)
|
|
922
|
+
used += credits
|
|
923
|
+
byModel[canon] = (byModel[canon] ?? 0) + credits
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
const percent = Math.min(100, Math.round((used / total) * 1000) / 10)
|
|
927
|
+
const fmt = n => Math.round(n).toLocaleString('en-US')
|
|
928
|
+
return {
|
|
929
|
+
used,
|
|
930
|
+
total,
|
|
931
|
+
percent,
|
|
932
|
+
resetsAt: period.resetsAt,
|
|
933
|
+
byModel,
|
|
934
|
+
windows: {
|
|
935
|
+
monthly: { percent, resetsAt: period.resetsAt },
|
|
936
|
+
credits: { resetsAt: '', text: `${fmt(used)} / ${fmt(total)} Credits (est.)` },
|
|
937
|
+
},
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
|
|
862
941
|
/** 各家固定官方端点(硬编码白名单;region 变体按序尝试)。 */
|
|
863
942
|
export const CODING_PLAN_ENDPOINTS = {
|
|
864
943
|
anthropic: ['https://api.anthropic.com/api/oauth/usage'],
|
|
@@ -891,6 +970,8 @@ export const CODING_PLAN_ENDPOINTS = {
|
|
|
891
970
|
commandcode: ['https://api.commandcode.ai/alpha/billing/credits'],
|
|
892
971
|
// SCNet 无 API-Key 化额度端点:本地 Credits 计量(见 scnetTokenPlanWindows),不走网络。
|
|
893
972
|
scnet: [],
|
|
973
|
+
// 千问 Token Plan 无 API-Key 化额度端点:本地 Credits 计量(见 qwenTokenPlanWindows),不走网络。
|
|
974
|
+
qwen: [],
|
|
894
975
|
// 火山方舟:管控面 OpenAPI(需 AK/SK+HMAC,非 Bearer),按 Action 变体尝试(见 VOLCENGINE_ACTIONS)
|
|
895
976
|
volcengine: VOLCENGINE_ACTIONS.map(action => `https://${VOLCENGINE_HOST}/?Action=${action}&Version=${VOLCENGINE_VERSION}`),
|
|
896
977
|
}
|
package/lib/index.js
CHANGED
|
@@ -23,7 +23,7 @@ import { backfillLegacyLedger, importLegacyHistory, repairForkSeed, repairProvid
|
|
|
23
23
|
import { createLlmStreamBilling } from './billing-stream.js'
|
|
24
24
|
import { OFFICIAL_PRICING_URL, OFFICIAL_PRICING_URL_ZH, LEGACY_BASE_BOUNDARY, normalizePrice, parsePricingHtml, costOf, providerPriceEntryFor, buildPriceCatalog, usdFromCost, isWrapperProviderId, wrapperUpstreamProvider } from './pricing.js'
|
|
25
25
|
import { createUsageDeduper, USAGE_DEDUP_WINDOW_MS, usageFingerprint } from './usage-dedup.js'
|
|
26
|
-
import { CODING_PLAN_PROVIDERS, CODING_PLAN_PROVIDER_IDS, queryCodingPlan, scnetTokenPlanWindows, emptyCustomBalance, queryCustomBalance, normalizeVolcengineKey } from './coding-plans.js'
|
|
26
|
+
import { CODING_PLAN_PROVIDERS, CODING_PLAN_PROVIDER_IDS, queryCodingPlan, scnetTokenPlanWindows, qwenTokenPlanWindows, emptyCustomBalance, queryCustomBalance, normalizeVolcengineKey } from './coding-plans.js'
|
|
27
27
|
import { recordSamples, buildPlanStats, canonicalWindowKey, periodStartOf, aggregateUsageSince, enabledPlanSetOf, pruneHourBuckets, suggestPlanAutoClasses } from './plan-billing.js'
|
|
28
28
|
import { fetchWithRetry } from './net.js'
|
|
29
29
|
import { stateSchema } from './typert.host.js'
|
|
@@ -86,6 +86,7 @@ const SERVER_MESSAGES = {
|
|
|
86
86
|
codingPlanRefreshed: '{provider} 额度已刷新',
|
|
87
87
|
codingPlanQueryFailed: '{provider} 额度查询失败:{message}',
|
|
88
88
|
scnetPlanCreditsInvalid: 'SCNet 月度 Credits 额度无效,请填写大于 0 的数值。',
|
|
89
|
+
qwenPlanCreditsInvalid: '千问月度 Credits 额度无效,请填写大于 0 的数值。',
|
|
89
90
|
legacyImportDone: '导入完成:更新 {days} 天、新增 {sessions} 个会话(扫描 {scanned} 份会话日志)。',
|
|
90
91
|
legacyImportNone: '没有可导入的安装前历史(扫描 {scanned} 份会话日志,缺失日期为空或已导入)。',
|
|
91
92
|
legacyImportFailed: '导入安装前历史失败:{message}',
|
|
@@ -142,6 +143,7 @@ const SERVER_MESSAGES = {
|
|
|
142
143
|
codingPlanRefreshed: '{provider} quota refreshed',
|
|
143
144
|
codingPlanQueryFailed: '{provider} quota query failed: {message}',
|
|
144
145
|
scnetPlanCreditsInvalid: 'Invalid SCNet monthly credits quota; enter a value greater than 0.',
|
|
146
|
+
qwenPlanCreditsInvalid: 'Invalid Qwen monthly credits quota; enter a value greater than 0.',
|
|
145
147
|
legacyImportDone: 'Import finished: {days} day(s) updated, {sessions} session(s) added (scanned {scanned} session logs).',
|
|
146
148
|
legacyImportNone: 'No pre-install history to import (scanned {scanned} session logs; missing dates are empty or already imported).',
|
|
147
149
|
legacyImportFailed: 'Failed to import pre-install history: {message}',
|
|
@@ -1362,6 +1364,12 @@ function createService(ctx, ledger) {
|
|
|
1362
1364
|
...await describePlanKey(credentials, id, ledger.config),
|
|
1363
1365
|
...cached,
|
|
1364
1366
|
windows: cached.windows !== null && typeof cached.windows === 'object' ? cached.windows : {},
|
|
1367
|
+
// 本地计量配置透传(scnet/qwen,issue #26/#78):设置页编辑入口需看到当前值。
|
|
1368
|
+
...(id === 'scnet' || id === 'qwen' ? {
|
|
1369
|
+
...(Number.isFinite(Number(cfg.planCredits)) && Number(cfg.planCredits) > 0 ? { planCredits: Number(cfg.planCredits) } : {}),
|
|
1370
|
+
...(typeof cfg.planStart === 'string' ? { planStart: cfg.planStart } : {}),
|
|
1371
|
+
} : {}),
|
|
1372
|
+
...(id === 'qwen' && cfg.rates !== null && typeof cfg.rates === 'object' && !Array.isArray(cfg.rates) ? { rates: cfg.rates } : {}),
|
|
1365
1373
|
}
|
|
1366
1374
|
}
|
|
1367
1375
|
return out
|
|
@@ -1386,6 +1394,18 @@ function createService(ctx, ledger) {
|
|
|
1386
1394
|
}
|
|
1387
1395
|
return
|
|
1388
1396
|
}
|
|
1397
|
+
// 千问 Token Plan(issue #78)与 SCNet 同型:平台无 API-Key 化额度端点(额度仅
|
|
1398
|
+
// 控制台可见,网关需 cookie+sec_token),按官方 Credits 抵扣率本地估算,跳过缓存间隔。
|
|
1399
|
+
if (id === 'qwen') {
|
|
1400
|
+
const result = qwenTokenPlanWindows(ledger.days ?? {}, config, Date.now())
|
|
1401
|
+
codingPlanCaches[id] = {
|
|
1402
|
+
fetchedAt: Date.now(),
|
|
1403
|
+
value: result === null
|
|
1404
|
+
? { ...emptyCodingPlan(), status: 'off', fetchedAt: Date.now(), message: tmsg(localeOf(ledger.config), 'qwenPlanCreditsInvalid') }
|
|
1405
|
+
: { status: 'ok', message: '', fetchedAt: Date.now(), windows: result.windows },
|
|
1406
|
+
}
|
|
1407
|
+
return
|
|
1408
|
+
}
|
|
1389
1409
|
const interval = Math.max(1, Number(config.refreshMinutes) || 15) * 60_000
|
|
1390
1410
|
let cache = codingPlanCaches[id]
|
|
1391
1411
|
if (!force && cache !== undefined && Date.now() - cache.fetchedAt < interval) return
|
package/lib/plan-billing.js
CHANGED
package/lib/store.js
CHANGED
|
@@ -91,6 +91,7 @@ export function defaultConfig() {
|
|
|
91
91
|
// SCNet Token Plan 无 API 额度端点:按官方 Credits 抵扣表本地估算(issue #26)。
|
|
92
92
|
// planCredits=月度 Credits 额度;planStart=订阅起始日(空 = 自然月)。
|
|
93
93
|
scnet: { enabled: false, display: 'settings', refreshMinutes: 15, apiKey: '', planCredits: 240000, planStart: '' },
|
|
94
|
+
qwen: { enabled: false, display: 'settings', refreshMinutes: 15, apiKey: '', planCredits: 500000, planStart: '', rates: {} },
|
|
94
95
|
// 火山方舟 Volcano Ark Coding Plan(issue #60):管控面 AK/SK+HMAC,三窗口(5h/weekly/monthly)
|
|
95
96
|
volcengine: { enabled: false, display: 'settings', refreshMinutes: 15, apiKey: '', accessKeyId: '', secretAccessKey: '' },
|
|
96
97
|
},
|
|
@@ -879,6 +880,25 @@ export function applyConfigPatch(current, rawPatch) {
|
|
|
879
880
|
entry.planCredits = Number.isFinite(credits) && credits > 0 ? credits : 240000
|
|
880
881
|
entry.planStart = typeof entry.planStart === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(entry.planStart) ? entry.planStart : ''
|
|
881
882
|
}
|
|
883
|
+
// 千问 Token Plan 本地计量专用字段(issue #78):月度 Credits 额度、订阅起始日
|
|
884
|
+
// 与抵扣率覆盖(归一模型名 → { input, cachedInput, output },仅正有限数保留)。
|
|
885
|
+
if (id === 'qwen') {
|
|
886
|
+
const credits = Number(entry.planCredits)
|
|
887
|
+
entry.planCredits = Number.isFinite(credits) && credits > 0 ? credits : 500000
|
|
888
|
+
entry.planStart = typeof entry.planStart === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(entry.planStart) ? entry.planStart : ''
|
|
889
|
+
const rates = entry.rates !== null && typeof entry.rates === 'object' && !Array.isArray(entry.rates) ? entry.rates : {}
|
|
890
|
+
const cleaned = {}
|
|
891
|
+
for (const [model, rate] of Object.entries(rates)) {
|
|
892
|
+
if (typeof model !== 'string' || model.length === 0 || rate === null || typeof rate !== 'object') continue
|
|
893
|
+
const num = v => { const n = Number(v); return Number.isFinite(n) && n > 0 ? n : null }
|
|
894
|
+
const input = num(rate.input)
|
|
895
|
+
const cachedInput = num(rate.cachedInput)
|
|
896
|
+
const output = num(rate.output)
|
|
897
|
+
if (input === null || cachedInput === null || output === null) continue
|
|
898
|
+
cleaned[model] = { input, cachedInput, output }
|
|
899
|
+
}
|
|
900
|
+
entry.rates = cleaned
|
|
901
|
+
}
|
|
882
902
|
// 火山方舟专用:AccessKeyID/SecretAccessKey(双凭据,空 = 未配置)。
|
|
883
903
|
if (id === 'volcengine') {
|
|
884
904
|
const rawId = typeof entry.accessKeyId === 'string' ? entry.accessKeyId.trim() : ''
|
|
@@ -1282,6 +1302,23 @@ export function sanitizeConfig(raw) {
|
|
|
1282
1302
|
plans[id].planCredits = Number.isFinite(credits) && credits > 0 ? credits : 240000
|
|
1283
1303
|
plans[id].planStart = typeof entry.planStart === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(entry.planStart) ? entry.planStart : ''
|
|
1284
1304
|
}
|
|
1305
|
+
if (id === 'qwen') {
|
|
1306
|
+
const credits = Number(entry.planCredits)
|
|
1307
|
+
plans[id].planCredits = Number.isFinite(credits) && credits > 0 ? credits : 500000
|
|
1308
|
+
plans[id].planStart = typeof entry.planStart === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(entry.planStart) ? entry.planStart : ''
|
|
1309
|
+
const rates = entry.rates !== null && typeof entry.rates === 'object' && !Array.isArray(entry.rates) ? entry.rates : {}
|
|
1310
|
+
const cleaned = {}
|
|
1311
|
+
for (const [model, rate] of Object.entries(rates)) {
|
|
1312
|
+
if (typeof model !== 'string' || model.length === 0 || rate === null || typeof rate !== 'object') continue
|
|
1313
|
+
const num = v => { const n = Number(v); return Number.isFinite(n) && n > 0 ? n : null }
|
|
1314
|
+
const input = num(rate.input)
|
|
1315
|
+
const cachedInput = num(rate.cachedInput)
|
|
1316
|
+
const output = num(rate.output)
|
|
1317
|
+
if (input === null || cachedInput === null || output === null) continue
|
|
1318
|
+
cleaned[model] = { input, cachedInput, output }
|
|
1319
|
+
}
|
|
1320
|
+
plans[id].rates = cleaned
|
|
1321
|
+
}
|
|
1285
1322
|
if (id === 'volcengine') {
|
|
1286
1323
|
const rawId = typeof entry.accessKeyId === 'string' ? entry.accessKeyId.trim() : ''
|
|
1287
1324
|
const rawApi = typeof entry.apiKey === 'string' ? entry.apiKey.trim() : ''
|
package/lib/typert.host.js
CHANGED
|
@@ -148,6 +148,12 @@ const configSchema = z.object({
|
|
|
148
148
|
// SCNet 本地计量专用(issue #26):月度 Credits 额度与订阅起始日;其余厂商无此二键。
|
|
149
149
|
planCredits: num.optional(),
|
|
150
150
|
planStart: z.string().optional(),
|
|
151
|
+
// 千问 Token Plan 抵扣率覆盖(issue #78):归一模型名 → 三费率;仅千问有此键。
|
|
152
|
+
rates: z.record(z.string(), z.object({
|
|
153
|
+
input: num,
|
|
154
|
+
cachedInput: num,
|
|
155
|
+
output: num,
|
|
156
|
+
})).optional(),
|
|
151
157
|
// 火山方舟双凭据(issue #60):AccessKeyID + SecretAccessKey
|
|
152
158
|
accessKeyId: z.string().optional(),
|
|
153
159
|
secretAccessKey: z.string().optional(),
|
|
@@ -275,6 +281,14 @@ const codingPlanSchema = z.object({
|
|
|
275
281
|
message: z.string(),
|
|
276
282
|
fetchedAt: num,
|
|
277
283
|
windows: z.record(z.string(), z.object({ percent: num.optional(), resetsAt: z.string(), text: z.string().optional() })),
|
|
284
|
+
// 本地计量配置透传(仅 scnet / qwen 有;settings 页编辑入口需要看到当前值)。
|
|
285
|
+
planCredits: num.optional(),
|
|
286
|
+
planStart: z.string().optional(),
|
|
287
|
+
rates: z.record(z.string(), z.object({
|
|
288
|
+
input: num,
|
|
289
|
+
cachedInput: num,
|
|
290
|
+
output: num,
|
|
291
|
+
})).optional(),
|
|
278
292
|
})
|
|
279
293
|
|
|
280
294
|
// Token Plan 统计(issue #64):每 provider×window 的本地聚合、每 1%/满窗估算与采样区间序列。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-cost-meter",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.13",
|
|
4
4
|
"description": "DeepSeek Harness 会话费用统计插件:本会话成本、当日费用、历史记录与官方价格同步,支持多厂商多模型价格计费(内置 90+ 模型价格目录与自动匹配)、主流 Coding Plan 订阅额度查询与显示(9 家,含 SCNet Token Plan 本地 Credits 计量与火山方舟 Volcano Ark Coding Plan AK/SK 签名查询)、自定义 Provider 余额查询(可配任意 HTTP 端点)与余额进度条、峰谷计价时段显示与切换前弹窗/系统通知提醒(位置/提前量/类型可配),界面中英双语。Session cost tracking plugin for DeepSeek Harness: per-conversation cost, daily totals, history and official price sync, with multi-vendor model pricing (built-in 90+ model catalog with auto-matching), Coding Plan quota queries & display for 9 vendors (incl. local credits metering for the SCNet Token Plan and Volcano Ark Coding Plan via AK/SK signature), plan/API dual-track billing with per-1% & full-window Token Plan usage estimates, custom provider balance lookup (configurable HTTP endpoint) and balance progress bar, plus peak/off-peak pricing display with pre-switch popup & system-notification alerts (position / lead time / type configurable), in a bilingual (Chinese/English) UI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -63,7 +63,9 @@
|
|
|
63
63
|
"https://openrouter.ai",
|
|
64
64
|
"https://api.siliconflow.cn",
|
|
65
65
|
"https://api.commandcode.ai",
|
|
66
|
-
"https://ax.ac.sugon.com"
|
|
66
|
+
"https://ax.ac.sugon.com",
|
|
67
|
+
"https://platform.qianwenai.com",
|
|
68
|
+
"https://platform-home.qianwenai.com"
|
|
67
69
|
]
|
|
68
70
|
}
|
|
69
71
|
},
|