From 96ecbd026e82fa1db0b51dea76c6ad852757c178 Mon Sep 17 00:00:00 2001 From: abearxiong Date: Mon, 2 Feb 2026 02:17:55 +0800 Subject: [PATCH] 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. --- .gitignore | 36 +++++++ AGENTS.md | 6 ++ CLAUDE.md | 106 +++++++++++++++++++ README.md | 15 +++ bun.lock | 72 +++++++++++++ index.ts | 96 +++++++++++++++++ opencode.json | 104 ++++++++++++++++++ package.json | 20 ++++ pnpm-lock.yaml | 216 ++++++++++++++++++++++++++++++++++++++ src/index.ts | 16 +++ src/test-bailian-debug.ts | 33 ++++++ src/test-bailian.ts | 23 ++++ src/test-cnb-debug.ts | 46 ++++++++ src/test-cnb.ts | 28 +++++ src/test-kevisual.ts | 26 +++++ tsconfig.json | 29 +++++ 16 files changed, 872 insertions(+) create mode 100644 .gitignore create mode 100644 AGENTS.md create mode 100644 CLAUDE.md create mode 100644 README.md create mode 100644 bun.lock create mode 100644 index.ts create mode 100644 opencode.json create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 src/index.ts create mode 100644 src/test-bailian-debug.ts create mode 100644 src/test-bailian.ts create mode 100644 src/test-cnb-debug.ts create mode 100644 src/test-cnb.ts create mode 100644 src/test-kevisual.ts create mode 100644 tsconfig.json 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 + } +}