{"_id":"s-aiproviders","_rev":"2-1db499c182dfd0c6d283a5a60fa8ccf9","name":"s-aiproviders","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.0":{"name":"s-aiproviders","version":"0.1.0","keywords":["ai","llm","openai","anthropic","claude","gemini","deepseek","kimi","qwen","doubao","zhipu","tokenplan","image-generation","streaming","sse","cli","skill"],"author":{"name":"lisiyuan0828"},"license":"MIT","_id":"s-aiproviders@0.1.0","maintainers":[{"name":"lisiyuan0828","email":"lisiyuan0828@gmail.com"}],"homepage":"https://github.com/lisiyuan0828/S-AIProviders#readme","bugs":{"url":"https://github.com/lisiyuan0828/S-AIProviders/issues"},"bin":{"s-aiproviders":"dist/cli/main.js"},"dist":{"shasum":"83ea740977c838cd898fddc49e4013e999fc806a","tarball":"https://registry.npmjs.org/s-aiproviders/-/s-aiproviders-0.1.0.tgz","fileCount":83,"integrity":"sha512-NddR4DgNrVrL7fw1Ce/6H84zsa3REypnTmfbMbY5F4JgN3WJ6xeOHUdbl7l6FhA8nCTk+q8CgwwjI015e8/tEA==","signatures":[{"sig":"MEQCIDJayLBmeZZNEhtWeTMIvxXjmK4NMZhSWPPhL0/gD9bkAiBoWfQdp6ztl9alut2OoxW7xCDsviw+Fu3ezvRZ+sjFog==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":214785},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.17"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./presets":{"types":"./dist/presets/index.d.ts","import":"./dist/presets/index.js"},"./image-gen":{"types":"./dist/image-gen/index.d.ts","import":"./dist/image-gen/index.js"},"./package.json":"./package.json"},"gitHead":"f312b47d23999fdc7e401f5a072973b00ebde0e0","scripts":{"chat":"tsx src/cli/main.ts chat","list":"tsx src/cli/main.ts list-presets","build":"tsc -p tsconfig.json && node scripts/postbuild-shebang.cjs dist/cli/main.js","clean":"rm -rf dist","image":"tsx src/cli/main.ts image","typecheck":"tsc -p tsconfig.json --noEmit","prepublishOnly":"pnpm clean && pnpm build"},"_npmUser":{"name":"lisiyuan0828","email":"lisiyuan0828@gmail.com"},"repository":{"url":"git+https://github.com/lisiyuan0828/S-AIProviders.git","type":"git"},"_npmVersion":"10.8.2","description":"Unified AI provider toolkit: OpenAI-compatible / Anthropic / Gemini chat streaming + image generation (OpenAI-compatible & Tencent Cloud TC3). Ships both a programmatic API and a CLI.","directories":{},"_nodeVersion":"20.18.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","typescript":"^5.7.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/s-aiproviders_0.1.0_1779176886652_0.9854849970506141","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"s-aiproviders","version":"0.1.1","description":"Unified AI provider toolkit: OpenAI-compatible / Anthropic / Gemini chat streaming + image generation (OpenAI-compatible & Tencent Cloud TC3). Ships both a programmatic API and a CLI.","license":"MIT","author":{"name":"lisiyuan0828"},"homepage":"https://github.com/lisiyuan0828/S-AIProviders#readme","repository":{"type":"git","url":"git+https://github.com/lisiyuan0828/S-AIProviders.git"},"bugs":{"url":"https://github.com/lisiyuan0828/S-AIProviders/issues"},"type":"module","main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","bin":{"s-aiproviders":"dist/cli/main.js"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./image-gen":{"types":"./dist/image-gen/index.d.ts","import":"./dist/image-gen/index.js"},"./presets":{"types":"./dist/presets/index.d.ts","import":"./dist/presets/index.js"},"./package.json":"./package.json"},"scripts":{"build":"tsc -p tsconfig.json && node scripts/postbuild-shebang.cjs dist/cli/main.js","typecheck":"tsc -p tsconfig.json --noEmit","clean":"rm -rf dist","chat":"tsx src/cli/main.ts chat","image":"tsx src/cli/main.ts image","list":"tsx src/cli/main.ts list-presets","prepublishOnly":"pnpm clean && pnpm build"},"engines":{"node":">=18.17"},"publishConfig":{"registry":"https://registry.npmjs.org/","access":"public"},"keywords":["ai","llm","openai","anthropic","claude","gemini","deepseek","kimi","qwen","doubao","zhipu","tokenplan","image-generation","streaming","sse","cli","skill"],"devDependencies":{"@types/node":"^22.0.0","tsx":"^4.19.0","typescript":"^5.7.0"},"_id":"s-aiproviders@0.1.1","gitHead":"60678396a136f79fc8d59039ec04315c8ccb5e18","_nodeVersion":"20.18.0","_npmVersion":"10.8.2","dist":{"integrity":"sha512-iBBPQCD5//xE90qal37h6EfC20Rz0L2nmeI0cC2KME9d1bJu/7CZ9BF7fVdKDZvtA6GXgEIOtDJ2ijl+MLAmMw==","shasum":"8b7fd748ab70a8401321664b5855e63233b2ce08","tarball":"https://registry.npmjs.org/s-aiproviders/-/s-aiproviders-0.1.1.tgz","fileCount":83,"unpackedSize":216406,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCID9KQbjc7NsPxq1/AgmjVGzfzhbcwxoQCiWzxO7FUuRkAiBbp/EyMLuSsH1oxKC4BYBufiKiYR/hf2uNkfKLp3eDWg=="}]},"_npmUser":{"name":"lisiyuan0828","email":"lisiyuan0828@gmail.com"},"directories":{},"maintainers":[{"name":"lisiyuan0828","email":"lisiyuan0828@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/s-aiproviders_0.1.1_1779246408804_0.48510275950041226"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-19T07:48:06.562Z","modified":"2026-05-20T03:06:49.032Z","0.1.0":"2026-05-19T07:48:06.792Z","0.1.1":"2026-05-20T03:06:48.943Z"},"bugs":{"url":"https://github.com/lisiyuan0828/S-AIProviders/issues"},"author":{"name":"lisiyuan0828"},"license":"MIT","homepage":"https://github.com/lisiyuan0828/S-AIProviders#readme","keywords":["ai","llm","openai","anthropic","claude","gemini","deepseek","kimi","qwen","doubao","zhipu","tokenplan","image-generation","streaming","sse","cli","skill"],"repository":{"type":"git","url":"git+https://github.com/lisiyuan0828/S-AIProviders.git"},"description":"Unified AI provider toolkit: OpenAI-compatible / Anthropic / Gemini chat streaming + image generation (OpenAI-compatible & Tencent Cloud TC3). Ships both a programmatic API and a CLI.","maintainers":[{"name":"lisiyuan0828","email":"lisiyuan0828@gmail.com"}],"readme":"# s-aiproviders\n\n> 一个包，对接所有模型。零依赖、统一抽象的 AI Provider 工具集 —— **库 + CLI 同一次安装**：你的应用里 `import { createProvider }`，命令行里 `npx s-aiproviders chat`，底层走的是同一份协议层代码。\n\n[English](./README.md) · [简体中文](./README.zh.md)\n\n[![npm](https://img.shields.io/npm/v/s-aiproviders.svg)](https://www.npmjs.com/package/s-aiproviders)\n[![license](https://img.shields.io/badge/license-MIT-green.svg)](#许可证)\n[![Node](https://img.shields.io/badge/node-%E2%89%A518.17-brightgreen.svg)](#环境要求)\n[![types](https://img.shields.io/badge/types-bundled-blue.svg)](#)\n[![runtime deps](https://img.shields.io/badge/runtime%20deps-0-success.svg)](#)\n\n---\n\n## 目录\n\n- [为什么需要 s-aiproviders](#为什么需要-s-aiproviders)\n- [能力矩阵](#能力矩阵)\n- [Provider 目录](#provider-目录)\n- [快速开始](#快速开始)\n- [3 种使用方式](#3-种使用方式)\n- [配置体系](#配置体系)\n- [API 参考](#api-参考)\n- [架构](#架构)\n- [仓库结构](#仓库结构)\n- [从源码构建](#从源码构建)\n- [与同类项目对比](#与同类项目对比)\n- [常见问题](#常见问题)\n- [版本策略](#版本策略)\n- [许可证](#许可证)\n\n---\n\n## 为什么需要 s-aiproviders\n\n每个接 AI 的项目都在重复同一套底层管线：解析 SSE、抹平三大对话方言、管理 API Key、同时支持 DALL·E 和腾讯云的异步签名生图、为设置页拼一份 provider 列表。s-aiproviders 把这些管线提炼成一个**零运行时依赖**的 TypeScript 包，并以 **3 种形态**复用同一份契约：\n\n1. 作为 **npm 库**，引入到 Node 服务、Electron 主进程、Vite 应用中。\n2. 作为 **CLI**（`npx s-aiproviders` 或全局安装），被 shell 脚本、自动化、`cron` 调用。\n3. 作为 **AI Agent Skill**（兼容 Claude Code / Cursor / Codebuddy），Agent 读取随包发布的 `SKILL.md` 后自动调 CLI。\n\n**设计原则**\n\n- **零运行时依赖**。仅基于平台原生 `fetch` + `ReadableStream`，不依赖 `openai`、`langchain`、`axios` 等任何第三方 SDK。\n- **唯一事实源**。库、CLI、Skill 是同一次安装，CLI 只是把同一份导出 API 包了一层。\n- **构造上即浏览器安全**。主入口绝不 import `node:*`，仅 Node 可用的图像生成模块挂在 `s-aiproviders/image-gen` 子路径，渲染端打包不会被污染。\n- **诚实的流式语义**。`chat()` 返回 `AsyncIterable<ChatChunk>`，**不抛错**：网络/解析失败会以 `{ type: 'error' }` 事件下发，调用方无需在 `for await` 外面包 try/catch。`AbortSignal` 端到端贯穿。\n- **边界明确**。库本身不读环境变量、不读文件、不依赖全局变量。所有配置由调用方传入 —— 适合 SaaS、多租户 Electron 应用、Serverless。\n\n## 能力矩阵\n\n| 能力 | 实现 | 浏览器安全 |\n|---|---|---|\n| 流式对话 | `openai-compatible` · `anthropic` · `gemini` | ✅ |\n| 文生图 | OpenAI Images 兼容 · 腾讯云 TC3（`SubmitTextToImageJob`，异步轮询） | ⚠️ 仅 Node |\n| 跨 provider 模型挑选 | 按能力优先级回退（`prefer: ['image-gen', 'vision', 'text']`） | ✅ |\n| 内置预设 | 9 个 chat + 4 个 image | ✅ |\n| 在线模型发现 | `provider.listModels()`（OpenAI 兼容端点） | ✅ |\n| SSE 原语 | 通用 `parseSse(stream, signal)` | ✅ |\n| Token 用量上报 | 三种方言归一化 | ✅ |\n| 取消 | 原生 `AbortSignal` 透传 | ✅ |\n\n## Provider 目录\n\n| ID | 显示名 | 协议 | 默认 base URL | 说明 |\n|---|---|---|---|---|\n| `tokenplan` ★ | 腾讯云 Token Plan | openai-compatible | `https://api.lkeap.cloud.tencent.com/plan/v3` | 一份订阅对接多家上游模型，国内推荐默认。 |\n| `openai` | OpenAI | openai-compatible | `https://api.openai.com/v1` | GPT-4o 系列。 |\n| `anthropic` | Anthropic | anthropic | `https://api.anthropic.com/v1` | Claude Opus / Sonnet。 |\n| `gemini` | Google Gemini | gemini | `https://generativelanguage.googleapis.com/v1beta` | 通过 `alt=sse` 走 SSE。 |\n| `deepseek` | DeepSeek | openai-compatible | `https://api.deepseek.com/v1` | Reasoner 与 chat。 |\n| `kimi` | Moonshot Kimi | openai-compatible | `https://api.moonshot.cn/v1` | 128k 上下文。 |\n| `qwen` | 通义千问 | openai-compatible | `https://dashscope.aliyuncs.com/compatible-mode/v1` | 阿里云 DashScope OpenAI 兼容模式。 |\n| `doubao` | 字节豆包 / 火山方舟 | openai-compatible | `https://ark.cn-beijing.volces.com/api/v3` | 火山引擎 ARK。 |\n| `zhipu` | 智谱 GLM | openai-compatible | `https://open.bigmodel.cn/api/paas/v4` | GLM-4 系列。 |\n| `openai-image` | OpenAI Image | openai-compatible | `https://api.openai.com/v1` | DALL·E 3 / gpt-image-1。 |\n| `hunyuan-image` | 腾讯混元生图 (lkeap) | openai-compatible | `https://api.lkeap.cloud.tencent.com/v1` | 混元的 OpenAI 兼容网关。 |\n| `hunyuan-image-tc3` | 腾讯混元生图 3.0 | TC3 签名异步 | `https://aiart.tencentcloudapi.com` | `apiKey: \"SecretId:SecretKey\"`。 |\n| `zhipu-image` | 智谱 CogView | openai-compatible | `https://open.bigmodel.cn/api/paas/v4` | CogView-3 / 3-Plus。 |\n\n获取机器可读列表：`npx s-aiproviders list-presets --json`，或 `import { BUILTIN_PRESETS } from 's-aiproviders'`。\n\n> 接入一个 OpenAI 兼容的新厂商是一行的事：`--provider openai --baseurl https://your-gateway/v1`，无需改代码。\n\n## 快速开始\n\n### 不安装直接试\n\n```bash\nnpx s-aiproviders list-presets\n\nnpx s-aiproviders chat \\\n  --provider tokenplan \\\n  --apikey \"$TOKENPLAN_API_KEY\" \\\n  --prompt \"用一句话解释 Server-Sent Events。\"\n```\n\n### 作为库安装\n\n```bash\npnpm add s-aiproviders\n# 或：npm install s-aiproviders\n# 或：yarn add s-aiproviders\n```\n\n```ts\nimport { createProvider } from 's-aiproviders';\n\nconst provider = createProvider('openai-compatible', {\n  apiKey: process.env.OPENAI_API_KEY!,\n  baseURL: 'https://api.openai.com/v1',\n});\n\nconst ac = new AbortController();\nfor await (const ev of provider.chat(\n  {\n    model: 'gpt-4o-mini',\n    messages: [\n      { role: 'system', content: 'You are concise.' },\n      { role: 'user', content: 'Explain SSE in one sentence.' },\n    ],\n  },\n  ac.signal,\n)) {\n  if (ev.type === 'delta') process.stdout.write(ev.text);\n  if (ev.type === 'usage') console.error('usage:', ev.usage);\n  if (ev.type === 'error') throw new Error(`${ev.code}: ${ev.message}`);\n}\n```\n\n### 生成一张图（Node 环境）\n\n```ts\nimport { generateImage } from 's-aiproviders/image-gen';\n\nconst { filePath } = await generateImage({\n  baseURL: 'https://api.openai.com/v1',\n  apiKey: process.env.OPENAI_API_KEY!,\n  model: 'gpt-image-1',\n  prompt: '一只在看书的迷你陶瓷水豚',\n  size: '1024x1024',\n  outputDir: './output',\n});\nconsole.log('saved:', filePath);\n```\n\n## 3 种使用方式\n\n| # | 形态 | 命令 | 适用 |\n|---|---|---|---|\n| 1 | **零安装** | `npx s-aiproviders <cmd>` | 一次性任务、CI/CD、沙箱 |\n| 2 | **全局 CLI** | `npm i -g s-aiproviders` 后 `s-aiproviders <cmd>` | 日常终端 |\n| 3 | **库引用** | `pnpm add s-aiproviders` 后 `import { createProvider }` | 嵌入到你自己的产品（Electron / Node 服务 / Vite） |\n\n也可以作为 **AI Agent Skill** 使用：包里随附 `SKILL.md` 与 `references/` 目录，把它们软链到 Agent 的 skills 目录（如 `~/.claude/skills/s-aiproviders/`），Agent 即可读取 manifest 并代为调用 CLI。详细步骤见 [`INSTALL.md`](./INSTALL.md)。\n\n## 配置体系\n\nCLI 解析每个参数（`provider` / `apiKey` / `baseURL` / `model`）时，按以下优先级查找：\n\n| 优先级 | 来源 | 示例 |\n|---|---|---|\n| 1（最高） | CLI flags | `--apikey sk-xxx` |\n| 2 | 项目级 `./.s-aiproviders/EXTEND.md` | 通过 `EXTEND.example.md` 提交模板 |\n| 3 | 用户级 `~/.s-aiproviders/EXTEND.md` | 个人默认值 |\n| 4 | 环境变量 | `SAIP_API_KEY`、`OPENAI_API_KEY`… |\n| 5（最低） | 预设默认 | `BUILTIN_PRESETS[*].defaultBaseURL`、`builtinModels[0]` |\n\n### 通用环境变量\n\n`SAIP_PROVIDER` · `SAIP_API_KEY` · `SAIP_BASE_URL` · `SAIP_MODEL` · `SAIP_PROTOCOL`\n\n### 各 provider 专用环境变量\n\n| Provider | 变量 |\n|---|---|\n| Token Plan | `TOKENPLAN_API_KEY` |\n| OpenAI / OpenAI Image | `OPENAI_API_KEY` |\n| Anthropic | `ANTHROPIC_API_KEY` |\n| Gemini | `GEMINI_API_KEY`（别名 `GOOGLE_API_KEY`） |\n| DeepSeek | `DEEPSEEK_API_KEY` |\n| Kimi | `MOONSHOT_API_KEY`（别名 `KIMI_API_KEY`） |\n| Qwen | `DASHSCOPE_API_KEY`（别名 `QWEN_API_KEY`） |\n| Doubao | `ARK_API_KEY`（别名 `DOUBAO_API_KEY`） |\n| Zhipu / Zhipu Image | `ZHIPU_API_KEY`（别名 `BIGMODEL_API_KEY`） |\n| Hunyuan (lkeap) | `LKEAP_API_KEY`（别名 `HUNYUAN_API_KEY`） |\n| Hunyuan TC3 | `TENCENTCLOUD_SECRET_ID` + `TENCENTCLOUD_SECRET_KEY` |\n\n### `EXTEND.md` 配置\n\n```yaml\ndefault_provider: tokenplan\ndefault_model:\n  tokenplan: tc-code-latest\n  openai: gpt-4o-mini\nproviders:\n  tokenplan:\n    api_key: sk-tplan-xxxxx\n    base_url: https://api.lkeap.cloud.tencent.com/plan/v3   # 可选\n    model:    tc-code-latest                                 # 可选\n  openai:\n    api_key: sk-xxxxx\n```\n\n完整 schema 与安全注意事项见 [`references/config/extend-md-schema.md`](./references/config/extend-md-schema.md)。\n\n## API 参考\n\n下列导出全部支持 tree-shaking，并附带完整 `.d.ts` 类型。\n\n### 入口\n\n| Specifier | 内容 |\n|---|---|\n| `s-aiproviders` | 库 API：类型、provider 工厂、预设、能力工具、SSE 解析器。**浏览器安全。** |\n| `s-aiproviders/image-gen` | `generateImage()` —— 仅 Node（依赖 `node:crypto`、`node:fs`）。 |\n| `s-aiproviders/presets` | 仅预设目录，便于更激进地 tree-shake。 |\n\n### 类型\n\n| 符号 | 说明 |\n|---|---|\n| `ProviderProtocol` | `'openai-compatible' \\| 'anthropic' \\| 'gemini'` |\n| `ChatRole` | `'user' \\| 'assistant' \\| 'system'` |\n| `ChatMessageInput` | `{ role: ChatRole; content: string }` |\n| `ChatRequest` | `{ model; messages; temperature?; maxTokens? }` |\n| `ChatChunk` | `delta` / `usage` / `done` / `error` 的判别联合类型 |\n| `ChatUsage` | `{ promptTokens; completionTokens; totalTokens? }` |\n| `ProviderInitConfig` | `{ apiKey; baseURL; timeoutMs? }` |\n| `IProvider` | `{ protocol; chat(req, signal); listModels?() }` |\n| `ModelInfo` | `{ id; label; contextWindow?; description?; capabilities? }` |\n| `ModelCapability` | `'text' \\| 'vision' \\| 'image-gen' \\| 'video-gen'` |\n| `ProviderKind` | `'chat' \\| 'image'` |\n| `ProviderPreset` | 完整预设结构（id、displayName、protocol、defaultBaseURL、builtinModels…） |\n\n### Provider 工厂\n\n| 符号 | 签名 |\n|---|---|\n| `createProvider` | `(protocol, cfg) => IProvider` |\n| `OpenAICompatibleProvider` / `AnthropicProvider` / `GeminiProvider` | 直接拿到类，需要时使用 |\n\n### 预设\n\n`BUILTIN_PRESETS`、`CHAT_PRESETS`、`IMAGE_PRESETS`、`findPreset(id)`，以及单独的常量：`TOKENPLAN_PRESET`、`OPENAI_PRESET`、`ANTHROPIC_PRESET`、`GEMINI_PRESET`、`DEEPSEEK_PRESET`、`KIMI_PRESET`、`QWEN_PRESET`、`DOUBAO_PRESET`、`ZHIPU_PRESET`、`OPENAI_IMAGE_PRESET`、`HUNYUAN_IMAGE_PRESET`、`HUNYUAN_IMAGE_TC3_PRESET`、`ZHIPU_IMAGE_PRESET`。\n\n### 能力工具\n\n| 符号 | 签名 |\n|---|---|\n| `modelHasCapability` | `(model, cap) => boolean` |\n| `isMultimodal` | `(model) => boolean` |\n| `pickModel` | `(providers, { prefer, providerId? }) => PickedModel \\| null` |\n\n### SSE 原语（进阶）\n\n| 符号 | 签名 |\n|---|---|\n| `parseSse` | `(stream: ReadableStream<Uint8Array>, signal: AbortSignal) => AsyncGenerator<SseEvent>` |\n\n### `s-aiproviders/image-gen`（仅 Node 子路径）\n\n| 符号 | 签名 |\n|---|---|\n| `generateImage` | `(input: ImageGenInput) => Promise<ImageGenResult>` |\n| `generateImageStandalone` | 向后兼容别名 |\n| `ImageGenInput` | `{ protocol?; baseURL; apiKey; model; prompt; size; outputDir?; fileBaseName? }` |\n| `ImageGenResult` | `{ filePath; size; model; latencyMs; revisedPrompt? }` |\n\n### CLI —— `s-aiproviders`\n\n```\ns-aiproviders <command> [flags]\n\nCommands:\n  chat              流式对话（输出文本）\n  image             生成一张图片（PNG）\n  list-presets      列出内置 provider\n  help              帮助\n\n通用 flags：\n  --provider <id>     预设 id\n  --apikey  <key>     API key（腾讯：写成 \"SecretId:SecretKey\"）\n  --baseurl <url>     覆盖预设默认 base URL\n  --model   <id>      上游模型 id\n  --json              机器可读 JSON 输出\n  --verbose           将解析过程打到 stderr\n\nchat 专属：\n  --prompt <text> | --promptfile <path> | （或通过 stdin 管道）\n  --system <text>\n  --temperature <n>\n  --maxtokens <n>\n\nimage 专属：\n  --prompt <text>     必填\n  --size <WxH>        默认 1024x1024\n  --output <dir>      默认 ./output\n  --name <base>       输出文件名（不含扩展名）\n\nlist-presets：\n  --kind chat|image\n  --json\n```\n\n退出码：`0` 成功 · `1` 运行时错误（网络 / HTTP / 腾讯超时）· `2` 用户错误（参数错、缺 apiKey）。\n\n完整接入示例（Electron 主进程、多 provider 回退、Node 服务模式）见 [`references/integration-as-library.md`](./references/integration-as-library.md)。\n\n## 架构\n\n```\n┌──────────────────────────────────────────────────────────┐\n│                       消费侧                              │\n│  ┌────────────────┐  ┌────────────────┐  ┌─────────────┐ │\n│  │   库 API       │  │   CLI (npx)    │  │ Agent Skill │ │\n│  │  (TS import)   │  │ s-aiproviders  │  │  SKILL.md   │ │\n│  └────────┬───────┘  └────────┬───────┘  └──────┬──────┘ │\n└───────────┼───────────────────┼─────────────────┼────────┘\n            │                   │                 │\n            ▼                   ▼                 ▼\n┌──────────────────────────────────────────────────────────┐\n│                     s-aiproviders                         │\n│                                                           │\n│  createProvider() ──► IProvider                          │\n│      │                  │                                 │\n│      ├─ openai-compatible.ts   (chat/completions + SSE)  │\n│      ├─ anthropic.ts           (messages + SSE)          │\n│      ├─ gemini.ts              (streamGenerateContent)   │\n│      └─ image-gen/             (DALL·E + TC3 异步签名)    │\n│                                                           │\n│  presets/{chat,image}.ts  · capabilities.ts (pickModel)  │\n│  sse.ts（共用 SSE 解析器，把 fetch ReadableStream 解成事件） │\n│  cli/（chat | image | list-presets，包了一层上面的 API）   │\n└──────────────────────────────────────────────────────────┘\n            │\n            ▼\n   fetch + ReadableStream + AbortSignal\n   （无 SDK 依赖、无 axios、无 OpenAI client）\n```\n\n除了显式的 Node-only `image-gen` 子路径与 CLI 运行时之外，核心**绝不向外伸手** —— 不读环境变量、不动 `fs`、不碰全局变量。这让 chat 主入口在浏览器、Electron 渲染进程、Cloudflare Workers、Vercel Edge 等任何提供 WHATWG `fetch` 的运行时之间真正同构。\n\n## 仓库结构\n\n```\nS-AIProviders/\n├── src/\n│   ├── index.ts                   # 公开库入口（浏览器安全）\n│   ├── types.ts                   # IProvider · ChatRequest · ChatChunk · ProviderPreset\n│   ├── sse.ts                     # 共用 SSE 解析器\n│   ├── openai-compatible.ts       # 协议实现\n│   ├── anthropic.ts               # 协议实现\n│   ├── gemini.ts                  # 协议实现\n│   ├── factory.ts                 # createProvider(protocol, cfg)\n│   ├── capabilities.ts            # pickModel · modelHasCapability · isMultimodal\n│   ├── presets/{chat,image,index}.ts   # 9 chat + 4 image 预设\n│   ├── image-gen/index.ts         # generateImage（仅 Node 子路径）\n│   └── cli/                       # s-aiproviders CLI（bin 入口）\n│       ├── main.ts                #   入口 — chat/image/list-presets\n│       ├── args.ts                #   零依赖 argv 解析\n│       ├── config.ts              #   CLI > EXTEND.md > env > preset 解析器\n│       └── commands/{chat,image,list}.ts\n├── references/                    # 给 AI Agent Skill 阅读的文档\n│   ├── config/extend-md-schema.md\n│   ├── config/first-time-setup.md\n│   ├── integration-as-library.md\n│   └── providers.md\n├── scripts/\n│   └── postbuild-shebang.cjs      # 构建后恢复 #!/usr/bin/env node 并 chmod +x\n├── SKILL.md                       # 给 AI Agent 读的 Skill manifest\n├── INSTALL.md                     # 终端用户安装指南\n├── README.md / README.zh.md\n├── package.json\n├── tsconfig.json\n└── dist/                          # 构建产物（已 gitignore）\n```\n\n## 从源码构建\n\n### 环境要求\n\n- Node.js **≥ 18.17**（使用原生 `fetch` 与 `ReadableStream`）\n- pnpm **≥ 9**（npm / yarn 也行）\n\n### 常用命令\n\n```bash\npnpm install         # 安装依赖\npnpm build           # tsc → dist/  +  恢复 dist/cli/main.js 的 shebang\npnpm typecheck       # 严格模式 tsc --noEmit\npnpm clean           # 删除 dist/\n\n# 开发模式跑 CLI（不需 build，走 tsx）\npnpm chat  -- --provider tokenplan --prompt \"hi\"\npnpm image -- --provider openai-image --prompt \"a cat\" --size 1024x1024\npnpm list  -- --kind chat\n\n# 跑构建后的 CLI\nnode dist/cli/main.js list-presets\n\n# 本地打包并在沙箱里验证 tarball\npnpm pack --pack-destination /tmp/saip\nmkdir -p /tmp/saip-test && cd /tmp/saip-test && npm init -y >/dev/null\nnpm install /tmp/saip/s-aiproviders-*.tgz\nnpx s-aiproviders list-presets\n```\n\n### 发布到 npm\n\n`prepublishOnly` 会自动跑 `clean` + `build`。`publishConfig.registry` 锁定到 `https://registry.npmjs.org/`，`access: public`，所以无论你本地 `~/.npmrc` 用哪个镜像源，发布永远走 npm 官方公网源。\n\n```bash\npnpm build\nnpm publish        # 会读 package.json 里的 publishConfig\n```\n\n## 与同类项目对比\n\n| | s-aiproviders | `openai` SDK | `@anthropic-ai/sdk` | `langchain` | `ai`（Vercel） |\n|---|---|---|---|---|---|\n| 多 provider | ✅ 13 个预设、`pickModel` 回退 | ❌ 仅 OpenAI | ❌ 仅 Claude | ✅ 但很重 | ✅ |\n| 运行时依赖 | **0** | 多个 | 多个 | 非常多 | 多个 |\n| 浏览器安全核心 | ✅ | 部分 | 部分 | ❌ | ✅ |\n| 文生图 | ✅ OpenAI + 腾讯 TC3 | ✅ 仅 OpenAI | ❌ | ✅ | ❌ |\n| 流式模型 | `AsyncIterable<ChatChunk>` | 混合（事件 / stream） | iterator | callback / stream | hooks（React 中心） |\n| 自带 CLI | ✅ 同次安装 | ❌ | ❌ | ❌ | ❌ |\n| 自带 AI Agent Skill | ✅ | ❌ | ❌ | ❌ | ❌ |\n| TypeScript 类型 | 内置 | 内置 | 内置 | 内置 | 内置 |\n| 安装体积 | ~60 KB tarball | 数百 KB | 数百 KB | MB 级 | 数百 KB |\n\ns-aiproviders 刻意保持精简 —— 它不追求替代 `langchain` 的 agent 循环或 `ai` 的 React Server Component 绑定，而是这些框架**底层**那一层：一个可移植的 wire-format 适配层，你既可以直接用，也可以在它之上再叠重型框架。\n\n## 常见问题\n\n**Q：支持工具调用 / function calling 吗？**\n暂不支持。当前 `chat()` 仅文本。工具调用在路线图上，会以 `ChatRequest.tools` 字段引入。如有需求请提 issue。\n\n**Q：为啥不直接用各家官方 SDK？**\n官方 SDK 各自带 HTTP client、各自的重试中间件、各自的流式抽象。三家叠在一个进程里既臃肿，错误形态又对不齐。s-aiproviders 用一份很小的 ESM 模块统一三家，全部走平台原生 `fetch`。\n\n**Q：能接入目录里没有的 provider 吗？**\n可以。如果它走 OpenAI `/chat/completions` wire 格式，直接 `--provider openai --baseurl https://your-gateway/v1`。如果协议完全不同，实现 `IProvider` 并加到工厂里就行，欢迎提 PR。\n\n**Q：image-gen 模块浏览器能用吗？**\n不能。它用 `node:crypto` 做 TC3 签名、用 `node:fs` 落盘。包的主入口故意不再导出它；要用请走 `s-aiproviders/image-gen` 子路径。Vite/Webpack 打浏览器包时，把它 externalise 掉，或干脆别在渲染端 import 这条子路径。\n\n**Q：取消怎么用？**\n往 `chat()` 传 `AbortSignal`。signal 会被透传给 `fetch` 和 SSE reader，所以 abort 会同时切断 HTTP 连接和迭代器。CLI 把 `SIGINT`（Ctrl-C）映射到同一个 signal。\n\n**Q：API key 安全怎么保障？**\n库本身**不**记录、不持久化、不上报 key —— 只在请求 `Authorization` 头里发给上游。CLI 按上文优先级链装载 key；建议把项目级 `EXTEND.md` 加到 `.gitignore`。无任何 telemetry。\n\n**Q：能在 Cloudflare Workers / Vercel Edge / Bun / Deno 上跑吗？**\nchat 主入口在任何提供 WHATWG `fetch` + `ReadableStream` + `AbortSignal` 的运行时上都能跑。image-gen 需要 Node 兼容的 `node:crypto` + `node:fs`，**不能**在 edge 运行时上跑。\n\n## 版本策略\n\ns-aiproviders 遵循语义化版本，从 `0.1.x` 起步。在 `0.x` 期间，minor 版本之间可能引入 break change，CHANGELOG 会显式标注。`1.0.0` 之后会锁定公开 API。\n\n## 许可证\n\n[MIT](./LICENSE) © s-aiproviders contributors。\n","readmeFilename":"README.zh.md"}