# pi-provider-kit

[English](README.md)

为 [Pi](https://pi.dev) 提供服务商扩展工具包，帮助 Pi 用户和扩展作者注册 LLM Provider、发现模型、调优请求并查看账户状态，同时保留 Pi 原生 Footer。

**状态：** 活跃开发中的 0.x 项目。当前 manifest 版本为 `0.4.0`；1.0 之前公开 API 可能变化。当前测试的兼容性基线是 Pi `0.83.x`。

维护者：[huanghui](https://codeberg.org/huanghui)。

[Issues](https://codeberg.org/huanghui/pi-provider-kit/issues) · [安全策略](https://codeberg.org/huanghui/pi-provider-kit/src/branch/main/SECURITY.md) · [参与贡献](https://codeberg.org/huanghui/pi-provider-kit/src/branch/main/CONTRIBUTING.md) · [支持](https://codeberg.org/huanghui/pi-provider-kit/src/branch/main/SUPPORT.md) · [变更记录](https://codeberg.org/huanghui/pi-provider-kit/src/branch/main/CHANGELOG.md)

## 功能

- **Provider Kit Host**：一个运行时统一拥有 Provider 注册、Status、Preflight、实时可用性检查和请求 tuner。
- **动态 Adapter Extension**：通过 Pi manifest 添加 Provider、Status、Preflight 或 Tuner 文件，并在 `/reload` 后生效。
- **缓存模型目录**：Charm Hyper 先使用不访问网络的 fallback，恢复 Provider-scoped 最近目录，然后在后台通过超时、取消、single-flight 和失败保留机制刷新。
- **价格与质量元数据**：优先使用 Provider 价格；OpenRouter 元数据可补全缺失字段并提供只读 Artificial Analysis 质量指标。明确的 Provider/model 价格调整会保留来源。
- **显式诊断**：`/status` 只读缓存，`/status refresh` 执行免费状态检查，只有 `/status check` 会发送真实模型请求。
- **内置集成**：Charm Hyper、DeepSeek、Google Gemini、OpenAI Codex、OpenCode Zen 和 OpenCode Go 的 Preflight/Status 适配器，以及 DeepSeek 请求 tuner。

## 安装

通过 Pi 安装：

```sh
pi install npm:pi-provider-kit
```

发布包有意包含 TypeScript 源文件，而不是编译后的 `dist/` 目录。Pi 是宿主并通过自己的扩展加载器加载本包；本包不是独立的 Node CLI。

## 环境要求

- Node.js `22.19.0` 或更高版本。
- Pi `0.83.x`（`@earendil-works/pi-coding-agent` `^0.83.0`）。后续 Pi 版本在完成测试前不属于支持范围。
- 所用 Provider 的凭证。Charm Hyper 支持 `$HYPER_API_KEY` 或 Pi 的 `/login` OAuth 流程；Pi 原生 Provider 的凭证由 Pi 自己的 Provider/auth 配置解析。

## 快速开始

1. 在 Pi 中配置目标 Provider 凭证。Charm Hyper 可以设置 `$HYPER_API_KEY`，也可以执行 `/login` 并选择 `Subscription` → `Charm Hyper`。不要把真实密钥提交或粘贴到项目文件中。
2. 启动 Pi 并选择 Charm Hyper 模型：

   ```text
   /model charm-hyper/deepseek-v4-pro
   ```

3. 查看结果：

   ```text
   /status
   ```

`/status` 从缓存显示当前 Provider/model、路由、鉴权状态、目录、预检、可用性和账户信息。使用 `/status refresh` 执行免费远程检查；如果明确接受一次真实模型请求及其可能产生的用量费用，则使用 `/status check`。

## 配置

### 凭证与本地状态

| 名称 | 必需 | 默认值 | 作用 |
|---|---:|---|---|
| `HYPER_API_KEY` | Charm Hyper API Key 模式 | 无 | Pi 解析内置 `charm-hyper` Provider 引用的密钥；OAuth 用户可以使用 `/login`。 |
| `PI_CODING_AGENT_DIR` | 否 | `~/.pi/agent` | 修改持久化 OpenRouter 元数据缓存的基础目录。 |

其他内置 Status 和 Preflight 适配器使用 Pi `ModelRegistry` 解析的凭证，不会自行读取任意环境变量。Status 报告不会打印凭证、OAuth token 或账户 ID。

默认 OpenRouter 元数据缓存位于 `<agent-dir>/provider-kit/openrouter-model-metadata.json`。其中只有最近一次成功的公开元数据快照，不包含 Provider 凭证或 prompt。

### Runtime 选项

程序化集成可以向 `createProviderKitRuntime()` 或 `createProviderKitHost()` 传入以下字段。公开 TypeScript 源代码中的 [`ProviderKitDependencies`](core/runtime-config.ts) 是权威定义。

| 选项 | 必需 | 默认值 | 作用 |
|---|---:|---:|---|
| `enableOfficialPricingFallback` | 否 | `true` | 在启动/reload 期间启用公开 OpenRouter 元数据查询。 |
| `pricingPolicies` | 否 | `{}` | 不修改 Provider adapter，应用一个明确的 Provider/model 价格调整。 |
| `modelDiscoveryTimeoutMs` | 否 | `3000` ms | 限制动态 Provider 目录请求。 |
| `statusRequestTimeoutMs` | 否 | `8000` ms | 限制 Status 和 Preflight 请求。 |
| `liveCheckRequestTimeoutMs` | 否 | `8000` ms | 限制主动执行的实时模型请求。 |
| `officialPricingUrl` | 否 | OpenRouter models 端点 | 选择公开元数据来源。 |
| `openRouterMetadataCachePath` | 否 | `<agent-dir>/provider-kit/openrouter-model-metadata.json` | 选择持久化元数据缓存文件。 |

适配器定义、校验、冲突处理、reload 行为和生命周期边界见[动态 Adapter Extension 契约](docs/adapter-extensions.md)。

### DeepSeek tuner 开关

内置 tuner 只匹配官方 `deepseek` Provider 以及 `deepseek-v4-pro`/`deepseek-v4-flash`。除非设为 `1` 或 `true`，以下开关均关闭：

| 变量 | 默认值 | 启用后的作用 |
|---|---|---|
| `PI_DEEPSEEK_TUNER_CHURN_FILTER` | 关闭 | 从 system prompt 中移除易变化的 session-overview 内容。 |
| `PI_DEEPSEEK_TUNER_NO_STRIP` | 关闭 | 保留历史 reasoning/content，不执行默认清理。 |
| `PI_DEEPSEEK_TUNER_NO_TOOL_REPAIR` | 关闭 | 禁用中断 tool pair 修复。 |
| `PI_DEEPSEEK_TUNER_NO_THINKING_INJECT` | 关闭 | 禁用 DeepSeek Pro 的自动 thinking 注入。 |

## API 与命令

### Pi 命令

| 命令 | 网络/费用行为 |
|---|---|
| `/status` | 读取当前缓存报告；不访问网络。 |
| `/status refresh` | 刷新账户状态和免费的 endpoint/auth/catalog 检查；不会生成模型输出。 |
| `/status check` | 执行上述刷新，并为当前 Provider/model 发送一次真实请求；可能产生用量。 |

参数是 positional mode。旗标形式、多个 mode 和未知参数会在请求前拒绝。Status 失败会在报告中显示错误分类及可用的重试信息；由于本包运行在 Pi 内部，不提供独立进程退出码。

### 公开 TypeScript API

`index.ts` 是包入口和公开 API 的源代码。它导出：

- `createProviderKitRuntime()` 和 `createProviderKitHost()`；
- `defineProviderExtension()`、`defineStatusExtension()`、`definePreflightExtension()` 和 `defineTunerExtension()`；
- adapter、manager、pricing、diagnostics 和 Provider Kit 类型定义；
- Charm Hyper、DeepSeek、Google、Codex 和 OpenCode 的内置 factory 与 parser。

根导出是 `.`；包还暴露 `./package.json`。capability 文件由 Pi 通过 `pi.extensions` manifest 加载，而不是通过 Node 子路径导入。

### 动态 Adapter Extension

默认入口是唯一的 Provider Kit Host。Pi 根据包 manifest 发现 capability 文件，因此可信的独立包可以在不修改 `index.ts` 的情况下添加适配器：

```text
providers/*.ts    # Provider Adapter Extension
status/*.ts       # Status Adapter Extension
preflight/*.ts    # Preflight Adapter Extension
tuners/*.ts       # Tuner Adapter Extension
```

capability 入口必须 default-export 对应的专用 helper：

```ts
import { defineProviderExtension } from "pi-provider-kit";

export default defineProviderExtension({
  id: "example-provider",
  create: async () => ({
    id: "example-provider",
    provider: {
      name: "Example Provider",
      baseUrl: "https://api.example.com/v1",
      apiKey: "$EXAMPLE_PROVIDER_API_KEY",
      api: "openai-completions",
      models: [{ id: "example-model" }],
    },
  }),
});
```

在包 manifest 中声明 capability glob：

```json
{
  "pi": {
    "extensions": [
      "./index.ts",
      "./providers/*.ts",
      "./status/*.ts",
      "./preflight/*.ts",
      "./tuners/*.ts"
    ]
  }
}
```

Adapter 变更会在 `/reload` 后生效；本包不运行文件 watcher，也不支持会话内热插拔。一个 Pi runtime 应只启用一个 Host，并且只安装可信的 Adapter Extension。Status、Preflight、Tuner、校验、冲突和 reload 行为详见[动态 Adapter Extension 契约](docs/adapter-extensions.md)。

### 自定义 Provider

Provider、可选的免费 Preflight、账户 Status 和请求 Tuner 是互相独立的 adapter。`ProviderAdapter` 有意不包含 quota 字段：

```ts
import {
  createProviderKitRuntime,
  type ProviderAdapter,
  type ProviderKitDefinition,
} from "pi-provider-kit";

const provider: ProviderAdapter = {
  id: "example-provider",
  provider: {
    name: "Example Provider",
    baseUrl: "https://api.example.com/v1",
    apiKey: "$EXAMPLE_PROVIDER_API_KEY",
    api: "openai-completions",
    models: [{ id: "example-model" }],
  },
};

export default createProviderKitRuntime(async (): Promise<ProviderKitDefinition> => ({
  providers: [provider],
  preflights: [],
  statuses: [],
  tuners: [],
}));
```

Provider 可以声明明确的价格调整，也可以在 runtime 中提供 `pricingPolicies`，而不修改 adapter：

```ts
provider.pricing = {
  defaultAdjustment: {
    multiplier: 0.8,
    label: "20% provider discount",
    source: "provider contract",
    appliesToReference: true,
  },
};
```

价格是按每 1M token 计算的本地估算。基础价格优先级为 Provider catalog、Provider fallback、OpenRouter，最后是 unavailable。没有基础价格时，折扣仍显示 unavailable，而不是免费。Pi 原生模型保留其配置的价格；status 会标注实际字段来源，并单独列出条件 token tiers。

## 内置 Provider 契约

- **Charm Hyper**：当前模型目录为 `https://hyper.charm.land/v1/provider`；HTTP 404 时暂时回退到旧的 `/v1/models`。credits status 为 `https://hyper.charm.land/v1/credits`。模型价格和能力来自目录，支持通过 Pi `/login` 使用 OAuth，账户状态显示 Hypercredits，并在可用时显示 OAuth team name。目录请求与 status 分离，远端不可用时使用缓存的 fallback。
- **DeepSeek**：Preflight 使用 `/models`；账户状态使用 `/user/balance` 并显示优先的 USD 总余额。赠送和充值组件有意不显示。
- **Google Gemini**：Preflight 使用 `/v1beta/models`，并要求当前模型支持 `generateContent`。
- **OpenCode Zen/Go**：公开模型目录返回 `endpoint/catalog` 不代表 API Key 有模型权限。OpenCode Go status 使用 `/zen/go/v1/usage`，显示滚动 5 小时、每周和每月美元额度窗口，以及 Zen balance fallback 状态。
- **OpenAI Codex**：Preflight 使用 ChatGPT OAuth token、其中的 `chatgpt_account_id` 和 ChatGPT backend 目录；status 使用 `/backend-api/wham/usage`，显示套餐和主 `codex` 窗口。这些 backend 契约可能独立于 Pi 变化。

## 限制与兼容性

- 本包是 Pi 扩展，不是独立服务器或 CLI；需要宿主的扩展加载器和 Provider API。
- 当前测试的宿主兼容范围是 Pi `0.83.x`。实时检查通过公开的 `ModelRegistry` 和 Provider `streamSimple()` 路径；在 Pi 0.83 上不会重放其他扩展未公开的请求准备或响应钩子。
- 每个 Pi runtime 只支持一个 Provider Kit Host。Adapter 的增删和变更会在 `/reload` 后生效。
- 启动/reload 可能请求公开的 Charm Hyper 模型目录和 OpenRouter 元数据；账户状态不会后台轮询。实时检查始终由用户显式触发，并可能消耗 Provider 配额。
- Provider 端点、价格、OAuth backend schema 和模型目录都是外部契约，可能独立于本包变化或不可用。Charm Hyper 模型发现同时接受当前 `/v1/provider` 契约和临时兼容的旧 `/v1/models` 响应。
- 本包不会自动建模 Batch、账户套餐、私有合同、路由、区域或时间窗口价格；Pi 的静态 cost 模型没有这些请求上下文。

## 安全与数据边界

Pi 扩展以当前用户权限运行。只安装你信任其源代码的包。扩展只将已配置的凭证发送到对应 Provider 端点，对远程 JSON 进行校验，并使用请求截止时间/取消；缓存只持久化公开 OpenRouter 元数据。实时检查使用最小 prompt，不写入会话，并可能产生用量。

不要把 API Key、OAuth token、私有 fixture 或个人数据放进 issue、示例、测试或提交。漏洞请遵循[安全策略](https://codeberg.org/huanghui/pi-provider-kit/src/branch/main/SECURITY.md)进行私下报告。

## 开发与验证

使用干净的依赖安装，并运行与 CI 相同的检查门禁：

```sh
npm ci
npm run audit:runtime
npm run check
npm test
npm run artifact:check
```

`npm run audit:runtime` 检查发布运行时依赖中的高严重度安全公告。`npm run check` 执行 Biome 和 TypeScript 类型检查。`npm test` 执行 mocked 行为及契约测试。`npm run artifact:check` 会在临时目录创建 npm tarball、检查 allowlist，在带有测试 Pi peer 的临时 consumer 中安装，并加载发布包中的 Pi 入口。普通检查不会调用付费 Provider API，也不会执行实时模型检查。

仓库布局是为 Pi 的 source-based package 契约有意保留的：

```text
index.ts                 # Host 入口和公开导出
core/                    # 共享 runtime 与 adapter 实现
providers/ status/       # Provider 与账户状态入口
preflight/ tuners/       # 免费检查与请求 tuner
test/                    # 行为与产物边界测试
docs/                    # 公开契约、术语表和 ADR
scripts/                 # 仅供维护者使用的验证脚本
```

npm 产物包含 Host、内置 capability 入口、`core/`、公开文档、`README*`、`CHANGELOG.md`、`LICENSE` 和 package metadata；不包含测试、维护者脚本、本地配置和被忽略的 `pi-provider-kit/` 私有 overlay。发布前请使用 `npm run artifact:check` 检查实际产物。

## 参与贡献

欢迎提交范围明确的 issue 和 pull request。提交变更前请阅读 [CONTRIBUTING.md](https://codeberg.org/huanghui/pi-provider-kit/src/branch/main/CONTRIBUTING.md)、搜索已有 issue，并运行完整验证门禁。公开行为变化应同时更新 canonical README 及其中文翻译、测试，并在适当时更新变更记录。

## 支持与发布

本项目仅对最新发布版本线提供 best-effort 支持，不承诺长期维护。缺陷和使用问题请阅读 [SUPPORT.md](https://codeberg.org/huanghui/pi-provider-kit/src/branch/main/SUPPORT.md)。公开 TypeScript API 使用 SemVer；在 1.0 之前，小版本之间不保证兼容。`[CHANGELOG.md](CHANGELOG.md)` 是权威发布历史。已发布版本的内容不会原地替换；变更应发布新版本。

## 许可证

[MIT](LICENSE)
