dsh-codearts 0.2.1 → 0.4.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/lib/index.d.mts CHANGED
@@ -118,28 +118,39 @@ interface CodeArtsResolved {
118
118
  securityToken: string;
119
119
  }
120
120
  declare function canonicalModel(id: string): string;
121
- /** 该模型可用的 max_tokens 上限(没实测过的退回全局保守值)。 */
121
+ /** 该模型可用的 max_tokens 上限(实测值与会话内学到的更低值取小)。 */
122
122
  declare function maxTokensCeilingFor(id: string): number;
123
+ interface PreparedChatBody {
124
+ url: string;
125
+ body: string;
126
+ model: string;
127
+ chatId: string;
128
+ /**
129
+ * 不用发出去就能判定的失败(目前是「上下文放不下」)。非空时调用方必须直接把它
130
+ * 回给 DSH —— 上游遇到同类问题只会给一句 "The request param is invalid",
131
+ * 那句话里没有模型、没有 token 数,用户无从下手。
132
+ */
133
+ error?: string;
134
+ }
123
135
  /**
124
136
  * 把 OpenAI 风格 chat body 适配给 CodeArts /api/v2/chat/completions。
125
137
  * 该端点虽是 OpenAI 兼容,但网关按自己的 schema 严格校验参数。所以这里
126
138
  * 不做原样透传,而是:
127
139
  * 1. model 走 canonicalModel(大小写敏感)
128
- * 2. max_completion_tokens → max_tokens(华为只认后者),并钳制到
129
- * MAX_TOKENS_CEILING(超限会流内报 InferHub.001001005.400
130
- * "The request param is invalid, Please check it")
140
+ * 2. max_completion_tokens → max_tokens(华为只认后者),并钳到该模型的上限;
141
+ * 再按 contextWindow 扣掉 prompt 估算,保证 prompt + max_tokens 装得下
142
+ * (两者任一超限,上游都只会流内回 InferHub.001001005.400
143
+ * "The request param is invalid, Please check it",不告诉我们是哪一个)
131
144
  * 3. 丢弃非白名单字段(stream_options / store / logprobs 等虽实测可容忍,
132
145
  * 但按 codearts2api 观察到的真实流量收窄,避免网关后续收紧)
133
146
  * 4. tools[].function 去掉 strict(OpenAI 专有)
134
147
  * 5. 注入 CodeArts 特有字段:chat_id(按对话派生,见 conversationChatId)、
135
148
  * prompt_cache_key、tool_stream
149
+ *
150
+ * @param contextWindow 该模型的上下文窗口(来自 catalog)。传 0 表示不知道,
151
+ * 此时只做上限钳制、不做「装不下」判断。
136
152
  */
137
- declare function prepareChatBody(rawJson: string): {
138
- url: string;
139
- body: string;
140
- model: string;
141
- chatId: string;
142
- };
153
+ declare function prepareChatBody(rawJson: string, contextWindow?: number): PreparedChatBody;
143
154
  /**
144
155
  * 把上游累计快照 SSE 转换成 OpenAI 增量流。
145
156
  *
@@ -392,14 +403,15 @@ declare class CodeArtsAccountPool {
392
403
  /**
393
404
  * 用量账本:把聊天流里的 usage 帧累计成「今天用了多少」。
394
405
  *
395
- * 为什么只能自己记:**CodeArts 没有额度/积分 API**(codearts2api 的 pool 里
396
- * `credits` 字段恒为 0,注释写着「CodeArts 无积分字段」;本插件也没有任何余额接口
397
- * 可用)。能拿到的真实数据只有两样:
406
+ * 为什么还要自己记:账号总额度有积分接口了(见 credits.ts,2026-10 从官方 Agent
407
+ * 客户端挖出并实测通过),但**本机经插件发出的请求**仍需自己累计 —— 上游不按
408
+ * 「这台机器」维度给统计。能拿到的真实数据有三样:
398
409
  * 1. 每个请求的 token `usage`(上游在流末尾回一帧 `{choices:[],usage:{...}}`);
399
- * 2. 插件自己知道的账号会话槽位占用(见 account-pool:每账号 3 个并发会话)。
410
+ * 2. 插件自己知道的账号会话槽位占用(见 account-pool:每账号 3 个并发会话);
411
+ * 3. 账号积分/额度(见 credits.ts)。
400
412
  *
401
- * 所以这里累计的是**本机经插件发出的请求**,不是账号总额度 —— 卡片上的文案必须
402
- * 照实说,官方额度明细在 `codearts.huaweicloud.com/portal/settings/personal-usage`。
413
+ * 所以这里累计的是**本机经插件发出的请求**,与卡片上的积分区块互补 —— 一个是本机
414
+ * 级、一个是账号级。官方额度明细在 `codearts.huaweicloud.com/portal/settings/personal-usage`。
403
415
  *
404
416
  * 落盘在 `$DSH_HOME/.codearts-usage.json`,按天存,保留最近 `RETENTION_DAYS` 天。
405
417
  *
@@ -519,6 +531,120 @@ interface CodeArtsAdapter {
519
531
  }
520
532
  declare function createCodeArtsAdapter(options: CodeArtsAdapterOptions): CodeArtsAdapter;
521
533
  //#endregion
534
+ //#region src/credits.d.ts
535
+ /** 福利网关:token 余额接口在这里(与 benefit/claim、gateway/config 同一个 host)。 */
536
+ declare const BENEFIT_GATEWAY_HOST = "https://opengw.developer.huaweicloud.com";
537
+ /** 一类积分(总量/已用/剩余/即将过期)。上游给不出的字段留 undefined。 */
538
+ interface CodeArtsCreditCategory {
539
+ total: number;
540
+ used: number;
541
+ remain: number;
542
+ /** 会在本周期结束时作废的部分。 */
543
+ expiring: number;
544
+ }
545
+ /** 积分快照(来自 statistics/plugin)。 */
546
+ interface CodeArtsCredits {
547
+ /** 套餐显示名,例如「体验版」。 */
548
+ planName: string;
549
+ /** 套餐英文名(中文缺失时的兜底)。 */
550
+ planNameEn: string;
551
+ /** 上游给的 spec_code,例如 codearts.agent.individual.trial。 */
552
+ specCode: string;
553
+ /** normal → active、closed → expired、freeze → frozen。 */
554
+ planStatus: 'active' | 'expired' | 'frozen' | 'unknown';
555
+ /** 是否按积分计费(false 表示 token 套餐)。 */
556
+ isCreditPackage: boolean;
557
+ /** 变更/购买套餐的官方页面。 */
558
+ packageUrl?: string;
559
+ /** 额度周期起止(end 就是「限额重置日期」)。 */
560
+ periodStart?: string;
561
+ periodEnd?: string;
562
+ /** 总积分(usageTotalPackageCredit)。 */
563
+ total: CodeArtsCreditCategory;
564
+ /** 赠送积分(usageBonusPackageCredit)。 */
565
+ bonus?: CodeArtsCreditCategory;
566
+ /** 基础套餐积分(usageBasicPackageCredit)。 */
567
+ basic?: CodeArtsCreditCategory;
568
+ /** 按需积分(usageOnDemandPackageCredit)。 */
569
+ onDemand?: CodeArtsCreditCategory;
570
+ /** 抓取时刻(毫秒)。 */
571
+ fetchedAt: number;
572
+ }
573
+ /** token 额度快照(来自 tokens/balance)。 */
574
+ interface CodeArtsTokenQuota {
575
+ dailyLimit: number;
576
+ dailyUsed: number;
577
+ monthlyLimit: number;
578
+ monthlyUsed: number;
579
+ /** 剩余额度。 */
580
+ balance: number;
581
+ /** 总额度。 */
582
+ quota: number;
583
+ }
584
+ interface CodeArtsCreditSnapshot {
585
+ credits: CodeArtsCredits;
586
+ /** 拿不到(接口失败/不支持)时为 undefined,不影响积分本身。 */
587
+ tokens?: CodeArtsTokenQuota;
588
+ }
589
+ /** 缓存里的一个结果:成功给值,失败给消息 —— 失败也要能被卡片看见。 */
590
+ type CodeArtsCreditState = {
591
+ phase: 'ok';
592
+ value: CodeArtsCreditSnapshot;
593
+ at: number;
594
+ } | {
595
+ phase: 'error';
596
+ message: string;
597
+ at: number;
598
+ };
599
+ /**
600
+ * 解析 `GET /snap-manager/v1/statistics/plugin` 的响应。
601
+ *
602
+ * 返回 undefined 表示这不是一份能用的积分文档(上游改了形状/账号没有积分体系)——
603
+ * 调用方据此报「积分不可用」,而不是显示一排 0 假装是真的。
604
+ */
605
+ declare function parseCreditStatistics(raw: unknown): CodeArtsCredits | undefined;
606
+ /** 解析 `GET /api/v1/user/tokens/balance` 的响应(`result` 里是额度口径)。 */
607
+ declare function parseTokenBalance(raw: unknown): CodeArtsTokenQuota | undefined;
608
+ /**
609
+ * 拉一次积分与 token 额度。
610
+ *
611
+ * 积分是主口径:它失败就整体失败(卡片要能看见原因)。token 额度是附带的,
612
+ * 它失败只让 `tokens` 缺席 —— 别让一个次要接口把主数字一起拖没。
613
+ */
614
+ declare function fetchCredits(credential: CodeArtsResolved, signal?: AbortSignal): Promise<CodeArtsCreditSnapshot>;
615
+ interface CreditCacheOptions {
616
+ ttlMs?: number;
617
+ failureTtlMs?: number;
618
+ /** 便于测试注入。 */
619
+ load?: (credential: CodeArtsResolved, signal?: AbortSignal) => Promise<CodeArtsCreditSnapshot>;
620
+ now?: () => number;
621
+ }
622
+ /**
623
+ * 积分缓存(TTL + 单飞)。
624
+ *
625
+ * 单飞很重要:卡片刷新、对话底部的轮询可能同时打进来,没有它就会并发问上游好几次;
626
+ * 而上游对同一账号的并发是有成本/风险的。
627
+ */
628
+ declare class CodeArtsCreditCache {
629
+ private state;
630
+ private inflight;
631
+ private readonly ttlMs;
632
+ private readonly failureTtlMs;
633
+ private readonly load;
634
+ private readonly now;
635
+ constructor(options?: CreditCacheOptions);
636
+ /** 当前缓存内容(不触发请求),供只想读快照的场景使用。 */
637
+ peek(): CodeArtsCreditState | undefined;
638
+ /** 丢掉缓存,下次 get 会重新拉(重新登录/添加账号后调用)。 */
639
+ invalidate(): void;
640
+ /**
641
+ * 取一次积分。命中缓存直接返回;否则合并成一次上游请求。
642
+ * **不抛异常**:失败以 `phase: 'error'` 返回,调用方(状态路由)照样能渲染。
643
+ */
644
+ get(credential: CodeArtsResolved, signal?: AbortSignal): Promise<CodeArtsCreditState>;
645
+ private run;
646
+ }
647
+ //#endregion
522
648
  //#region src/discovery.d.ts
523
649
  interface AccountDiscovery {
524
650
  accountId: string;
@@ -568,4 +694,4 @@ interface Config {
568
694
  declare const Config: z<Config>;
569
695
  declare function apply(ctx: Context, rawConfig: Config): void;
570
696
  //#endregion
571
- export { type AccountRuntime, CODEARTS_PROVIDER, CODEARTS_SETTINGS_NS, CodeArtsAccountPool, CodeArtsAccountStore, type CodeArtsAccountSummary, type CodeArtsAdapter, CodeArtsCatalog, type CodeArtsCredential, CodeArtsCredentialStore, type CodeArtsModelInfo, type CodeArtsShim, CodeArtsUsageTracker, Config, type DiscoveryOutcome, FALLBACK_CODEARTS_MODELS, type ModelDiscoveryResult, SNAP_HOST, type UpstreamErrorKind, type UsageSnapshot, type UsageTotals, apply, canonicalModel, chatStream, codeartsAccountsPath, codeartsAuthPath, codeartsUsagePath, createCodeArtsAdapter, createCodeArtsShim, discoverModels, fetchModels, filterEnabledModels, inject, maxTokensCeilingFor, name, prepareChatBody, refreshCatalog };
697
+ export { type AccountRuntime, BENEFIT_GATEWAY_HOST, CODEARTS_PROVIDER, CODEARTS_SETTINGS_NS, CodeArtsAccountPool, CodeArtsAccountStore, type CodeArtsAccountSummary, type CodeArtsAdapter, CodeArtsCatalog, type CodeArtsCredential, CodeArtsCredentialStore, CodeArtsCreditCache, type CodeArtsCreditCategory, type CodeArtsCreditSnapshot, type CodeArtsCreditState, type CodeArtsCredits, type CodeArtsModelInfo, type CodeArtsShim, type CodeArtsTokenQuota, CodeArtsUsageTracker, Config, type DiscoveryOutcome, FALLBACK_CODEARTS_MODELS, type ModelDiscoveryResult, SNAP_HOST, type UpstreamErrorKind, type UsageSnapshot, type UsageTotals, apply, canonicalModel, chatStream, codeartsAccountsPath, codeartsAuthPath, codeartsUsagePath, createCodeArtsAdapter, createCodeArtsShim, discoverModels, fetchCredits, fetchModels, filterEnabledModels, inject, maxTokensCeilingFor, name, parseCreditStatistics, parseTokenBalance, prepareChatBody, refreshCatalog };