@nelsonlongxiang/dsh-python-bridge
v0.4.3
Published
Managed Python extension child speaking newline-delimited JSON-RPC over stdio
Readme
@nelsonlongxiang/dsh-python-bridge
一个托管的 Python 扩展子进程,通过 stdio 以换行分隔的 JSON-RPC 2.0 与宿主通信。
PythonBridge 通过 dsh-subprocess seam 启动一个 dsh-ext serve 子进程,每行交换一个 JSON 对象:请求携带 {"jsonrpc":"2.0","id",...,"method",...,"params":{}},响应以相同 id 返回 result 或 error。子进程在首次调用时惰性启动;任何已结束的子进程(包括正常退出)都会被丢弃,以便下次调用时重新启动;释放时先通过 stdin EOF 优雅退出,超时后再升级到终止。
本包既是普通库,也是 DSH Cordis 插件:作为插件加载时会注册 pythonBridge 服务,让其他插件无需手动传入 subprocess seam 即可创建桥接实例。
库用法
从私仓安装:
npm install @nelsonlongxiang/dsh-python-bridgeimport { PythonBridge, PythonBridgeError, DEFAULT_EOF_GRACE_MS, resolveEofGrace } from '@nelsonlongxiang/dsh-python-bridge'
import type { SubprocessHandle, SubprocessSpawnSpec } from '@deepseek-ai/dsh-subprocess'
// 在 Cordis 插件内部:`ctx` 是携带 subprocess seam 的注入上下文。
const ctx = {
subprocess: {
spawn: (_spec: SubprocessSpawnSpec): SubprocessHandle => {
throw new Error('not invoked in this snippet')
},
},
} as unknown as { subprocess: { spawn: (spec: SubprocessSpawnSpec) => SubprocessHandle } }
const bridge = new PythonBridge({
argv: ['dsh-ext', 'serve'],
cwd: process.cwd(),
eofGraceMs: DEFAULT_EOF_GRACE_MS,
spawn: (spec) => ctx.subprocess.spawn(spec),
})
const compositions = await bridge.call('graph/list', {})JSON-RPC 错误会被包装成 PythonBridgeError,携带线上的 code、message 和 data;传输或启动失败则以其底层错误拒绝,并同时让所有在途调用失败。公共助手 DEFAULT_EOF_GRACE_MS 与 resolveEofGrace(value, caller?) 是 EOF 宽限默认值及其校验的唯一来源:resolveEofGrace 对非有限、非正数或超过定时器上限的值抛出 TypeError,pythonBridge 服务在 create 时会对每个 eofGraceMs 执行该校验。
插件用法
库还是 bundle:边界规范
- 做成库:交付物是供其他插件包
import的运行时 API。不声明dsh.bundle,dsh plugin add只把它装成普通依赖并打印一次导向性警告、不激活任何层。库绝不插入插件行。 - 做成 bundle(插件):交付物是配置层——经
dsh.bundle.patch声明、插入或覆盖插件行的 patch 文件。只有这种形态能被用户直接启用;插件形态的包若缺少该声明,就是典型的"装了但不生效"异常。
本包两者兼是——PythonBridge 运行时 API 加 pythonBridge 宿主服务行——因此 0.3.4 起声明 dsh.bundle,dsh plugin add 自动激活其层(从零实测:自动进入 dsh.profile.bundles,行正常组合且无警告)。
一个行 id 只能有一个来源。 loader 在 boot 时对重复 entry id 硬性拒绝(实测 duplicate loader entry id: python-bridge 直接崩溃启动)。若某个组合已从别的 patch 插入 python-bridge 行(先例:@nelsonlongxiang/dsh-native-teams),该 profile 不得再直接安装本包——只保留一个来源,删掉另一个;组合宿主应迁移为依赖本 bundle 并删除自己的 python-bridge 插入行。历史:0.3.1 声明了 dsh.bundle 而 dsh-native-teams 同时插入该行,冲突导致启动崩溃(0.3.2 回滚,0.3.3 为回滚加修复版)。
本 bundle 自身的 patch 只插入这一行:
- insert:
- id: python-bridge
name: '@nelsonlongxiang/dsh-python-bridge'其他插件可以通过 inject 注入 pythonBridge:
import { PythonBridge, PythonBridgeError } from '@nelsonlongxiang/dsh-python-bridge'
export const name = 'my-python-consumer'
export const inject = ['pythonBridge']
export function apply(ctx: any): void {
const bridge = ctx.pythonBridge.create({
argv: ['python', '-m', 'my_backend', 'serve'],
cwd: process.cwd(),
eofGraceMs: DEFAULT_EOF_GRACE_MS,
})
ctx.effect(() => () => { void bridge.dispose() }, 'my-consumer: bridge teardown')
// 使用 `bridge.call(...)` 与 Python 子进程通信。
}调用方拥有返回的 bridge 实例,并负责在使用完毕后 dispose()。
模型可见性
桥接调用
模型会看到什么
不会看到任何内容:这座桥只负责在 harness 与 dsh-ext serve 子进程之间搬运宿主侧的工具和服务调用。把结果写进 prompt 的工作由消费方负责。
Token 影响
无 — 桥接不产生任何 prompt 内容。
KV Cache 影响
无 — 桥接不产生任何请求内容。
已知限制与待办
- 子进程是单线程且进程内唯一的:崩溃会导致在途调用失败,下次调用时桥接会重新 spawn,但默认不会自动重试。
- 一个桥接拥有一个子进程;跨域共享一个
dsh-ext serve子进程的功能推迟到出现第三个域时再实现。
