dsh-opendesign-corouter 0.1.1 → 0.1.3

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 CHANGED
@@ -32,7 +32,7 @@ dsh plugin --profile web add dsh-opendesign-corouter
32
32
  也可打开 [Actions → CI](https://github.com/KouzakiUmi/dsh-opendesign-corouter/actions/workflows/ci.yml),从成功构建下载 **`dsh-opendesign-corouter-build`** artifact。登录 GitHub 后解压 ZIP,取得 `.tgz` 与 `.sha256`,再安装本地包:
33
33
 
34
34
  ```console
35
- dsh plugin --profile web add /absolute/path/to/dsh-opendesign-corouter-0.1.1.tgz
35
+ dsh plugin --profile web add /absolute/path/to/dsh-opendesign-corouter-0.1.2.tgz
36
36
  ```
37
37
 
38
38
  `web` 只是示例,请替换为目标 Profile。Desktop 使用桌面安装器提供的 `dsh` 入口;独立公共 CLI 不能代管保留的 `desktop` Profile。安装后的启用与重载按目标 DSH 的管理流程进行。
@@ -48,14 +48,16 @@ dsh plugin --profile web add /absolute/path/to/dsh-opendesign-corouter-0.1.1.tgz
48
48
 
49
49
  ## 功能预览
50
50
 
51
- ### 两个 Provider,目录分开
51
+ ### 两个 Provider,逐模型配置
52
52
 
53
53
  | Provider | 模型数 | 用途 |
54
54
  | --- | ---: | --- |
55
55
  | `opendesign-corouter-plan` | 12 | 官网 Design Plan 套餐赠送模型 |
56
56
  | `opendesign-corouter-paid` | 21 | 其余公开对话模型 |
57
57
 
58
- 模型 ID 来自 [CoRouter 官方目录](https://corouter.open-design.ai/zh/models/)。媒体生成接口不在对话目录中;缺乏可靠证据的能力字段保持未知。
58
+ 上表为内置目录。模型页可从 OpenAI `/models` 端口刷新,逐个启用或停用模型,并配置温度、top-p、默认输出上限、上下文预算及思考深度。停用同时影响模型选择器和直接调用;参数会保存并应用到实际请求。媒体生成接口不在对话目录中,未知能力不按名称猜测。
59
+
60
+ 推理协议可选 **OpenAI Chat Completions、OpenAI Responses、Anthropic Messages**。模型发现始终使用 OpenAI 端口,不随推理协议切换。
59
61
 
60
62
  > **分组不等于计费隔离。** 赠送额度耗尽后是否转扣套餐余额或钱包,由服务端和账号权益决定。选择“套餐赠送”不能保证绝不消耗钱包余额。
61
63
 
@@ -66,7 +68,7 @@ dsh plugin --profile web add /absolute/path/to/dsh-opendesign-corouter-0.1.1.tgz
66
68
  - **充值 / 赠送金额**:钱包中对应池的可用金额。
67
69
  - **总额度**:接口报告的总余额,不再与分池数字重复相加。
68
70
 
69
- 接口缺失或权限不足时显示未知 / 部分可用,不补成 0。桌面通道权限较窄时可能只读到窗口与总额;独立登录可补充控制台分池读取。额度属于当前登录身份,插件未核对它与推理 Key 的账号 / 工作区是否一致。
71
+ 接口缺失或权限不足时显示未知 / 部分可用,不补成 0。窗口重置、订阅账期结束和下次自动刷新分别显示。桌面通道只有部分额度且已有完整独立会话时,会整体切换到独立会话并标明来源,不拼接账号数据。额度身份与推理 Key 的账号 / 工作区仍需保持一致。
70
72
 
71
73
  ### 桌面优先,也能独立登录
72
74
 
package/lib/adapter.d.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  import { LlmAdapter, type GenerateOptions, type LlmModelInfo, type LlmResolvedModelInfo, type PreparedAdapterCall, type StreamChunk } from '@deepseek-ai/dsh-llm';
2
2
  import type { ImageAttachmentRef } from '@deepseek-ai/dsh-attachment';
3
+ import { type CatalogModel } from './catalog.js';
3
4
  import { type RuntimeConfig } from './config.js';
4
5
  export interface AdapterOptions {
5
6
  config(): RuntimeConfig;
7
+ models?(): readonly CatalogModel[];
6
8
  apiKey(ref: string): Promise<string | undefined>;
7
9
  imageDataUrl?(image: ImageAttachmentRef, signal: AbortSignal): Promise<string | undefined>;
8
10
  onEmptyTurn?(): void;
@@ -17,6 +19,7 @@ export declare class CoRouterAdapter extends LlmAdapter {
17
19
  id: string;
18
20
  name: string;
19
21
  };
22
+ private modelOf;
20
23
  listModels(provider: string): Promise<readonly LlmModelInfo[]>;
21
24
  resolveModel(provider: string, model: string, signal?: AbortSignal): Promise<LlmResolvedModelInfo>;
22
25
  prepareCall(provider: string, model: string, signal?: AbortSignal): Promise<PreparedAdapterCall>;
package/lib/client.d.ts CHANGED
@@ -1,7 +1,10 @@
1
1
  import * as React from 'react';
2
2
  import openDesignCoRouterRemote, { type CoRouterRemote } from './generated/typert.remote-client.js';
3
3
  import type { TypertClientRemote } from '@deepseek-ai/dsh-typert-protocol';
4
- import type { QuotaSnapshot } from './quota.js';
4
+ import { type Status, type StatusModel } from './remote.js';
5
+ import type { QuotaSnapshot, QuotaWindow } from './quota.js';
6
+ import { type ApiProtocol } from './protocol-options.js';
7
+ import type { ModelSettings, ModelSettingsMap } from './model-settings.js';
5
8
  declare const TABS: readonly [{
6
9
  readonly id: "overview";
7
10
  readonly label: "概览";
@@ -17,37 +20,42 @@ declare const TABS: readonly [{
17
20
  }];
18
21
  type TabId = typeof TABS[number]['id'];
19
22
  export type RpcCaller = <T>(method: keyof CoRouterRemote, request?: unknown) => Promise<T>;
20
- interface FormValues {
23
+ export interface FormValues {
21
24
  emptyTurnGuard?: boolean;
22
25
  useDesktopCredential?: boolean;
23
26
  baseURL?: string;
24
27
  apiKeyEnv?: string;
28
+ apiProtocol?: ApiProtocol;
29
+ modelSettings?: ModelSettingsMap;
30
+ quotaRefreshSeconds?: number;
31
+ requestTimeoutMs?: number;
32
+ streamIdleTimeoutMs?: number;
25
33
  }
26
- interface FormSnapshot {
34
+ export interface FormSnapshot {
27
35
  status: 'loading' | 'ready' | 'unavailable';
28
36
  value: FormValues | undefined;
29
37
  writable: boolean;
30
38
  }
31
- interface ConfigForm {
39
+ export interface ConfigForm {
32
40
  getSnapshot(): FormSnapshot;
33
41
  subscribe(listener: () => void): () => void;
34
42
  set(field: string, value: unknown): Promise<boolean>;
35
43
  }
36
- interface ConfigForms {
44
+ export interface ConfigForms {
37
45
  get(namespace: string): ConfigForm;
38
46
  }
39
- interface SlotDescriptor<P> {
47
+ export interface SlotDescriptor<P> {
40
48
  name: 'settings.section';
41
49
  id: string;
42
50
  order: number;
43
51
  label: () => string;
44
52
  inject: () => P;
45
53
  }
46
- interface Slots {
54
+ export interface Slots {
47
55
  inject(name: 'settings.section', register: () => void | (() => void)): void | (() => void);
48
56
  register<P>(descriptor: SlotDescriptor<P>, component: React.ComponentType<P>): void | (() => void);
49
57
  }
50
- interface ClientContext {
58
+ export interface ClientContext {
51
59
  remote: TypertClientRemote & {
52
60
  $mount(contribution: typeof openDesignCoRouterRemote): Promise<() => void>;
53
61
  };
@@ -55,11 +63,11 @@ interface ClientContext {
55
63
  inject(services: ['configForms'], apply: (ctx: ConfigScope) => void): void;
56
64
  inject(services: ['remote.openDesignCoRouter'], apply: (ctx: RemoteScope) => void): void;
57
65
  }
58
- interface RemoteScope {
66
+ export interface RemoteScope {
59
67
  remote: TypertClientRemote;
60
68
  effect(effect: () => void | (() => void), label?: string): void;
61
69
  }
62
- interface ConfigScope {
70
+ export interface ConfigScope {
63
71
  configForms: ConfigForms;
64
72
  slots: Slots;
65
73
  effect(effect: () => void | (() => void), label?: string): void;
@@ -68,15 +76,113 @@ export interface SettingsProps {
68
76
  caller: RpcCaller;
69
77
  form: ConfigForm;
70
78
  }
79
+ interface Notice {
80
+ kind: 'status' | 'error';
81
+ text: string;
82
+ }
71
83
  export declare function formatUsd(value: number | undefined): string;
72
- export declare function QuotaView({ quota }: {
84
+ export declare function formatTime(value: string | undefined): string;
85
+ /** Token capacities use the same zh-CN grouping as amounts so large windows stay readable. */
86
+ export declare function formatTokens(value: number | undefined): string;
87
+ export interface QuotaViewProps {
73
88
  quota: QuotaSnapshot | undefined;
74
- }): React.DetailedReactHTMLElement<{
89
+ nextAutoRefreshAt?: string | undefined;
90
+ refreshIntervalSeconds?: number | undefined;
91
+ }
92
+ /** Earliest still-future window reset; quota recovery comes only from `windows[].resetsAt`. */
93
+ export declare function nextWindowResetAt(windows: readonly QuotaWindow[], now: number): string | undefined;
94
+ export declare function QuotaView({ quota, nextAutoRefreshAt, refreshIntervalSeconds }: QuotaViewProps): React.DetailedReactHTMLElement<{
75
95
  className: string;
76
96
  role: "status";
77
97
  }, HTMLElement> | React.FunctionComponentElement<{
78
98
  children?: React.ReactNode | undefined;
79
99
  }>;
100
+ /** Whole-map model settings writer: absent entry means "inherit every default". */
101
+ export declare function writeModelSettings(map: ModelSettingsMap | undefined, id: string, next: ModelSettings | undefined): ModelSettingsMap;
102
+ /** One model's explicit settings; unknown ids read as "no overrides". */
103
+ export declare function modelSettingsEntry(map: ModelSettingsMap | undefined, id: string): ModelSettings;
104
+ /**
105
+ * Explicit settings always win. When the settings map is authoritative (a ready form snapshot or
106
+ * the Host's mirror), an absent entry means "enabled"; only a non-authoritative view falls back to
107
+ * `Status.models[].enabled`, which can lag one refresh behind a reset.
108
+ */
109
+ export declare function modelEnabled(model: StatusModel, settings: ModelSettings | undefined, authoritative?: boolean): boolean;
110
+ export interface NumberInputSpec {
111
+ label: string;
112
+ min: number;
113
+ max: number;
114
+ integer?: boolean;
115
+ allowEmpty?: boolean;
116
+ }
117
+ export type NumberInputResult = {
118
+ ok: true;
119
+ value: number | undefined;
120
+ } | {
121
+ ok: false;
122
+ message: string;
123
+ };
124
+ /** Parse a label-bound numeric setting; an empty string means "inherit" when allowed. */
125
+ export declare function parseNumberInput(raw: string, spec: NumberInputSpec): NumberInputResult;
126
+ /** Absolute endpoint of the selected protocol; the gateway itself stays read-only. */
127
+ export declare function protocolEndpoint(baseURL: string, protocol: ApiProtocol): string;
128
+ export declare function normaliseQuotaRefreshSeconds(value: number | undefined): number | undefined;
129
+ export interface QuotaAutoRefresh {
130
+ delayMs: number;
131
+ at: string;
132
+ intervalSeconds: number;
133
+ }
134
+ /**
135
+ * Schedule the next quota refresh: the automatic interval normally, or earlier when the Host's
136
+ * cache policy reports its next planned read (`nextRefreshAt`, which is never a quota reset).
137
+ * During failure backoff the host plan wins, even when the normal interval is shorter.
138
+ * A timestamp that is already past must not create a 0 ms retry loop, so it falls back to the
139
+ * regular interval instead of hammering the console.
140
+ */
141
+ export declare function quotaAutoRefresh(secondsRaw: number | undefined, nextRefreshAt: string | undefined, now: number, backingOff?: boolean): QuotaAutoRefresh | undefined;
142
+ export interface OverviewProps {
143
+ status: Status | undefined;
144
+ busy: boolean;
145
+ onRefresh: () => void;
146
+ nextAutoRefreshAt?: string | undefined;
147
+ refreshIntervalSeconds?: number | undefined;
148
+ onConnection?: (() => void) | undefined;
149
+ }
150
+ export declare function Overview({ status, busy, onRefresh, nextAutoRefreshAt, refreshIntervalSeconds, onConnection }: OverviewProps): React.FunctionComponentElement<{
151
+ children?: React.ReactNode | undefined;
152
+ }>;
153
+ export interface ModelRowProps {
154
+ model: StatusModel;
155
+ settings: ModelSettings;
156
+ /** Whether an absent settings entry means enabled rather than the host catalog flag. */
157
+ authoritative: boolean;
158
+ writable: boolean;
159
+ pending: boolean;
160
+ onCommit: (next: ModelSettings | undefined) => Promise<boolean>;
161
+ }
162
+ export declare function ModelRow({ model, settings, authoritative, writable, pending, onCommit }: ModelRowProps): React.DetailedReactHTMLElement<{
163
+ className: string;
164
+ }, HTMLElement>;
165
+ export interface ModelsProps {
166
+ status: Status | undefined;
167
+ formSnapshot: FormSnapshot;
168
+ setFormField: (field: string, value: unknown) => Promise<boolean>;
169
+ pendingField: readonly string[];
170
+ onRefreshModels: () => void;
171
+ refreshingModels: boolean;
172
+ notice: Notice | null;
173
+ }
174
+ export declare function Models({ status, formSnapshot, setFormField, pendingField, onRefreshModels, refreshingModels, notice }: ModelsProps): React.FunctionComponentElement<{
175
+ children?: React.ReactNode | undefined;
176
+ }>;
177
+ export interface AdvancedProps {
178
+ status: Status | undefined;
179
+ formSnapshot: FormSnapshot;
180
+ setFormField: (field: string, value: unknown) => Promise<boolean>;
181
+ pendingField: readonly string[];
182
+ }
183
+ export declare function Advanced({ status, formSnapshot, setFormField, pendingField }: AdvancedProps): React.FunctionComponentElement<{
184
+ children?: React.ReactNode | undefined;
185
+ }>;
80
186
  export declare function tabAfter(current: TabId, key: string): TabId | undefined;
81
187
  export declare function SettingsPage({ caller, form }: SettingsProps): React.DetailedReactHTMLElement<{
82
188
  className: string;