npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ai-llm-service

v1.0.4

Published

A lightweight isomorphic LLM service class with streaming, retry, fallback, and multi-provider support

Downloads

615

Readme

AiLLMService

npm version License: MIT TypeScript

轻量同构 LLM 调用服务类,基于 fetch 同时运行在浏览器、Node.js、小程序、React Native 端,统一各厂商 API 差异。

✨ 特性

  • 🎯 类型安全 - 完整的 TypeScript 类型推导,编译时捕获错误
  • 🔀 多模型/多 Provider - 统一接口适配 OpenAI、Anthropic、DeepSeek、Moonshot 等厂商
  • 🤖 Anthropic 原生支持 - 适配 Claude Messages API,自动转换消息/工具/流式格式
  • 🔄 自动主备切换 - 主模型失败自动降级到备用模型,支持多级 fallback 链
  • 🔁 重试机制 - 可配置重试次数、延迟、退避策略(固定/指数),自动识别可重试错误(429/5xx)
  • 🌊 流式封装 - onStream 回调自动拼接 content / reasoning / toolCalls,无需手动处理 SSE
  • 🛠 Tools Use - 声明式 tools 定义 + onToolCall 回调,支持自动多轮 tool 调用,内置循环保护
  • 📦 JSON 容错解析 - 集成 json-repair-js + best-effort-json-parser,三层级降级解析 LLM 输出的破损/不完整 JSON
  • 📝 HTML/Markdown 提取 - responseFormat: 'html' / 'markdown' 自动去掉 code block 包裹
  • 🧠 思维链支持 - enableThinking 自动映射为各厂商格式
  • ⚙️ 默认参数 - defaultParams 避免每次重复传参
  • 🪵 调试日志 - 内置 Logger 中间件,debug: true 一键开启请求/响应/错误日志
  • 🔌 中间件机制 - 请求/响应管道,可插入自定义逻辑(鉴权、限流、缓存等)
  • 超时控制 - 全局 + 单次请求超时,基于 AbortController
  • 🛑 请求中止 - 传入 signal: AbortSignal 随时中止请求
  • 📊 用量追踪 - 返回 usage(prompt/completion/total tokens)
  • 🔄 同构兼容 - 基于 fetch 实现,可运行在浏览器、Node.js、小程序、Cloudflare Worker 等环境

📦 安装

npm install ai-llm-service

Node.js 版本要求

| Node.js | 说明 | | ------- | ------------------------------------------- | | ≥ 18 | 内置 fetch,开箱即用 | | 16 ~ 17 | 需安装 node-fetch 并通过 fetch 配置传入 |

// Node.js 16/17 兼容方式
import fetch from 'node-fetch'

const llm = new AiLLMService({
  providers: [...],
  defaultModel: 'gpt-4o',
  fetch,
})

🚀 快速开始

import { AiLLMService } from 'ai-llm-service'

const llm = new AiLLMService({
  providers: [
    {
      name: 'openai',
      baseURL: 'https://api.openai.com/v1',
      apiKey: 'sk-xxx',
      models: ['gpt-4o', 'gpt-4o-mini'],
    },
    {
      name: 'anthropic',
      providerType: 'anthropic',
      baseURL: 'https://api.anthropic.com',
      apiKey: 'sk-ant-xxx',
      models: ['claude-sonnet-4-5'],
    },
    {
      name: 'deepseek',
      baseURL: 'https://api.deepseek.com/v1',
      apiKey: 'sk-xxx',
      models: ['deepseek-chat', 'deepseek-reasoner'],
    },
  ],
  defaultModel: 'gpt-4o',
  debug: true,
})

基础调用

const res = await llm.completion({
  messages: [{ role: 'user', content: '你好' }],
})

// res.content  → "你好!有什么可以帮你的?"
// res.model    → "gpt-4o"
// res.provider → "openai"
// res.usage    → { prompt_tokens: 10, completion_tokens: 15, total_tokens: 25 }

流式调用

const res = await llm.completion({
  messages: [{ role: 'user', content: '写一首诗' }],
  stream: true,
  onStream: (chunk, { delta, reasoningDelta, content, reasoning, toolCalls, done }) => {
    process.stdout.write(delta) // 增量输出
  },
})

// 流结束后 res.content 是拼接好的完整内容
// res.reasoning  → 思维链完整内容
// res.toolCalls  → 累计的 tool 调用

JSON 解析

// responseFormat: 'json' — 自动从 content 中解析 JSON(支持 markdown code block)
const res = await llm.completion({
  messages: [{ role: 'user', content: '返回一个包含 name 和 age 的 JSON' }],
  responseFormat: 'json',
})
// res.content → { name: "张三", age: 25 }  (object)

// responseFormat: 'json_object' — 同时发送 API 的 response_format + 自动解析
const res = await llm.completion({
  messages: [...],
  responseFormat: 'json_object',
})

HTML / Markdown 提取

// responseFormat: 'html' — 自动去掉 ```html ... ``` 包裹
const res = await llm.completion({
  messages: [{ role: 'user', content: '生成一个登录页面的 HTML' }],
  responseFormat: 'html',
})
// res.content → "<div class=\"login\">...</div>"  (纯 HTML,无 code block)

// responseFormat: 'markdown' — 自动去掉 ```markdown / ```md ... ``` 包裹
const res = await llm.completion({
  messages: [{ role: 'user', content: '生成一份 Markdown 格式的周报' }],
  responseFormat: 'markdown',
})

Tools Use

const res = await llm.completion({
  messages: [{ role: 'user', content: '北京今天天气怎么样?' }],
  tools: [
    {
      type: 'function',
      function: {
        name: 'get_weather',
        description: '获取指定城市的天气信息',
        parameters: {
          type: 'object',
          properties: {
            city: { type: 'string', description: '城市名称' },
          },
          required: ['city'],
        },
      },
    },
  ],
  onToolCall: async toolCall => {
    const { city } = toolCall.function.args // 自动解析
    const result = await getWeather(city)
    return JSON.stringify(result) // 自动回填到 messages 继续对话
  },
})

默认参数

const llm = new AiLLMService({
  providers: [...],
  defaultModel: 'gpt-4o',
  defaultParams: {
    temperature: 0.7,
    maxTokens: 4096,
    responseFormat: 'json',
  },
})

// 自动继承 defaultParams,无需重复传参
const res = await llm.completion({
  messages: [{ role: 'user', content: '你好' }],
})

// 单次调用可覆盖
const res2 = await llm.completion({
  messages: [...],
  temperature: 0.1,
})

主备切换

const llm = new AiLLMService({
  providers: [
    { name: 'openai', baseURL: '...', apiKey: 'sk-xxx', models: ['gpt-4o'] },
    {
      name: 'anthropic',
      providerType: 'anthropic',
      baseURL: '...',
      apiKey: 'sk-ant-xxx',
      models: ['claude-sonnet-4-5'],
    },
    { name: 'deepseek', baseURL: '...', apiKey: 'sk-xxx', models: ['deepseek-chat'] },
  ],
  defaultModel: 'gpt-4o',
  fallbacks: {
    'gpt-4o': ['claude-sonnet-4-5', 'deepseek-chat'], // 多级降级
  },
})

重试机制

const llm = new AiLLMService({
  providers: [...],
  defaultModel: 'gpt-4o',
  retry: {
    maxRetries: 3,           // 最大重试次数
    delay: 1000,             // 初始延迟 ms
    backoff: 'exponential',  // 'fixed' | 'exponential'
  },
})

请求中止

const controller = new AbortController()
llm.completion({ messages: [...], signal: controller.signal })
controller.abort() // 随时中止

自定义中间件

llm.use({
  name: 'auth',
  beforeRequest: (ctx, request) => {
    request.headers['X-Custom-Auth'] = 'token'
    return request
  },
  afterResponse: (ctx, result) => {
    console.log('cost:', result.usage)
    return result
  },
})

自定义 Provider

import { BaseProvider, registerProvider } from 'ai-llm-service'

class MyProvider extends BaseProvider {
  buildRequest(params, model) {
    /* ... */
  }
  parseResponse(data) {
    /* ... */
  }
  parseStreamChunk(data) {
    /* ... */
  }
}

registerProvider('my-provider', MyProvider)

const llm = new AiLLMService({
  providers: [{ name: 'my-provider', baseURL: '...', apiKey: 'xxx' }],
  defaultModel: 'my-model',
})

📖 API

new AiLLMService(config)

| 参数 | 类型 | 必填 | 说明 | | --------------- | -------------------------- | ---- | ------------------------------ | | providers | ProviderConfig[] | ✅ | Provider 列表 | | defaultModel | string | ✅ | 默认模型 | | fallbacks | Record<string, string[]> | | 模型主备映射 | | defaultParams | DefaultableParams | | 默认参数,每次 completion 合并 | | retry | Partial<RetryConfig> | | 重试配置 | | timeout | number | | 全局超时 ms,默认 60000 | | maxToolRounds | number | | Tool 调用最大轮数,默认 20 | | debug | boolean | | 调试模式,默认 false | | logger | Logger | | 自定义 logger | | fetch | typeof fetch | | 自定义 fetch(Node < 18 兼容) |

ProviderConfig

| 参数 | 类型 | 必填 | 说明 | | -------------- | ------------------------- | ---- | ----------------------------- | | name | string | ✅ | Provider 名称标识 | | baseURL | string | ✅ | API 基础地址 | | apiKey | string | ✅ | API Key | | providerType | 'openai' \| 'anthropic' | | API 格式类型,默认 'openai' | | models | string[] | | 支持的模型列表,用于自动路由 | | headers | Record<string, string> | | 额外自定义请求头 | | fetch | typeof fetch | | 自定义 fetch |

CompletionResult

| 字段 | 类型 | 说明 | | ----------- | ------------------ | ----------------------- | | content | string \| object | 文本内容或解析后的 JSON | | reasoning | string | 思维链完整内容 | | toolCalls | ToolCall[] | Tool 调用列表 | | usage | Usage | 用量统计 | | model | string | 实际使用的模型 | | provider | string | 实际使用的 provider |

StreamParsedData(onStream 第二个参数)

| 字段 | 类型 | 说明 | | ---------------- | ------------ | ---------------------- | | delta | string | 本次增量文本 | | reasoningDelta | string | 本次增量思维链 | | content | string | 累计文本(拼接好的) | | reasoning | string | 累计思维链(拼接好的) | | toolCalls | ToolCall[] | 累计 tool 调用 | | done | boolean | 是否结束 |

📚 文档

🔧 构建

npm run build       # father 构建 esm + cjs
npm run typecheck   # TypeScript 类型检查
npm test            # 运行集成测试

📄 License

MIT © lizhooh