mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-10-26 16:55:08 +00:00
feat(web): add Session Log upload preference in General settings (#5337)
This commit is contained in:
1 parent
5f782ebf12
commit
7ded036fdb
54 files changed
+843
-35
No files matched your search
+2
-2
@@ -9,8 +9,8 @@
|
||||
en: f9721b730c7e70c7
|
||||
zh: 367e7839912cb726
|
||||
/agent-note-default-deepseek-session-log-upload/decision:
|
||||
en: ec3b05fb70ae490f
|
||||
zh: 59b0691e327855b4
|
||||
en: b72ef475a41b1a23
|
||||
zh: b17a68367679730b
|
||||
/agent-note-default-deepseek-session-log-upload/alternatives-considered:
|
||||
en: 4cc4b61d76f84ff3
|
||||
zh: bdd29ea66037f744
|
||||
|
||||
@@ -9,7 +9,9 @@ Ordinary DeepSeek requests do not contain the complete canonical Session traject
|
||||
|
||||
## Decision
|
||||
|
||||
`session-log-deepseek.Config.enabled` defaults to `true` in every process. An explicit `enabled: false` disables the contribution. The plugin does not inspect test-runner or snapshot environment variables.
|
||||
`session-log-deepseek.Config.enabled` defaults to `true` in every process. A resolved `enabled: false` disables the contribution; configuration precedence determines which value takes effect. The plugin does not inspect test-runner or snapshot environment variables.
|
||||
|
||||
The `enabled` value is read for each request through a volatile reference so a saved preference takes effect without restarting the plugin. Profile writes can override bundle defaults under the [profile-owned configuration rules](2026-09-19-profile-owned-live-configuration.md); home patches and command-line overlays still reject conflicting writes. Disabling upload leaves the acceptance watermark unchanged because it records provider acceptance, not user eligibility. Re-enabling therefore sends the unaccepted suffix, including events recorded while disabled. Already prepared requests retain their payload.
|
||||
|
||||
This supersedes only the opt-in default in the [request-extension decision](2026-08-21-deepseek-llm-api-request-extensions.md); that note still owns field serialization, destinations, acceptance, and retry semantics. The headless and ACP corpus base patches and Web scaffold explicitly disable upload. Later scenario patches can enable it. The SDK text-turn recording omits the setting and exercises the shipped default, including durable acceptance events.
|
||||
|
||||
|
||||
@@ -9,7 +9,9 @@ Status: implemented
|
||||
|
||||
## 决策
|
||||
|
||||
`session-log-deepseek.Config.enabled` 在所有进程中均默认为 `true`。显式设置 `enabled: false` 可关闭贡献。插件不读取测试运行器或快照环境变量。
|
||||
`session-log-deepseek.Config.enabled` 在所有进程中均默认为 `true`。最终生效的 `enabled: false` 会关闭贡献;配置优先级决定采用哪个值。插件不读取测试运行器或快照环境变量。
|
||||
|
||||
每次请求通过 volatile 引用读取 `enabled`,因此保存的偏好无需重启插件即可生效。按照 [profile 所有的配置规则](2026-09-19-profile-owned-live-configuration.zh.md),profile 写入可覆盖组合包默认值;home patch 和命令行 overlay 仍会拒绝冲突写入。关闭上传不改变接受水位,因为它记录提供方的接受结果,而不是用户是否允许上传。重新开启后会发送尚未接受的后缀,包括关闭期间记录的事件。已准备的请求保留原有载荷。
|
||||
|
||||
本决策仅取代[请求扩展决策](2026-08-21-deepseek-llm-api-request-extensions.zh.md)中的主动启用默认策略;该记录仍负责字段序列化、目的地址、接受与重试语义。headless 和 ACP 语料的基础 patch 以及 Web scaffold 显式关闭上传。后续场景 patch 可以启用上传。SDK text-turn 录制省略该设置,验证产品默认值及持久接受事件。
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
en: 12bba432c07573bf
|
||||
zh: ab6b365cfb95003f
|
||||
/dsh-cli-behavior-reference/shared-deployment-behavior:
|
||||
en: 24e3da3716d261fe
|
||||
zh: 060611966db4ca69
|
||||
en: 6a7abc9339e8f37f
|
||||
zh: 133adb834c8e195d
|
||||
/dsh-cli-behavior-reference/source-execution:
|
||||
en: 05c2b23790f7b296
|
||||
zh: 51f809020fb230a1
|
||||
@@ -126,7 +126,7 @@ New sessions in base-backed profiles default to the `workspace-write` permission
|
||||
|
||||
The base bundle mounts the native DeepSeek adapter, settings and credential providers, stable `web_search` and `web_fetch`, the public-only HTTP fetch provider, default-on DeepSeek session-log upload, and feedback-gated OTel upload for all users. Provider credentials resolve from the inherited environment, `$DSH_HOME/.credentials.yaml`, the invoking directory's `.env`, then `$DSH_HOME/.env`; the managed document is never materialized into `process.env`, while both `.env` files are ordinary launch environment layers. Search uses `DEEPSEEK_API_KEY` and accepts `DEEPSEEK_SEARCH_BASE_URL`. Enabled fetch calls run in every sandbox and approval mode without per-call confirmation; the provider rejects non-public destinations before connecting. The Web app disables the base tool row and exposes the same tools through its `cordis`, `ptc`, and `standard` agent presets.
|
||||
|
||||
Feedback is recorded in the Session log without starting model work. The [DeepSeek session-log contributor](../../../packages/session/session-log-deepseek/README.md) sends complete unaccepted log suffixes with subsequent DeepSeek requests by default, including requests sent through configured gateways; set its `enabled` configuration to `false` to opt out. [OTel session upload](../../../packages/session/session-telemetry-otel/README.md) applies to all users and providers, including `deepseek-official`, without requiring a request header. The base defaults to `FEEDBACK_ONLY`: new own text feedback, message ratings, edits, and withdrawals release the complete canonical prefix through that event, including stored context; later records wait for the next explicit feedback. Inherited parent feedback does not authorize a fork. Requests, restoration, mount, and HMR do not trigger capture. SDK batching may finish an authorized upload without further interaction or model work. `DSH_TELEMETRY_MODE=DISABLED` disables OTel delivery; `FULL` is rejected, and any non-empty `DSH_TELEMETRY_DISABLED` disables its row. `DSH_TELEMETRY_OTLP_URL` selects the collector. Handoff is best-effort, not collector acceptance; no durable outbox or retry guarantee is provided. These OTel settings do not enable or disable the DeepSeek contribution. Neither path changes model input, but exports can include message text, tool arguments and results, and workspace paths.
|
||||
Feedback is recorded in the Session log without starting model work. The [DeepSeek session-log contributor](../../../packages/session/session-log-deepseek/README.md) sends complete unaccepted log suffixes with subsequent DeepSeek requests by default, including requests sent through configured gateways; turn off **Settings → General → Upload Session Log when using the official model API** in Web, or set its `enabled` configuration to `false` to opt out. [OTel session upload](../../../packages/session/session-telemetry-otel/README.md) applies to all users and providers, including `deepseek-official`, without requiring a request header. The base defaults to `FEEDBACK_ONLY`: new own text feedback, message ratings, edits, and withdrawals release the complete canonical prefix through that event, including stored context; later records wait for the next explicit feedback. Inherited parent feedback does not authorize a fork. Requests, restoration, mount, and HMR do not trigger capture. SDK batching may finish an authorized upload without further interaction or model work. `DSH_TELEMETRY_MODE=DISABLED` disables OTel delivery; `FULL` is rejected, and any non-empty `DSH_TELEMETRY_DISABLED` disables its row. `DSH_TELEMETRY_OTLP_URL` selects the collector. Handoff is best-effort, not collector acceptance; no durable outbox or retry guarantee is provided. These OTel settings do not enable or disable the DeepSeek contribution. Neither path changes model input, but exports can include message text, tool arguments and results, and workspace paths.
|
||||
|
||||
Install external plugin bundles through `dsh plugin --profile <name> add <package-or-git-spec>`. The installed package owns its dependencies and contributes its declared `cordis.patch.yml` layer. The CLI also ships `@deepseek-ai/dsh-mcp-client` as a dependency for patch layers, but no MCP server is enabled by default because each server command is trusted executable code outside the agent sandbox.
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ dsh web --help
|
||||
|
||||
基础组合包挂载原生 DeepSeek 适配器、settings 与凭据提供方、稳定的 `web_search` 和 `web_fetch`、仅限公网的 HTTP fetch 提供方,默认开启的 DeepSeek 会话日志上传,以及面向所有用户的反馈门控 OTel 上传。提供方凭据依次从继承环境、`$DSH_HOME/.credentials.yaml`、调用目录的 `.env` 和 `$DSH_HOME/.env` 解析;受管文档从不物化进 `process.env`,而两个 `.env` 文件都是普通启动环境层。搜索使用 `DEEPSEEK_API_KEY` 并接受 `DEEPSEEK_SEARCH_BASE_URL`。已启用的抓取调用会在所有 sandbox 与审批模式下执行,无需逐次确认;提供方会在连接前拒绝非公开目的地址。Web app 会禁用 base 工具配置项,再通过 `cordis`、`ptc` 与 `standard` agent preset 暴露相同工具。
|
||||
|
||||
反馈记录在会话日志中,不会启动模型工作。[DeepSeek 会话日志贡献器](../../../packages/session/session-log-deepseek/README.zh.md)默认随后续 DeepSeek 请求发送尚未确认接收的完整日志后缀,包括经已配置网关发送的请求;将其 `enabled` 配置设为 `false` 可关闭上传。[OTel 会话上传](../../../packages/session/session-telemetry-otel/README.zh.md)适用于所有用户和提供方,包括 `deepseek-official`,无需请求头。基础配置默认使用 `FEEDBACK_ONLY`:新的自身文本反馈、消息评分、编辑与撤回会释放截至该事件的完整规范日志前缀,包含存储的上下文;后续记录等待下一次显式反馈。继承的父级反馈不构成 fork 的授权。请求、恢复、挂载和 HMR 不触发捕获。SDK 批处理可完成已授权上传,无需进一步交互或模型工作。`DSH_TELEMETRY_MODE=DISABLED` 禁止 OTel 投递;`FULL` 被拒绝,任何非空的 `DSH_TELEMETRY_DISABLED` 都会禁用其配置行。`DSH_TELEMETRY_OTLP_URL` 选择采集端。交接尽力而为,不代表采集端接受;不提供持久化 outbox 或重试保证。这些 OTel 设置不会开启或关闭 DeepSeek 贡献。两条路径都不改变模型输入,但导出可能包含消息文本、工具参数和结果,以及工作区路径。
|
||||
反馈记录在会话日志中,不会启动模型工作。[DeepSeek 会话日志贡献器](../../../packages/session/session-log-deepseek/README.zh.md)默认随后续 DeepSeek 请求发送尚未确认接收的完整日志后缀,包括经已配置网关发送的请求;可在 Web 的**设置 → 通用 → 在使用官方模型 API 时上传 Session Log**中关闭上传,或将其 `enabled` 配置设为 `false`。[OTel 会话上传](../../../packages/session/session-telemetry-otel/README.zh.md)适用于所有用户和提供方,包括 `deepseek-official`,无需请求头。基础配置默认使用 `FEEDBACK_ONLY`:新的自身文本反馈、消息评分、编辑与撤回会释放截至该事件的完整规范日志前缀,包含存储的上下文;后续记录等待下一次显式反馈。继承的父级反馈不构成 fork 的授权。请求、恢复、挂载和 HMR 不触发捕获。SDK 批处理可完成已授权上传,无需进一步交互或模型工作。`DSH_TELEMETRY_MODE=DISABLED` 禁止 OTel 投递;`FULL` 被拒绝,任何非空的 `DSH_TELEMETRY_DISABLED` 都会禁用其配置行。`DSH_TELEMETRY_OTLP_URL` 选择采集端。交接尽力而为,不代表采集端接受;不提供持久化 outbox 或重试保证。这些 OTel 设置不会开启或关闭 DeepSeek 贡献。两条路径都不改变模型输入,但导出可能包含消息文本、工具参数和结果,以及工作区路径。
|
||||
|
||||
通过 `dsh plugin --profile <name> add <package-or-git-spec>` 安装外部插件组合包。安装的包拥有其依赖,并贡献其声明的 `cordis.patch.yml` 层。CLI 还随附 `@deepseek-ai/dsh-mcp-client` 作为供 patch 层使用的依赖,但默认不启用 MCP 服务器,因为每条服务器命令都是 agent 沙箱之外的受信任可执行代码。
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"@deepseek-ai/cordis-plugin-group": "workspace:~",
|
||||
"@deepseek-ai/dsh-client-modules": "workspace:*",
|
||||
"@deepseek-ai/dsh-client-store": "workspace:*",
|
||||
"@deepseek-ai/dsh-llm-mock-server": "workspace:*",
|
||||
"@deepseek-ai/dsh-client-test-runtime": "workspace:*",
|
||||
"@deepseek-ai/dsh-client-ui-deliverables": "workspace:*",
|
||||
"@deepseek-ai/dsh-client-ui-dockkit": "workspace:*",
|
||||
|
||||
@@ -29,4 +29,6 @@
|
||||
- button "编辑快捷键"
|
||||
- text: 繁忙时的发送行为 智能体运行时 Enter 键和发送按钮的行为;Cmd/Ctrl+Enter 使用另一行为
|
||||
- button "排队发送"
|
||||
- text: 在使用官方模型 API 时上传 Session Log 帮助改进 DeepSeek 模型与产品
|
||||
- switch "在使用官方模型 API 时上传 Session Log"
|
||||
- text: 当前版本:{{version}}
|
||||
@@ -29,4 +29,6 @@
|
||||
- button "Edit shortcuts"
|
||||
- text: Send behavior while busy What Enter and the Send button do while the agent is running; Cmd/Ctrl+Enter uses the other behavior
|
||||
- button "Queue"
|
||||
- text: Upload Session Log when using the official model API Help improve DeepSeek models and products.
|
||||
- switch "Upload Session Log when using the official model API"
|
||||
- text: "Current version: {{version}}"
|
||||
@@ -29,4 +29,6 @@
|
||||
- button "Edit shortcuts"
|
||||
- text: Send behavior while busy What Enter and the Send button do while the agent is running; Cmd/Ctrl+Enter uses the other behavior
|
||||
- button "Queue"
|
||||
- text: Upload Session Log when using the official model API Help improve DeepSeek models and products.
|
||||
- switch "Upload Session Log when using the official model API"
|
||||
- text: "Current version: {{version}}"
|
||||
@@ -29,4 +29,6 @@
|
||||
- button "Edit shortcuts"
|
||||
- text: Send behavior while busy What Enter and the Send button do while the agent is running; Cmd/Ctrl+Enter uses the other behavior
|
||||
- button "Queue"
|
||||
- text: Upload Session Log when using the official model API Help improve DeepSeek models and products.
|
||||
- switch "Upload Session Log when using the official model API"
|
||||
- text: "Current version: {{version}}"
|
||||
@@ -29,4 +29,6 @@
|
||||
- button "编辑快捷键"
|
||||
- text: 繁忙时的发送行为 智能体运行时 Enter 键和发送按钮的行为;Cmd/Ctrl+Enter 使用另一行为
|
||||
- button "排队发送"
|
||||
- text: 在使用官方模型 API 时上传 Session Log 帮助改进 DeepSeek 模型与产品
|
||||
- switch "在使用官方模型 API 时上传 Session Log"
|
||||
- text: 当前版本:{{version}}
|
||||
@@ -682,7 +682,7 @@ export async function launchWebScaffold(options: LaunchOptions = {}): Promise<We
|
||||
// Live fields use a shared deployment layer; process-specific ports and roots stay in CLI overlays.
|
||||
const formEntries = new Set(['agent-default-model', 'agent-preset-registry', 'llm-deepseek', 'llm-pi-ai',
|
||||
'web-search-deepseek', 'agent-loop', 'subagent', 'bash-sandbox', 'pwsh-sandbox',
|
||||
'ui-theme', 'locale', 'ui-chat', 'ui-conversation', 'ui-settings', 'ui-settings-general', 'permission'])
|
||||
'session-log-deepseek', 'ui-theme', 'locale', 'ui-chat', 'ui-conversation', 'ui-settings', 'ui-settings-general', 'permission'])
|
||||
const formDefaults: PatchOptions[] = []
|
||||
const processOverlays = overlayPatches.map((patch) => {
|
||||
if (patch.id === undefined || !formEntries.has(patch.id) || patch.config === undefined) return patch
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/** Browser saves control the session-log field on real DeepSeek HTTP requests. */
|
||||
import { mkdtemp, rm, writeFile, readFile } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { chromium } from 'playwright'
|
||||
import yaml from 'js-yaml'
|
||||
import { expect, it, onTestFinished } from 'vitest'
|
||||
import { credentialRef } from '@deepseek-ai/dsh-credentials'
|
||||
import { startMockLlmServer } from '@deepseek-ai/dsh-llm-mock-server'
|
||||
import { launchWebScaffold, webSnapshotMode } from './scaffold.ts'
|
||||
import { connectFreshWorkspace, newEnglishPage, openSettings } from './support.ts'
|
||||
|
||||
it.skipIf(webSnapshotMode() === 'record')('persists the upload switch and omits or resumes session logs on subsequent API requests', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'dsh-upload-browser-'))
|
||||
onTestFinished(() => rm(root, { recursive: true, force: true }))
|
||||
const server = await startMockLlmServer({ sequence: ['success', 'success', 'success'], successText: 'UPLOAD_SETTING_DONE' })
|
||||
onTestFinished(() => server.close())
|
||||
const key = credentialRef('DSH_UPLOAD_BROWSER_KEY')
|
||||
const overlay = join(root, 'upload.yml')
|
||||
await writeFile(overlay, JSON.stringify([
|
||||
{ id: 'llm-deepseek', config: { baseURL: server.baseURL, apiKeyEnv: key } },
|
||||
{ id: 'session-log-deepseek', config: { enabled: true } },
|
||||
{ id: 'agent-default-model', config: { provider: 'deepseek-official', model: 'deepseek-v4-flash' } },
|
||||
]))
|
||||
const scaffold = await launchWebScaffold({ deepSeekMissingCredential: true, extraOverlayPath: overlay })
|
||||
onTestFinished(() => scaffold.close())
|
||||
await scaffold.ctx.credentials.set(key, 'upload-browser-test-key')
|
||||
const browser = await chromium.launch()
|
||||
onTestFinished(() => browser.close())
|
||||
const page = await newEnglishPage(browser)
|
||||
await page.goto(scaffold.authenticatedUrl)
|
||||
await connectFreshWorkspace(page, scaffold.workspaceCwd)
|
||||
const input = page.locator('[data-composer-input]').first()
|
||||
const send = async (text: string) => {
|
||||
const settled = scaffold.whenTurnSettled()
|
||||
await input.fill(text)
|
||||
await input.press('Enter')
|
||||
return settled
|
||||
}
|
||||
const setUpload = async (enabled: boolean) => {
|
||||
await openSettings(page, 'en')
|
||||
const toggle = page.getByRole('switch', { name: 'Upload Session Log when using the official model API' })
|
||||
await expect.poll(() => toggle.getAttribute('aria-checked')).toBe(String(!enabled))
|
||||
await toggle.click()
|
||||
await expect.poll(() => toggle.getAttribute('aria-checked')).toBe(String(enabled))
|
||||
await expect.poll(async () => {
|
||||
const patches = yaml.load(await readFile(join(scaffold.harnessHome, 'profiles/scaffold/cordis.patch.yml'), 'utf8')) as { id: string; config?: { enabled?: boolean } }[]
|
||||
return patches.find(patch => patch.id === 'session-log-deepseek')?.config?.enabled ?? true
|
||||
}).toBe(enabled)
|
||||
await page.keyboard.press('Escape')
|
||||
}
|
||||
|
||||
const sessionId = await send('upload-enabled-marker')
|
||||
expect(server.requests).toHaveLength(1)
|
||||
expect(server.requests[0]!.body).toHaveProperty('dsh_session_log')
|
||||
const first = server.requests[0]!.body as { dsh_session_log: { throughSeq: number } }
|
||||
await setUpload(false)
|
||||
await page.reload()
|
||||
await openSettings(page, 'en')
|
||||
await expect.poll(() => page.getByRole('switch', { name: 'Upload Session Log when using the official model API' }).getAttribute('aria-checked')).toBe('false')
|
||||
await page.keyboard.press('Escape')
|
||||
expect(await send('upload-disabled-marker')).toBe(sessionId)
|
||||
expect(server.requests).toHaveLength(2)
|
||||
expect(server.requests[1]!.body).not.toHaveProperty('dsh_session_log')
|
||||
await setUpload(true)
|
||||
expect(await send('upload-resumed-marker')).toBe(sessionId)
|
||||
expect(server.requests).toHaveLength(3)
|
||||
const resumed = server.requests[2]!.body as { dsh_session_log: { events: unknown[] } }
|
||||
expect(resumed).toHaveProperty('dsh_session_log.afterSeq', first.dsh_session_log.throughSeq)
|
||||
expect(JSON.stringify(resumed.dsh_session_log.events)).toContain('"upload-disabled-marker"')
|
||||
expect(server.requests.map(request => request.outcome)).toEqual(['completed', 'completed', 'completed'])
|
||||
})
|
||||
@@ -74,6 +74,7 @@
|
||||
"tests/plugin-install-github.e2e.ts",
|
||||
"tests/client-plugin-live.e2e.ts",
|
||||
"tests/settings-chrome.e2e.ts",
|
||||
"tests/session-log-upload.e2e.ts",
|
||||
"tests/caption-overlays.e2e.ts",
|
||||
"tests/settings-appearance.e2e.ts",
|
||||
"tests/menu-material.e2e.ts",
|
||||
|
||||
@@ -2557,19 +2557,20 @@ export interface JsonRpcConfig {
|
||||
## `@deepseek-ai/dsh-session-log-deepseek`
|
||||
|
||||
- `inject`: `deepseekLlmApiExtensions` · `sessions`
|
||||
- `refs`: `Volatile` (`@deepseek-ai/cordis`)
|
||||
- `source`: [`packages/session/session-log-deepseek/src/index.ts:39`](../packages/session/session-log-deepseek/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
/** Session-log request contribution configuration. */
|
||||
export interface Config {
|
||||
/** Contribute `dsh_session_log` to official DeepSeek requests. Defaults to `true`. */
|
||||
enabled?: boolean
|
||||
enabled: Volatile<boolean>
|
||||
/**
|
||||
* Largest serialized `dsh_session_log` field, in UTF-8 bytes, that one request carries.
|
||||
* A request uploads the longest pending event prefix that fits; later requests continue
|
||||
* after its acceptance. Defaults to 8 MiB.
|
||||
*/
|
||||
maxBytes?: number
|
||||
maxBytes: number
|
||||
}
|
||||
```
|
||||
<!-- END GENERATED config-catalog:@deepseek-ai/dsh-session-log-deepseek -->
|
||||
@@ -4346,6 +4347,7 @@ These load from a `cordis.yml` entry with no `config:` block; they declare no co
|
||||
| `@deepseek-ai/dsh-client-ui-settings-general` | — | [`packages/client/ui-settings-general/src/index.ts`](../packages/client/ui-settings-general/src/index.ts) |
|
||||
| `@deepseek-ai/dsh-client-ui-settings-plugin-inventory` | — | [`packages/client/ui-settings-plugin-inventory/src/index.ts`](../packages/client/ui-settings-plugin-inventory/src/index.ts) |
|
||||
| `@deepseek-ai/dsh-client-ui-settings-plugins` | — | [`packages/client/ui-settings-plugins/src/index.ts`](../packages/client/ui-settings-plugins/src/index.ts) |
|
||||
| `@deepseek-ai/dsh-client-ui-settings-session-log` | — | [`packages/client/ui-settings-session-log/src/index.ts`](../packages/client/ui-settings-session-log/src/index.ts) |
|
||||
| `@deepseek-ai/dsh-client-ui-settings-shell` | — | [`packages/client/ui-settings-shell/src/index.ts`](../packages/client/ui-settings-shell/src/index.ts) |
|
||||
| `@deepseek-ai/dsh-client-ui-settings-subagent` | — | [`packages/client/ui-settings-subagent/src/index.ts`](../packages/client/ui-settings-subagent/src/index.ts) |
|
||||
| `@deepseek-ai/dsh-client-ui-settings-web-search` | — | [`packages/client/ui-settings-web-search/src/index.ts`](../packages/client/ui-settings-web-search/src/index.ts) |
|
||||
|
||||
@@ -2559,19 +2559,20 @@ export interface JsonRpcConfig {
|
||||
## `@deepseek-ai/dsh-session-log-deepseek`
|
||||
|
||||
- `inject`: `deepseekLlmApiExtensions` · `sessions`
|
||||
- `refs`: `Volatile` (`@deepseek-ai/cordis`)
|
||||
- `source`: [`packages/session/session-log-deepseek/src/index.ts:39`](../packages/session/session-log-deepseek/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
/** Session-log request contribution configuration. */
|
||||
export interface Config {
|
||||
/** Contribute `dsh_session_log` to official DeepSeek requests. Defaults to `true`. */
|
||||
enabled?: boolean
|
||||
enabled: Volatile<boolean>
|
||||
/**
|
||||
* Largest serialized `dsh_session_log` field, in UTF-8 bytes, that one request carries.
|
||||
* A request uploads the longest pending event prefix that fits; later requests continue
|
||||
* after its acceptance. Defaults to 8 MiB.
|
||||
*/
|
||||
maxBytes?: number
|
||||
maxBytes: number
|
||||
}
|
||||
```
|
||||
<!-- END GENERATED config-catalog:@deepseek-ai/dsh-session-log-deepseek -->
|
||||
@@ -4348,6 +4349,7 @@ export interface Config {
|
||||
| `@deepseek-ai/dsh-client-ui-settings-general` | — | [`packages/client/ui-settings-general/src/index.ts`](../packages/client/ui-settings-general/src/index.ts) |
|
||||
| `@deepseek-ai/dsh-client-ui-settings-plugin-inventory` | — | [`packages/client/ui-settings-plugin-inventory/src/index.ts`](../packages/client/ui-settings-plugin-inventory/src/index.ts) |
|
||||
| `@deepseek-ai/dsh-client-ui-settings-plugins` | — | [`packages/client/ui-settings-plugins/src/index.ts`](../packages/client/ui-settings-plugins/src/index.ts) |
|
||||
| `@deepseek-ai/dsh-client-ui-settings-session-log` | — | [`packages/client/ui-settings-session-log/src/index.ts`](../packages/client/ui-settings-session-log/src/index.ts) |
|
||||
| `@deepseek-ai/dsh-client-ui-settings-shell` | — | [`packages/client/ui-settings-shell/src/index.ts`](../packages/client/ui-settings-shell/src/index.ts) |
|
||||
| `@deepseek-ai/dsh-client-ui-settings-subagent` | — | [`packages/client/ui-settings-subagent/src/index.ts`](../packages/client/ui-settings-subagent/src/index.ts) |
|
||||
| `@deepseek-ai/dsh-client-ui-settings-web-search` | — | [`packages/client/ui-settings-web-search/src/index.ts`](../packages/client/ui-settings-web-search/src/index.ts) |
|
||||
|
||||
@@ -191,6 +191,7 @@ flowchart TD
|
||||
pkg_client_ui_settings_models["client-ui-settings-models"]
|
||||
pkg_client_ui_settings_plugin_inventory["client-ui-settings-plugin-inventory"]
|
||||
pkg_client_ui_settings_plugins["client-ui-settings-plugins"]
|
||||
pkg_client_ui_settings_session_log["client-ui-settings-session-log"]
|
||||
pkg_client_ui_settings_shell["client-ui-settings-shell"]
|
||||
pkg_client_ui_settings_subagent["client-ui-settings-subagent"]
|
||||
pkg_client_ui_settings_web_search["client-ui-settings-web-search"]
|
||||
@@ -1440,6 +1441,7 @@ flowchart TD
|
||||
| [`client-ui-settings-models`](../packages/client/ui-settings-models) | `client` | — |
|
||||
| [`client-ui-settings-plugin-inventory`](../packages/client/ui-settings-plugin-inventory) | `client` | — |
|
||||
| [`client-ui-settings-plugins`](../packages/client/ui-settings-plugins) | `client` | — |
|
||||
| [`client-ui-settings-session-log`](../packages/client/ui-settings-session-log) | `client` | — |
|
||||
| [`client-ui-settings-shell`](../packages/client/ui-settings-shell) | `client` | — |
|
||||
| [`client-ui-settings-subagent`](../packages/client/ui-settings-subagent) | `client` | — |
|
||||
| [`client-ui-settings-web-search`](../packages/client/ui-settings-web-search) | `client` | — |
|
||||
|
||||
@@ -193,6 +193,7 @@ flowchart TD
|
||||
pkg_client_ui_settings_models["client-ui-settings-models"]
|
||||
pkg_client_ui_settings_plugin_inventory["client-ui-settings-plugin-inventory"]
|
||||
pkg_client_ui_settings_plugins["client-ui-settings-plugins"]
|
||||
pkg_client_ui_settings_session_log["client-ui-settings-session-log"]
|
||||
pkg_client_ui_settings_shell["client-ui-settings-shell"]
|
||||
pkg_client_ui_settings_subagent["client-ui-settings-subagent"]
|
||||
pkg_client_ui_settings_web_search["client-ui-settings-web-search"]
|
||||
@@ -1442,6 +1443,7 @@ flowchart TD
|
||||
| [`client-ui-settings-models`](../packages/client/ui-settings-models) | `client` | — |
|
||||
| [`client-ui-settings-plugin-inventory`](../packages/client/ui-settings-plugin-inventory) | `client` | — |
|
||||
| [`client-ui-settings-plugins`](../packages/client/ui-settings-plugins) | `client` | — |
|
||||
| [`client-ui-settings-session-log`](../packages/client/ui-settings-session-log) | `client` | — |
|
||||
| [`client-ui-settings-shell`](../packages/client/ui-settings-shell) | `client` | — |
|
||||
| [`client-ui-settings-subagent`](../packages/client/ui-settings-subagent) | `client` | — |
|
||||
| [`client-ui-settings-web-search`](../packages/client/ui-settings-web-search) | `client` | — |
|
||||
|
||||
@@ -393,6 +393,9 @@
|
||||
- id: ui-agent-preset
|
||||
name: '@deepseek-ai/dsh-client-ui-agent-preset'
|
||||
|
||||
- id: ui-settings-session-log
|
||||
name: '@deepseek-ai/dsh-client-ui-settings-session-log'
|
||||
|
||||
# The Built-in plugins settings section: the navigation entry and the tab
|
||||
# row the inventory tab registers into. The official configuration pages
|
||||
# are the companion rows below.
|
||||
|
||||
@@ -175,7 +175,8 @@
|
||||
"@deepseek-ai/dsh-client-shortcuts": "workspace:*",
|
||||
"@deepseek-ai/dsh-client-ui-shortcuts": "workspace:*",
|
||||
"@deepseek-ai/dsh-client-product-analytics": "workspace:*",
|
||||
"@deepseek-ai/dsh-host-product-telemetry-otel": "workspace:*"
|
||||
"@deepseek-ai/dsh-host-product-telemetry-otel": "workspace:*",
|
||||
"@deepseek-ai/dsh-client-ui-settings-session-log": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/cordis": "workspace:~",
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
en: 1eb35b015a6c423f
|
||||
zh: a8a3dceee2c9e943
|
||||
/client-web-gui-browser-half/packages:
|
||||
en: 2a964f7daf3586d5
|
||||
zh: 2ca0adf9479a1ac5
|
||||
en: 1aa93b8088fb1962
|
||||
zh: 10feaed78bc6408c
|
||||
/client-web-gui-browser-half/related-documentation:
|
||||
en: 598d07b07449128f
|
||||
zh: e8c40708ac0e603e
|
||||
|
||||
@@ -77,6 +77,7 @@ The kernel packages boot and serve the page; the UI feature packages present it.
|
||||
| [`ui-settings-models/`](ui-settings-models/README.md) | Provides model-provider configuration and DeepSeek onboarding | — |
|
||||
| [`ui-settings-shell/`](ui-settings-shell/README.md) | Provides the shell settings page on the Plugins page | — |
|
||||
| [`ui-settings-agent-loop/`](ui-settings-agent-loop/README.md) | Provides the agent-loop settings page on the Plugins page | — |
|
||||
| [`ui-settings-session-log/`](ui-settings-session-log/README.md) | Controls API Session-log upload in General settings | — |
|
||||
| [`ui-settings-subagent/`](ui-settings-subagent/README.md) | Provides the Subagent settings page on the Plugins page | — |
|
||||
| [`ui-settings-web-search/`](ui-settings-web-search/README.md) | Provides the web-search settings page on the Plugins page | — |
|
||||
| [`ui-plugin-manager/`](ui-plugin-manager/README.md) | Contributes the sidebar Plugins panel: install, enable, disable, retry, and compose installed packages | — |
|
||||
|
||||
@@ -77,6 +77,7 @@ kind: "package-group"
|
||||
| [`ui-settings-models/`](ui-settings-models/README.zh.md) | 提供模型提供方配置与 DeepSeek 引导 | — |
|
||||
| [`ui-settings-shell/`](ui-settings-shell/README.zh.md) | 在插件页提供终端设置页 | — |
|
||||
| [`ui-settings-agent-loop/`](ui-settings-agent-loop/README.zh.md) | 在插件页提供 Agent 循环设置页 | — |
|
||||
| [`ui-settings-session-log/`](ui-settings-session-log/README.zh.md) | 在通用设置中控制随 API 上传会话日志 | — |
|
||||
| [`ui-settings-subagent/`](ui-settings-subagent/README.zh.md) | 在插件页提供子智能体设置页 | — |
|
||||
| [`ui-settings-web-search/`](ui-settings-web-search/README.zh.md) | 在插件页提供网页搜索设置页 | — |
|
||||
| [`ui-plugin-manager/`](ui-plugin-manager/README.zh.md) | 贡献侧栏的“插件”面板:安装、启用、停用、重试与组合已安装的包 | — |
|
||||
|
||||
@@ -80,7 +80,7 @@ export function apply(ctx: ClientContext): void {
|
||||
setEnabled: enabled => ctx.configForms.developerTools.setEnabled(enabled),
|
||||
}),
|
||||
}, DeveloperToolsRow))
|
||||
// Last row: every feature-registered preference row orders below 100.
|
||||
// Version information follows the core preferences.
|
||||
ctx.slots.inject('settings.general.item', () => ctx.slots.register({
|
||||
name: 'settings.general.item', id: 'current-version', order: 100, locale: NS,
|
||||
}, CurrentVersionRow))
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
|
||||
# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
|
||||
# After editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/client/ui-settings-session-log/README.md
|
||||
/:
|
||||
en: 71cee35d4ba9ca79
|
||||
zh: c2aec2b4d3d7bd54
|
||||
/deepseek-ai-dsh-client-ui-settings-session-log:
|
||||
en: 12fe698ae615f9ea
|
||||
zh: a6331c5e6a70830b
|
||||
/deepseek-ai-dsh-client-ui-settings-session-log/summary:
|
||||
en: 39c3846ca9dfd3e4
|
||||
zh: 8a745b54482403f8
|
||||
/deepseek-ai-dsh-client-ui-settings-session-log/table-of-contents:
|
||||
en: d152484eb41ac6b4
|
||||
zh: bda879a892f240f0
|
||||
/deepseek-ai-dsh-client-ui-settings-session-log/use-this-package:
|
||||
en: 57f1a5defa6e8d89
|
||||
zh: 88525c972f18b6ac
|
||||
/deepseek-ai-dsh-client-ui-settings-session-log/understand-the-implementation:
|
||||
en: 573cd565e22a6f6e
|
||||
zh: d214978ce1c43a86
|
||||
/deepseek-ai-dsh-client-ui-settings-session-log/further-exploration:
|
||||
en: a78c5129dd79131e
|
||||
zh: 6dddf577871572d6
|
||||
/deepseek-ai-dsh-client-ui-settings-session-log/model-experience:
|
||||
en: dd26d7193f9ca4ce
|
||||
zh: 954a7085b03beb8a
|
||||
/deepseek-ai-dsh-client-ui-settings-session-log/model-experience/kv-cache-effect:
|
||||
en: c8b5628ff5370218
|
||||
zh: 5ebb7ff0959a3a69
|
||||
/deepseek-ai-dsh-client-ui-settings-session-log/known-limitations-and-deferred-work:
|
||||
en: 637707c46284b048
|
||||
zh: 5d3feddb58cb9b50
|
||||
/deepseek-ai-dsh-client-ui-settings-session-log/known-limitations-and-deferred-work/dev-note:
|
||||
en: 0a1253c6f7b5e8b9
|
||||
zh: 9f56528562c7c914
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
description: "Control Session-log upload with DeepSeek API requests from General settings."
|
||||
kind: "package-reference"
|
||||
---
|
||||
|
||||
# @deepseek-ai/dsh-client-ui-settings-session-log
|
||||
|
||||
English | [中文](README.zh.md)
|
||||
|
||||
## Summary
|
||||
|
||||
Use **Upload Session Log when using the official model API** above the version number in **Settings → General** to control Session-log upload with DeepSeek API requests. The switch shows the accepted Host setting and saves each change immediately. It appears only while the Host exposes the upload setting; read-only clients cannot change it.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Use this package](#use-this-package)
|
||||
- [Understand the implementation](#understand-the-implementation)
|
||||
- [Further Exploration](#further-exploration)
|
||||
- [Model Experience](#model-experience)
|
||||
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
|
||||
- [Dev Note](#dev-note)
|
||||
|
||||
-----
|
||||
|
||||
<a id="use-this-package"></a>
|
||||
## Use this package
|
||||
|
||||
The Web bundle mounts this companion. Its switch edits the Host plugin's `enabled` field through the shared configuration form. A save failure keeps the accepted value and shows a toast that survives closing Settings. See [Session-log upload](../../session/session-log-deepseek/README.md#configuration) for request timing, resumed uploads, and the separate OpenTelemetry setting.
|
||||
|
||||
-----
|
||||
|
||||
<a id="understand-the-implementation"></a>
|
||||
## Understand the implementation
|
||||
|
||||
<details>
|
||||
<summary>Implementation details — click to expand</summary>
|
||||
|
||||
The companion contributes a `settings.general.item` row while `session-log-deepseek` is served and a `shell.overlay` toast for mutation outcomes. The shared form owns persistence and reconnect synchronization; the companion owns only pending-write state and localized notices.
|
||||
|
||||
</details>
|
||||
|
||||
-----
|
||||
|
||||
<a id="further-exploration"></a>
|
||||
## Further Exploration
|
||||
|
||||
- [Settings forms](../ui-settings/README.md) — accepted Host values and ordered writes.
|
||||
- [Session-log upload](../../session/session-log-deepseek/README.md) — request contribution and acceptance tracking.
|
||||
- [Web Client](../../../docs/subsystems/web-client.md) — plugin composition and settings placement.
|
||||
|
||||
<a id="model-experience"></a>
|
||||
## Model Experience
|
||||
|
||||
None, as this browser preference contributes no model-visible content.
|
||||
|
||||
#### KV Cache effect
|
||||
|
||||
None; the setting controls request metadata outside model input.
|
||||
|
||||
## Known Limitations and Deferred Work
|
||||
|
||||
<a id="known-limitations-and-deferred-work"></a>
|
||||
|
||||
- The row is unavailable when the Host does not expose the `session-log-deepseek` namespace.
|
||||
- No runtime invariant companion is published: accepted enablement comes directly from the shared configuration form, without an independent copy.
|
||||
|
||||
<a id="dev-note"></a>
|
||||
### Dev Note
|
||||
|
||||
None.
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
description: "在通用设置中控制随 DeepSeek API 请求上传会话日志。"
|
||||
kind: "package-reference"
|
||||
---
|
||||
|
||||
# @deepseek-ai/dsh-client-ui-settings-session-log
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
## 概述
|
||||
|
||||
使用**设置 → 通用**中版本号上方的**在使用官方模型 API 时上传 Session Log**开关控制随 DeepSeek API 请求上传会话日志。开关显示 Host 已接受的设置,并立即保存每次修改。仅当 Host 提供上传设置时显示该项;只读客户端无法修改。
|
||||
|
||||
## 目录
|
||||
|
||||
- [使用本包](#use-this-package)
|
||||
- [理解实现](#understand-the-implementation)
|
||||
- [进一步探索](#further-exploration)
|
||||
- [模型体验](#model-experience)
|
||||
- [已知限制与暂缓事项](#known-limitations-and-deferred-work)
|
||||
- [开发备注](#dev-note)
|
||||
|
||||
-----
|
||||
|
||||
<a id="use-this-package"></a>
|
||||
## 使用本包
|
||||
|
||||
Web bundle 挂载本配套插件。开关通过共享配置表单修改 Host 插件的 `enabled` 字段。保存失败时保留已接受的值,并显示关闭设置后仍可见的提示。请求生效时机、恢复上传和独立的 OpenTelemetry 设置见[会话日志上传](../../session/session-log-deepseek/README.zh.md#configuration)。
|
||||
|
||||
-----
|
||||
|
||||
<a id="understand-the-implementation"></a>
|
||||
## 理解实现
|
||||
|
||||
<details>
|
||||
<summary>实现细节——点击展开</summary>
|
||||
|
||||
Host 提供 `session-log-deepseek` 时,配套插件贡献一个 `settings.general.item` 行,并通过 `shell.overlay` 提示保存结果。共享表单负责持久化和重连同步;配套插件仅管理待完成写入状态和本地化提示。
|
||||
|
||||
</details>
|
||||
|
||||
-----
|
||||
|
||||
<a id="further-exploration"></a>
|
||||
## 进一步探索
|
||||
|
||||
- [设置表单](../ui-settings/README.zh.md)——Host 已接受的值与有序写入。
|
||||
- [会话日志上传](../../session/session-log-deepseek/README.zh.md)——请求字段与接收进度。
|
||||
- [Web 客户端](../../../docs/subsystems/web-client.zh.md)——插件组合与设置位置。
|
||||
|
||||
<a id="model-experience"></a>
|
||||
## 模型体验
|
||||
|
||||
无;此浏览器设置不贡献模型可见内容。
|
||||
|
||||
#### KV Cache 影响
|
||||
|
||||
无;该设置控制模型输入之外的请求元数据。
|
||||
|
||||
## 已知限制与暂缓事项
|
||||
|
||||
<a id="known-limitations-and-deferred-work"></a>
|
||||
|
||||
- Host 不提供 `session-log-deepseek` 命名空间时,该行不可用。
|
||||
- 不发布运行时不变量配套插件:已接受的启用状态直接来自共享配置表单,没有独立副本。
|
||||
|
||||
<a id="dev-note"></a>
|
||||
### 开发备注
|
||||
|
||||
无。
|
||||
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"name": "@deepseek-ai/dsh-client-ui-settings-session-log",
|
||||
"description": "General settings control for Session-log upload with DeepSeek API requests",
|
||||
"version": "0.1.7-rc.2",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
|
||||
"directory": "packages/client/ui-settings-session-log"
|
||||
},
|
||||
"type": "module",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/types/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./lib/types/index.d.ts",
|
||||
"default": "./lib/index.js"
|
||||
},
|
||||
"./client": {
|
||||
"types": "./lib/types/client/index.d.ts",
|
||||
"default": "./lib/client.js"
|
||||
},
|
||||
"./src/*": "./src/*",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"dsh": {
|
||||
"client": {
|
||||
"inject": [
|
||||
"@deepseek-ai/dsh-client-locale",
|
||||
"@deepseek-ai/dsh-client-ui-settings",
|
||||
"@deepseek-ai/dsh-client-ui-layout"
|
||||
],
|
||||
"platform": "web"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"bundle": "tsdown",
|
||||
"watch": "tsdown --watch"
|
||||
},
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/cordis": "workspace:~"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/cordis": "workspace:~",
|
||||
"@deepseek-ai/dsh-api-remotes": "workspace:*",
|
||||
"@deepseek-ai/dsh-client-locale": "workspace:*",
|
||||
"@deepseek-ai/dsh-client-store": "workspace:*",
|
||||
"@deepseek-ai/dsh-client-test-runtime": "workspace:*",
|
||||
"@deepseek-ai/dsh-client-ui-primitives": "workspace:*",
|
||||
"@deepseek-ai/dsh-client-ui-renderer": "workspace:*",
|
||||
"@deepseek-ai/dsh-client-ui-settings": "workspace:*",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:*",
|
||||
"@types/react": "~18.3.1",
|
||||
"react": "^18.2.0",
|
||||
"@deepseek-ai/dsh-client-ui-layout": "workspace:*",
|
||||
"react-dom": "^18.2.0",
|
||||
"@types/react-dom": "~18.3.0"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
"lib/client.js",
|
||||
"lib/types/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 24px;
|
||||
padding: 16px 0;
|
||||
border-bottom: 0.5px solid var(--dsw-alias-border-l2);
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin-top: 4px;
|
||||
color: var(--dsw-alias-label-secondary);
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/** General settings row and persistent shell notice for API log uploads. */
|
||||
import { Switch, Toast } from '@deepseek-ai/dsh-client-ui-primitives'
|
||||
import type { ObservableSnapshot } from '@deepseek-ai/dsh-client-store'
|
||||
import type { ConfigFormSnapshot } from '@deepseek-ai/dsh-client-ui-settings/client'
|
||||
import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import type { UploadMutation, UploadSettings } from './upload-preference.ts'
|
||||
import css from './UploadRow.module.css'
|
||||
|
||||
/** Accepted settings and mutation callbacks supplied by the plugin. */
|
||||
export interface UploadInjected {
|
||||
hooks: {
|
||||
upload: ObservableSnapshot<ConfigFormSnapshot<UploadSettings>>
|
||||
mutation: ObservableSnapshot<UploadMutation>
|
||||
}
|
||||
setEnabled(enabled: boolean): Promise<void>
|
||||
dismiss(): void
|
||||
}
|
||||
|
||||
type Face = InjectFace<UploadInjected> & PropsLocale<'settings.sessionLog'>
|
||||
|
||||
/**
|
||||
* Render the accepted API upload state.
|
||||
* @param props - settings hooks, writer and localized copy.
|
||||
* @returns the bottom preference row.
|
||||
*/
|
||||
export function UploadRow({ useUpload, useMutation, setEnabled, t }: PropsRuntime<'settings.general.item'> & Face) {
|
||||
const upload = useUpload(value => value)
|
||||
const busy = useMutation(value => value.busy)
|
||||
return <div className={css.row}>
|
||||
<div>
|
||||
<div className={css.title}>{t('title')}</div>
|
||||
<div className={css.description}>{t('description')}</div>
|
||||
</div>
|
||||
<Switch checked={upload.value?.enabled === true} label={t('title')}
|
||||
disabled={busy || upload.status !== 'ready' || !upload.writable}
|
||||
onChange={(enabled) => { void setEnabled(enabled) }} />
|
||||
</div>
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep the save outcome visible after settings closes.
|
||||
* @param props - mutation hook, dismissal and localized copy.
|
||||
* @returns the current toast, or nothing.
|
||||
*/
|
||||
export function UploadToast({ useMutation, dismiss, t }: PropsRuntime<'shell.overlay'> & Face) {
|
||||
const state = useMutation(value => value)
|
||||
if (state.notice === null) return null
|
||||
return <Toast key={state.sequence} text={t(state.notice)} onDone={dismiss}
|
||||
{...state.notice === 'saved' ? { tone: 'success' as const } : {}} />
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/** General settings companion for the Host Session-log upload configuration. */
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import type {} from '@deepseek-ai/dsh-client-locale/client'
|
||||
import type {} from '@deepseek-ai/dsh-client-ui-settings/client'
|
||||
import type {} from '@deepseek-ai/dsh-client-ui-layout/client'
|
||||
import type {} from '@deepseek-ai/dsh-client-ui-renderer/client'
|
||||
import { UploadPreference, type UploadSettings } from './upload-preference.ts'
|
||||
import { UploadRow, UploadToast, type UploadInjected } from './UploadRow.tsx'
|
||||
import { en, zh } from './locales.ts'
|
||||
|
||||
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
||||
interface LocaleNamespaceMap {
|
||||
/** API Session-log preference copy. */
|
||||
'settings.sessionLog': keyof typeof en
|
||||
}
|
||||
}
|
||||
|
||||
/** Services used by the browser companion. */
|
||||
export const inject = ['slots', 'locale', 'configForms']
|
||||
|
||||
/**
|
||||
* Register the preference while its Host configuration is available.
|
||||
* @param ctx - browser plugin context.
|
||||
*/
|
||||
export function apply(ctx: Context): void {
|
||||
const locale = 'settings.sessionLog'
|
||||
const namespace = 'session-log-deepseek'
|
||||
ctx.effect(() => ctx.locale.register(locale, { en, zh }))
|
||||
const form = ctx.configForms.get<UploadSettings>(namespace)
|
||||
const preference = new UploadPreference(form)
|
||||
const face = (): UploadInjected => ({
|
||||
hooks: { upload: form, mutation: preference.state },
|
||||
setEnabled: enabled => preference.setEnabled(enabled),
|
||||
dismiss: () => { preference.dismiss() },
|
||||
})
|
||||
ctx.effect(() => ctx.configForms.whileServed([namespace], () => ctx.slots.inject('settings.general.item', () =>
|
||||
ctx.slots.register({ name: 'settings.general.item', id: namespace, order: 90, locale, inject: face }, UploadRow))))
|
||||
ctx.slots.inject('shell.overlay', () => ctx.slots.register({
|
||||
name: 'shell.overlay', id: 'session-log-upload-toast', locale, inject: face,
|
||||
}, UploadToast))
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/** Copy for the API Session-log upload preference. */
|
||||
export const en = {
|
||||
title: 'Upload Session Log when using the official model API',
|
||||
description: 'Help improve DeepSeek models and products.',
|
||||
saved: 'Preference saved',
|
||||
failed: 'Could not save preference',
|
||||
}
|
||||
|
||||
/** Chinese preference copy. */
|
||||
export const zh: Record<keyof typeof en, string> = {
|
||||
title: '在使用官方模型 API 时上传 Session Log',
|
||||
description: '帮助改进 DeepSeek 模型与产品',
|
||||
saved: '设置已保存',
|
||||
failed: '无法保存设置',
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/** Ordered preference writes and notices that survive the settings panel. */
|
||||
import { createSnapshotStore } from '@deepseek-ai/dsh-client-store'
|
||||
import type { ConfigForm } from '@deepseek-ai/dsh-client-ui-settings/client'
|
||||
|
||||
/** Fields exposed by the Session-log plugin. */
|
||||
export interface UploadSettings {
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
/** Mutation state shared by the preference row and shell notice. */
|
||||
export interface UploadMutation {
|
||||
busy: boolean
|
||||
notice: 'saved' | 'failed' | null
|
||||
sequence: number
|
||||
}
|
||||
|
||||
/** Writes the Host setting without publishing an optimistic upload state. */
|
||||
export class UploadPreference {
|
||||
/** Observable mutation outcome. */
|
||||
readonly state = createSnapshotStore<UploadMutation>({ busy: false, notice: null, sequence: 0 })
|
||||
|
||||
/** @param form - Host-owned configuration form. */
|
||||
constructor(private readonly form: ConfigForm<UploadSettings>) {}
|
||||
|
||||
/**
|
||||
* Persist enablement; refusal or transport failure leaves the accepted value visible.
|
||||
* @param enabled - requested upload state.
|
||||
* @returns completion after the write settles.
|
||||
*/
|
||||
async setEnabled(enabled: boolean): Promise<void> {
|
||||
if (this.state.getSnapshot().busy) return
|
||||
this.state.update((state) => { state.busy = true; state.notice = null })
|
||||
let accepted = false
|
||||
try {
|
||||
accepted = await this.form.set('enabled', enabled)
|
||||
} catch (_error) {
|
||||
// The shell notice reports the failed write while the form retains accepted state.
|
||||
}
|
||||
this.state.update((state) => {
|
||||
state.busy = false
|
||||
state.notice = accepted ? 'saved' : 'failed'
|
||||
state.sequence++
|
||||
})
|
||||
}
|
||||
|
||||
/** Clear the displayed mutation notice. */
|
||||
dismiss(): void {
|
||||
this.state.update((state) => { state.notice = null })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
declare module '*.module.css' {
|
||||
const classes: Record<string, string>
|
||||
export default classes
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/** Browser companion for the Session-log upload preference. */
|
||||
|
||||
/** Mount the browser entry through the Loader. */
|
||||
export function apply(): void {}
|
||||
@@ -0,0 +1,59 @@
|
||||
/** The preference follows namespace availability and plugin disposal. */
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { expect, it, vi } from 'vitest'
|
||||
import { SlotRegistry } from '@deepseek-ai/dsh-client-ui-renderer/client'
|
||||
import { TestRemote } from '@deepseek-ai/dsh-client-test-runtime'
|
||||
import { LocaleRuntime } from '@deepseek-ai/dsh-client-locale/client'
|
||||
import { apply as settingsApply, inject as settingsInject } from '@deepseek-ai/dsh-client-ui-settings/client'
|
||||
import { apply, inject } from '../src/client/index.ts'
|
||||
import { apply as hostApply } from '../src/index.ts'
|
||||
import type { UploadInjected } from '../src/client/UploadRow.tsx'
|
||||
|
||||
it('adds the last General row only while served and removes all contributions on disposal', async () => {
|
||||
expect(hostApply).not.toThrow()
|
||||
const ctx = new Context()
|
||||
try {
|
||||
await ctx.plugin(SlotRegistry).await()
|
||||
ctx.provide('locale', new LocaleRuntime(ctx))
|
||||
const describe = vi.fn(async () => ({ ok: true as const, value: {
|
||||
writable: true, hasDocument: true,
|
||||
namespaces: [] as { ns: string; schema: object; value: object; applies: 'live'; secrets: never[]; revision: number }[],
|
||||
} }))
|
||||
const remote = new TestRemote(ctx, { settings: { describe } })
|
||||
await ctx.plugin({ inject: [...settingsInject], apply: settingsApply }).await()
|
||||
const slots = ctx.get('slots') as SlotRegistry
|
||||
slots.register({ name: 'root', children: {
|
||||
'settings.general.item': { kind: 'list', scope: 'root' },
|
||||
'shell.overlay': { kind: 'list', scope: 'root' },
|
||||
} } as never, () => null)
|
||||
const fiber = ctx.plugin({ inject, apply })
|
||||
await fiber.await()
|
||||
await ctx.configForms.describe().ensure()
|
||||
expect(slots.entries('settings.general.item')).toHaveLength(0)
|
||||
describe.mockResolvedValue({ ok: true, value: { writable: true, hasDocument: true, namespaces: [{
|
||||
ns: 'session-log-deepseek', schema: {}, value: { enabled: true }, applies: 'live', secrets: [], revision: 1,
|
||||
}] } })
|
||||
remote.emit('settings/document-updated', ['session-log-deepseek', 1])
|
||||
await vi.waitFor(() => { expect(slots.entries('settings.general.item')).toHaveLength(1) })
|
||||
expect(slots.entries('settings.general.item')[0]!.options).toMatchObject({ id: 'session-log-deepseek', order: 90 })
|
||||
expect(slots.entries('shell.overlay')).toHaveLength(1)
|
||||
const row = slots.entries('settings.general.item')[0]!
|
||||
const face = row.inject!() as UploadInjected & Record<string, unknown>
|
||||
const form = ctx.configForms.get('session-log-deepseek')
|
||||
const set = vi.spyOn(form, 'set').mockResolvedValue(false)
|
||||
await face.setEnabled(false)
|
||||
expect(set).toHaveBeenCalledWith('enabled', false)
|
||||
expect(face.hooks.mutation.getSnapshot().notice).toBe('failed')
|
||||
const toast = slots.entries('shell.overlay')[0]!.inject!() as UploadInjected & Record<string, unknown>
|
||||
expect(toast.hooks.mutation).toBe(face.hooks.mutation)
|
||||
toast.dismiss()
|
||||
expect(face.hooks.mutation.getSnapshot().notice).toBeNull()
|
||||
describe.mockResolvedValue({ ok: true, value: { writable: true, hasDocument: true, namespaces: [] } })
|
||||
remote.emit('settings/document-updated', ['session-log-deepseek', 2])
|
||||
await vi.waitFor(() => { expect(slots.entries('settings.general.item')).toHaveLength(0) })
|
||||
await fiber.dispose()
|
||||
expect(slots.entries('shell.overlay')).toHaveLength(0)
|
||||
} finally {
|
||||
await ctx.fiber.dispose()
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1 @@
|
||||
在使用官方模型 API 时上传 Session Log帮助改进 DeepSeek 模型与产品
|
||||
@@ -0,0 +1,79 @@
|
||||
// @vitest-environment jsdom
|
||||
/** The switch follows accepted Host state, including refused and delayed saves. */
|
||||
import { afterEach, expect, it, vi } from 'vitest'
|
||||
import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import { bindSnapshotSelector } from '@deepseek-ai/dsh-client-test-runtime'
|
||||
import { createSnapshotStore } from '@deepseek-ai/dsh-client-store'
|
||||
import type { ConfigForm, ConfigFormSnapshot } from '@deepseek-ai/dsh-client-ui-settings/client'
|
||||
import type { ComponentProps } from 'react'
|
||||
import { UploadRow, UploadToast } from '../src/client/UploadRow.tsx'
|
||||
import { UploadPreference, type UploadSettings } from '../src/client/upload-preference.ts'
|
||||
import { en, zh } from '../src/client/locales.ts'
|
||||
|
||||
afterEach(cleanup)
|
||||
|
||||
function fixture(writable = true) {
|
||||
const formState = createSnapshotStore<ConfigFormSnapshot<UploadSettings>>({
|
||||
status: 'ready', value: { enabled: true }, base: {}, user: {}, revision: 0, writable, mode: 'host',
|
||||
})
|
||||
const set = vi.fn(async (_field: string, enabled: unknown) => {
|
||||
formState.update((state) => { state.value = { enabled: enabled === true } })
|
||||
return true
|
||||
})
|
||||
const form: ConfigForm<UploadSettings> = {
|
||||
getSnapshot: () => formState.getSnapshot(), subscribe: listener => formState.subscribe(listener),
|
||||
set, unset: vi.fn(), mutate: vi.fn(),
|
||||
}
|
||||
const preference = new UploadPreference(form)
|
||||
const props = {
|
||||
useUpload: bindSnapshotSelector(formState), useMutation: bindSnapshotSelector(preference.state),
|
||||
setEnabled: (enabled: boolean) => preference.setEnabled(enabled), dismiss: () => { preference.dismiss() },
|
||||
t: (key: keyof typeof en) => en[key],
|
||||
} as ComponentProps<typeof UploadRow>
|
||||
return { formState, set, preference, props }
|
||||
}
|
||||
|
||||
it('shows a saved preference and keeps its notice after the row unmounts', async () => {
|
||||
const { props, set } = fixture()
|
||||
const row = render(<UploadRow {...props} />)
|
||||
render(<UploadToast {...props} />)
|
||||
fireEvent.click(screen.getByRole('switch', { name: en.title }))
|
||||
await waitFor(() => { expect(screen.getByRole('switch').getAttribute('aria-checked')).toBe('false') })
|
||||
expect(set).toHaveBeenCalledWith('enabled', false)
|
||||
row.unmount()
|
||||
expect(screen.getByText(en.saved)).toBeTruthy()
|
||||
})
|
||||
|
||||
it.each(['refused', 'rejected'] as const)('retains accepted state after a %s write', async (failure) => {
|
||||
const { props, set } = fixture()
|
||||
if (failure === 'refused') set.mockResolvedValue(false)
|
||||
else set.mockRejectedValue(new Error('Disconnected'))
|
||||
render(<><UploadRow {...props} /><UploadToast {...props} /></>)
|
||||
fireEvent.click(screen.getByRole('switch'))
|
||||
await screen.findByText(en.failed)
|
||||
expect(screen.getByRole('switch').getAttribute('aria-checked')).toBe('true')
|
||||
})
|
||||
|
||||
it('disables input until a pending write settles', async () => {
|
||||
const { props, set, preference } = fixture()
|
||||
const pending = Promise.withResolvers<boolean>()
|
||||
set.mockReturnValue(pending.promise)
|
||||
render(<UploadRow {...props} />)
|
||||
fireEvent.click(screen.getByRole('switch'))
|
||||
expect(screen.getByRole('switch').hasAttribute('disabled')).toBe(true)
|
||||
await preference.setEnabled(true)
|
||||
expect(set).toHaveBeenCalledTimes(1)
|
||||
pending.resolve(false)
|
||||
await waitFor(() => { expect(screen.getByRole('switch').hasAttribute('disabled')).toBe(false) })
|
||||
})
|
||||
|
||||
it('shows the Chinese label and cannot edit read-only settings', async () => {
|
||||
const { props, set } = fixture(false)
|
||||
const translated: Record<string, string> = zh
|
||||
const { container } = render(<UploadRow {...props} t={key => translated[key] ?? key} />)
|
||||
await expect(container.textContent + '\n').toMatchFileSnapshot('./expected/upload-row.zh.txt')
|
||||
expect(screen.getByRole('switch', { name: zh.title }).hasAttribute('disabled')).toBe(true)
|
||||
expect(screen.getByRole('switch').getAttribute('aria-checked')).toBe('true')
|
||||
fireEvent.click(screen.getByRole('switch'))
|
||||
expect(set).not.toHaveBeenCalled()
|
||||
})
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.client.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "lib/types"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../locale/tsconfig.client.json"
|
||||
},
|
||||
{
|
||||
"path": "../../../vendor/cordis"
|
||||
},
|
||||
{
|
||||
"path": "../store"
|
||||
},
|
||||
{
|
||||
"path": "../../test-support/client-runtime"
|
||||
},
|
||||
{
|
||||
"path": "../ui-primitives"
|
||||
},
|
||||
{
|
||||
"path": "../ui-renderer"
|
||||
},
|
||||
{
|
||||
"path": "../ui-layout"
|
||||
},
|
||||
{
|
||||
"path": "../ui-settings/tsconfig.client.json"
|
||||
},
|
||||
{
|
||||
"path": "../../api/remotes/tsconfig.client.json"
|
||||
},
|
||||
{
|
||||
"path": "../ui-slots"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import { clientBundle } from '../tsdown.client.ts'
|
||||
|
||||
export default clientBundle('@deepseek-ai/dsh-client-ui-settings-session-log', ['lib/types/index.js'])
|
||||
@@ -2358,6 +2358,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
|
||||
'client-ui-permission-presets PermissionRow id \'permission\'',
|
||||
'client-ui-settings-general DeveloperToolsRow id \'developer-tools\'',
|
||||
'client-ui-settings-general CurrentVersionRow id \'current-version\'',
|
||||
'client-ui-settings-session-log UploadRow',
|
||||
'client-ui-shortcuts ShortcutsRow id \'shortcuts\'',
|
||||
'client-ui-theme AppearanceRow id \'appearance\'',
|
||||
'client-ui-theme FontSizeRow id \'font-size\'',
|
||||
@@ -2805,6 +2806,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
|
||||
'client-ui-schedule ScheduleDeleteToast id \'schedule.delete-toast\'',
|
||||
'client-ui-settings-account DesktopOnboardingEntry id \'desktop-onboarding\'',
|
||||
'client-ui-settings-account AccountPlatformHost id \'account.platform-page\'',
|
||||
'client-ui-settings-session-log UploadToast id \'session-log-upload-toast\'',
|
||||
'client-ui-shortcuts ShortcutReference id \'shortcuts\'',
|
||||
'client-ui-workspace SessionRenameDialog id \'workspace.session-rename\'',
|
||||
'client-ui-workspace SessionArchiveConfirmDialog id \'workspace.session-archive\'',
|
||||
|
||||
+1
@@ -96,6 +96,7 @@ Every package below exports a Cordis plugin that a bundle patch can name in a Lo
|
||||
| `@deepseek-ai/dsh-client-ui-settings-models` | yes | Models settings and shared product-onboarding dialogs over existing settings and credential joins |
|
||||
| `@deepseek-ai/dsh-client-ui-settings-plugin-inventory` | no | Read-only Cordis Loader inventory tab in Web Plugins settings |
|
||||
| `@deepseek-ai/dsh-client-ui-settings-plugins` | no | Built-in plugins settings section for the dsh web client: the Settings navigation entry and the tab chrome feature-owned tabs register into |
|
||||
| `@deepseek-ai/dsh-client-ui-settings-session-log` | no | General settings control for Session-log upload with DeepSeek API requests |
|
||||
| `@deepseek-ai/dsh-client-ui-settings-shell` | no | Settings page of the shell executor on the dsh web client's Plugins page: the command timeout and the per-stream output cap of the shell namespace |
|
||||
| `@deepseek-ai/dsh-client-ui-settings-subagent` | no | Settings page of Subagent delegation on the dsh web client's Plugins page: recursion depth, parallel capacity, and the models agents may choose for subagents |
|
||||
| `@deepseek-ai/dsh-client-ui-settings-web-search` | no | Settings page of the DeepSeek web-search provider on the dsh web client's Plugins page: its API key, endpoint, and per-request search budget |
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
en: e2aa29e41bc101c4
|
||||
zh: 2ef629f2a444bc13
|
||||
/deepseek-ai-dsh-session-log-deepseek/configuration:
|
||||
en: 1e8712ec4f0fefcc
|
||||
zh: 5ffe754b2cefdd21
|
||||
en: 1b899e63d8039643
|
||||
zh: d489461bbb618b5d
|
||||
/deepseek-ai-dsh-session-log-deepseek/request-field:
|
||||
en: 197814e2c48d05b5
|
||||
zh: f4616fb23a3c7a32
|
||||
|
||||
@@ -27,10 +27,12 @@ Incremental canonical session-log upload for official DeepSeek LLM API requests.
|
||||
|
||||
| Key | Default | Meaning |
|
||||
|---|---:|---|
|
||||
| `enabled` | `true` | Register the `dsh_session_log` contribution. Set it to `false` to stop Session-log upload. |
|
||||
| `enabled` | `true` | Send the `dsh_session_log` contribution. Changes apply to the next request; `false` stops Session-log upload. |
|
||||
| `maxBytes` | 8 MiB | Largest serialized `dsh_session_log` field, in UTF-8 bytes, that one request carries. |
|
||||
|
||||
Shipped profiles mount the plugin, so the default configuration registers the request field and appends the acceptance watermark; an overlay opts out with `enabled: false`.
|
||||
Shipped profiles mount the plugin, so the default configuration registers the request field and appends the acceptance watermark; `enabled: false` stops the contribution. The Web switch can override bundle defaults through the active profile; home patches and command-line overlays retain precedence and reject conflicting form writes.
|
||||
|
||||
The Web **Settings → General → Upload Session Log when using the official model API** switch persists `enabled` through the Host configuration. In-flight requests keep their prepared payload. Re-enabling resumes the unaccepted suffix, including events recorded while disabled. OpenTelemetry feedback uploads have a separate setting.
|
||||
|
||||
<a id="request-field"></a>
|
||||
## Request field
|
||||
|
||||
@@ -27,10 +27,12 @@ kind: "package-reference"
|
||||
|
||||
| 配置键 | 默认值 | 含义 |
|
||||
|---|---:|---|
|
||||
| `enabled` | `true` | 注册 `dsh_session_log` 贡献。将其设为 `false` 可停止会话日志上传。 |
|
||||
| `enabled` | `true` | 发送 `dsh_session_log` 贡献。修改从下一次请求生效;设为 `false` 可停止会话日志上传。 |
|
||||
| `maxBytes` | 8 MiB | 单次请求携带的 `dsh_session_log` 字段序列化后的最大 UTF-8 字节数。 |
|
||||
|
||||
随附 profile 会挂载该插件,因此默认配置会注册请求字段并追加接受水位;overlay 可用 `enabled: false` 选择退出。
|
||||
随附 profile 会挂载该插件,因此默认配置会注册请求字段并追加接受水位;`enabled: false` 会停止贡献。Web 开关可通过当前 profile 覆盖组合包默认值;home patch 和命令行 overlay 保持更高优先级,会拒绝与其冲突的表单写入。
|
||||
|
||||
Web **设置 → 通用 → 在使用官方模型 API 时上传 Session Log** 开关通过 Host 配置持久化 `enabled`。进行中的请求保留已准备的载荷。重新开启后继续上传尚未确认的后缀,包括关闭期间记录的事件。OpenTelemetry 反馈上传使用独立设置。
|
||||
|
||||
<a id="request-field"></a>
|
||||
## 请求字段
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
import { Buffer } from 'node:buffer'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import type { Context, Volatile } from '@deepseek-ai/cordis'
|
||||
import z from '@deepseek-ai/schemastery'
|
||||
import { brandString } from '@deepseek-ai/dsh-brand'
|
||||
import type {} from '@deepseek-ai/dsh-deepseek-llm-api-extensions'
|
||||
@@ -38,18 +38,18 @@ export const inject = ['deepseekLlmApiExtensions', 'sessions']
|
||||
/** Session-log request contribution configuration. */
|
||||
export interface Config {
|
||||
/** Contribute `dsh_session_log` to official DeepSeek requests. Defaults to `true`. */
|
||||
enabled?: boolean
|
||||
enabled: Volatile<boolean>
|
||||
/**
|
||||
* Largest serialized `dsh_session_log` field, in UTF-8 bytes, that one request carries.
|
||||
* A request uploads the longest pending event prefix that fits; later requests continue
|
||||
* after its acceptance. Defaults to 8 MiB.
|
||||
*/
|
||||
maxBytes?: number
|
||||
maxBytes: number
|
||||
}
|
||||
|
||||
/** Validated Session-log request contribution configuration. */
|
||||
export const Config: z<Config> = z.object({
|
||||
enabled: z.boolean().default(true),
|
||||
export const Config = z.object({
|
||||
enabled: z.boolean().default(true).volatile(),
|
||||
maxBytes: z.number().step(1).min(1).default(8 * 1024 * 1024),
|
||||
})
|
||||
|
||||
@@ -175,16 +175,16 @@ export function acceptedThrough(session: Session): SessionSeqCursor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the incremental `dsh_session_log` request contribution when enabled.
|
||||
* Register the incremental request contribution; enablement is read for each request.
|
||||
* @param ctx - plugin context carrying Sessions and the DeepSeek request-extension registry.
|
||||
* @param config - validated configuration.
|
||||
*/
|
||||
export function apply(ctx: Context, config: Config): void {
|
||||
if (config.enabled !== true) return
|
||||
// Schemastery validates and fills the defaults before `apply` runs.
|
||||
const { maxBytes } = config as Required<Config>
|
||||
const { maxBytes } = config
|
||||
ctx.deepseekLlmApiExtensions.register('dsh_session_log', {
|
||||
prepare: (request) => {
|
||||
if (!config.enabled.get()) return undefined
|
||||
// TODO: Define an explicit wire result for direct or stale-session calls if they become a supported product path.
|
||||
if (request.sessionId === undefined) return undefined
|
||||
const session = ctx.sessions.get(brandString<SessionId>(request.sessionId))
|
||||
|
||||
@@ -20,9 +20,9 @@ describe('session-log upload configuration', () => {
|
||||
// Import under each environment to catch environment-dependent schema defaults.
|
||||
vi.resetModules()
|
||||
const { Config } = await import('../src/index.ts')
|
||||
expect(Config({}).enabled).toBe(enabled)
|
||||
expect(Config({ enabled: true }).enabled).toBe(true)
|
||||
expect(Config({ enabled: false }).enabled).toBe(false)
|
||||
expect(Config({}).enabled.get()).toBe(enabled)
|
||||
expect(Config({ enabled: true }).enabled.get()).toBe(true)
|
||||
expect(Config({ enabled: false }).enabled.get()).toBe(false)
|
||||
} finally {
|
||||
vi.unstubAllEnvs()
|
||||
vi.resetModules()
|
||||
|
||||
@@ -32,7 +32,9 @@ afterEach(async () => {
|
||||
})
|
||||
|
||||
/** Boot the official upload route through the Loader with one session-log configuration. */
|
||||
async function boot(sessionLog: SessionLogDeepSeek.Config, mock: MockLlmServerOptions): Promise<{ ctx: Context; server: MockLlmServer }> {
|
||||
async function boot(
|
||||
sessionLog: { enabled: boolean; maxBytes?: number }, mock: MockLlmServerOptions,
|
||||
): Promise<{ ctx: Context; server: MockLlmServer }> {
|
||||
root = await mkdtemp(join(tmpdir(), 'dsh-feedback-upload-'))
|
||||
vi.stubEnv('DSH_HOME', root)
|
||||
vi.stubEnv('DEEPSEEK_API_KEY', 'feedback-test-key')
|
||||
@@ -174,3 +176,33 @@ it('drains a Session log above the configured maxBytes across consecutive routed
|
||||
await handle.close()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
it('changes API log upload on the next request without replacing the plugin fiber', async () => {
|
||||
const { ctx, server } = await boot({ enabled: true }, { sequence: ['success'], repeatLast: true })
|
||||
const session = ctx.sessions.create(SessionId('live-upload-setting'))
|
||||
session.append('user/message', createUserMessage({ content: [{ type: 'text', text: 'Hello' }], source: { kind: 'user' } }), { surfaceOp: 'append' })
|
||||
const entry = [...ctx.loader.entries()].find(entry => entry.options.name === '@deepseek-ai/dsh-session-log-deepseek')!
|
||||
const fiber = entry.fiber
|
||||
const request = async () => {
|
||||
const chunks = []
|
||||
for await (const chunk of ctx.llm.stream({ provider: 'deepseek-official', model: 'deepseek-v4-flash', sessionId: session.id, messages: session.deriveMessages() })) chunks.push(chunk)
|
||||
expect(chunks.at(-1)).toMatchObject({ type: 'finish', reason: { kind: 'stop' } })
|
||||
}
|
||||
await request()
|
||||
const accepted = SessionLogDeepSeek.acceptedThrough(session)
|
||||
await entry.update({ config: { enabled: false } })
|
||||
await ctx.loader.await()
|
||||
expect(entry.fiber === fiber).toBe(true)
|
||||
recordFeedback(session, { text: 'Recorded while API upload is off' })
|
||||
await request()
|
||||
expect(server.requests[1]!.body).not.toHaveProperty('dsh_session_log')
|
||||
expect(SessionLogDeepSeek.acceptedThrough(session)).toBe(accepted)
|
||||
await entry.update({ config: { enabled: true } })
|
||||
await ctx.loader.await()
|
||||
expect(entry.fiber === fiber).toBe(true)
|
||||
await request()
|
||||
const resumed = (server.requests[2]!.body as { dsh_session_log: DeepSeekSessionLogExtension }).dsh_session_log
|
||||
expect(resumed.afterSeq).toBe(accepted)
|
||||
expect(resumed.events.some(event => event.type === 'feedback/record')).toBe(true)
|
||||
})
|
||||
Generated
+51
@@ -789,6 +789,9 @@ importers:
|
||||
'@deepseek-ai/dsh-launch-environment':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/util/launch-environment
|
||||
'@deepseek-ai/dsh-llm-mock-server':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/test-support/llm-mock-server
|
||||
'@deepseek-ai/dsh-pwsh-local':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/shell/pwsh-local
|
||||
@@ -2503,6 +2506,9 @@ importers:
|
||||
'@deepseek-ai/dsh-client-ui-settings-plugins':
|
||||
specifier: workspace:*
|
||||
version: link:../../client/ui-settings-plugins
|
||||
'@deepseek-ai/dsh-client-ui-settings-session-log':
|
||||
specifier: workspace:*
|
||||
version: link:../../client/ui-settings-session-log
|
||||
'@deepseek-ai/dsh-client-ui-settings-shell':
|
||||
specifier: workspace:*
|
||||
version: link:../../client/ui-settings-shell
|
||||
@@ -5009,6 +5015,51 @@ importers:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
|
||||
packages/client/ui-settings-session-log:
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis':
|
||||
specifier: workspace:~
|
||||
version: link:../../../vendor/cordis
|
||||
'@deepseek-ai/dsh-api-remotes':
|
||||
specifier: workspace:*
|
||||
version: link:../../api/remotes
|
||||
'@deepseek-ai/dsh-client-locale':
|
||||
specifier: workspace:*
|
||||
version: link:../locale
|
||||
'@deepseek-ai/dsh-client-store':
|
||||
specifier: workspace:*
|
||||
version: link:../store
|
||||
'@deepseek-ai/dsh-client-test-runtime':
|
||||
specifier: workspace:*
|
||||
version: link:../../test-support/client-runtime
|
||||
'@deepseek-ai/dsh-client-ui-layout':
|
||||
specifier: workspace:*
|
||||
version: link:../ui-layout
|
||||
'@deepseek-ai/dsh-client-ui-primitives':
|
||||
specifier: workspace:*
|
||||
version: link:../ui-primitives
|
||||
'@deepseek-ai/dsh-client-ui-renderer':
|
||||
specifier: workspace:*
|
||||
version: link:../ui-renderer
|
||||
'@deepseek-ai/dsh-client-ui-settings':
|
||||
specifier: workspace:*
|
||||
version: link:../ui-settings
|
||||
'@deepseek-ai/dsh-client-ui-slots':
|
||||
specifier: workspace:*
|
||||
version: link:../ui-slots
|
||||
'@types/react':
|
||||
specifier: ~18.3.1
|
||||
version: 18.3.31
|
||||
'@types/react-dom':
|
||||
specifier: ~18.3.0
|
||||
version: 18.3.7(@types/react@18.3.31)
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1
|
||||
react-dom:
|
||||
specifier: ^18.2.0
|
||||
version: 18.3.1(react@18.3.1)
|
||||
|
||||
packages/client/ui-settings-shell:
|
||||
devDependencies:
|
||||
'@deepseek-ai/cordis':
|
||||
|
||||
@@ -133,6 +133,7 @@ const SENTENCE_MODEL_EXPERIENCE: Readonly<Record<string, SentenceContract>> = {
|
||||
'packages/client/ui-permission-presets': { kind: 'indirect', reason: 'The picker submits the host /permission command; the knob events it appends own the model-visible effect through the sandbox/approval consumers.' },
|
||||
'packages/client/ui-settings-plugins': { kind: 'none', reason: 'Browser-side settings surface; registers no model surface.' },
|
||||
'packages/client/ui-settings-shell': { kind: 'none', reason: 'Browser-side settings surface; registers no model surface.' },
|
||||
'packages/client/ui-settings-session-log': { kind: 'none', reason: 'Browser preference controls request metadata, not model input.' },
|
||||
'packages/client/ui-settings-agent-loop': { kind: 'none', reason: 'Browser-side settings surface; registers no model surface.' },
|
||||
'packages/client/ui-settings-subagent': { kind: 'none', reason: 'Browser-side settings surface; registers no model surface.' },
|
||||
'packages/client/ui-settings-web-search': { kind: 'none', reason: 'Browser-side settings surface; registers no model surface.' },
|
||||
|
||||
@@ -282,6 +282,7 @@
|
||||
"@deepseek-ai/dsh-client-ui-settings-plugins": ["./packages/client/ui-settings-plugins/src"],
|
||||
"@deepseek-ai/dsh-client-ui-settings-shell": ["./packages/client/ui-settings-shell/src"],
|
||||
"@deepseek-ai/dsh-client-ui-settings-agent-loop": ["./packages/client/ui-settings-agent-loop/src"],
|
||||
"@deepseek-ai/dsh-client-ui-settings-session-log": ["./packages/client/ui-settings-session-log/src"],
|
||||
"@deepseek-ai/dsh-client-ui-settings-subagent": ["./packages/client/ui-settings-subagent/src"],
|
||||
"@deepseek-ai/dsh-client-ui-settings-web-search": ["./packages/client/ui-settings-web-search/src"],
|
||||
"@deepseek-ai/dsh-client-ui-jobs": ["./packages/client/ui-jobs/src"],
|
||||
|
||||
@@ -122,6 +122,7 @@
|
||||
{ "path": "./packages/client/ui-settings-plugins" },
|
||||
{ "path": "./packages/client/ui-settings-shell" },
|
||||
{ "path": "./packages/client/ui-settings-agent-loop" },
|
||||
{ "path": "./packages/client/ui-settings-session-log" },
|
||||
{ "path": "./packages/client/ui-settings-subagent" },
|
||||
{ "path": "./packages/client/ui-settings-web-search" },
|
||||
{ "path": "./packages/client/ui-user-questions" },
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
"apps/web/tests/plugin-install-github.e2e.ts",
|
||||
"apps/web/tests/client-plugin-live.e2e.ts",
|
||||
"apps/web/tests/settings-chrome.e2e.ts",
|
||||
"apps/web/tests/session-log-upload.e2e.ts",
|
||||
"apps/web/tests/caption-overlays.e2e.ts",
|
||||
"apps/web/tests/settings-appearance.e2e.ts",
|
||||
"apps/web/tests/menu-material.e2e.ts",
|
||||
|
||||
Reference in new issue
Block a user