mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
docs: fix release smoke test failures
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/user/develop/basic/index.md
|
||||
index.md: 494b7869be6ffdf5767fac260b36b2585305b516
|
||||
index.zh.md: a55b8e31151c5cfa5445069974be9fa022fea1eb
|
||||
index.md: 08199624e638aaf4a36b04446c39c228b2af6025
|
||||
index.zh.md: c45a30d0bfffaf4a6c78303f9ca043c3397c8a08
|
||||
|
||||
@@ -45,14 +45,16 @@ export function apply(ctx: Context) {
|
||||
|
||||
## Register it in cordis.yml
|
||||
|
||||
Create `scratch-plugin/cordis.yml` as a Web overlay that inserts the local plugin:
|
||||
Run `pwd` from the repository root, then create `scratch-plugin/cordis.yml` as a Web overlay that inserts the local plugin. Replace `/absolute/path/to/deepseek-harness` below with the printed path:
|
||||
|
||||
```yaml
|
||||
- insert:
|
||||
- id: hello
|
||||
name: './src/my-plugin.ts'
|
||||
name: '/absolute/path/to/deepseek-harness/scratch-plugin/src/my-plugin.ts'
|
||||
```
|
||||
|
||||
The plugin path must be absolute. A patch file contributes configuration but does not change the profile directory from which the loader resolves module paths.
|
||||
|
||||
Start the Web UI with that overlay:
|
||||
|
||||
```sh
|
||||
|
||||
@@ -45,14 +45,16 @@ export function apply(ctx: Context) {
|
||||
|
||||
## 注册到 cordis.yml
|
||||
|
||||
创建 `scratch-plugin/cordis.yml`,作为插入本地插件的 Web 覆盖层:
|
||||
在仓库根目录运行 `pwd`,然后创建 `scratch-plugin/cordis.yml`,作为插入本地插件的 Web 覆盖层。请将下文的 `/absolute/path/to/deepseek-harness` 替换为命令打印的路径:
|
||||
|
||||
```yaml
|
||||
- insert:
|
||||
- id: hello
|
||||
name: './src/my-plugin.ts'
|
||||
name: '/absolute/path/to/deepseek-harness/scratch-plugin/src/my-plugin.ts'
|
||||
```
|
||||
|
||||
插件路径必须是绝对路径。patch 文件只贡献配置,不会改变 loader 解析模块路径时使用的 profile 目录。
|
||||
|
||||
使用该覆盖层启动 Web UI:
|
||||
|
||||
```sh
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/user/develop/basic/publish.md
|
||||
publish.md: 588531a28020ebe620643cd1aaaa43de000e658a
|
||||
publish.zh.md: b86bd43369c027972705394fded43ae053248c0f
|
||||
publish.md: edc85b74049c5b993efb02663195fcc0001b4620
|
||||
publish.zh.md: cb824ad12eec0d706efa17b28f3bdc874ba59131
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
English | [中文](publish.zh.md)
|
||||
|
||||
The previous tutorials loaded a local plugin through a `--patch` overlay. This tutorial packages it as an installable **bundle**, installs it into a **profile** with `dsh plugin add`, and explains the layer order that determines the composed configuration. Complete [plugin configuration](./config.md) first.
|
||||
The previous tutorials loaded a local plugin through a `--patch` overlay. This tutorial packages it as an installable **bundle**, installs it into a **profile** with `pnpm dsh plugin add`, and explains the layer order that determines the composed configuration. Complete [plugin configuration](./config.md) first.
|
||||
|
||||
## Two concepts, two manifests
|
||||
|
||||
@@ -11,10 +11,16 @@ Installation is built on two concepts. Both are described by a `package.json`, b
|
||||
- A **bundle** is an npm package that ships a configuration layer. Its manifest declares `dsh.bundle`, answering "what does this package contribute?": a patch file that inserts or overrides plugin rows.
|
||||
- A **profile** is a directory under `$DSH_HOME/profiles/<name>` describing one runnable composition. Its manifest declares `dsh.profile`, answering "which bundles compose this setup, in what order?".
|
||||
|
||||
A bundle is what you author and distribute; a profile is what a user boots with `dsh --profile <name>`. Nothing is both.
|
||||
A bundle is what you author and distribute; a profile is what a user boots from this source checkout with `pnpm dsh --profile <name>`. Nothing is both.
|
||||
|
||||
### The bundle manifest
|
||||
|
||||
From the repository root, create the package directory:
|
||||
|
||||
```sh
|
||||
mkdir -p hello-plugin
|
||||
```
|
||||
|
||||
```
|
||||
hello-plugin/
|
||||
├── package.json # declares dsh.bundle
|
||||
@@ -22,6 +28,8 @@ hello-plugin/
|
||||
└── index.js # plugin modules the patch rows reference
|
||||
```
|
||||
|
||||
Create `hello-plugin/package.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "dsh-hello-plugin",
|
||||
@@ -33,7 +41,17 @@ hello-plugin/
|
||||
}
|
||||
```
|
||||
|
||||
The patch file is a YAML array of patch entries, like the `--patch` overlays you have been writing, except plugin rows reference the package by name instead of a relative source path so Node resolution finds the installed code:
|
||||
Create `hello-plugin/index.js` with the plugin entry point:
|
||||
|
||||
```js
|
||||
export const name = 'hello-plugin'
|
||||
|
||||
export function apply() {
|
||||
console.log('[hello-plugin] plugin loaded!')
|
||||
}
|
||||
```
|
||||
|
||||
Create `hello-plugin/cordis.patch.yml`. The patch is a YAML array like the `--patch` overlays you have been writing, except plugin rows reference the package by name instead of a relative source path so Node resolution finds the installed code:
|
||||
|
||||
```yaml
|
||||
- insert:
|
||||
@@ -41,7 +59,7 @@ The patch file is a YAML array of patch entries, like the `--patch` overlays you
|
||||
name: dsh-hello-plugin
|
||||
```
|
||||
|
||||
A package without the `dsh.bundle` declaration still installs, but only as a plain dependency: `dsh plugin` prints a warning and activates no layer. Use that package format for a library that plugin packages import rather than a plugin users enable.
|
||||
A package without the `dsh.bundle` declaration still installs, but only as a plain dependency: `pnpm dsh plugin` prints a warning and activates no layer. Use that package format for a library that plugin packages import rather than a plugin users enable.
|
||||
|
||||
### The profile manifest
|
||||
|
||||
@@ -50,15 +68,14 @@ A profile directory holds two files:
|
||||
- `package.json` — the profile's out-of-tree plugin dependencies (managed by pnpm) plus the `dsh.profile` manifest with its ordered `bundles` list.
|
||||
- `cordis.patch.yml` — the user's own patch layer, applied after every bundle layer.
|
||||
|
||||
You never write a profile manifest by hand: `dsh plugin` creates and maintains it. The next section shows the result.
|
||||
You never write a profile manifest by hand: `pnpm dsh plugin` creates and maintains it. The next section shows the result.
|
||||
|
||||
## Install into a profile
|
||||
|
||||
`dsh plugin --profile <name> <args...>` forwards to pnpm in the profile directory, so every pnpm verb works. Install your package from its checkout:
|
||||
`pnpm dsh plugin --profile <name> <args...>` forwards to pnpm in the profile directory, so every pnpm verb works. From the repository root, install the package checkout:
|
||||
|
||||
```sh
|
||||
cd hello-plugin
|
||||
dsh plugin --profile demo add .
|
||||
pnpm dsh plugin --profile demo add ./hello-plugin
|
||||
```
|
||||
|
||||
The first use initializes the profile (with `@deepseek-ai/dsh-base` as its first bundle), pnpm links the checkout, and `dsh` appends the bundle to `dsh.profile.bundles` because the package declares `dsh.bundle`:
|
||||
@@ -84,11 +101,11 @@ The first use initializes the profile (with `@deepseek-ai/dsh-base` as its first
|
||||
Verify the layer without booting, then boot:
|
||||
|
||||
```sh
|
||||
dsh --profile demo --dump-config # shows a "# == dsh-hello-plugin" layer
|
||||
dsh --profile demo
|
||||
pnpm dsh --profile demo --dump-config # shows a "# == dsh-hello-plugin" layer
|
||||
pnpm dsh --profile demo
|
||||
```
|
||||
|
||||
`dsh plugin --profile demo remove dsh-hello-plugin` removes both the dependency and the layer.
|
||||
`pnpm dsh plugin --profile demo remove dsh-hello-plugin` removes both the dependency and the layer.
|
||||
|
||||
## The loading order
|
||||
|
||||
@@ -136,7 +153,7 @@ On `--help`, the provider publishes no service, so those rows never activate. Lo
|
||||
Publishing to a registry is not required — users can install straight from a git host:
|
||||
|
||||
```sh
|
||||
dsh plugin --profile demo add github:you/hello-plugin
|
||||
pnpm dsh plugin --profile demo add github:you/hello-plugin
|
||||
```
|
||||
|
||||
But a git install fetches **sources, not built artifacts**: nothing runs your `build` script, so a TypeScript package arrives without its `lib/` output and fails to load. Two things must happen, one on each side:
|
||||
@@ -155,8 +172,8 @@ Treat that allowance as what it is: **permission to execute the package's code o
|
||||
|
||||
If you would rather not ask users for the allowance, distribute built artifacts instead — neither form needs any build permission:
|
||||
|
||||
- **Publish to npm** with `lib/` built at `pnpm publish` time; `dsh plugin add your-package` then installs prebuilt code.
|
||||
- **Ship a tarball** from `pnpm pack`; users run `dsh plugin add ./hello-plugin-0.1.0.tgz`.
|
||||
- **Publish to npm** with `lib/` built at `pnpm publish` time; `pnpm dsh plugin add your-package` then installs prebuilt code.
|
||||
- **Ship a tarball** from `pnpm pack`; users run `pnpm dsh plugin add ./hello-plugin-0.1.0.tgz`.
|
||||
|
||||
## Next steps
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[English](publish.md) | 中文
|
||||
|
||||
前几篇教程通过 `--patch` overlay 加载本地插件。本教程把它打包成可安装的**组合包**(bundle),用 `dsh plugin add` 安装进一个 **profile**,并解释决定组合后配置的层顺序。请先完成[插件配置](./config.md)。
|
||||
前几篇教程通过 `--patch` overlay 加载本地插件。本教程把它打包成可安装的**组合包**(bundle),用 `pnpm dsh plugin add` 安装进一个 **profile**,并解释决定组合后配置的层顺序。请先完成[插件配置](./config.md)。
|
||||
|
||||
## 两个概念,两种 manifest
|
||||
|
||||
@@ -11,10 +11,16 @@
|
||||
- **组合包**是附带一个配置层的 npm 包。它的 manifest 声明 `dsh.bundle`,回答的是"这个包贡献什么?":一个插入或覆盖插件行的 patch 文件。
|
||||
- **profile** 是位于 `$DSH_HOME/profiles/<name>` 下、描述一份可启动组合的目录。它的 manifest 声明 `dsh.profile`,回答的是"这套配置由哪些组合包按什么顺序组成?"。
|
||||
|
||||
组合包是你编写并分发的东西;profile 是用户用 `dsh --profile <name>` 启动的东西。没有东西同时是两者。
|
||||
组合包是你编写并分发的东西;profile 是用户在当前源码 checkout 中用 `pnpm dsh --profile <name>` 启动的东西。没有东西同时是两者。
|
||||
|
||||
### 组合包 manifest
|
||||
|
||||
在仓库根目录创建包目录:
|
||||
|
||||
```sh
|
||||
mkdir -p hello-plugin
|
||||
```
|
||||
|
||||
```
|
||||
hello-plugin/
|
||||
├── package.json # declares dsh.bundle
|
||||
@@ -22,6 +28,8 @@ hello-plugin/
|
||||
└── index.js # plugin modules the patch rows reference
|
||||
```
|
||||
|
||||
创建 `hello-plugin/package.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "dsh-hello-plugin",
|
||||
@@ -33,7 +41,17 @@ hello-plugin/
|
||||
}
|
||||
```
|
||||
|
||||
patch 文件与一直在写的 `--patch` overlay 一样,是一个 patch 条目的 YAML 数组;区别是插件行按包名而不是相对源码路径引用这个包,这样 Node 的模块解析才能找到已安装的代码:
|
||||
创建 `hello-plugin/index.js`,写入插件入口:
|
||||
|
||||
```js
|
||||
export const name = 'hello-plugin'
|
||||
|
||||
export function apply() {
|
||||
console.log('[hello-plugin] plugin loaded!')
|
||||
}
|
||||
```
|
||||
|
||||
创建 `hello-plugin/cordis.patch.yml`。这个 patch 与一直在写的 `--patch` overlay 一样,是一个 patch 条目的 YAML 数组;区别是插件行按包名而不是相对源码路径引用这个包,这样 Node 的模块解析才能找到已安装的代码:
|
||||
|
||||
```yaml
|
||||
- insert:
|
||||
@@ -41,7 +59,7 @@ patch 文件与一直在写的 `--patch` overlay 一样,是一个 patch 条目
|
||||
name: dsh-hello-plugin
|
||||
```
|
||||
|
||||
没有 `dsh.bundle` 声明的包仍然可以安装,但只作为普通依赖:`dsh plugin` 会打印警告,且不激活任何层。如果一个库供插件包 import,而不是供用户启用,就使用这种包格式。
|
||||
没有 `dsh.bundle` 声明的包仍然可以安装,但只作为普通依赖:`pnpm dsh plugin` 会打印警告,且不激活任何层。如果一个库供插件包 import,而不是供用户启用,就使用这种包格式。
|
||||
|
||||
### profile manifest
|
||||
|
||||
@@ -50,15 +68,14 @@ profile 目录包含两个文件:
|
||||
- `package.json` — profile 的树外插件依赖(由 pnpm 管理),加上 `dsh.profile` manifest 及其有序的 `bundles` 列表。
|
||||
- `cordis.patch.yml` — 用户自己的 patch 层,在每个组合包层之后应用。
|
||||
|
||||
profile manifest 从不需要手写:`dsh plugin` 负责创建和维护它。下一节展示其结果。
|
||||
profile manifest 从不需要手写:`pnpm dsh plugin` 负责创建和维护它。下一节展示其结果。
|
||||
|
||||
## 安装进 profile
|
||||
|
||||
`dsh plugin --profile <name> <args...>` 在 profile 目录内转发给 pnpm,因此所有 pnpm 子命令都可用。从 checkout 安装你的包:
|
||||
`pnpm dsh plugin --profile <name> <args...>` 在 profile 目录内转发给 pnpm,因此所有 pnpm 子命令都可用。在仓库根目录安装该包的 checkout:
|
||||
|
||||
```sh
|
||||
cd hello-plugin
|
||||
dsh plugin --profile demo add .
|
||||
pnpm dsh plugin --profile demo add ./hello-plugin
|
||||
```
|
||||
|
||||
首次使用会初始化 profile(`@deepseek-ai/dsh-base` 作为它的第一个组合包),pnpm 链接该 checkout,而 `dsh` 因为这个包声明了 `dsh.bundle`,把它追加进 `dsh.profile.bundles`:
|
||||
@@ -84,11 +101,11 @@ dsh plugin --profile demo add .
|
||||
先不启动、只验证该层,再启动:
|
||||
|
||||
```sh
|
||||
dsh --profile demo --dump-config # shows a "# == dsh-hello-plugin" layer
|
||||
dsh --profile demo
|
||||
pnpm dsh --profile demo --dump-config # shows a "# == dsh-hello-plugin" layer
|
||||
pnpm dsh --profile demo
|
||||
```
|
||||
|
||||
`dsh plugin --profile demo remove dsh-hello-plugin` 会同时移除依赖和对应的层。
|
||||
`pnpm dsh plugin --profile demo remove dsh-hello-plugin` 会同时移除依赖和对应的层。
|
||||
|
||||
## 加载顺序
|
||||
|
||||
@@ -136,7 +153,7 @@ dsh --profile demo
|
||||
发布到注册表不是必须的——用户可以直接从 git 托管安装:
|
||||
|
||||
```sh
|
||||
dsh plugin --profile demo add github:you/hello-plugin
|
||||
pnpm dsh plugin --profile demo add github:you/hello-plugin
|
||||
```
|
||||
|
||||
但 git 安装拉取的是**源码,不是构建产物**:没有任何环节运行你的 `build` 脚本,因此 TypeScript 包到手时没有 `lib/` 输出,加载会失败。必须两边各做一件事:
|
||||
@@ -155,8 +172,8 @@ dsh plugin --profile demo add github:you/hello-plugin
|
||||
|
||||
如果不想让用户做这项授权,就改为分发构建产物——以下两种形式都不需要任何构建权限:
|
||||
|
||||
- **发布到 npm**,在 `pnpm publish` 时构建好 `lib/`;`dsh plugin add your-package` 安装的就是预构建代码。
|
||||
- **交付 tarball**:用 `pnpm pack` 打包;用户执行 `dsh plugin add ./hello-plugin-0.1.0.tgz`。
|
||||
- **发布到 npm**,在 `pnpm publish` 时构建好 `lib/`;`pnpm dsh plugin add your-package` 安装的就是预构建代码。
|
||||
- **交付 tarball**:用 `pnpm pack` 打包;用户执行 `pnpm dsh plugin add ./hello-plugin-0.1.0.tgz`。
|
||||
|
||||
## 下一步
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/user/develop/framework/events.md
|
||||
events.md: 4a6ecbad614cf2debccaee9ace1086273d8fb95b
|
||||
events.zh.md: c77747bf6ac767c69e4f2d9cfe7375b71bd388e6
|
||||
events.md: 1d9fe5c8f5068de6ad8b2abaa85cf67be35c8459
|
||||
events.zh.md: 8bb9447a270cc2db966b1e01298a831a60a1b9c1
|
||||
|
||||
@@ -40,7 +40,7 @@ ctx.on('my-plugin/ready', ({ id }) => {
|
||||
|
||||
### bail — short circuit
|
||||
|
||||
Listeners run in order; the first non-`undefined` result becomes the final result:
|
||||
Listeners run in order; the first result other than `null`, `false`, or `undefined` becomes the final result:
|
||||
|
||||
```ts ignore-check
|
||||
// Dispatch
|
||||
@@ -49,13 +49,13 @@ const result = ctx.bail('some-check', input)
|
||||
// Listen: a returned value stops later listeners.
|
||||
ctx.on('some-check', (input) => {
|
||||
if (shouldBlock(input)) return 'blocked'
|
||||
// Return undefined to continue to the next listener.
|
||||
// Return null, false, or undefined to continue to the next listener.
|
||||
})
|
||||
```
|
||||
|
||||
### serial — ordered execution
|
||||
|
||||
Listeners run in registration order and asynchronous results are awaited. The first listener to return a non-empty value stops further execution:
|
||||
Listeners run in registration order and asynchronous results are awaited. The first result other than `null`, `false`, or `undefined` stops further execution:
|
||||
|
||||
```ts ignore-check
|
||||
await ctx.serial('setup-phase', context)
|
||||
|
||||
@@ -40,7 +40,7 @@ ctx.on('my-plugin/ready', ({ id }) => {
|
||||
|
||||
### bail — 短路
|
||||
|
||||
依次调用监听器,第一个非 `undefined` 的返回值将作为最终结果:
|
||||
监听器按顺序运行,第一个不是 `null`、`false` 或 `undefined` 的返回值会成为最终结果:
|
||||
|
||||
```ts ignore-check
|
||||
// Dispatch
|
||||
@@ -49,13 +49,13 @@ const result = ctx.bail('some-check', input)
|
||||
// Listen: a returned value stops later listeners.
|
||||
ctx.on('some-check', (input) => {
|
||||
if (shouldBlock(input)) return 'blocked'
|
||||
// Return undefined to continue to the next listener.
|
||||
// Return null, false, or undefined to continue to the next listener.
|
||||
})
|
||||
```
|
||||
|
||||
### serial — 顺序执行
|
||||
|
||||
监听器按注册顺序依次执行,并等待异步结果;第一个返回非空值的监听器会终止后续执行:
|
||||
监听器按注册顺序依次执行,并等待异步结果;第一个不是 `null`、`false` 或 `undefined` 的返回值会终止后续执行:
|
||||
|
||||
```ts ignore-check
|
||||
await ctx.serial('setup-phase', context)
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/user/develop/framework/service.md
|
||||
service.md: a9e873f1ba969b1f2d22266f8ec03207f84eeddc
|
||||
service.zh.md: a201280bc38176f57f5354ab205ae700c7b04a89
|
||||
service.md: 03f4e7dc4df934495a4b203066183753b621339e
|
||||
service.zh.md: 2f4c01e0ea6a87a87ca694b265b07e07098f3e59
|
||||
|
||||
@@ -117,7 +117,7 @@ This prevents a plugin from calling a service that no longer exists.
|
||||
name: '@deepseek-ai/cordis-plugin-group'
|
||||
group: true
|
||||
isolate:
|
||||
bash: true
|
||||
shell: true
|
||||
config:
|
||||
- name: '@deepseek-ai/dsh-bash-local'
|
||||
config:
|
||||
@@ -128,7 +128,7 @@ This prevents a plugin from calling a service that no longer exists.
|
||||
name: '@deepseek-ai/cordis-plugin-group'
|
||||
group: true
|
||||
isolate:
|
||||
bash: true
|
||||
shell: true
|
||||
config:
|
||||
- name: '@deepseek-ai/dsh-bash-local'
|
||||
config:
|
||||
|
||||
@@ -108,6 +108,8 @@ export function apply(ctx: Context) {
|
||||
|
||||
这可以防止插件调用已不存在的服务。
|
||||
|
||||
<a id="service-isolation"></a>
|
||||
|
||||
## 服务隔离
|
||||
|
||||
`cordis.yml` 支持服务隔离——同一个服务可以有多个实例,不同插件组看到不同实例:
|
||||
@@ -117,7 +119,7 @@ export function apply(ctx: Context) {
|
||||
name: '@deepseek-ai/cordis-plugin-group'
|
||||
group: true
|
||||
isolate:
|
||||
bash: true
|
||||
shell: true
|
||||
config:
|
||||
- name: '@deepseek-ai/dsh-bash-local'
|
||||
config:
|
||||
@@ -128,7 +130,7 @@ export function apply(ctx: Context) {
|
||||
name: '@deepseek-ai/cordis-plugin-group'
|
||||
group: true
|
||||
isolate:
|
||||
bash: true
|
||||
shell: true
|
||||
config:
|
||||
- name: '@deepseek-ai/dsh-bash-local'
|
||||
config:
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/user/develop/practice/index.md
|
||||
index.md: 56ca7249b8b156ceb78327b547413dbf65726dcc
|
||||
index.zh.md: aec5980ea45cfd5f8f7408877743920e68e15cbd
|
||||
index.md: cc6bd7a234305f6fa193341f15354b40855f72e9
|
||||
index.zh.md: aed13b00f9bc74946aace614953bed30705c8281
|
||||
|
||||
@@ -23,7 +23,7 @@ The Bash execution capability consists of:
|
||||
└─────────────┘ └──────────────────┘ └──────────────┘
|
||||
▲ │
|
||||
└────────────────────────────────────────────┘
|
||||
inject: ['bash']
|
||||
inject: ['shell']
|
||||
```
|
||||
|
||||
## Benefits of the split
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
└─────────────┘ └──────────────────┘ └──────────────┘
|
||||
▲ │
|
||||
└────────────────────────────────────────────┘
|
||||
inject: ['bash']
|
||||
inject: ['shell']
|
||||
```
|
||||
|
||||
## 拆分的好处
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/user/develop/practice/llm-adapter.md
|
||||
llm-adapter.md: aba4a6d0c8ee42e78ca5a804d9a0dd9b31c1e240
|
||||
llm-adapter.zh.md: 5c7a6483e29a28871257df6a31e13d859a245097
|
||||
llm-adapter.md: 882e82d880ac622cf886c6c24e75a1987e1c6702
|
||||
llm-adapter.zh.md: 27c480af2a19a68ac35900a2ce1b5e9dbdc85bf8
|
||||
|
||||
@@ -32,12 +32,12 @@ class MyAdapter extends LlmAdapter {
|
||||
|
||||
export interface Config {
|
||||
apiKey: string
|
||||
models: string[]
|
||||
providers: string[]
|
||||
}
|
||||
|
||||
export const Config: Schema<Config> = Schema.object({
|
||||
apiKey: Schema.string().required(),
|
||||
models: Schema.array(Schema.string()).required(),
|
||||
providers: Schema.array(Schema.string()).required(),
|
||||
})
|
||||
|
||||
export const name = 'my-llm-adapter'
|
||||
@@ -45,7 +45,7 @@ export const inject = ['llm']
|
||||
|
||||
export function apply(ctx: Context, config: Config) {
|
||||
const adapter = new MyAdapter(config.apiKey)
|
||||
ctx.llm.registerAdapter(config.models, adapter)
|
||||
ctx.llm.registerAdapter(config.providers, adapter)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -117,10 +117,10 @@ Override `resolveModel(provider, model, signal?)` to return exact provider/model
|
||||
## Register an adapter
|
||||
|
||||
```ts ignore-check
|
||||
ctx.llm.registerAdapter(['model-name-1', 'model-name-2'], adapter)
|
||||
ctx.llm.registerAdapter(['my-provider'], adapter)
|
||||
```
|
||||
|
||||
The first argument lists the model names handled by the adapter. If `cordis.yml` selects `model: model-name-1`, the service routes that request to this adapter.
|
||||
The first argument lists provider routes handled by the adapter. `GenerateOptions.provider` selects the registered adapter, while `GenerateOptions.model` passes an adapter-owned model id without lifecycle registration. Override `listModels()` when the adapter can advertise model choices to selectors.
|
||||
|
||||
## Use it from cordis.yml
|
||||
|
||||
@@ -129,18 +129,16 @@ The first argument lists the model names handled by the adapter. If `cordis.yml`
|
||||
name: './src/my-llm-adapter.ts'
|
||||
config:
|
||||
apiKey: !!js process.env.MY_API_KEY
|
||||
models:
|
||||
- my-model-v1
|
||||
- my-model-v2
|
||||
providers:
|
||||
- my-provider
|
||||
|
||||
- id: agent-loop
|
||||
name: '@deepseek-ai/dsh-agent-loop'
|
||||
config:
|
||||
agents:
|
||||
- id: main
|
||||
provider: my-llm
|
||||
model: my-model-v1 # References the model registered above.
|
||||
workspaceContext: false
|
||||
provider: my-provider
|
||||
model: my-model-v1
|
||||
```
|
||||
|
||||
## Reference implementations
|
||||
|
||||
@@ -32,12 +32,12 @@ class MyAdapter extends LlmAdapter {
|
||||
|
||||
export interface Config {
|
||||
apiKey: string
|
||||
models: string[]
|
||||
providers: string[]
|
||||
}
|
||||
|
||||
export const Config: Schema<Config> = Schema.object({
|
||||
apiKey: Schema.string().required(),
|
||||
models: Schema.array(Schema.string()).required(),
|
||||
providers: Schema.array(Schema.string()).required(),
|
||||
})
|
||||
|
||||
export const name = 'my-llm-adapter'
|
||||
@@ -45,7 +45,7 @@ export const inject = ['llm']
|
||||
|
||||
export function apply(ctx: Context, config: Config) {
|
||||
const adapter = new MyAdapter(config.apiKey)
|
||||
ctx.llm.registerAdapter(config.models, adapter)
|
||||
ctx.llm.registerAdapter(config.providers, adapter)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -117,10 +117,10 @@ async function* exampleChunks(): AsyncIterable<StreamChunk> {
|
||||
## 注册适配器
|
||||
|
||||
```ts ignore-check
|
||||
ctx.llm.registerAdapter(['model-name-1', 'model-name-2'], adapter)
|
||||
ctx.llm.registerAdapter(['my-provider'], adapter)
|
||||
```
|
||||
|
||||
第一个参数是该适配器支持的模型名列表。当用户在 `cordis.yml` 中配置 `model: model-name-1` 时,框架会将请求路由到该适配器。
|
||||
第一个参数是该适配器处理的提供方路由列表。`GenerateOptions.provider` 选择已注册的适配器,`GenerateOptions.model` 则传入由适配器拥有、无需在生命周期启动时注册的模型 id。适配器能够向选择器公布模型选项时,请覆写 `listModels()`。
|
||||
|
||||
## 在 cordis.yml 中使用
|
||||
|
||||
@@ -129,18 +129,16 @@ ctx.llm.registerAdapter(['model-name-1', 'model-name-2'], adapter)
|
||||
name: './src/my-llm-adapter.ts'
|
||||
config:
|
||||
apiKey: !!js process.env.MY_API_KEY
|
||||
models:
|
||||
- my-model-v1
|
||||
- my-model-v2
|
||||
providers:
|
||||
- my-provider
|
||||
|
||||
- id: agent-loop
|
||||
name: '@deepseek-ai/dsh-agent-loop'
|
||||
config:
|
||||
agents:
|
||||
- id: main
|
||||
provider: my-llm
|
||||
model: my-model-v1 # References the model registered above.
|
||||
workspaceContext: false
|
||||
provider: my-provider
|
||||
model: my-model-v1
|
||||
```
|
||||
|
||||
## 实战参考
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/user/guide/index.md
|
||||
index.md: 80d288b1aba37e7f0863fe5fc8237cbd2a6ab9b5
|
||||
index.zh.md: addfbc94ff93ed015e52f509a23a3f981e36770b
|
||||
index.md: 282a5c11b317a8fb8706bb03f41cf03fb2aca49d
|
||||
index.zh.md: 4ec53b16fc8987b5eb40ef4854cb41d1436a9015
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
|
||||
English | [中文](index.zh.md)
|
||||
|
||||
Start the Web UI through the [root README](../../../README.md#run); the command prints its URL. This guide begins after that server is running.
|
||||
|
||||
The invoking directory is the default workspace, so the agent can inspect and modify the project where you started `dsh`.
|
||||
Start the Web UI through the [root README](../../../README.md#run); the command prints its URL. This guide begins after that server is running. The `dsh` process uses its invoking directory as the default filesystem location, but a fresh Web UI has no selected workspace until you add one.
|
||||
|
||||
## Configure a model
|
||||
|
||||
@@ -12,6 +10,10 @@ Open **Settings → Models**, enter a DeepSeek API key, and save it. The model r
|
||||
|
||||
The [model configuration guide](./providers.md) covers other providers and custom OpenAI-compatible endpoints.
|
||||
|
||||
## Choose a workspace
|
||||
|
||||
Click **Choose workspace**, add the project directory where you started `dsh`, and select it. The session composer remains unavailable until a workspace is selected.
|
||||
|
||||
## Run a task
|
||||
|
||||
Start a session and send:
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
|
||||
[English](index.md) | 中文
|
||||
|
||||
先按照[根 README](../../../README.md#run)启动 Web UI;命令会打印其访问地址。本指南从服务器已经运行的状态开始。
|
||||
|
||||
调用目录是默认工作区,因此 agent(智能体)可以检查并修改启动 `dsh` 时所在的项目。
|
||||
先按照[根 README](../../../README.md#run)启动 Web UI;命令会打印其访问地址。本指南从服务器已经运行的状态开始。`dsh` 进程会把调用目录作为默认文件系统位置,但新的 Web UI 在添加工作区前不会选中任何工作区。
|
||||
|
||||
## 配置模型
|
||||
|
||||
@@ -12,6 +10,10 @@
|
||||
|
||||
[模型配置指南](./providers.md)介绍其他提供方和自定义 OpenAI 兼容端点。
|
||||
|
||||
## 选择工作区
|
||||
|
||||
点击**选择工作区**,添加启动 `dsh` 时所在的项目目录,然后选中它。选中工作区前,会话输入框不可用。
|
||||
|
||||
## 运行任务
|
||||
|
||||
启动一个会话并发送:
|
||||
|
||||
Reference in New Issue
Block a user