Appearance
Node.js / TypeScript 接入 Ulnvault:一份配置,三种落法
结论先行:Node 侧还是官方
openai包,baseURL指向https://ai.ulnit.com/v1、apiKey读环境变量。分情况的是运行形态:Next.js 走服务端路由,脚本串行最稳,常驻服务必须自己加限流与退避。
初始化:ESM 与 TypeScript
ts
// npm i openai
import OpenAI from "openai";
export const vault = new OpenAI({
apiKey: process.env.ULNVAULT_KEY,
baseURL: "https://ai.ulnit.com/v1",
timeout: 60_000,
maxRetries: 2,
});
const r = await vault.chat.completions.create({
model: "openai/gpt-5.6-luna-pro",
messages: [{ role: "user", content: "把这行日志归类并给一句结论" }],
});
console.log(r.choices[0].message.content, r.usage?.total_tokens);model 在类型里是字符串,站内模型 ID 直接写,换模型只改一个字符串。
流式响应
ts
const s = await vault.chat.completions.create({
model: "x-ai/grok-4.6",
messages: [{ role: "user", content: "生成一段商品卖点" }],
stream: true,
});
for await (const part of s) {
process.stdout.write(part.choices[0]?.delta?.content ?? "");
}Web 服务里要把它包成 ReadableStream 或 SSE 再吐给浏览器,别让浏览器端持有 Key。流式与非流式的计费口径相同,只影响首字延迟。
三种落法对照
| 运行形态 | Key 放在哪 | 落地要点 |
|---|---|---|
| Next.js 应用 | 服务端路由的环境变量 | Route Handler 转发,浏览器只收到文本流 |
| 一次性脚本 | 本机 .env / shell 变量 | 串行最稳,重跑要做幂等 |
| 常驻后端服务 | 密钥管理服务注入 | 信号量 + 队列 + 退避三件套 |
错误分类与退避
ts
async function withBackoff(fn: () => Promise<any>, tries = 4) {
let wait = 500;
for (let i = 0; i < tries; i++) {
try {
return await fn();
} catch (e: any) {
const code = e?.status ?? 0;
if (code !== 429 && code < 500) throw e; // 参数类错误重试无用
if (i === tries - 1) throw e;
await new Promise((r) => setTimeout(r, wait + Math.random() * 200));
wait *= 2;
}
}
}401/403 是密钥问题,改配置才继续;400 是请求体问题,重试纯浪费;只有 429 与 5xx 值得退避。退避要带随机抖动,否则多实例会同时恢复、同时再被限流。队列设计见 /use-cases/high-concurrency.md。
成本对照(每百万 token,采集 2026-10-08)
| model 字段 | 输入单价 | 输出单价 | 上下文长度 | 在 Node 服务里的位置 |
|---|---|---|---|---|
| openai/gpt-5.6-luna-pro | $0.22 | $1.32 | 105 万 | 默认档:接口文案、结构化抽取 |
| x-ai/grok-4.6 | $2.2 | $6.6 | 50 万 | 需要判断力的问答与推理 |
| qwen/qwen3.8-flash | $0.165 | $0.517 | 100 万 | 高 QPS 的短分类与打标 |
| z-ai/glm-5.3-flash | $0.165 | $0.55 | 105 万 | 长日志与长文档摘要 |
每月 100 万 token 输入走 qwen3.8-flash 约 $0.165,走 gpt-5.6-luna-pro 约 $0.22。把每条请求的 usage 记进日志,月底按路由聚合就知道钱花在哪。
常见问题
ESM 项目里 import 报错怎么办?
openai 同时提供 ESM 与 CJS 入口;Node 18 以上直接 import OpenAI from "openai",CommonJS 项目改回 require。
客户端放函数内还是模块外?
放模块作用域让热启动实例复用它,冷启动重建的开销可忽略。
超时设多少合适?
普通接口 60 秒够用,长文生成 120~300 秒;平台函数超时要大于 SDK 超时,否则只能看到平台级报错。
怎么防止某条路由把额度吃光?
按路由设档位并分别统计用量;给高消耗路由单独设并发上限,异常流量先降档再限流。
价格与采集时间?
单价按 2026-10-08 实时核验;站内价 = OpenRouter 挂牌 ×1.1。