feat: Initialize test-ai-sdk project with Bun

- Add README.md with installation and usage instructions.
- Create bun.lock and pnpm-lock.yaml for dependency management.
- Implement main functionality in index.ts to test AI providers.
- Add opencode.json configuration for various AI providers.
- Create package.json to define project dependencies and scripts.
- Add TypeScript configuration in tsconfig.json for project setup.
- Implement test scripts for different AI providers in src directory.
This commit is contained in:
2026-02-02 02:17:55 +08:00
commit 96ecbd026e
16 changed files with 872 additions and 0 deletions

36
.gitignore vendored Normal file
View File

@@ -0,0 +1,36 @@
# dependencies (bun install)
node_modules
# output
out
dist
*.tgz
# code coverage
coverage
*.lcov
# logs
logs
_.log
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# caches
.eslintcache
.cache
*.tsbuildinfo
# IntelliJ based IDEs
.idea
# Finder (MacOS) folder config
.DS_Store
!.env*example

6
AGENTS.md Normal file
View File

@@ -0,0 +1,6 @@
这是一个 bun 项目,测试 ai-sdk 功能。包是通过以下命令安装的:
```bash
bun add ai
bun add @ai-sdk/openai
```

106
CLAUDE.md Normal file
View File

@@ -0,0 +1,106 @@
Default to using Bun instead of Node.js.
- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
- Use `bun test` instead of `jest` or `vitest`
- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
- Use `bunx <package> <command>` instead of `npx <package> <command>`
- Bun automatically loads .env, so don't use dotenv.
## APIs
- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
- `Bun.redis` for Redis. Don't use `ioredis`.
- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
- `WebSocket` is built-in. Don't use `ws`.
- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
- Bun.$`ls` instead of execa.
## Testing
Use `bun test` to run tests.
```ts#index.test.ts
import { test, expect } from "bun:test";
test("hello world", () => {
expect(1).toBe(1);
});
```
## Frontend
Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
Server:
```ts#index.ts
import index from "./index.html"
Bun.serve({
routes: {
"/": index,
"/api/users/:id": {
GET: (req) => {
return new Response(JSON.stringify({ id: req.params.id }));
},
},
},
// optional websocket support
websocket: {
open: (ws) => {
ws.send("Hello, world!");
},
message: (ws, message) => {
ws.send(message);
},
close: (ws) => {
// handle close
}
},
development: {
hmr: true,
console: true,
}
})
```
HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
```html#index.html
<html>
<body>
<h1>Hello, world!</h1>
<script type="module" src="./frontend.tsx"></script>
</body>
</html>
```
With the following `frontend.tsx`:
```tsx#frontend.tsx
import React from "react";
import { createRoot } from "react-dom/client";
// import .css files directly and it works
import './index.css';
const root = createRoot(document.body);
export default function Frontend() {
return <h1>Hello, world!</h1>;
}
root.render(<Frontend />);
```
Then, run index.ts
```sh
bun --hot ./index.ts
```
For more information, read the Bun API docs in `node_modules/bun-types/docs/**.mdx`.

15
README.md Normal file
View File

@@ -0,0 +1,15 @@
# test-ai-sdk
To install dependencies:
```bash
bun install
```
To run:
```bash
bun run index.ts
```
This project was created using `bun init` in bun v1.3.6. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.

72
bun.lock Normal file
View File

@@ -0,0 +1,72 @@
{
"lockfileVersion": 1,
"configVersion": 1,
"workspaces": {
"": {
"name": "test-ai-sdk",
"dependencies": {
"@ai-sdk/anthropic": "^3.0.35",
"@ai-sdk/openai": "^3.0.25",
"@ai-sdk/openai-compatible": "^2.0.26",
"@kevisual/ai": "^0.0.24",
"ai": "^6.0.67",
},
"devDependencies": {
"@types/bun": "latest",
"dotenv": "^17.2.3",
},
"peerDependencies": {
"typescript": "^5",
},
},
},
"packages": {
"@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.35", "https://registry.npmmirror.com/@ai-sdk/anthropic/-/anthropic-3.0.35.tgz", { "dependencies": { "@ai-sdk/provider": "3.0.7", "@ai-sdk/provider-utils": "4.0.13" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Y3g/5uVj621XSB9lGF7WrD7qR+orhV5xpaYkRF8kfj2j4W7e7BBGIvxcdsCf85FjJbc6tKQdNTZ84ZEqT3Y5TQ=="],
"@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.32", "https://registry.npmmirror.com/@ai-sdk/gateway/-/gateway-3.0.32.tgz", { "dependencies": { "@ai-sdk/provider": "3.0.7", "@ai-sdk/provider-utils": "4.0.13", "@vercel/oidc": "3.1.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-7clZRr07P9rpur39t1RrbIe7x8jmwnwUWI8tZs+BvAfX3NFgdSVGGIaT7bTz2pb08jmLXzTSDbrOTqAQ7uBkBQ=="],
"@ai-sdk/openai": ["@ai-sdk/openai@3.0.25", "https://registry.npmmirror.com/@ai-sdk/openai/-/openai-3.0.25.tgz", { "dependencies": { "@ai-sdk/provider": "3.0.7", "@ai-sdk/provider-utils": "4.0.13" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-DsaN46R98+D1W3lU3fKuPU3ofacboLaHlkAwxJPgJ8eup1AJHmPK1N1y10eJJbJcF6iby8Tf/vanoZxc9JPUfw=="],
"@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.26", "https://registry.npmmirror.com/@ai-sdk/openai-compatible/-/openai-compatible-2.0.26.tgz", { "dependencies": { "@ai-sdk/provider": "3.0.7", "@ai-sdk/provider-utils": "4.0.13" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-l6jdFjI1C2eDAEm7oo+dnRn0oG1EkcyqfbEZ7ozT0TnYrah6amX2JkftYMP1GRzNtAeCB3WNN8XspXdmi6ZNlQ=="],
"@ai-sdk/provider": ["@ai-sdk/provider@3.0.7", "https://registry.npmmirror.com/@ai-sdk/provider/-/provider-3.0.7.tgz", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-VkPLrutM6VdA924/mG8OS+5frbVTcu6e046D2bgDo00tehBANR1QBJ/mPcZ9tXMFOsVcm6SQArOregxePzTFPw=="],
"@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.13", "https://registry.npmmirror.com/@ai-sdk/provider-utils/-/provider-utils-4.0.13.tgz", { "dependencies": { "@ai-sdk/provider": "3.0.7", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-HHG72BN4d+OWTcq2NwTxOm/2qvk1duYsnhCDtsbYwn/h/4zeqURu1S0+Cn0nY2Ysq9a9HGKvrYuMn9bgFhR2Og=="],
"@kevisual/ai": ["@kevisual/ai@0.0.24", "https://registry.npmmirror.com/@kevisual/ai/-/ai-0.0.24.tgz", { "dependencies": { "@kevisual/logger": "^0.0.4", "@kevisual/permission": "^0.0.3", "@kevisual/query": "^0.0.38" } }, "sha512-7jvZk1/L//VIClK7usuNgN4ZA9Etgbooka1Sj5quE/0UywR+NNnwqXVZ89Y1fBhI1TkhauDsdJBAtcQ7r/vbVw=="],
"@kevisual/logger": ["@kevisual/logger@0.0.4", "https://registry.npmmirror.com/@kevisual/logger/-/logger-0.0.4.tgz", {}, "sha512-+fpr92eokSxoGOW1SIRl/27lPuO+zyY+feR5o2Q4YCNlAdt2x64NwC/w8r/3NEC5QenLgd4K0azyKTI2mHbARw=="],
"@kevisual/permission": ["@kevisual/permission@0.0.3", "https://registry.npmmirror.com/@kevisual/permission/-/permission-0.0.3.tgz", {}, "sha512-8JsA/5O5Ax/z+M+MYpFYdlioHE6jNmWMuFSokBWYs9CCAHNiSKMR01YLkoVDoPvncfH/Y8F5K/IEXRCbptuMNA=="],
"@kevisual/query": ["@kevisual/query@0.0.38", "https://registry.npmmirror.com/@kevisual/query/-/query-0.0.38.tgz", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-bfvbSodsZyMfwY+1T2SvDeOCKsT/AaIxlVe0+B1R/fNhlg2MDq2CP0L9HKiFkEm+OXrvXcYDMKPUituVUM5J6Q=="],
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "https://registry.npmmirror.com/@opentelemetry/api/-/api-1.9.0.tgz", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
"@standard-schema/spec": ["@standard-schema/spec@1.1.0", "https://registry.npmmirror.com/@standard-schema/spec/-/spec-1.1.0.tgz", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"@types/bun": ["@types/bun@1.3.8", "https://registry.npmmirror.com/@types/bun/-/bun-1.3.8.tgz", { "dependencies": { "bun-types": "1.3.8" } }, "sha512-3LvWJ2q5GerAXYxO2mffLTqOzEu5qnhEAlh48Vnu8WQfnmSwbgagjGZV6BoHKJztENYEDn6QmVd949W4uESRJA=="],
"@types/node": ["@types/node@25.2.0", "https://registry.npmmirror.com/@types/node/-/node-25.2.0.tgz", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w=="],
"@vercel/oidc": ["@vercel/oidc@3.1.0", "https://registry.npmmirror.com/@vercel/oidc/-/oidc-3.1.0.tgz", {}, "sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w=="],
"ai": ["ai@6.0.67", "https://registry.npmmirror.com/ai/-/ai-6.0.67.tgz", { "dependencies": { "@ai-sdk/gateway": "3.0.32", "@ai-sdk/provider": "3.0.7", "@ai-sdk/provider-utils": "4.0.13", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-xBnTcByHCj3OcG6V8G1s6zvSEqK0Bdiu+IEXYcpGrve1iGFFRgcrKeZtr/WAW/7gupnSvBbDF24BEv1OOfqi1g=="],
"bun-types": ["bun-types@1.3.8", "https://registry.npmmirror.com/bun-types/-/bun-types-1.3.8.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-fL99nxdOWvV4LqjmC+8Q9kW3M4QTtTR1eePs94v5ctGqU8OeceWrSUaRw3JYb7tU3FkMIAjkueehrHPPPGKi5Q=="],
"dotenv": ["dotenv@17.2.3", "https://registry.npmmirror.com/dotenv/-/dotenv-17.2.3.tgz", {}, "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w=="],
"eventsource-parser": ["eventsource-parser@3.0.6", "https://registry.npmmirror.com/eventsource-parser/-/eventsource-parser-3.0.6.tgz", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="],
"json-schema": ["json-schema@0.4.0", "https://registry.npmmirror.com/json-schema/-/json-schema-0.4.0.tgz", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="],
"tslib": ["tslib@2.8.1", "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"typescript": ["typescript@5.9.3", "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"undici-types": ["undici-types@7.16.0", "https://registry.npmmirror.com/undici-types/-/undici-types-7.16.0.tgz", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
"zod": ["zod@4.3.6", "https://registry.npmmirror.com/zod/-/zod-4.3.6.tgz", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
}
}

96
index.ts Normal file
View File

@@ -0,0 +1,96 @@
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
import { createAnthropic } from '@ai-sdk/anthropic';
import { generateText } from 'ai';
import 'dotenv/config';
interface ProviderConfig {
npm: string;
name: string;
models: Record<string, { name: string }>;
options: {
baseURL?: string;
apiKey: string;
};
}
interface OpenCodeConfig {
provider: Record<string, ProviderConfig>;
}
// 解析环境变量占位符 {env:VAR_NAME}
function resolveEnvVars(value: string): string {
return value.replace(/{env:([^}]+)}/g, (_, varName) => {
const envValue = process.env[varName];
if (!envValue) {
throw new Error(`Environment variable ${varName} is not set`);
}
return envValue;
});
}
async function testProvider(providerName: string, config: ProviderConfig) {
console.log(`\nTesting provider: ${config.name}`);
const apiKey = resolveEnvVars(config.options.apiKey);
const baseURL = config.options.baseURL ? resolveEnvVars(config.options.baseURL) : undefined;
let provider;
console.log('baseURL:', baseURL);
console.log('apiKey:', apiKey ? '[REDACTED]' : '[MISSING]');
if (config.npm === '@ai-sdk/openai-compatible') {
provider = createOpenAICompatible({
baseURL: baseURL!,
name: providerName,
apiKey,
});
} else if (config.npm === '@ai-sdk/anthropic') {
provider = createAnthropic({
baseURL: baseURL,
apiKey,
});
} else {
throw new Error(`Unsupported npm package: ${config.npm}`);
}
const modelKeys = Object.keys(config.models);
if (modelKeys.length === 0) {
throw new Error('No models configured for this provider');
}
const modelName = modelKeys[0];
try {
const result = await generateText({
model: provider(modelName),
prompt: 'Say hello in one sentence.',
});
console.log(`Model: ${modelName}`);
console.log(`Response: ${result.text}`);
return true;
} catch (error) {
console.error(`Error: ${error}`);
return false;
}
}
async function main() {
const config: OpenCodeConfig = await Bun.file('opencode.json').json();
console.log('Loaded opencode.json configuration');
console.log(`Found ${Object.keys(config.provider).length} provider(s)`);
const results = await Promise.all(
Object.entries(config.provider).map(([name, config]) =>
testProvider(name, config).catch((error) => {
console.error(`Provider ${name} failed: ${error}`);
return false;
})
)
);
console.log('\n=== Summary ===');
Object.keys(config.provider).forEach((name, i) => {
console.log(`${name}: ${results[i] ? '✓ Success' : '✗ Failed'}`);
});
}
main().catch(console.error);

104
opencode.json Normal file
View File

@@ -0,0 +1,104 @@
{
"$schema": "https://opencode.ai/config.json",
"autoshare": false,
"share": "disabled",
"autoupdate": true,
"permission": "allow",
"watcher": {
"ignore": [
"node_modules/**",
"dist/**",
".git/**"
]
},
"plugin": [],
"provider": {
"custom-zhipu": {
"npm": "@ai-sdk/openai-compatible",
"name": "国内智谱AI",
"models": {
"GLM-4.7": {
"name": "GLM-4.7"
}
},
"options": {
"baseURL": "https://open.bigmodel.cn/api/coding/paas/v4",
"apiKey": "{env:ZHIPU_API_KEY}"
}
},
"custom-minimax": {
"npm": "@ai-sdk/anthropic",
"name": "国内MiniMax",
"models": {
"MiniMax-M2.1": {
"name": "MiniMax-M2.1"
}
},
"options": {
"baseURL": "https://api.minimaxi.com/anthropic/v1",
"apiKey": "{env:MINIMAX_API_KEY}"
}
},
"custom-doubao": {
"npm": "@ai-sdk/openai-compatible",
"name": "国内火山AI",
"models": {
"ark-code-latest": {
"name": "ark-code-latest"
}
},
"options": {
"baseURL": "https://ark.cn-beijing.volces.com/api/coding/v3",
"apiKey": "{env:VOLCENGINE_API_KEY}"
}
},
"custom-bailian": {
"npm": "@ai-sdk/openai-compatible",
"name": "国内百炼AI开发订阅 xx",
"models": {
"qwen3-coder-plus": {
"name": "qwen3-coder-plus"
}
},
"options": {
"baseURL": "https://coding.dashscope.aliyuncs.com/v1",
"apiKey": "{env:BAILIAN_CODE_API_KEY}"
}
},
"custom-cnb": {
"npm": "@ai-sdk/openai-compatible",
"name": "国内CNB开发订阅 xx",
"models": {
"hunyuan": {
"name": "hunyuan-a13b"
}
},
"options": {
"baseURL": "https://api.cnb.cool/{env:CNB_REPO_SLUG}/-/ai/",
"apiKey": "{env:CNB_API_KEY}"
}
},
"custom-kevisual": {
"npm": "@ai-sdk/openai-compatible",
"name": "自用newapi搭建的开发集合订阅 xx",
"models": {
"qwen3-coder-plus": {
"name": "qwen3-coder-plus"
},
"ark-code-latest": {
"name": "ark-code-latest"
},
"GLM-4.7": {
"name": "GLM-4.7"
},
"MiniMax-M2.1": {
"name": "MiniMax-M2.1"
}
},
"options": {
"baseURL": "https://newapi.kevisual.cn/v1",
"apiKey": "{env:KEVISUAL_NEW_API_KEY}"
}
}
}
}

20
package.json Normal file
View File

@@ -0,0 +1,20 @@
{
"name": "test-ai-sdk",
"module": "index.ts",
"type": "module",
"private": true,
"devDependencies": {
"@types/bun": "latest",
"dotenv": "^17.2.3"
},
"peerDependencies": {
"typescript": "^5"
},
"dependencies": {
"@ai-sdk/anthropic": "^3.0.35",
"@ai-sdk/openai": "^3.0.25",
"@ai-sdk/openai-compatible": "^2.0.26",
"@kevisual/ai": "^0.0.24",
"ai": "^6.0.67"
}
}

216
pnpm-lock.yaml generated Normal file
View File

@@ -0,0 +1,216 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
dependencies:
'@ai-sdk/anthropic':
specifier: ^3.0.35
version: 3.0.35(zod@4.3.6)
'@ai-sdk/openai':
specifier: ^3.0.25
version: 3.0.25(zod@4.3.6)
'@ai-sdk/openai-compatible':
specifier: ^2.0.26
version: 2.0.26(zod@4.3.6)
'@kevisual/ai':
specifier: ^0.0.24
version: 0.0.24
ai:
specifier: ^6.0.67
version: 6.0.67(zod@4.3.6)
devDependencies:
'@types/bun':
specifier: latest
version: 1.3.8
dotenv:
specifier: ^17.2.3
version: 17.2.3
packages:
'@ai-sdk/anthropic@3.0.35':
resolution: {integrity: sha512-Y3g/5uVj621XSB9lGF7WrD7qR+orhV5xpaYkRF8kfj2j4W7e7BBGIvxcdsCf85FjJbc6tKQdNTZ84ZEqT3Y5TQ==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.25.76 || ^4.1.8
'@ai-sdk/gateway@3.0.32':
resolution: {integrity: sha512-7clZRr07P9rpur39t1RrbIe7x8jmwnwUWI8tZs+BvAfX3NFgdSVGGIaT7bTz2pb08jmLXzTSDbrOTqAQ7uBkBQ==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.25.76 || ^4.1.8
'@ai-sdk/openai-compatible@2.0.26':
resolution: {integrity: sha512-l6jdFjI1C2eDAEm7oo+dnRn0oG1EkcyqfbEZ7ozT0TnYrah6amX2JkftYMP1GRzNtAeCB3WNN8XspXdmi6ZNlQ==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.25.76 || ^4.1.8
'@ai-sdk/openai@3.0.25':
resolution: {integrity: sha512-DsaN46R98+D1W3lU3fKuPU3ofacboLaHlkAwxJPgJ8eup1AJHmPK1N1y10eJJbJcF6iby8Tf/vanoZxc9JPUfw==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.25.76 || ^4.1.8
'@ai-sdk/provider-utils@4.0.13':
resolution: {integrity: sha512-HHG72BN4d+OWTcq2NwTxOm/2qvk1duYsnhCDtsbYwn/h/4zeqURu1S0+Cn0nY2Ysq9a9HGKvrYuMn9bgFhR2Og==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.25.76 || ^4.1.8
'@ai-sdk/provider@3.0.7':
resolution: {integrity: sha512-VkPLrutM6VdA924/mG8OS+5frbVTcu6e046D2bgDo00tehBANR1QBJ/mPcZ9tXMFOsVcm6SQArOregxePzTFPw==}
engines: {node: '>=18'}
'@kevisual/ai@0.0.24':
resolution: {integrity: sha512-7jvZk1/L//VIClK7usuNgN4ZA9Etgbooka1Sj5quE/0UywR+NNnwqXVZ89Y1fBhI1TkhauDsdJBAtcQ7r/vbVw==}
'@kevisual/logger@0.0.4':
resolution: {integrity: sha512-+fpr92eokSxoGOW1SIRl/27lPuO+zyY+feR5o2Q4YCNlAdt2x64NwC/w8r/3NEC5QenLgd4K0azyKTI2mHbARw==}
'@kevisual/permission@0.0.3':
resolution: {integrity: sha512-8JsA/5O5Ax/z+M+MYpFYdlioHE6jNmWMuFSokBWYs9CCAHNiSKMR01YLkoVDoPvncfH/Y8F5K/IEXRCbptuMNA==}
'@kevisual/query@0.0.38':
resolution: {integrity: sha512-bfvbSodsZyMfwY+1T2SvDeOCKsT/AaIxlVe0+B1R/fNhlg2MDq2CP0L9HKiFkEm+OXrvXcYDMKPUituVUM5J6Q==}
'@opentelemetry/api@1.9.0':
resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==}
engines: {node: '>=8.0.0'}
'@standard-schema/spec@1.1.0':
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
'@types/bun@1.3.8':
resolution: {integrity: sha512-3LvWJ2q5GerAXYxO2mffLTqOzEu5qnhEAlh48Vnu8WQfnmSwbgagjGZV6BoHKJztENYEDn6QmVd949W4uESRJA==}
'@types/node@25.2.0':
resolution: {integrity: sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w==}
'@vercel/oidc@3.1.0':
resolution: {integrity: sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w==}
engines: {node: '>= 20'}
ai@6.0.67:
resolution: {integrity: sha512-xBnTcByHCj3OcG6V8G1s6zvSEqK0Bdiu+IEXYcpGrve1iGFFRgcrKeZtr/WAW/7gupnSvBbDF24BEv1OOfqi1g==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.25.76 || ^4.1.8
bun-types@1.3.8:
resolution: {integrity: sha512-fL99nxdOWvV4LqjmC+8Q9kW3M4QTtTR1eePs94v5ctGqU8OeceWrSUaRw3JYb7tU3FkMIAjkueehrHPPPGKi5Q==}
dotenv@17.2.3:
resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==}
engines: {node: '>=12'}
eventsource-parser@3.0.6:
resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==}
engines: {node: '>=18.0.0'}
json-schema@0.4.0:
resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
undici-types@7.16.0:
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
zod@4.3.6:
resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==}
snapshots:
'@ai-sdk/anthropic@3.0.35(zod@4.3.6)':
dependencies:
'@ai-sdk/provider': 3.0.7
'@ai-sdk/provider-utils': 4.0.13(zod@4.3.6)
zod: 4.3.6
'@ai-sdk/gateway@3.0.32(zod@4.3.6)':
dependencies:
'@ai-sdk/provider': 3.0.7
'@ai-sdk/provider-utils': 4.0.13(zod@4.3.6)
'@vercel/oidc': 3.1.0
zod: 4.3.6
'@ai-sdk/openai-compatible@2.0.26(zod@4.3.6)':
dependencies:
'@ai-sdk/provider': 3.0.7
'@ai-sdk/provider-utils': 4.0.13(zod@4.3.6)
zod: 4.3.6
'@ai-sdk/openai@3.0.25(zod@4.3.6)':
dependencies:
'@ai-sdk/provider': 3.0.7
'@ai-sdk/provider-utils': 4.0.13(zod@4.3.6)
zod: 4.3.6
'@ai-sdk/provider-utils@4.0.13(zod@4.3.6)':
dependencies:
'@ai-sdk/provider': 3.0.7
'@standard-schema/spec': 1.1.0
eventsource-parser: 3.0.6
zod: 4.3.6
'@ai-sdk/provider@3.0.7':
dependencies:
json-schema: 0.4.0
'@kevisual/ai@0.0.24':
dependencies:
'@kevisual/logger': 0.0.4
'@kevisual/permission': 0.0.3
'@kevisual/query': 0.0.38
'@kevisual/logger@0.0.4': {}
'@kevisual/permission@0.0.3': {}
'@kevisual/query@0.0.38':
dependencies:
tslib: 2.8.1
'@opentelemetry/api@1.9.0': {}
'@standard-schema/spec@1.1.0': {}
'@types/bun@1.3.8':
dependencies:
bun-types: 1.3.8
'@types/node@25.2.0':
dependencies:
undici-types: 7.16.0
'@vercel/oidc@3.1.0': {}
ai@6.0.67(zod@4.3.6):
dependencies:
'@ai-sdk/gateway': 3.0.32(zod@4.3.6)
'@ai-sdk/provider': 3.0.7
'@ai-sdk/provider-utils': 4.0.13(zod@4.3.6)
'@opentelemetry/api': 1.9.0
zod: 4.3.6
bun-types@1.3.8:
dependencies:
'@types/node': 25.2.0
dotenv@17.2.3: {}
eventsource-parser@3.0.6: {}
json-schema@0.4.0: {}
tslib@2.8.1: {}
undici-types@7.16.0: {}
zod@4.3.6: {}

16
src/index.ts Normal file
View File

@@ -0,0 +1,16 @@
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
import { generateText } from 'ai';
// 使用自定义 doubao provider
const doubao = createOpenAICompatible({
baseURL: 'https://ark.cn-beijing.volces.com/api/coding/v3',
name: 'custom-doubao',
apiKey: process.env.DOUBAO_API_KEY!,
});
const { text } = await generateText({
model: doubao('ark-code-latest'),
prompt: 'What is an agent?',
});
console.log(text);

33
src/test-bailian-debug.ts Normal file
View File

@@ -0,0 +1,33 @@
import 'dotenv/config';
const endpoint = 'https://coding.dashscope.aliyuncs.com/apps/anthropic/messages';
// 测试不同的认证方式
const authMethods = [
{ name: 'x-api-key', headers: { 'x-api-key': process.env.BAILIAN_CODE_API_KEY! } },
{ name: 'Authorization Bearer', headers: { 'Authorization': `Bearer ${process.env.BAILIAN_CODE_API_KEY!}` } },
{ name: 'Authorization Bearer sk-', headers: { 'Authorization': `Bearer sk-${process.env.BAILIAN_CODE_API_KEY!}` } },
{ name: 'Authorization sk-', headers: { 'Authorization': `sk-${process.env.BAILIAN_CODE_API_KEY!}` } },
];
for (const { name, headers } of authMethods) {
console.log(`\nTesting: ${name}`);
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...headers,
},
body: JSON.stringify({
model: 'qwen3-coder-plus',
max_tokens: 100,
messages: [{ role: 'user', content: 'Hello' }],
}),
});
console.log(`Status: ${response.status}`);
const text = await response.text();
if (text) {
console.log(`Response: ${text.substring(0, 300)}`);
}
}

23
src/test-bailian.ts Normal file
View File

@@ -0,0 +1,23 @@
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
import { createAnthropic } from '@ai-sdk/anthropic';
import { generateText } from 'ai';
import 'dotenv/config';
// 尝试不同的端点配置
const bailian = createOpenAICompatible({
baseURL: 'https://coding.dashscope.aliyuncs.com/v1',
name: 'custom-bailian',
apiKey: process.env.BAILIAN_CODE_API_KEY!,
});
// const bailian = createAnthropic({
// baseURL: 'https://coding.dashscope.aliyuncs.com/apps/anthropic/messages',
// name: 'custom-bailian',
// apiKey: process.env.BAILIAN_CODE_API_KEY!,
// });
const { text } = await generateText({
model: bailian('qwen3-coder-plus'),
prompt: 'What is an agent?',
});
console.log('Response:', text);

46
src/test-cnb-debug.ts Normal file
View File

@@ -0,0 +1,46 @@
import 'dotenv/config';
function resolveEnvVars(value: string): string {
return value.replace(/{env:([^}]+)}/g, (_, varName) => {
const envValue = process.env[varName];
if (!envValue) {
throw new Error(`Environment variable ${varName} is not set`);
}
return envValue;
});
}
const endpoint = 'https://cnb.cool/kevisual/cnb/-/ai/chat/completions';
const apiKey = process.env.CNB_API_KEY!;
console.log('Endpoint:', endpoint);
console.log('API Key:', apiKey ? 'Set' : 'Not set');
console.log('API Key length:', apiKey.length);
console.log('API Key prefix:', apiKey.substring(0, 10) + '...');
// 尝试不同的认证头
const authHeaders = [
{ name: 'Authorization Bearer', headers: { 'Authorization': `Bearer ${apiKey}` } },
{ name: 'Authorization (no Bearer)', headers: { 'Authorization': apiKey } },
{ name: 'x-api-key', headers: { 'x-api-key': apiKey } },
{ name: 'api-key', headers: { 'api-key': apiKey } },
];
for (const { name, headers } of authHeaders) {
console.log(`\n=== Testing: ${name} ===`);
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...headers,
},
body: JSON.stringify({
model: 'hunyuan-a13b',
messages: [{ role: 'user', content: '你好' }],
}),
});
console.log(`Status: ${response.status}`);
const text = await response.text();
console.log('Response:', text);
}

28
src/test-cnb.ts Normal file
View File

@@ -0,0 +1,28 @@
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
import { generateText } from 'ai';
import 'dotenv/config';
// 解析环境变量占位符
function resolveEnvVars(value: string): string {
return value.replace(/{env:([^}]+)}/g, (_, varName) => {
const envValue = process.env[varName];
if (!envValue) {
throw new Error(`Environment variable ${varName} is not set`);
}
return envValue;
});
}
// 使用 custom-cnb provider
const cnb = createOpenAICompatible({
baseURL: resolveEnvVars('{env:CNB_API_ENDPOINT}/{env:CNB_REPO_SLUG}/-/ai/'),
name: 'custom-cnb',
apiKey: process.env.CNB_API_KEY!,
});
const { text } = await generateText({
model: cnb('hunyuan-a13b'),
prompt: 'What is an agent?',
});
console.log('Response:', text);

26
src/test-kevisual.ts Normal file
View File

@@ -0,0 +1,26 @@
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
import { generateText } from 'ai';
import 'dotenv/config';
// 使用 custom-kevisual provider (聚合了多个模型)
const kevisual = createOpenAICompatible({
baseURL: 'https://newapi.kevisual.cn/v1',
name: 'custom-kevisual',
apiKey: process.env.KEVISUAL_NEW_API_KEY!,
});
// 测试所有可用模型
const models = ['qwen3-coder-plus', 'ark-code-latest', 'GLM-4.7', 'MiniMax-M2.1'];
for (const model of models) {
console.log(`\n=== Testing model: ${model} ===`);
try {
const { text } = await generateText({
model: kevisual(model),
prompt: 'Say hello in one sentence.',
});
console.log(`Response: ${text}`);
} catch (error) {
console.error(`Error: ${error}`);
}
}

29
tsconfig.json Normal file
View File

@@ -0,0 +1,29 @@
{
"compilerOptions": {
// Environment setup & latest features
"lib": ["ESNext"],
"target": "ESNext",
"module": "Preserve",
"moduleDetection": "force",
"jsx": "react-jsx",
"allowJs": true,
// Bundler mode
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"noEmit": true,
// Best practices
"strict": true,
"skipLibCheck": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
// Some stricter flags (disabled by default)
"noUnusedLocals": false,
"noUnusedParameters": false,
"noPropertyAccessFromIndexSignature": false
}
}