{"_id":"@buddhilive/dsh-llm","name":"@buddhilive/dsh-llm","dist-tags":{"alpha":"0.1.2-alpha.3","latest":"0.1.2-alpha.3"},"versions":{"0.1.2-alpha.3":{"name":"@buddhilive/dsh-llm","description":"Provider-neutral LLM service interface for the DeepSeek Harness","version":"0.1.2-alpha.3","publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/Buddhilive/buddhi-ai-harness.git","directory":"packages/llm/llm"},"type":"module","main":"lib/index.js","types":"lib/types/index.d.ts","exports":{".":{"types":"./lib/types/index.d.ts","default":"./lib/index.js"},"./invariant":{"types":"./lib/types/invariant.d.ts","default":"./lib/invariant.js"},"./types":{"types":"./lib/types/types.d.ts","default":"./lib/types/types.js"},"./brand":{"types":"./lib/types/brand.d.ts","default":"./lib/types/brand.js"},"./message":{"types":"./lib/types/message.d.ts","default":"./lib/types/message.js"},"./typert":{"types":"./lib/typert.host.d.ts","default":"./lib/typert.host.js"},"./remote":{"types":"./lib/typert.remote-client.d.ts","default":"./lib/typert.remote-client.js"},"./src/*":"./src/*","./package.json":"./package.json"},"license":"MIT","peerDependencies":{"@deepseek-ai/cordis":"^4.0.2"},"dependencies":{"zod":"^4.4.3","@buddhilive/dsh-util-crypto":"^0.1.2-alpha.3","@deepseek-ai/schemastery":"^3.18.2","@buddhilive/dsh-brand":"^0.1.2-alpha.3","@buddhilive/dsh-timeout":"^0.1.2-alpha.3","@buddhilive/dsh-util-values":"^0.1.2-alpha.3","@buddhilive/dsh-typert-protocol":"^0.1.2-alpha.3"},"devDependencies":{"@buddhilive/dsh-attachment":"^0.1.2-alpha.3","@buddhilive/dsh-invariants":"^0.1.2-alpha.3","@deepseek-ai/cordis":"^4.0.2"},"_id":"@buddhilive/dsh-llm@0.1.2-alpha.3","bugs":{"url":"https://github.com/Buddhilive/buddhi-ai-harness/issues"},"homepage":"https://github.com/Buddhilive/buddhi-ai-harness#readme","_integrity":"sha512-7k4oEf4sZnK7nI3JRTt1/HIjUSeP+W9CmhjLO62iCC/4QtPA2mAYvihvCkLeY1qeCiVYb67W8LqucK73An0MQQ==","_resolved":"C:\\DevDojo\\Buddhi\\buddhi-ai-harness\\dist\\npm\\buddhilive-dsh-llm-0.1.2-alpha.3.tgz","_from":"file:C:/DevDojo/Buddhi/buddhi-ai-harness/dist/npm/buddhilive-dsh-llm-0.1.2-alpha.3.tgz","_nodeVersion":"24.13.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-7k4oEf4sZnK7nI3JRTt1/HIjUSeP+W9CmhjLO62iCC/4QtPA2mAYvihvCkLeY1qeCiVYb67W8LqucK73An0MQQ==","shasum":"958bb894e2ee237adad76d3f78ad85ddb43f5a6f","tarball":"https://registry.npmjs.org/@buddhilive/dsh-llm/-/dsh-llm-0.1.2-alpha.3.tgz","fileCount":37,"unpackedSize":320934,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIH5ilNkXug5UrpXKi2AoWeLwy+9M0xRZUfARgwlUW4kiAiAnyZg618KEF2maXne+ik/re8MjTbneX2J18eHOnPTBmA=="}]},"_npmUser":{"name":"buddhilive","email":"visitbudkavin@gmail.com"},"directories":{},"maintainers":[{"name":"buddhilive","email":"visitbudkavin@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dsh-llm_0.1.2-alpha.3_1788164942104_0.5158616932516049"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-31T08:29:01.917Z","0.1.2-alpha.3":"2026-08-31T08:29:02.304Z","modified":"2026-08-31T08:29:02.932Z"},"maintainers":[{"name":"buddhilive","email":"visitbudkavin@gmail.com"}],"description":"Provider-neutral LLM service interface for the DeepSeek Harness","homepage":"https://github.com/Buddhilive/buddhi-ai-harness#readme","repository":{"type":"git","url":"git+https://github.com/Buddhilive/buddhi-ai-harness.git","directory":"packages/llm/llm"},"bugs":{"url":"https://github.com/Buddhilive/buddhi-ai-harness/issues"},"license":"MIT","readme":"---\ndescription: \"面向用户与维护者的提供方无关模型调用服务说明：流式发起请求、注册提供方适配器或解析模型元数据。\"\nkind: \"package-reference\"\n---\n\n# @buddhilive/dsh-llm\n\n[English](README.md) | 中文\n\n## 概述\n\n`@buddhilive/dsh-llm` 是位于 harness LLM 能力核心的提供方无关模型调用服务。任何向模型提供方发起流式请求的组合都会经过它，它拥有 agent loop（智能体循环）、会话日志和所有插件共同使用的共享词汇——消息、内容块与原始流式分片。借助它，你可以注册提供方适配器、流式发起一次模型调用、列出与发现模型、解析精确模型元数据与调用默认值，并捕获每个提供方的重试策略；每个请求都会被记录，因此始终可以从会话日志重建。它不执行重试，也不拥有任何提供方协议逻辑：适配器翻译各自提供方的格式，可选包 `dsh-llm-retry` 在持久步骤边界上重跑失败的请求。请求在分发前会被深度冻结，因此 middleware 与适配器只能读取，绝不能改写。\n\n## 目录\n\n- [使用本包](#use-this-package)\n- [理解实现](#understand-the-implementation)\n- [进一步探索](#further-exploration)\n- [模型体验](#model-experience)\n- [已知限制与延期工作](#known-limitations-and-deferred-work)\n- [开发备注](#dev-note)\n\n-----\n\n<a id=\"use-this-package\"></a>\n## 使用本包\n\n任何调用模型提供方的组合——agent loop、会话标题生成器、压缩（compaction）摘要器——都会通过本服务流式发起请求。与至少一个提供方适配器一起挂载它；服务本身没有任何配置，也不包含提供方协议代码。\n\n### 何时选择\n\n当插件或组合需要调用模型时选择本包：它是进入提供方适配器的唯一受支持路径，并在 loop、会话日志与每个消费方之间保持同一套词汇。当需要提供方特定的协议行为（那属于 `dsh-llm-deepseek` 或 `dsh-llm-pi-ai` 之类的适配器）或重试执行（那属于 `dsh-llm-retry`）时，不要选择它。\n\n### 最小组合\n\n挂载服务与至少一个适配器，然后在每个请求中按名称选择提供方：\n\n```yaml\n- name: '@buddhilive/dsh-llm'\n- name: '@buddhilive/dsh-llm-deepseek'\n  config:\n    apiKeyEnv: DEEPSEEK_API_KEY\n```\n\n流会返回 token 级分片，并始终以一个终止 `finish` 分片结束；`BlockAssembler` 把分片组装为内容块与消息，loop 记录每个分片以供回放：\n\n```text\nfor await (const chunk of ctx.llm.stream({\n  provider: 'deepseek-official',\n  model: 'deepseek-v4-flash',\n  messages: [createUserMessage({ content: [{ type: 'text', text: 'Hello' }] })],\n})) {\n  // chunks: block-start, text-delta, ..., usage, finish\n}\n```\n\n挂载成功后，`ctx.llm.listProviders()` 会按注册顺序报告已注册路由。\n\n### 你可以做什么\n\n- **流式发起一次模型调用**——`ctx.llm.stream(options)` 为任何已注册提供方与模型产出原始分片（token 级增量）；消费方用 `BlockAssembler` 组装。\n- **注册提供方适配器**——一个适配器拥有一个或多个提供方路由，其注册会捕获该路由的重试策略；重复注册同一路由会以 `DUPLICATE_ADAPTER` 失败。\n- **通过配置暴露并激活提供方**——适配器声明可配置提供方路由与 settings namespace，配置界面因此可以激活休眠提供方并编辑连接事实，无需重启。\n- **发现与解析模型**——列出适配器公布的模型、询问端点它提供哪些模型，并解析某个精确模型的上下文窗口、输出默认值、推理（reasoning）强度与输入模态。\n- **校验调用配置**——显式或配置的推理强度会在任何提供方 I/O 之前对照精确模型校验；请求省略输出上限时，会填入适配器配置的输出上限。\n\n### 失败与恢复\n\n每个流都恰好以一个终止 `finish` 分片结束：失败为 `{ kind: 'error', failure }`，取消为 `{ kind: 'aborted', failure }`。失败携带稳定 code，如 `NO_ADAPTER`、`MISSING_CREDENTIAL`、`AUTH`、`RATE_LIMIT` 与 `CONTEXT_WINDOW_EXCEEDED`；消费方依据 code 路由，绝不解析消息文本。点名未注册提供方的请求会以 `NO_ADAPTER` 失败，格式错误的凭据会以 `INVALID_CREDENTIAL` 失败，而不是表现为不透明的 fetch 错误。本服务从不自行重跑请求：重试是 `dsh-llm-retry` 在 agent 失败步骤扩展点上的职责。\n\n-----\n\n<a id=\"understand-the-implementation\"></a>\n## 理解实现\n\n<details>\n<summary>实现细节——点击展开</summary>\n\n本节解释服务背后的设计；可观察行为已在[使用本包](#use-this-package)中完整说明。\n\n### 设计理念\n\n服务建立在一个分离之上：**逻辑约定是提供方无关的，适配器拥有协议。** 它一次性地定义规范消息、内容块与流式分片词汇，每个提供方适配器只把自己的协议格式翻译为该词汇。注册表是拓扑的拥有者——适配器路由、可配置提供方条目与发现 offer 都在这里注册，并随其 fiber 一起 dispose（资源释放）——而请求始终是会话日志的纯函数：loop 构建的请求以深度冻结状态到达，因此监听器与适配器只能读取，绝不能改写。\n\n### 源码地图\n\n| 文件 | 职责 |\n|---|---|\n| [`src/index.ts`](src/index.ts) | `LlmRuntime` 服务：适配器注册表、可配置提供方目录、模型发现、调用准备与流式边界 |\n| [`src/types.ts`](src/types.ts) | `StreamChunk` 协议、内容块映射、结束原因与共享词汇 |\n| [`src/message.ts`](src/message.ts) | 投递、历史与请求共享的不可变消息构造函数 |\n| [`src/assembler.ts`](src/assembler.ts) | `BlockAssembler`：分片到块的增量组装 |\n| [`src/call-config.ts`](src/call-config.ts) | 调用配置校验、适配器默认值填入与请求冻结 |\n| [`src/retry-policy.ts`](src/retry-policy.ts) | 提供方自有重试策略解析（normal 与 always 模式） |\n| [`src/error.ts`](src/error.ts) | `HarnessError`/`LlmError` 分类体系与提供方无关失败 code |\n| [`src/content.ts`](src/content.ts) | 共享图片内容辅助函数，包括请求图片卸载 |\n| [`src/api-key.ts`](src/api-key.ts) | 每个适配器共享的凭据格式校验 |\n| [`src/adapter-failure.ts`](src/adapter-failure.ts) | 把失败归一化为终止 finish 分片 |\n\n### 主流程\n\n请求会对照其精确模型的能力——上下文窗口、输出默认值、推理强度与输入模态——校验，填入任何适配器配置的默认值，然后整个请求被深度冻结。`prepareCall()` 把这些事实、分离的上下文与重试策略绑定到执行最终分发的精确适配器代次，因此 HMR 或动态设置无法把一个代次的图片能力与另一代次的端点混用。支持图片的适配器把持久引用投影为路由专用请求版本；`resolveImageAttachmentAccess()` 会单独把附件提供方的可选宿主对象映射进当前工具执行世界，而不改变请求图片或其 `variantId`。纯文本路由接收确定性的逐图片占位符，包括嵌套工具结果图片，而不会改写仅追加会话历史。`offloadRequestImagesWithPolicy()` 按原始字节或 base64 大小以及图片数或字节步长，确定性地从最旧图片开始移除；纯函数 `offloadedImagePrefixCount()` 公开同一决策，使路由所属的请求定价无需构建投影即可复现它。对视觉 token 收费的适配器声明按路由的 `imageRequestPricing`，`ctx.llm.imageRequestPricing(provider, model)` 为 token meter 同步解析它。分发经过 `llm/stream` waterfall，随后分片以 token 级增量返回，每个适配器结果都以唯一一个终止 `finish` 分片到达消费方。\n\n### 不变式\n\n- **模型可见 ⟺ 已记录**——到达提供方请求的任何内容都可以从会话日志重建；loop 构建的请求被深度冻结，绝不改写。\n- **回放状态只在同一适配器内流动**——仅当同一适配器实例同时拥有历史路由与目标路由时，assistant 回放状态才会随行；否则在分发前被丢弃。\n- **已准备调用是一次性的**——已准备调用只能分发一次，且其调用配置字段必须与准备好的配置一致。\n- **图片投影遵循捕获的路由**——只有支持图片的模型会把持久 `ImageBlock` 引用转换为路由专用请求版本；纯文本模型接收稳定占位符。\n- **协议顺序**——`usage` 先于 `finish`，工具参数保持原始 JSON 字符串，终止 `finish` 之后不再有任何内容。\n- **注册表变更具有原子性**——路由与目录注册会在任何变动前整体校验候选集合，因此被拒绝的变更会让此前状态继续服务。\n\n</details>\n\n-----\n\n<a id=\"further-exploration\"></a>\n## 进一步探索\n\n当包级约定不够用时阅读以下页面。它们从共享类型逐步进入具体适配器、重试执行器与计量服务。\n\n- [LLM 流式子系统](../../../docs/subsystems/llm-streaming.zh.md)——消息与块类型、组装后的模型请求、`StreamChunk` 协议与适配器约定。\n- [llm-deepseek 适配器](../llm-deepseek/README.zh.md)——DeepSeek chat-completions 直连实现。\n- [llm-pi-ai 适配器](../llm-pi-ai/README.zh.md)——基于 pi-ai 的多提供方实现。\n- [llm-retry](../llm-retry/README.zh.md)——重跑失败模型请求的重试执行器。\n- [Token 计量](../token-meter/README.zh.md)——具备回放感知的请求与上下文压力测量。\n- [孪生 LLM 适配器](../../../.agents/notes/implemented/architecture/2026-06-13-twin-llm-adapters.zh.md)——为什么 DeepSeek 路由交付两个结构不同的适配器。\n- [LLM 流终止失败](../../../.agents/notes/implemented/architecture/2026-07-29-terminal-llm-stream-failures.zh.md)——模型请求结果与插件失败之间的服务边界。\n\n-----\n\n<a id=\"model-experience\"></a>\n## 模型体验\n\n没有直接影响，因为 LLM 服务不添加内容；适配器决定何时添加本包导出的共享图片描述符与逐图片占位符。\n\n#### KV Cache 影响\n\n推理强度的具体化会保留已组装请求前缀。图片身份与请求预览文本是确定性的，可选执行世界路径则按请求解析；路径变化或图片卸载边界变化可能从该图片起阻止复用。\n\n## 已知限制与延期工作\n\n<a id=\"known-limitations-and-deferred-work\"></a>\n\n\n这些限制说明本服务在哪里停止、由其他包或未来工作接续。它们是当前包约束，不是任务积压。\n\n- **本服务不提供重试执行、缓存或速率限制**——提供方注册会存储重试策略，但一次流仍是一次提供方尝试；`@buddhilive/dsh-llm-retry` 在持久 agent 步骤边界上执行该策略。\n- **`GenerateOptions` 采样只包含 `temperature`／`maxTokens`／`stop`**——没有 `tool_choice`、`top_p` 或 penalty 字段；有产生方落地时词汇才会增长（见[已删除惰性旋钮](../../../.agents/notes/archived/simplification/2026-07-04-drop-inert-request-knobs.md)）。\n- **只有出现实际产生方后，相应变体才会加入**——`prefill`、逐工具 `strict`、内容块 `cache` 提示和 `agent` 消息来源变体都没有产生方（见 [Agent Note](../../../.agents/notes/archived/simplification/2026-07-04-prune-producerless-vocabulary-variants.md)）。\n- **`BlockAssembler` 只处理核心块类型**——插件添加块类型的流若从未由 `block-end` 关闭，`blocks()` 会抛出异常。\n- **`GenerateOptions.sessionId` 是本地声明的品牌类型**——导入 dsh-session 的 `SessionId` 会产生依赖循环。\n\n<a id=\"dev-note\"></a>\n### 开发备注\n\n<details>\n<summary>维护者的工作上下文——点击展开</summary>\n\n本开发备注是不具权威性的工作上下文：开放问题与尚未决定的探索方向。已交付的行为与既定理由以上文、包代码和相关 Agent Note 为准。\n\n#### 开放事项\n\n- `GenerateOptions.sessionId` 是本地声明的品牌类型，因为导入 dsh-session 的 `SessionId` 会造成依赖循环；未来拥有 id 的包可以消除该权宜之计。\n- 推理强度标识符是由适配器定义的不透明字符串，只对照各适配器公布集合解析；跨适配器共享强度词汇尚未决定。\n- `llm/adapters-updated` 事件按设计不携带载荷；消费方重新读取注册表，而不是在事件中接收新拓扑。\n\n</details>\n","readmeFilename":"README.zh.md","_rev":"1-ff2d6545048053f233a9562d7225cb73"}