dsh-plugin-model-tuning 2.0.2 → 3.0.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.
Files changed (6) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +83 -219
  3. package/client.js +463 -1020
  4. package/cordis.patch.yml +10 -19
  5. package/index.js +20 -178
  6. package/package.json +5 -11
package/cordis.patch.yml CHANGED
@@ -1,24 +1,15 @@
1
- # dsh-plugin-model-tuning v2 — 自 wiring + 显式替换宿主自带的标题生成行。
1
+ # dsh-plugin-model-tuning v3 self-wiring (dsh.bundle.patch mechanism).
2
2
  #
3
- # 顺序即语义:
4
- # 1. 先 disable dsh-base 的 `session-title-llm` 行(@deepseek-ai/dsh-session-title-first-prompt-llm)。
5
- # `ctx.sessionTitle.register()` 只允许一个 provider,第二次注册直接抛错,
6
- # 所以必须先摘掉原行再插入本行;反过来 boot 会失败。
7
- # 2. 再 insert 本包(v2 起一个包承担:模型参数页 + 标题路由 + 绘图工具/面板)。
3
+ # v3 keeps only the drawing capability: the `generate_image` /
4
+ # `list_image_models` tools plus the `imageGeneration` Remote service behind the
5
+ # settings page. The per-model compatibility switches on the official Models
6
+ # page and the drawing page need no extra rows.
8
7
  #
9
- # config 中标题预算字段与 dsh-base 的 `session-title-llm` 完全一致,因此无论
10
- # 设置处于默认「继承」还是被卸载回退,标题生成的提示词预算、字节上限、
11
- # 超时行为都不变。绘图 models/defaultModel 与标题 titleMode/titleProvider/
12
- # titleModel 是 volatile 字段,热更新即时生效。
13
- - id: session-title-llm
14
- disabled: true
15
-
8
+ # The v2 patch disabled the shipped `session-title-llm` row and inserted this
9
+ # bundle as a replacement title provider. That is gone: the shipped row already
10
+ # supports a provider/model route plus budgets in its Config, so title routing
11
+ # is configured again in the profile's cordis.patch.yml (and the official
12
+ # "标题随对话更新" optional bundle can be used normally).
16
13
  - insert:
17
14
  - id: model-tuning
18
15
  name: dsh-plugin-model-tuning
19
- config:
20
- targetWords: 5
21
- targetCjkCharacters: 10
22
- maxInputBytes: 4096
23
- maxOutputTokens: 64
24
- timeoutMs: 60000
package/index.js CHANGED
@@ -1,33 +1,17 @@
1
1
  /**
2
- * Host half of `dsh-plugin-model-tuning` (v2: merged with the former
3
- * `dsh-plugin-title-model` and `dsh-plugin-image-generation`).
2
+ * Host half of `dsh-plugin-model-tuning` v3.
4
3
  *
5
- * Three responsibilities, one entry:
6
- *
7
- * 1. Session-title routing (ex dsh-plugin-title-model). This plugin REPLACES
8
- * the deployment's shipped title provider row
9
- * (`@deepseek-ai/dsh-session-title-first-prompt-llm`) through the bundle
10
- * patch in `cordis.patch.yml`. It is still exactly one provider:
11
- * `ctx.sessionTitle.register()` throws on a second registration, so a
12
- * competing generator cannot coexist. Everything except the route is
13
- * delegated to the public shared policy `generateSessionTitleWithLlm` from
14
- * `@deepseek-ai/dsh-session-title-llm`, resolved from the RUNNING
15
- * deployment (see hostRequire) — never from this package's directory, which
16
- * for a linked workspace package would risk a second copy of the host's
17
- * shared module graph.
18
- *
19
- * 2. Image generation (ex dsh-plugin-image-generation): the `generate_image`
20
- * and `list_image_models` tools plus the `imageGeneration` Remote service
21
- * that the 直接绘图 panel drives. Transport lives in `./core.js`.
22
- *
23
- * 3. Nothing else: the settings surface (模型参数 / 用途 / 绘图 tabs) lives in
24
- * the browser implementation exported from `./client`.
4
+ * v3 keeps exactly one capability here: image generation (the former
5
+ * `dsh-plugin-image-generation`). Session-title routing was removed because the
6
+ * host's own `@deepseek-ai/dsh-session-title-first-prompt-llm` row already
7
+ * supports a provider/model route plus budgets in its Config; configure it in
8
+ * the profile's `cordis.patch.yml`. The per-model compatibility switches and
9
+ * the catalog-route reasoning override need no host half: the browser half
10
+ * writes the provider's settings namespace directly.
25
11
  *
26
12
  * @module dsh-plugin-model-tuning
27
13
  */
28
14
  import { randomUUID } from 'node:crypto'
29
- import { pathToFileURL } from 'node:url'
30
- import { createRequire } from 'node:module'
31
15
  import z from '@deepseek-ai/schemastery'
32
16
  import { defineTool } from '@deepseek-ai/dsh-tools'
33
17
  import { TypertRemoteService, RemoteError } from '@deepseek-ai/dsh-typert-protocol'
@@ -36,27 +20,12 @@ import { discoverImageModels, generateImages, resolveRequest, validateConfig } f
36
20
  export const name = 'dsh-plugin-model-tuning'
37
21
 
38
22
  /** Services this plugin reads. */
39
- export const inject = ['tools', 'attachments', 'sessionTitle', 'llm']
23
+ export const inject = ['tools', 'attachments']
40
24
 
41
- /** Settings namespace owned by this plugin (one entry, one schema). */
25
+ /** Settings namespace owned by this plugin. */
42
26
  export const NS = 'model-tuning'
43
27
 
44
- /**
45
- * Provider identity recorded with generated titles. Kept equal to the row this
46
- * plugin replaces so durable logs and the folded title source stay unchanged.
47
- */
48
- const TITLE_PROVIDER_ID = 'session-title-llm'
49
-
50
- /**
51
- * Unified Config: image-generation fields (models / defaultModel) plus the
52
- * title route fields (titleMode / titleProvider / titleModel), all volatile.
53
- *
54
- * The title budget fields mirror the `session-title-llm` config in
55
- * `@deepseek-ai/dsh-base` so that removing this bundle's `config:` block
56
- * cannot change title behaviour. Title fields are prefixed `title` so the
57
- * pre-merge `title-model` section could be migrated mechanically; the former
58
- * `image-generation` section's `models`/`defaultModel` keys are kept verbatim.
59
- */
28
+ /** One drawing model. `source` is legacy provenance kept so old linked entries still parse. */
60
29
  const imageModelSchema = z.object({
61
30
  id: z.string().required(), name: z.string(), model: z.string().required(),
62
31
  api: z.union(['openai-images']).default('openai-images'),
@@ -71,142 +40,16 @@ const imageModelSchema = z.object({
71
40
  output_format: z.string(), response_format: z.string(), style: z.string(),
72
41
  })
73
42
 
43
+ /**
44
+ * Config: drawing models only. Removed v2 title fields (`titleMode` etc.) are
45
+ * tolerated by the loader's schema and ignored, so an existing profile keeps
46
+ * loading without a migration step.
47
+ */
74
48
  export const Config = z.object({
75
- // ── 绘图(ex image-generation;键名原样保留,旧数据可直接搬入) ──
76
49
  models: z.array(imageModelSchema).default([]).volatile(),
77
50
  defaultModel: z.string().volatile(),
78
- // ── 标题生成(ex title-model;title 前缀迁移自 mode/provider/model) ──
79
- titleMode: z.union([z.const('inherit'), z.const('custom')]).default('inherit').volatile(),
80
- titleProvider: z.string().volatile(),
81
- titleModel: z.string().volatile(),
82
- // ── 标题请求预算(用户可在「用途」页改;默认值与 dsh-base 的 session-title-llm 行一致) ──
83
- targetWords: z.number().step(1).min(1).default(5).volatile(),
84
- targetCjkCharacters: z.number().step(1).min(1).default(10).volatile(),
85
- maxInputBytes: z.number().step(1).min(1).default(4096).volatile(),
86
- maxOutputTokens: z.number().step(1).min(1).default(64).volatile(),
87
- timeoutMs: z.number().step(1).min(1).default(60000).volatile(),
88
51
  })
89
52
 
90
- /** Reject a custom title mode that cannot name both halves of a route. */
91
- export function validateTitleModel(value) {
92
- const provider = value?.titleProvider
93
- const model = value?.titleModel
94
- const hasProvider = provider !== undefined && provider !== null && provider !== ''
95
- const hasModel = model !== undefined && model !== null && model !== ''
96
- if (hasProvider !== hasModel) throw new Error('model-tuning: titleProvider 与 titleModel 必须同时填写或同时留空')
97
- if (value?.titleMode === 'custom' && !hasProvider) throw new Error('model-tuning: titleMode 为 custom 时必须同时选择 provider 与 model')
98
- }
99
-
100
- /**
101
- * Resolve the exact route recorded for one generation request.
102
- * @param settings - resolved title fields (`titleMode`/`titleProvider`/`titleModel`).
103
- * @param request - service-owned request carrying the session's logged route.
104
- * @returns the provider/model pair to dispatch.
105
- */
106
- export function resolveTitleRoute(settings, request) {
107
- validateTitleModel(settings)
108
- if (settings?.titleMode === 'custom') {
109
- return { provider: settings.titleProvider, model: settings.titleModel }
110
- }
111
- if (request?.route === undefined) {
112
- throw new Error('session-title-llm: no logged request route is available; configure provider and model together')
113
- }
114
- return { provider: request.route.provider, model: request.route.model }
115
- }
116
-
117
- /** The shared title policy module, loaded once per process. */
118
- const HELPER_PACKAGE = '@deepseek-ai/dsh-session-title-llm'
119
-
120
- /**
121
- * Resolve the shared title policy.
122
- *
123
- * A plain `import '@deepseek-ai/dsh-session-title-llm'` would have to be
124
- * resolvable from THIS package's directory, which for a linked workspace package
125
- * means risking another installation of the host's shared module graph. The
126
- * helper therefore resolves from the deployment that loads this plugin.
127
- */
128
- function hostRequire() {
129
- const anchors = [process.argv[1], process.execPath, import.meta.url]
130
- for (const anchor of anchors) {
131
- if (anchor === undefined) continue
132
- try {
133
- const require = createRequire(anchor)
134
- require.resolve(HELPER_PACKAGE)
135
- return require
136
- } catch {
137
- // Try the next anchor: a test or tool process may not sit inside the deployment.
138
- }
139
- }
140
- return undefined
141
- }
142
-
143
- let helperPromise
144
- function loadTitleHelper() {
145
- helperPromise ??= (async () => {
146
- const require = hostRequire()
147
- if (require === undefined) {
148
- throw new Error(
149
- `model-tuning: 无法从宿主解析 ${HELPER_PACKAGE};本插件必须与提供该包的部署一起运行。`,
150
- )
151
- }
152
- const loaded = await import(pathToFileURL(require.resolve(HELPER_PACKAGE)).href)
153
- const generate = loaded.generateSessionTitleWithLlm
154
- if (typeof generate !== 'function') {
155
- throw new Error(`model-tuning: ${HELPER_PACKAGE} 未导出 generateSessionTitleWithLlm`)
156
- }
157
- return generate
158
- })()
159
- return helperPromise
160
- }
161
-
162
- /**
163
- * Read the live title budget.
164
- *
165
- * Every field is user-editable from the 用途 tab, so each generation re-reads
166
- * them instead of freezing the apply-time values. A reasoning model that only
167
- * starts answering after a long think needs a larger `maxOutputTokens` here;
168
- * that is the user's call, not a constant in this file.
169
- * @param config - the plugin's live Config.
170
- * @returns the prompt/byte/token/timeout policy for one title request.
171
- */
172
- export function titleBudgetOf(config) {
173
- const policy = {
174
- targetWords: config.targetWords.get(),
175
- targetCjkCharacters: config.targetCjkCharacters.get(),
176
- maxInputBytes: config.maxInputBytes.get(),
177
- maxOutputTokens: config.maxOutputTokens.get(),
178
- timeoutMs: config.timeoutMs.get(),
179
- }
180
- for (const [key, value] of Object.entries(policy)) {
181
- if (!Number.isInteger(value) || value < 1) throw new Error(`model-tuning: ${key} 必须是正整数`)
182
- }
183
- return policy
184
- }
185
-
186
- /**
187
- * Register the settings-owned title provider.
188
- * @param ctx - context exposing session-title and LLM services.
189
- * @param config - the live Config (route and budget are re-read per generation).
190
- */
191
- export async function registerTitleProvider(ctx, config) {
192
- const generateSessionTitleWithLlm = await loadTitleHelper()
193
- validateTitleModel({ titleMode: config.titleMode.get(), titleProvider: config.titleProvider.get(), titleModel: config.titleModel.get() })
194
- titleBudgetOf(config)
195
- ctx.sessionTitle.register({
196
- id: TITLE_PROVIDER_ID,
197
- automatic: 'first-prompt',
198
- async generate(request) {
199
- const route = resolveTitleRoute({
200
- titleMode: config.titleMode.get(), titleProvider: config.titleProvider.get(), titleModel: config.titleModel.get(),
201
- }, request)
202
- const selected = request.messages.slice(0, 1)
203
- return await generateSessionTitleWithLlm(ctx, titleBudgetOf(config), { ...request, route }, selected, TITLE_PROVIDER_ID)
204
- },
205
- })
206
- }
207
-
208
- // ── 绘图:工具 + Remote 服务(ex dsh-plugin-image-generation/index.js) ──
209
-
210
53
  const IMAGE_PARAMETERS = {
211
54
  model: { type: 'string', description: 'Configuration ID from list_image_models, not the upstream model ID. Omit to use the configured default.' },
212
55
  prompt: { type: 'string', required: true, description: 'Full self-contained description of the image to generate. Chat history is not sent.' },
@@ -356,11 +199,10 @@ export function registerImageTools(ctx, config) {
356
199
  }
357
200
 
358
201
  /**
359
- * Register everything: title provider, image tools, and the Remote service.
360
- * @param ctx - context exposing tools, attachments, sessionTitle and llm.
361
- * @param config - the unified Config described above.
202
+ * Register the drawing tools and their Remote service.
203
+ * @param ctx - context exposing tools, attachments and llm.
204
+ * @param config - the drawing Config.
362
205
  */
363
- export async function apply(ctx, config) {
364
- await registerTitleProvider(ctx, config)
206
+ export function apply(ctx, config) {
365
207
  registerImageTools(ctx, config)
366
208
  }
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "dsh-plugin-model-tuning",
3
- "version": "2.0.2",
3
+ "version": "3.0.0",
4
4
  "license": "MIT",
5
- "keywords": ["dsh-plugin", "deepseek-harness", "dsh", "model", "settings", "reasoning", "context", "session-title", "image-generation"],
5
+ "keywords": ["dsh-plugin", "deepseek-harness", "dsh", "model", "settings", "reasoning", "compat", "image-generation"],
6
6
  "publishConfig": { "registry": "https://registry.npmjs.org" },
7
7
  "repository": { "type": "git", "url": "git+https://github.com/tofu-zyc/dsh-plugin-tofu-box.git", "directory": "dsh-plugin-model-tuning" },
8
8
  "bugs": { "url": "https://github.com/tofu-zyc/dsh-plugin-tofu-box/issues" },
9
9
  "homepage": "https://github.com/tofu-zyc/dsh-plugin-tofu-box/tree/main/dsh-plugin-model-tuning",
10
- "description": "「设置 → 模型调参」页:模型参数(推理档位/上下文/最大输出/输入模态)、「用途」页(标题生成独立路由,替换宿主标题行)、「绘图」页(绘图模型配置 + generate_image 工具 + 直接绘图面板)。v2 合并了原 dsh-plugin-title-model 与 dsh-plugin-image-generation。",
10
+ "description": "只补官方没有的两块:①「设置 → 模型」卡片里的兼容开关与内置目录路由推理覆盖(官方 Models 页没有的 compat.supportsDeveloperRole / compat.forceAdaptiveThinking / modelOverrides.reasoningEfforts);②「设置 → 绘图」页(绘图模型配置 + generate_image / list_image_models 工具 + 直接绘图面板)。v3 起标题生成路由交还官方 session-title 行,模型参数页交还官方 Models 页。",
11
11
  "type": "module",
12
12
  "main": "index.js",
13
13
  "exports": {
@@ -24,7 +24,7 @@
24
24
  "platform": "web",
25
25
  "inject": [
26
26
  "@deepseek-ai/dsh-client-ui-settings",
27
- "@deepseek-ai/dsh-client-ui-tool",
27
+ "@deepseek-ai/dsh-client-ui-settings-models",
28
28
  "@deepseek-ai/dsh-api-remotes"
29
29
  ]
30
30
  }
@@ -32,13 +32,7 @@
32
32
  "peerDependencies": {
33
33
  "@deepseek-ai/dsh-tools": ">=0.1.6-alpha.1",
34
34
  "@deepseek-ai/dsh-typert-protocol": ">=0.1.6-alpha.1",
35
- "@deepseek-ai/schemastery": "^3.18.2",
36
- "@deepseek-ai/dsh-session-title-llm": ">=0.1.6-alpha.2"
37
- },
38
- "peerDependenciesMeta": {
39
- "@deepseek-ai/dsh-session-title-llm": {
40
- "optional": true
41
- }
35
+ "@deepseek-ai/schemastery": "^3.18.2"
42
36
  },
43
37
  "scripts": {
44
38
  "test": "node --test tests/*.test.mjs"