From 03fa169a81c8572f6fcd1a93dc09b8e2fb21d25d Mon Sep 17 00:00:00 2001 From: cfngc4594 Date: Tue, 28 Apr 2026 17:58:50 +0800 Subject: [PATCH] refactor(ai): update AI provider configuration and replace OpenAI references with new AI model setup --- .env.example | 7 ++--- README.md | 9 ++++--- bun.lock | 47 ++------------------------------- compose.cn.yml | 5 ++-- compose.local.cn.yml | 5 ++-- compose.local.yml | 6 ++--- compose.yml | 5 ++-- package.json | 3 +-- src/app/actions/ai-improve.ts | 10 +++---- src/app/actions/ai-testcase.ts | 10 +++---- src/app/actions/analyze-code.ts | 4 +-- src/app/actions/analyze.ts | 6 +---- src/app/api/chat/route.ts | 4 +-- src/lib/ai.ts | 32 ++++++++++++++++------ 14 files changed, 61 insertions(+), 92 deletions(-) diff --git a/.env.example b/.env.example index 8878a33..13c1d72 100644 --- a/.env.example +++ b/.env.example @@ -19,9 +19,10 @@ AUTH_GITHUB_SECRET="your_github_client_secret" # The base URL for authentication callbacks, typically the root URL of your application AUTH_URL="http://localhost:3000" -# OpenAI API key and base URL for AI services -OPENAI_API_KEY="your_openai_api_key" -OPENAI_BASE_URL="your_openai_base_url_if_custom" +# AI provider configuration +AI_API_KEY="your_ai_api_key" +AI_BASE_URL="your_ai_base_url_if_custom" +AI_MODEL="your_ai_model_id" # Docker Remote Access Configuration DOCKER_HOST_MODE="remote_or_blank" diff --git a/README.md b/README.md index f984904..da6d758 100644 --- a/README.md +++ b/README.md @@ -123,11 +123,12 @@ bun install AUTH_URL="http://localhost:3000" # Replace with your production URL if deployed ``` - - **OpenAI API Configuration** (Optional): - If you use OpenAI-based features, provide your API key and custom endpoint (if applicable): + - **AI Provider Configuration**: + If you use AI features, provide your API key, model, and custom endpoint: ```sh - OPENAI_API_KEY="your_openai_api_key" # Required for AI features - OPENAI_BASE_URL="your_openai_base_url_if_custom" # Optional, for self-hosted proxies + AI_API_KEY="your_ai_api_key" # Required for AI features + AI_BASE_URL="your_ai_base_url_if_custom" # Required, for OpenAI-compatible endpoints + AI_MODEL="your_ai_model_id" # Required ``` - **Docker Remote Access Configuration** (Optional): diff --git a/bun.lock b/bun.lock index e25f1e3..bc6107f 100644 --- a/bun.lock +++ b/bun.lock @@ -4,8 +4,7 @@ "": { "name": "monaco-editor-lsp-next", "dependencies": { - "@ai-sdk/deepseek": "^0.2.14", - "@ai-sdk/openai": "^1.3.0", + "@ai-sdk/openai-compatible": "0.2.14", "@ai-sdk/react": "^1.2.0", "@auth/prisma-adapter": "^2.8.0", "@fontsource/fira-code": "^5.1.1", @@ -111,11 +110,7 @@ }, }, "packages": { - "@ai-sdk/deepseek": ["@ai-sdk/deepseek@0.2.14", "https://registry.npmmirror.com/@ai-sdk/deepseek/-/deepseek-0.2.14.tgz", { "dependencies": { "@ai-sdk/openai-compatible": "0.2.14", "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8" }, "peerDependencies": { "zod": "^3.0.0" } }, "sha512-TISD1FzBWuQkHEHoVustoJILV33ZNgfYxeTkq1xU2vHEZuWTGZV7/IlXixyFsfqDCdVgrbLeIABk5FuCw7niLg=="], - - "@ai-sdk/openai": ["@ai-sdk/openai@1.3.22", "https://registry.npmmirror.com/@ai-sdk/openai/-/openai-1.3.22.tgz", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8" }, "peerDependencies": { "zod": "^3.0.0" } }, "sha512-QwA+2EkG0QyjVR+7h6FE7iOu2ivNqAVMm9UJZkVxxTk5OIq5fFJDTEI/zICEMuHImTTXR2JjsL6EirJ28Jc4cw=="], - - "@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@0.2.14", "https://registry.npmmirror.com/@ai-sdk/openai-compatible/-/openai-compatible-0.2.14.tgz", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8" }, "peerDependencies": { "zod": "^3.0.0" } }, "sha512-icjObfMCHKSIbywijaoLdZ1nSnuRnWgMEMLgwoxPJgxsUHMx0aVORnsLUid4SPtdhHI3X2masrt6iaEQLvOSFw=="], + "@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@0.2.14", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8" }, "peerDependencies": { "zod": "^3.0.0" } }, "sha512-icjObfMCHKSIbywijaoLdZ1nSnuRnWgMEMLgwoxPJgxsUHMx0aVORnsLUid4SPtdhHI3X2masrt6iaEQLvOSFw=="], "@ai-sdk/provider": ["@ai-sdk/provider@1.1.3", "https://registry.npmmirror.com/@ai-sdk/provider/-/provider-1.1.3.tgz", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg=="], @@ -1973,28 +1968,6 @@ "@radix-ui/react-alert-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.2", "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.2.2.tgz", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-y7TBO4xN4Y94FvcWIOIh18fM4R1A8S4q1jhoz4PNzOoHsFcN8pogcFmZrTYAm4F9VRUrWP/Mw7xSKybIeRI+CQ=="], - "@radix-ui/react-avatar/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], - - "@radix-ui/react-collapsible/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], - - "@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.2", "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.2.2.tgz", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-y7TBO4xN4Y94FvcWIOIh18fM4R1A8S4q1jhoz4PNzOoHsFcN8pogcFmZrTYAm4F9VRUrWP/Mw7xSKybIeRI+CQ=="], - - "@radix-ui/react-dialog/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], - - "@radix-ui/react-dismissable-layer/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], - - "@radix-ui/react-dropdown-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], - - "@radix-ui/react-focus-scope/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], - - "@radix-ui/react-menu/@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], - - "@radix-ui/react-menu/@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.7", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ=="], - - "@radix-ui/react-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], - - "@radix-ui/react-menu/@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.10", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q=="], - "@radix-ui/react-popover/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.9", "https://registry.npmmirror.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.9.tgz", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.2", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-way197PiTvNp+WBP7svMJasHl+vibhWGQDb6Mgf5mhEWJkgb85z7Lfl9TUdkqpWsf8GRNmoopx9ZxCyDzmgRMQ=="], "@radix-ui/react-popover/@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.6", "https://registry.npmmirror.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.6.tgz", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.2", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-r9zpYNUQY+2jWHWZGyddQLL9YHkM/XvSFHVcWs7bdVuxMAnCwTAuy6Pf47Z4nw7dYcUou1vg/VgjjrrH03VeBw=="], @@ -2003,10 +1976,6 @@ "@radix-ui/react-popover/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.2", "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.2.2.tgz", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-y7TBO4xN4Y94FvcWIOIh18fM4R1A8S4q1jhoz4PNzOoHsFcN8pogcFmZrTYAm4F9VRUrWP/Mw7xSKybIeRI+CQ=="], - "@radix-ui/react-portal/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], - - "@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.2", "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.2.2.tgz", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-y7TBO4xN4Y94FvcWIOIh18fM4R1A8S4q1jhoz4PNzOoHsFcN8pogcFmZrTYAm4F9VRUrWP/Mw7xSKybIeRI+CQ=="], - "@radix-ui/react-select/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.9", "https://registry.npmmirror.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.9.tgz", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.2", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-way197PiTvNp+WBP7svMJasHl+vibhWGQDb6Mgf5mhEWJkgb85z7Lfl9TUdkqpWsf8GRNmoopx9ZxCyDzmgRMQ=="], "@radix-ui/react-select/@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.6", "https://registry.npmmirror.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.6.tgz", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.2", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-r9zpYNUQY+2jWHWZGyddQLL9YHkM/XvSFHVcWs7bdVuxMAnCwTAuy6Pf47Z4nw7dYcUou1vg/VgjjrrH03VeBw=="], @@ -2015,14 +1984,6 @@ "@radix-ui/react-select/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.2", "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.2.2.tgz", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-y7TBO4xN4Y94FvcWIOIh18fM4R1A8S4q1jhoz4PNzOoHsFcN8pogcFmZrTYAm4F9VRUrWP/Mw7xSKybIeRI+CQ=="], - "@radix-ui/react-separator/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], - - "@radix-ui/react-tooltip/@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.7", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ=="], - - "@radix-ui/react-tooltip/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], - - "@radix-ui/react-tooltip/@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.3", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug=="], - "@types/ssh2/@types/node": ["@types/node@18.19.100", "https://registry.npmmirror.com/@types/node/-/node-18.19.100.tgz", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-ojmMP8SZBKprc3qGrGk8Ujpo80AXkrP7G2tOT4VWr5jlr5DHjsJF+emXJz+Wm0glmy4Js62oKMdZZ6B9Y+tEcA=="], "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], @@ -2097,10 +2058,6 @@ "@radix-ui/react-alert-dialog/@radix-ui/react-dialog/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.8", "https://registry.npmmirror.com/@radix-ui/react-portal/-/react-portal-1.1.8.tgz", { "dependencies": { "@radix-ui/react-primitive": "2.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-hQsTUIn7p7fxCPvao/q6wpbxmCwgLrlz+nOrJgC+RwfZqWY/WN+UMqkXzrtKbPrF82P43eCTl3ekeKuyAQbFeg=="], - "@radix-ui/react-menu/@radix-ui/react-popper/@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="], - - "@radix-ui/react-tooltip/@radix-ui/react-popper/@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="], - "@types/ssh2/@types/node/undici-types": ["undici-types@5.26.5", "https://registry.npmmirror.com/undici-types/-/undici-types-5.26.5.tgz", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], diff --git a/compose.cn.yml b/compose.cn.yml index f9db057..608b9dd 100644 --- a/compose.cn.yml +++ b/compose.cn.yml @@ -37,8 +37,9 @@ services: - AUTH_GITHUB_ID=${AUTH_GITHUB_ID} - AUTH_GITHUB_SECRET=${AUTH_GITHUB_SECRET} - AUTH_URL=${AUTH_URL} - - OPENAI_API_KEY=${OPENAI_API_KEY} - - OPENAI_BASE_URL=${OPENAI_BASE_URL} # Optional + - AI_API_KEY=${AI_API_KEY} + - AI_BASE_URL=${AI_BASE_URL} + - AI_MODEL=${AI_MODEL} # Remote Docker configuration (activate only for remote Docker): # - DOCKER_HOST_MODE=${DOCKER_HOST_MODE} # Options: "remote" or leave empty for local # - DOCKER_REMOTE_PROTOCOL=${DOCKER_REMOTE_PROTOCOL} # Example: "https" or "http" or "ssh" diff --git a/compose.local.cn.yml b/compose.local.cn.yml index 5b6ccda..af8e62c 100644 --- a/compose.local.cn.yml +++ b/compose.local.cn.yml @@ -40,8 +40,9 @@ services: - AUTH_GITHUB_ID=${AUTH_GITHUB_ID} - AUTH_GITHUB_SECRET=${AUTH_GITHUB_SECRET} - AUTH_URL=${AUTH_URL} - - OPENAI_API_KEY=${OPENAI_API_KEY} - - OPENAI_BASE_URL=${OPENAI_BASE_URL} # Optional + - AI_API_KEY=${AI_API_KEY} + - AI_BASE_URL=${AI_BASE_URL} + - AI_MODEL=${AI_MODEL} # Remote Docker configuration (activate only for remote Docker): # - DOCKER_HOST_MODE=${DOCKER_HOST_MODE} # Options: "remote" or leave empty for local # - DOCKER_REMOTE_PROTOCOL=${DOCKER_REMOTE_PROTOCOL} # Example: "https" or "http" or "ssh" diff --git a/compose.local.yml b/compose.local.yml index d4aabf1..c99468d 100644 --- a/compose.local.yml +++ b/compose.local.yml @@ -40,9 +40,9 @@ services: - AUTH_GITHUB_ID=${AUTH_GITHUB_ID} - AUTH_GITHUB_SECRET=${AUTH_GITHUB_SECRET} - AUTH_URL=${AUTH_URL} - - OPENAI_API_KEY=${OPENAI_API_KEY} - - OPENAI_BASE_URL=${OPENAI_BASE_URL} # Optional - - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY} + - AI_API_KEY=${AI_API_KEY} + - AI_BASE_URL=${AI_BASE_URL} + - AI_MODEL=${AI_MODEL} # Remote Docker configuration (activate only for remote Docker): # - DOCKER_HOST_MODE=${DOCKER_HOST_MODE} # Options: "remote" or leave empty for local # - DOCKER_REMOTE_PROTOCOL=${DOCKER_REMOTE_PROTOCOL} # Example: "https" or "http" or "ssh" diff --git a/compose.yml b/compose.yml index 6cb453e..5dcb2fc 100644 --- a/compose.yml +++ b/compose.yml @@ -37,8 +37,9 @@ services: - AUTH_GITHUB_ID=${AUTH_GITHUB_ID} - AUTH_GITHUB_SECRET=${AUTH_GITHUB_SECRET} - AUTH_URL=${AUTH_URL} - - OPENAI_API_KEY=${OPENAI_API_KEY} - - OPENAI_BASE_URL=${OPENAI_BASE_URL} # Optional + - AI_API_KEY=${AI_API_KEY} + - AI_BASE_URL=${AI_BASE_URL} + - AI_MODEL=${AI_MODEL} # Remote Docker configuration (activate only for remote Docker): # - DOCKER_HOST_MODE=${DOCKER_HOST_MODE} # Options: "remote" or leave empty for local # - DOCKER_REMOTE_PROTOCOL=${DOCKER_REMOTE_PROTOCOL} # Example: "https" or "http" or "ssh" diff --git a/package.json b/package.json index b417046..599eb7b 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,7 @@ "seed": "tsx prisma/seed.ts" }, "dependencies": { - "@ai-sdk/deepseek": "^0.2.14", - "@ai-sdk/openai": "^1.3.0", + "@ai-sdk/openai-compatible": "0.2.14", "@ai-sdk/react": "^1.2.0", "@auth/prisma-adapter": "^2.8.0", "@fontsource/fira-code": "^5.1.1", diff --git a/src/app/actions/ai-improve.ts b/src/app/actions/ai-improve.ts index b8a0901..0db505b 100644 --- a/src/app/actions/ai-improve.ts +++ b/src/app/actions/ai-improve.ts @@ -6,7 +6,7 @@ import { OptimizeCodeOutputSchema, } from "@/types/ai-improve"; import prisma from "@/lib/prisma"; -import { deepseek } from "@/lib/ai"; +import { model } from "@/lib/ai"; import { CoreMessage, generateText } from "ai"; /** @@ -17,8 +17,6 @@ import { CoreMessage, generateText } from "ai"; export const optimizeCode = async ( input: OptimizeCodeInput ): Promise => { - const model = deepseek("deepseek-chat"); - // 获取题目详情(如果提供了problemId) let problemDetails = ""; let templateDetails = ""; @@ -134,7 +132,7 @@ Format: `; console.log("Prompt:", prompt); - // 发送请求给OpenAI + // 发送请求给 AI provider const messages: CoreMessage[] = [{ role: "user", content: prompt }]; let text; try { @@ -144,8 +142,8 @@ Format: }); text = response.text; } catch (error) { - console.error("Error generating text with OpenAI:", error); - throw new Error("Failed to generate response from OpenAI"); + console.error("Error generating text with AI provider:", error); + throw new Error("Failed to generate response from AI provider"); } // 解析LLM响应 diff --git a/src/app/actions/ai-testcase.ts b/src/app/actions/ai-testcase.ts index ddb6d4c..fffe01f 100644 --- a/src/app/actions/ai-testcase.ts +++ b/src/app/actions/ai-testcase.ts @@ -6,7 +6,7 @@ import { AITestCaseOutputSchema, } from "@/types/ai-testcase"; -import { deepseek } from "@/lib/ai"; +import { model } from "@/lib/ai"; import { CoreMessage, generateText } from "ai"; import prisma from "@/lib/prisma"; @@ -18,8 +18,6 @@ import prisma from "@/lib/prisma"; export const generateAITestcase = async ( input: AITestCaseInput ): Promise => { - const model = deepseek("deepseek-chat"); - let problemDetails = ""; if (input.problemId) { @@ -119,7 +117,7 @@ Respond **ONLY** with this JSON structure. `; - // 发送请求给OpenAI + // 发送请求给 AI provider const messages: CoreMessage[] = [{ role: "user", content: prompt }]; let text; try { @@ -129,8 +127,8 @@ Respond **ONLY** with this JSON structure. }); text = response.text; } catch (error) { - console.error("Error generating text with OpenAI:", error); - throw new Error("Failed to generate response from OpenAI"); + console.error("Error generating text with AI provider:", error); + throw new Error("Failed to generate response from AI provider"); } // 解析LLM响应 diff --git a/src/app/actions/analyze-code.ts b/src/app/actions/analyze-code.ts index b312bdf..c3ecb27 100644 --- a/src/app/actions/analyze-code.ts +++ b/src/app/actions/analyze-code.ts @@ -3,7 +3,7 @@ import "server-only"; import { z } from "zod"; import prisma from "@/lib/prisma"; import { generateText, tool } from "ai"; -import { deepseek } from "@ai-sdk/deepseek"; +import { model } from "@/lib/ai"; import { Complexity } from "@/types/complexity"; interface analyzeCodeProps { @@ -30,7 +30,7 @@ export const analyzeCode = async ({ }); await generateText({ - model: deepseek("deepseek-chat"), + model, system: `You are an AI assistant that rigorously analyzes code for time and space complexity, and assesses overall code quality. **Time/Space Complexity MUST be one of these values:** diff --git a/src/app/actions/analyze.ts b/src/app/actions/analyze.ts index 1c2f1d4..b39356e 100644 --- a/src/app/actions/analyze.ts +++ b/src/app/actions/analyze.ts @@ -6,7 +6,7 @@ import { Complexity, } from "@/types/complexity"; import prisma from "@/lib/prisma"; -import { openai } from "@/lib/ai"; +import { model } from "@/lib/ai"; import { auth } from "@/lib/auth"; import { CoreMessage, generateText } from "ai"; import { CodeAnalysis } from "@/generated/client"; @@ -14,8 +14,6 @@ import { CodeAnalysis } from "@/generated/client"; export const analyzeComplexity = async ( content: string ): Promise => { - const model = openai("gpt-4o-mini"); - const prompt = ` Analyze the time and space complexity of the following programming code snippet. Determine the Big O notation from this list: ${Complexity.options.join(", ")}. @@ -89,8 +87,6 @@ export const getAnalysis = async ( }; export const optimizeCode = async (content: string): Promise => { - const model = openai("gpt-4o-mini"); - const prompt = ` Optimize the following code snippet for better performance, readability, and maintainability. Provide ONLY the improved code without any explanations, comments, or additional text. diff --git a/src/app/api/chat/route.ts b/src/app/api/chat/route.ts index c2073cf..3bb2501 100644 --- a/src/app/api/chat/route.ts +++ b/src/app/api/chat/route.ts @@ -1,5 +1,5 @@ import { streamText } from "ai"; -import { deepseek } from "@/lib/ai"; +import { model } from "@/lib/ai"; // Allow streaming responses up to 30 seconds export const maxDuration = 30; @@ -20,7 +20,7 @@ For the best response, please take your time to carefully consider my questions, ** Reply in the user's language ! **`; const result = streamText({ - model: deepseek("deepseek-chat"), + model, system: system, messages: messages, }); diff --git a/src/lib/ai.ts b/src/lib/ai.ts index 05d5c1e..f7046d9 100644 --- a/src/lib/ai.ts +++ b/src/lib/ai.ts @@ -1,13 +1,29 @@ import "server-only"; -import { createOpenAI } from "@ai-sdk/openai"; -import { createDeepSeek } from "@ai-sdk/deepseek"; +import { createOpenAICompatible } from "@ai-sdk/openai-compatible"; -export const deepseek = createDeepSeek({ - apiKey: process.env.DEEPSEEK_API_KEY ?? "", +const getRequiredEnv = ( + name: "AI_API_KEY" | "AI_BASE_URL" | "AI_MODEL", +): string => { + const value = process.env[name]; + + if (!value) { + throw new Error(`Missing required environment variable: ${name}`); + } + + return value; +}; + +const apiKey = getRequiredEnv("AI_API_KEY"); + +const baseURL = getRequiredEnv("AI_BASE_URL"); + +export const modelId = getRequiredEnv("AI_MODEL"); + +export const aiProvider = createOpenAICompatible({ + name: "ai", + apiKey, + baseURL, }); -export const openai = createOpenAI({ - apiKey: process.env.OPENAI_API_KEY, - baseURL: process.env.OPENAI_BASE_URL, -}); +export const model = aiProvider(modelId);