Repository navigation
Plugin Model Provider CN
本文以虚构的 acme_llm 供应商为例,说明要实现哪些部分,怎样把一个上游大语言模型 API 接入 1flowbase。示例采用 API Key、无跨调用会话状态、进程调用、文本生成与模型列表 的范围。品牌、域名和模型编号是占位值,实际接入时替换。
当前宿主基线为 2143c8d4。官方插件参考基线为 87c3468。本文给出开发和打包步骤,不附带已经实现完成的 Acme 插件制品。
如果差异仅是 API 地址、API Key 和模型编号,且上游请求与响应都能由现有 openai_compatible 正确处理,优先配置现有供应商实例并验证。
以下差异通常需要供应商插件:特殊鉴权、请求参数、流式事件、推理内容、工具调用、模型目录、用量计量或上游特有的状态管理。写新插件前,应列出这些差异;只有名称不同通常不需要重新实现协议。
工作流 / 模型调用入口
→ 宿主选择供应商实例、解析配置与凭据
→ RuntimeExtensionHost 发送 Provider 标准输入
→ acme_llm 转换为上游请求
→ 上游返回内容、工具调用、用量或错误
→ acme_llm 转换为 Provider 事件与最终结果
→ 宿主继续工作流并向客户端投影
| 插件负责 | 宿主负责 |
|---|---|
| 供应商配置定义及专有校验 | 配置入口、实例管理和凭据持久化 |
| 上游地址、鉴权头和参数转换 | 工作区分配、调用选择及权限约束 |
| 模型列表与供应商能力描述 | 模型目录展示及选择 |
| 内容、工具调用、终态和用量转换 | 工作流执行、工具调度、调用追踪 |
| 保留上游错误的有效信息 | 按既有契约向调用方传递结果与错误 |
| 实现声明的网络出口交接等能力 | 提供对应运行时上下文与资源管理 |
插件返回工具调用请求,由宿主执行相应工具;普通供应商插件不需要因此获得任意子进程或数据库权限。新增普通供应商通常也不需要新增画布节点、登录认证方式或业务数据建模定义。
官方仓库现有供应商位于 runtime-extensions/@taichuy/<provider_code>/。自己的发布者可以使用自己的源码目录;目录组织不等于 manifest 中的发布者身份。
acme_llm/
├─ manifest.yaml 插件身份、协议、执行方式与能力
├─ Cargo.toml Rust 构建及依赖
├─ src/
│ ├─ main.rs 标准输入输出分派与退出
│ └─ lib.rs 配置、模型目录、请求与响应转换
├─ provider/
│ └─ acme_llm.yaml 配置表单、发现方式与参数表单
├─ models/
│ └─ llm/
│ ├─ acme-chat.yaml 静态模型资料,按发现方式提供
│ └─ _position.yaml 静态模型排序
├─ i18n/ 插件展示资源
├─ readme/ 安装包内的使用说明
└─ _assets/ 图标等资源
lib.rs 是便于测试的实现组织建议,并非安装协议要求。正式 .1flowbasepkg 包含 manifest、运行资源和 bin/ 可执行文件,源码构建目录不作为运行入口。
在 1flowbase 主仓库根目录执行,目标路径须为空或不存在:
node scripts/node/plugin/cli.js init ../acme-plugins/acme_llm当前工具会生成基础文件、样例模型和展示资源。它的默认 Rust 程序只是回显,没有实现真实上游请求;默认 manifest 还使用官方发布者信息,第三方开发者必须调整。
下面给出本例的 manifest。minimum_host_version 暂以本次宿主包版本 0.4.1 为起点,正式发布应填写实际验证过的最低版本;同号宿主版本也不能替代提交级契约验证。
manifest_version: 1
plugin_id: acme_llm
publisher_namespace: acme
version: 0.1.0
vendor: Acme
display_name: Acme 大语言模型
description: Acme 文本生成供应商插件
source_kind: uploaded
trust_level: unverified
consumption_kind: runtime_extension
execution_mode: process_per_call
slot_codes:
- model_provider
binding_targets:
- workspace
selection_mode: assignment_then_select
minimum_host_version: 0.4.1
contract_version: 1flowbase.provider/v2
schema_version: 1flowbase.plugin.manifest/v1
permissions:
network: outbound_only
secrets: provider_instance_only
storage: none
mcp: none
subprocess: deny
runtime:
protocol: stdio_json
entry: bin/acme_llm-provider
capabilities:
- config.validate
- models.list
limits:
timeout_ms: 30000
memory_bytes: 268435456
node_contributions: []uploaded / unverified 表示本例采用自行构建上传的路径,最终能否安装仍由宿主安装策略决定。插件不能通过把字段写成 verified_official 获得官方身份。publisher_namespace 是身份信息;vendor 是展示或法律主体信息,两者不能互相代替。
这里继续使用 v1 安装清单 + 1flowbase.provider/v2 执行协议。新组合架构的 v2 多贡献清单并不是供应商协议的升级替代物,不要只改 manifest_version 来“接入新架构”。
process_per_call 适合本例。需要跨调用状态的供应商应参考现有 stateful_provider_worker 实现,连同 worker 会话、取消和恢复协议一起设计,不能只改执行方式字段。
在 provider/acme_llm.yaml 中声明供应商专有配置。本例假设上游兼容 OpenAI Chat 请求,并提供动态模型列表:
provider_code: acme_llm
display_name: Acme 大语言模型
protocol: openai_compatible
help_url: https://example.com/acme-llm/docs
default_base_url: https://api.example.com
model_discovery: hybrid
supports_model_fetch_without_credentials: false
config_schema:
- key: base_url
label: 接口地址
type: string
required: true
default: https://api.example.com
- key: api_key
label: API 密钥
type: secret
required: true实际 API Key 由使用者在供应商实例配置中录入,不能写进插件归档。base_url 是否包含 /v1、如何拼接 /chat/completions,必须在插件实现中统一,避免重复或漏掉路径段。
发现模式决定模型目录来源:
model_discovery |
插件工作 |
|---|---|
static |
提供静态模型文件;宿主从包内读取模型列表 |
dynamic |
实现 list_models,返回标准模型描述 |
hybrid |
提供静态资料并实现动态查询,由宿主按现有规则合并 |
models.list 是允许宿主使用模型列表操作的能力声明;静态模式不因此要求发起一个上游请求。
将脚手架生成的 acme_llm_chat.yaml 替换为真实模型资料,并同步 _position.yaml,避免无效样例继续出现在模型列表中。本例的 models/llm/acme-chat.yaml 可以从下面开始,数值必须替换为真实上游能力:
model: acme-chat
label: Acme 文本模型
family: llm
capabilities:
- stream
context_window: 32768
max_output_tokens: 4096静态 YAML 使用 model / label;运行时 ProviderModelDescriptor 使用 model_id / display_name,两者是各自契约的字段,不能随意混用。动态返回还包含 source、流式/工具/多模态支持标志、上下文与输出上限、provider_metadata。
如果上游支持温度、最大输出长度等控制,在供应商 parameter_form 中声明,并在运行时从 model_parameters 读取和转换。不能只画出配置控件而忽略其后端语义。
现有 Provider 请求外层使用 method 与 input。能力名和方法名并不相同:
| 功能 | 声明或启用条件 | 运行时入口 | 本例范围 |
|---|---|---|---|
| 配置校验 | config.validate |
method: validate |
实现;检查必要字段,并明确是否连通上游验证 |
| 模型列表 | models.list |
method: list_models,静态模式由宿主读取 |
实现动态查询与标准描述转换 |
| 文本生成 | Provider 生成契约及所选模型能力 |
method: invoke,operation: generate;默认生成时该字段可省略 |
实现 |
| 流式响应 | 模型声明 stream
|
逐行输出事件,最后输出结果 | 实现 |
| 工具调用、多模态、结构化输出 | 对应模型及协议能力 |
invoke 的相应输入和输出 |
后续按上游真实能力补充 |
| 调用前计数 | count_tokens |
invoke,operation: count_tokens
|
可选;需支持完整提示词语义 |
| 网络出口交接 | network_egress_handoff/v1 |
消费宿主下发的出口上下文 | 需要宿主统一出口时实现并声明 |
| 供应商 OAuth、余额、额度窗口等 | 对应供应商声明与方法契约 |
auth、balance、usage 等 |
本例 API Key 版本不要求实现 |
不要把 config.validate 直接当成 wire 的 method。对未知方法或不支持的能力返回明确错误,不能返回脚手架的通用成功回显。
生成输入使用 ProviderInvocationInput:包括 provider_instance_id、provider_code、protocol、model、provider_config、messages、system、tools、model_parameters、required_capabilities 及运行上下文等。
这个输入不是可直接转发的上游 Chat JSON。插件必须转换消息块、系统提示、工具参数和上游特有字段;无法满足 required_capabilities 时明确拒绝,不能静默丢掉内容。不同供应商对推理历史、缓存和协议上下文的支持应依据实际契约声明。
非流式方法返回 ProviderStdioResponse 的 ok / result / error 结构。流式生成使用每行一个 JSON 对象的输出,下面是一个最小成功终态示意:
{"type":"text_delta","delta":"你好"}
{"type":"finish","reason":"stop"}
{"type":"result","result":{"final_content":"你好","tool_calls":[],"mcp_calls":[],"usage":{"input_tokens":8,"output_tokens":2,"total_tokens":10},"finish_reason":"stop","provider_metadata":{}}}stdout 留给协议数据,诊断写入 stderr;流式事件写出后及时 flush。宿主需要完整终态和最终结果,不能只输出文本增量后退出。若连接提前中断或上游协议异常,不应伪造 stop 成功结束。
用量字段采用宿主契约的 input_tokens、output_tokens 等名称,不能直接把 prompt_tokens / completion_tokens 当成同名字段输出。上游缺少的计量保持未知,不冒充零;缓存和推理 token 是否包含在其他计量中,要按上游定义转换,避免重复累计。
错误使用 ProviderRuntimeError / ProviderStdioError 表达,保留上游有效消息与 provider_details,不要统一改成“调用失败”而丢掉排障信息。插件自己的诊断不得额外打印本地 API Key 或凭据配置。
完成文本流式生成后,再逐项增加所需能力:
- 工具调用:保持调用标识稳定,正确拼接参数增量并提交完整调用;宿主执行工具。
- 推理与历史:区分展示用推理内容和上游继续对话所需的签名或历史块。
- 计数与用量:调用前计数与调用后实际用量是不同能力,不能拿一个估计数代替真实返回值。
- 网络出口:声明交接能力后必须使用受支持的宿主出口上下文,不能忽略指定出口而自行直连。
- 取消与进程退出:响应宿主取消、关闭连接并回收自有资源;不把断流当成成功。
- 价格、余额、配额:仅在有准确来源且实现对应契约时提供,不是基础文本生成的必选项。
这些工作决定供应商可被哪些流程使用。只支持纯文本的插件可以诚实声明纯文本能力,无需一次实现所有可选功能。
先完成真实实现,再构建对应运行平台的二进制。以下命令仍从 1flowbase 主仓库根目录执行,以 Linux x86_64 GNU 为例;需要对应 Rust target 和链接环境:
cargo build \
--manifest-path ../acme-plugins/acme_llm/Cargo.toml \
--release \
--target x86_64-unknown-linux-gnu \
--target-dir ../acme-plugins/acme_llm/target假定 Cargo 二进制名为 acme_llm-provider,使用主仓库的实际打包入口:
node scripts/node/plugin/cli.js package ../acme-plugins/acme_llm \
--out ./tmp/plugin-packages/acme_llm \
--runtime-binary ../acme-plugins/acme_llm/target/x86_64-unknown-linux-gnu/release/acme_llm-provider \
--target x86_64-unknown-linux-gnu--runtime-binary 必须指向真实编译产物,--target 必须与二进制一致。Windows 等其他平台要分别构建并打包,不能只修改 target 标签。打包会生成 .1flowbasepkg 与校验信息;打包成功只证明产物生成,不能替代后端完整清单校验与真实调用验收。
当前 init 会生成现有插件展示资源约定所用的 i18n 等文件;本轮三篇 Wiki 只写中文,不调整插件工具本身的资源规则。
- 在开发环境的插件扩展中心上传正式包,确认目标平台、身份、版本与信任策略允许安装。
- 按扩展中心流程分配到目标工作区,确认供应商被发现。
- 新建供应商实例,填写真实测试地址和 API Key。
- 执行已声明的配置校验与模型列表操作,确认字段与能力展示正确。
- 在模型调用或工作流中选择该实例和模型,观察实际文本流、最终结果、用量和错误。
- 验证取消、停用、升级与第二个工作区的隔离行为。
若安装策略拒绝自建包,应使用项目支持的开发安装策略;不要伪造官方签名信息。不要直接覆盖一个正在使用的安装目录。
demo init / demo dev 可以生成和展示辅助页面,但当前生成 demo 含模拟结果。页面能够打开或显示“成功”,不等于已经调用了真实插件或宿主运行时。
| 场景 | 需要的证据 |
|---|---|
| 安装与目录发现 | 正式归档安装成功,供应商、实例配置和模型可选择 |
| 正常生成 | 实际上游响应被转换为正确增量与唯一最终结果 |
| 配置与鉴权错误 | 缺字段、无效 Key 有明确失败,不出现假成功 |
| 模型与限流错误 | 无效模型、429、上游错误保留可诊断信息 |
| 流中断与取消 | 不伪造成功终态,进程和连接得到回收 |
| 能力一致性 | 声明支持的功能真实可用;不支持的请求明确拒绝 |
| 用量 | 输入、输出、缓存与推理计量符合供应商原始语义 |
| 隔离 | 不串用其他实例或工作区的凭据与运行状态 |
| 升级 | 新包版本被识别,既有实例配置按既定契约工作 |
涉及纯文档示例时无需执行上述真实上游测试;交付实际供应商插件时必须补齐对应证据。manifest.yaml 的 version 是插件发行版本,Cargo 版本只服务构建;修改功能后应升级插件发行版本。
按协议相近程度选择参考,不要把参考供应商的所有特殊逻辑一并复制:
- 通用 OpenAI 兼容插件:地址、凭据与通用协议适配。
- DeepSeek 插件:独立进程分派、模型资料、流式终止、用量和计数实现。
- Anthropic 插件:不同上游协议下的适配参考。
- Provider 输入与声明契约
- Provider 事件、结果与错误契约
- 宿主 Provider 调用实现
- 插件初始化和打包入口
- 当前脚手架模板
runtime-extension-sdk 中的 ManagedHookWorker / ManagedEventWorker 用于新受管 Hook 与事件契约;普通模型供应商应从上述 Provider 契约和现有实现开始。