Provider API
Flue 的模型层是 Pi 的 Provider 协议的直接使用:一个 Provider 是一个 Pi Provider 对象,运行时持有一个 Pi Models 注册表,所有模型调用都通过它解析。Flue 添加了三样东西——一个构建时的 providers 配置用于选择内置哪些 Provider、setProvider() 用于在运行时注册 Provider,以及 cloudflareBindingProvider() 用于通过 env.AI 进行 Workers AI 调度。其他一切——Provider 对象、认证解析、自定义端点——都是 Pi 自身的 API,直接使用。入门教程请参阅模型——自定义 Provider;本页是完整的契约。
导出:
import { setProvider } from '@flue/runtime';
import {
cloudflareBindingProvider,
type CloudflareAIBinding,
type CloudflareBindingProviderOptions,
} from '@flue/runtime/cloudflare/workers-ai';
import { type CloudflareGatewayOptions } from '@flue/runtime/cloudflare';
// Provider 构造是 Pi 的 API,直接使用:
import { createProvider, envApiKeyAuth } from '@earendil-works/pi-ai';
import { anthropicProvider } from '@earendil-works/pi-ai/providers/anthropic';
providers 配置
// vite.config.ts
flue({ providers: ['anthropic', 'openai'] });
通过 Provider ID 选择生成的服务器入口注册哪些 Provider。每个条目对应生成入口中的一个 @earendil-works/pi-ai/providers/<id> 工厂导入——除了 'cloudflare',它选择 Flue 自己的 Workers AI 绑定 Provider——因此只有列出的 Provider(它们的目录和延迟加载的协议实现)会包含在构建中。
- 省略时:所有内置 Provider 都会注册,包括 Cloudflare 目标上的 Workers AI 绑定 Provider。默认值保留了零配置解析——Pi 目录中的任何
'provider/model-id'说明符都可以工作,使用 Provider 环境变量中的凭据(ANTHROPIC_API_KEY、OPENAI_API_KEY等)。 - 设置后:列表是穷举的。 引用未列出 Provider 的说明符在模型解析时失败,在 Cloudflare 目标上,只有当列表中包含
'cloudflare'时才会注册绑定 Provider。在 Node 目标上命名'cloudflare'是配置错误(绑定仅存在于 Workers 上)。自定义 Provider 不受影响——使用setProvider()注册它们。 - 未知 ID 会导致构建失败。 生成的导入
@earendil-works/pi-ai/providers/<id>无法解析,构建错误会指出它。 - 用户注册优先。 生成的注册会跳过已注册的 Provider ID,因此
app.ts中的setProvider()会覆盖列出的内置 Provider,无论模块评估顺序如何。 flue run忽略该列表。 它只加载智能体模块——没有app.ts,没有生成的入口——并始终注册完整的内置集。收窄是服务器构建的关注点。
flue.config.ts 中也接受同一字段;内联插件选项按字段优先。参见配置。
setProvider()
function setProvider(provider: Provider): void;
向运行时注册一个 Pi Provider,以 provider.id 为键:setProvider(p) 后,p.id === 'acme' 时,说明符 'acme/some-model' 通过它解析。接受任何 Provider——内置工厂(anthropicProvider())、用于自定义端点的 createProvider(...)、cloudflareBindingProvider(...),或测试中伪造 Provider 的 .provider。
import { createProvider, envApiKeyAuth } from '@earendil-works/pi-ai';
import { openAICompletionsApi } from '@earendil-works/pi-ai/api/openai-completions.lazy';
import { setProvider } from '@flue/runtime';
setProvider(
createProvider({
id: 'ollama',
auth: { apiKey: { name: 'Ollama (keyless)', resolve: async () => ({ auth: {} }) } },
models: [/* Model 对象;每个携带自己的 baseUrl */],
api: openAICompletionsApi(),
}),
);
行为:
- 每次调用替换该 ID 的先前 Provider。 调用不会累积或合并;同一 ID 的最新
setProvider()生效,包括覆盖生成入口的内置注册(它们会跳过已注册的 ID)。 - 注册表是模块范围的、内存中的。 在模块顶层调用
setProvider(),在任何智能体运行之前。在 Node.js 目标上,一个进程托管所有智能体,因此app.ts中的注册覆盖一切。在 Cloudflare 目标上,每个智能体对话运行在自己的 Durable Object 隔离环境中;app.ts在每个隔离环境中评估,因此顶层注册适用于所有地方。flue run只加载智能体模块,从不加载app.ts——当注册也必须在那里生效时,将其放在智能体模块中。 - 注册是声明式和延迟的。 调用不执行网络 I/O 和凭据验证;错误的端点或密钥在第一次模型请求时作为 Provider 错误出现。没有公开的取消注册函数。
- 凭据通过 Provider 自身的
auth解析。 内置工厂携带 Pi 的环境变量解析(envApiKeyAuth);自定义 Provider 声明自己的解析器——固定密钥、环境变量读取或动态交换。Flue 不在之上添加凭据层。参见 Pi——认证。
模型解析
模型说明符是 'provider-id/model-id',在第一个 / 处分割。解析在每次模型调用时发生,针对实时注册表:
- Provider ID 必须已注册——通过
providers配置默认值或setProvider()。未知的 Provider ID 会抛出异常,列出已注册的 ID 和两种注册路径。 - 模型 ID 必须是 Provider 声明的(
provider.getModels())。未知的模型 ID 会抛出异常,列出已声明的 ID。 - 例外:携带动态模型模板的 Provider——
cloudflareBindingProvider()是 Flue 附带的唯一一个——以零元数据解析未声明的模型 ID:reasoning: false(转发的thinkingLevel被丢弃)、input: ['text'](图像块被替换为"(image omitted)"占位符)、contextWindow: 0(未知——阈值压缩无法启用)、maxTokens: 0,以及全零成本。命中此回退的网关供应商 ID(anthropic/…、openai/…)会记录每个 ID 一次的警告,指出模型和降级情况——通常的修复方式是使用更新的 pi-ai,其网关目录知道该模型;@cf/…ID 静默地以此方式解析,因为它们的线路格式不需要目录条目。
解析失败抛出普通的 Error(不是 FlueError 类别),在模型调用解析说明符时抛出。没有 / 或模型 ID 为空('acme/')的说明符是无效的。
模型元数据——上下文窗口、成本、推理能力、输入模态、逐模型头信息——位于 Provider 声明的 Model 对象上。要覆盖内置 Provider 的元数据,注册一个携带所需值的替代 Provider(参见模型——自定义 Provider);没有单独的覆盖接口。
cloudflareBindingProvider()
function cloudflareBindingProvider(options: CloudflareBindingProviderOptions): Provider;
interface CloudflareBindingProviderOptions {
binding: CloudflareAIBinding; // env.AI
gateway?: CloudflareGatewayOptions | false;
streamIdleTimeoutMs?: number;
}
构建 cloudflare Provider:通过 Workers AI 绑定的 run(modelId, payload, options) 在进程内调度 Pi 模型——没有 baseUrl、没有 apiKey、没有 HTTP 端点。绑定和解析的网关选项在 Provider 的闭包中捕获。从自己的子路径导出 @flue/runtime/cloudflare/workers-ai:导入工厂会将绑定调度代码放入构建中,因此它刻意不与 @flue/runtime/cloudflare 桶一起加载。
binding— 捕获的env.AI引用。gateway— 通过此 Provider 的每个run调用的 AI Gateway 路由。三态:省略时通过 Cloudflare 的默认 AI Gateway 路由(选项对象{ id: 'default' },绑定按需为账户配置);CloudflareGatewayOptions对象替换默认值;false选择退出——不向run传递网关选项。streamIdleTimeoutMs— 模型流在请求因可重试中断而失败之前,可以不传输任何字节的最大时间。默认为五分钟——较宽松,因为长时间思考的模型在没有保持连接或推理增量流传输时可能是正常的沉默。0禁用此防护。
行为:
- Cloudflare 目标上的注册。 当
providers配置省略或包含'cloudflare'时,生成的 Worker 入口运行setProvider(cloudflareBindingProvider({ binding: env.AI }))——除非已注册了 ID 为cloudflare的 Provider。app.ts的导入被提升到生成入口的主体之上,因此用户注册始终优先;这就是项目如何指定命名网关、调整缓存或选择退出的方式。不包含'cloudflare'的providers列表既不发出注册也不发出导入。参见模型——Cloudflare Workers AI 和 Cloudflare 目标指南。 - 元数据水合。 Provider 声明 Pi 的
cloudflare-workers-ai目录,重新标记到cloudflareID 上,并加入 Pi 的cloudflare-ai-gateway目录:网关条目的裸模型 ID 以其网关 URL 路径段中的供应商为前缀(gpt-5.6-terra→openai/gpt-5.6-terra,与绑定的寻址方式匹配),因此网关模型携带其真实的上下文窗口、成本、推理能力(thinkingLevelMap)、输入模态和线路格式api。网关/compat条目被跳过——它们是 Workers AI 目录已声明的@cf/…ID 的别名。任何其他 ID 仍然解析——绑定接受任意模型 ID——使用模型解析中的零元数据默认值。 - 线路格式。 调度为每个模型选择序列化方式:首先使用水合目录的
api;对于目录不知道的 ID,使用供应商前缀(anthropic/…→ Anthropic Messages,openai/…→ OpenAI Responses);对于@cf/…ID 和未知供应商使用 OpenAI 兼容的 chat-completions 形状。Responses 分支将协议处理委托给 pi-ai 导出的原语,通过模型的目录thinkingLevelMap映射推理努力度,并为无状态重放请求加密推理内容。目录api没有绑定分支的模型会产生明确的流错误,而不是静默的错误载荷。每个形状通过binding.run以returnRawResponse: true和解析的gateway选项传递;请求体不携带model字段——run()参数指定目标。 - 失败。 非 OK 的绑定响应抛出
CloudflareAIBindingError(type: 'cloudflare_ai_binding_error'),从@flue/runtime/cloudflare导出;413 额外携带meta.reason: 'request_too_large'并触发压缩恢复。参见错误——CloudflareAIBindingError。 - Node 导入安全。 工厂及其类型在 Node.js 上可导入(绑定形状是结构化的);只有通过它调用模型才需要真正的绑定。
CloudflareGatewayOptions
interface CloudflareGatewayOptions {
id: string;
skipCache?: boolean;
cacheTtl?: number;
cacheKey?: string;
metadata?: Record<string, number | string | boolean | null>;
collectLog?: boolean;
eventId?: string;
requestTimeoutMs?: number;
}
通过 cloudflareBindingProvider() 路由的每个 binding.run(...) 调用应用的 AI Gateway 选项。形状镜像 Cloudflare Worker 绑定方法文档,它定义了每个字段的提供者端语义;除 requestTimeoutMs 外的每个字段都在 gateway 选项中原样转发。从 @flue/runtime/cloudflare 导出。
id— 要路由的 AI Gateway ID(slug)。指定网关选项时必需。skipCache— 绕过请求的网关缓存。cacheTtl— 缓存 TTL 覆盖,以秒为单位。cacheKey— 缓存键覆盖。metadata— 在网关日志条目上显示的任意元数据。collectLog— 强制收集(或不收集)请求日志。eventId— 用于日志关联的自定义事件 ID。requestTimeoutMs— 网关强制的边界,以毫秒为单位,针对响应第一部分的时间(不是总时长——与streamIdleTimeoutMs配合处理流中停滞)。绑定没有等效字段,因此这作为请求的cf-aig-request-timeout头发出,而不是在gateway选项中转发。
CloudflareAIBinding
interface CloudflareAIBinding {
run(
modelId: string,
inputs: Record<string, unknown>,
options?: Record<string, unknown>,
): Promise<Response | Record<string, unknown>>;
}
Workers AI 绑定的最小结构形状,从 @flue/runtime/cloudflare 导出。它刻意是结构化的——不是 Cloudflare 的 Ai 类型——因此工厂在 Node.js 上仍可导入。将真正的 env.AI 绑定作为 CloudflareBindingProviderOptions 的 binding 传递。
Provider 遥测
模型轮次可观测性事件(turn_request.request 和 turn.request)通过 Provider ID 到可观测性约定的固定规范化来标识 Provider(ModelRequestInfo.providerName);表外的 ID 原样传递。报告的服务器主机和端口从解析的模型的 baseUrl 中解析。
| Provider ID | providerName |
|---|---|
amazon-bedrock | aws.bedrock |
azure-openai-responses | azure.ai.openai |
google | gcp.gemini |
google-vertex | gcp.vertex_ai |
mistral | mistral_ai |
moonshotai, moonshotai-cn | moonshot_ai |
xai | x_ai |
相同的事件始终将未修改的 Provider ID 作为 ModelRequestInfo.providerId 报告。
注册不会改变什么
- Pi 的目录本身。 注册在解析时遮蔽一个 Provider ID;它从不添加、删除或编辑目录条目。
- 任何持久化的内容。 注册存在于当前模块范围的进程内存中。它们不被持久化,不跨进程或隔离环境共享,每次启动时从模块顶层代码重建。
- 其他 Provider ID 的早期注册。 每次调用只影响一个 Provider ID。