commit 96ecbd026e82fa1db0b51dea76c6ad852757c178 Author: abearxiong Date: Mon Feb 2 02:17:55 2026 +0800 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. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..31725a1 --- /dev/null +++ b/.gitignore @@ -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 \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..2861d37 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,6 @@ +这是一个 bun 项目,测试 ai-sdk 功能。包是通过以下命令安装的: + +```bash +bun add ai +bun add @ai-sdk/openai +``` \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..764c1dd --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,106 @@ + +Default to using Bun instead of Node.js. + +- Use `bun ` instead of `node ` or `ts-node ` +- Use `bun test` instead of `jest` or `vitest` +- Use `bun build ` instead of `webpack` or `esbuild` +- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install` +- Use `bun run + + +``` + +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

Hello, world!

; +} + +root.render(); +``` + +Then, run index.ts + +```sh +bun --hot ./index.ts +``` + +For more information, read the Bun API docs in `node_modules/bun-types/docs/**.mdx`. diff --git a/README.md b/README.md new file mode 100644 index 0000000..ee4953b --- /dev/null +++ b/README.md @@ -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. diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..b8ac273 --- /dev/null +++ b/bun.lock @@ -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=="], + } +} diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..3361010 --- /dev/null +++ b/index.ts @@ -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; + options: { + baseURL?: string; + apiKey: string; + }; +} + +interface OpenCodeConfig { + provider: Record; +} + +// 解析环境变量占位符 {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); diff --git a/opencode.json b/opencode.json new file mode 100644 index 0000000..77a066c --- /dev/null +++ b/opencode.json @@ -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}" + } + } + } +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..7a6dea3 --- /dev/null +++ b/package.json @@ -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" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..c20e092 --- /dev/null +++ b/pnpm-lock.yaml @@ -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: {} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..d1a44aa --- /dev/null +++ b/src/index.ts @@ -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); diff --git a/src/test-bailian-debug.ts b/src/test-bailian-debug.ts new file mode 100644 index 0000000..02ae190 --- /dev/null +++ b/src/test-bailian-debug.ts @@ -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)}`); + } +} diff --git a/src/test-bailian.ts b/src/test-bailian.ts new file mode 100644 index 0000000..4857ff8 --- /dev/null +++ b/src/test-bailian.ts @@ -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); diff --git a/src/test-cnb-debug.ts b/src/test-cnb-debug.ts new file mode 100644 index 0000000..b6b5550 --- /dev/null +++ b/src/test-cnb-debug.ts @@ -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); +} diff --git a/src/test-cnb.ts b/src/test-cnb.ts new file mode 100644 index 0000000..03269ba --- /dev/null +++ b/src/test-cnb.ts @@ -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); diff --git a/src/test-kevisual.ts b/src/test-kevisual.ts new file mode 100644 index 0000000..6550894 --- /dev/null +++ b/src/test-kevisual.ts @@ -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}`); + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..bfa0fea --- /dev/null +++ b/tsconfig.json @@ -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 + } +}