# dsh-llm-bailian-kimi [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 的 LLM 适配器插件, 用于接入**阿里云百炼(DashScope)上的 Kimi 模型**。 只解决一件事:**在 DSH 里用百炼的 `kimi/kimi-k3`,并且能发图片。** - ✅ 图片输入(多模态) - ✅ 流式输出 + 思考链(`reasoning_content`) - ✅ Function Calling - ✅ token / 缓存命中统计 - ✅ 纯 ESM JavaScript,**无构建步骤**——`dsh plugin add` 一步装完,不会触发 pnpm 的 `allowBuilds` 拦截 ## 安装 ```sh dsh plugin --profile web add github:sologuy/dsh-llm-bailian-kimi ``` 然后在 `$DSH_HOME/.credentials.yaml` 里放百炼 API Key: ```yaml DASHSCOPE_API_KEY: sk-xxxxxxxx ``` 重启 DSH 即可在模型选择器里看到「阿里云百炼」分组。 ### 不重启热挂载 如果不想重启正在跑的实例,改用 `$DSH_HOME/cordis.patch.yml`(DSH 会 watch 该文件并事务性重新应用): ```yaml - insert: - id: llm-bailian name: dsh-llm-bailian ``` 需要插件能按包名被解析到,即位于 `$DSH_HOME/profiles/node_modules/` 下。 > ⚠️ **热挂载能换"挂不挂",换不了已 import 进内存的代码。** > 改了插件源码、或改了包名但文件路径不变时,Node 的 ESM 模块缓存会继续返回旧模块, > 必须重启 DSH 进程才生效。会话都持久化在磁盘上,重启不会丢。 ## 配置 插件开箱即用(默认只带 `kimi/kimi-k3`)。要改模型目录或端点,在 `$DSH_HOME/settings.yaml` 加 `llm-bailian-kimi:` 段——该段热加载,改完存盘即在下一次请求生效: ```yaml llm-bailian-kimi: apiKeyEnv: DASHSCOPE_API_KEY baseURL: https://dashscope.aliyuncs.com/compatible-mode/v1 reasoningEffort: max # high | max models: - id: kimi/kimi-k3 name: Kimi K3 · 百炼 description: 1M 上下文 · 支持图片 contextWindow: 1000000 input: [text, image] # ← 声明 image 才能发图 ``` | 字段 | 默认值 | 说明 | |---|---|---| | `apiKeyEnv` | `DASHSCOPE_API_KEY` | 凭证引用名,按请求解析 | | `baseURL` | 百炼 OpenAI 兼容端点 | 换 workspace 端点时改这里 | | `reasoningEffort` | `max` | 调用方未指定时的兜底档位 | | `maxTokens` | `65536` | 单请求输出上限默认值 | | `defaultContextWindow` | `1000000` | 目录里没写 `contextWindow` 的模型用它 | | `headers` | `{}` | 附加请求头(如 workspace 路由) | | `streamIdleTimeoutMs` | `300000` | 流空闲超时 | 模型条目的 `input` 不声明 `image` 就是纯文本模型——这是对端点的**声明**而非探测,声明了但端点不支持会在请求时被服务端拒绝。 ## 范围:这不是通用百炼适配器 插件注册的路由是 **`bailian-kimi`**,不是 `bailian` —— 名字是刻意收窄的,因为它的思考参数派发 (`thinking: {type: 'enabled'}` + `reasoning_effort`)是 Kimi 方言,换个模型就未必成立。实测: | 百炼模型 | 本插件的思考派发 | |---|---| | `kimi/kimi-k3` | ✅ | | `qwen3.8-max` | ✅(顺带能用,但未做完整验证) | | `deepseek-v3.2` | ❌ 400 `'reasoning_effort' must be one of: 'none','minimal','low','medium','high'` | 而且 `off` 档位被刻意拿掉了(见下方坑 3),这对能关思考的模型是错的。 要接百炼上的其他模型,建议另开 `dsh-llm-bailian-qwen` / `-glm` 之类的兄弟插件, 各自占一个路由名。两个插件抢同一个路由会直接 `DUPLICATE_ADAPTER`,整个注册失败。 ## 为什么需要这个插件 DSH 自带两个适配器,接百炼时都过不去: | 适配器 | 阻塞点 | |---|---| | `dsh-llm-deepseek` | 对图片直接抛 `UNSUPPORTED_CONTENT`,根本不发图 | | `dsh-llm-pi-ai` | 对未知 baseURL 自动推断 `supportsDeveloperRole: true`,把系统提示发成 `role: 'developer'`,百炼返回 `400 role 'developer' is not allowed`。而 DSH 的 `PiAiCompatProfile` 只暴露 `thinkingFormat` / `supportsReasoningEffort` 两个开关,**配置层够不到这个字段** | > **这个插件本质上是在绕过 upstream 缺失的一个配置字段。** > 如果 `PiAiCompatProfile` 补上 `supportsDeveloperRole`(约 3~5 行),接百炼就退化成一段 > `settings.yaml` 配置,本插件即可退役。欢迎去 upstream 推动这个改动。 顺带说明:百炼官方文档写"图片仅支持公网 URL,不支持 Base64",但**实测 base64 完全可用**,含 7MB 大图(见 `probe/RESULTS.md`)。所以本插件不需要 OSS 签名 URL 那一套。这属于未文档化行为,未来若收紧,只需改 `lib/serialize.js` 的 `imagePart()` 一个函数。 ## 百炼特有的三个坑(实现时已处理) 1. **系统提示必须用 `role: 'system'`**,`developer` 会被 400 拒绝。 2. **工具调用续片的 `id` / `name` 是 JSON `null`**,不是空串也不是缺字段。判断必须写 `typeof x === 'string' && x.length > 0`;写 `!== undefined` 会被 `null` 击穿, 首片的有效值被续片覆盖,工具调用随即失败。 3. **绝不能发 `thinking: {type: 'disabled'}`**。`kimi-k3` 不允许关思考,会返回一条 极具误导性的 `400 invalid temperature: only 0.6 is allowed for this model`。 因此本插件只提供 `high` / `max` 两档,**不提供 `off`**。 另有一条 Node 侧注意事项:`using` 显式资源管理语法 Node 22 原生 ESM 尚不支持,本插件用显式 `[Symbol.dispose]()`;且**不要加 `export default`**——Cordis 以模块命名空间读取 `name/inject/Config/apply`,多一个默认导出会让它改用裸函数,从而丢掉 `Config` schema 的默认值。 ## 验证 `probe/` 下是可复现的实测脚本,对着你自己的百炼 Key 跑: ```sh export DASHSCOPE_API_KEY=sk-xxxx python3 probe/mkimg.py # 生成三档测试图(含随机数字,排除幻觉) python3 probe/t_image.py # base64 图片输入 python3 probe/t_wire.py # wire 兼容性边界(9 项) python3 probe/t_stream_tool.py # 流式 + 工具调用分片形态 ``` 实测结果记录在 [`probe/RESULTS.md`](probe/RESULTS.md)。 ## 兼容性 针对 DSH `0.1.0-rc.6` 开发与验证。契约以运行时 `node_modules/@deepseek-ai/dsh-llm/lib/types/*.d.ts` 为准。 ## License MIT