diff --git a/.github/workflows/docker-build-push.yml b/.github/workflows/docker-build-push.yml deleted file mode 100644 index 1445d2c..0000000 --- a/.github/workflows/docker-build-push.yml +++ /dev/null @@ -1,138 +0,0 @@ -# name: Build and Push Docker Image - -# on: -# push: -# branches: -# - main -# paths: -# - 'backend/**' -# - 'frontend/**' -# - 'docker-compose.yml' -# - '.github/workflows/docker-build-push.yml' -# pull_request: -# branches: -# - main -# paths: -# - 'backend/**' -# - 'frontend/**' -# - 'docker-compose.yml' -# - '.github/workflows/docker-build-push.yml' - -# env: -# REGISTRY: docker.io -# BACKEND_IMAGE_NAME: mathmodelagent-backend -# FRONTEND_IMAGE_NAME: mathmodelagent-frontend - -# jobs: -# build-backend: -# runs-on: ubuntu-latest -# outputs: -# image-tag: ${{ steps.meta.outputs.tags }} -# image-digest: ${{ steps.build.outputs.digest }} - -# steps: -# - name: Checkout code -# uses: actions/checkout@v4 - -# - name: Set up Docker Buildx -# uses: docker/setup-buildx-action@v3 - -# - name: Log in to Docker Hub -# if: github.event_name != 'pull_request' -# uses: docker/login-action@v3 -# with: -# registry: ${{ env.REGISTRY }} -# username: ${{ secrets.DOCKER_USERNAME }} -# password: ${{ secrets.DOCKER_PASSWORD }} - -# - name: Extract metadata for backend -# id: meta -# uses: docker/metadata-action@v5 -# with: -# images: ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.BACKEND_IMAGE_NAME }} -# tags: | -# type=ref,event=branch -# type=ref,event=pr -# type=sha,prefix={{branch}}- -# type=raw,value=latest,enable={{is_default_branch}} - -# - name: Build and push backend Docker image -# id: build -# uses: docker/build-push-action@v5 -# with: -# context: ./backend -# file: ./backend/Dockerfile -# push: ${{ github.event_name != 'pull_request' }} -# tags: ${{ steps.meta.outputs.tags }} -# labels: ${{ steps.meta.outputs.labels }} -# cache-from: type=gha -# cache-to: type=gha,mode=max -# platforms: linux/amd64,linux/arm64 - -# build-frontend: -# runs-on: ubuntu-latest -# outputs: -# image-tag: ${{ steps.meta.outputs.tags }} -# image-digest: ${{ steps.build.outputs.digest }} - -# steps: -# - name: Checkout code -# uses: actions/checkout@v4 - -# - name: Set up Docker Buildx -# uses: docker/setup-buildx-action@v3 - -# - name: Log in to Docker Hub -# if: github.event_name != 'pull_request' -# uses: docker/login-action@v3 -# with: -# registry: ${{ env.REGISTRY }} -# username: ${{ secrets.DOCKER_USERNAME }} -# password: ${{ secrets.DOCKER_PASSWORD }} - -# - name: Extract metadata for frontend -# id: meta -# uses: docker/metadata-action@v5 -# with: -# images: ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.FRONTEND_IMAGE_NAME }} -# tags: | -# type=ref,event=branch -# type=ref,event=pr -# type=sha,prefix={{branch}}- -# type=raw,value=latest,enable={{is_default_branch}} - -# - name: Build and push frontend Docker image -# id: build -# uses: docker/build-push-action@v5 -# with: -# context: ./frontend -# file: ./frontend/Dockerfile -# push: ${{ github.event_name != 'pull_request' }} -# tags: ${{ steps.meta.outputs.tags }} -# labels: ${{ steps.meta.outputs.labels }} -# cache-from: type=gha -# cache-to: type=gha,mode=max -# platforms: linux/amd64,linux/arm64 - -# security-scan: -# runs-on: ubuntu-latest -# needs: [build-backend, build-frontend] -# if: github.event_name != 'pull_request' - -# strategy: -# matrix: -# component: [backend, frontend] - -# steps: -# - name: Run Trivy vulnerability scanner -# uses: aquasecurity/trivy-action@master -# with: -# image-ref: ${{ needs[format('build-{0}', matrix.component)].outputs.image-tag }} -# format: 'sarif' -# output: 'trivy-results-${{ matrix.component }}.sarif' - -# - name: Upload Trivy scan results to GitHub Security tab -# uses: github/codeql-action/upload-sarif@v3 -# if: always() -# with: -# sarif_file: 'trivy-results-${{ matrix.component }}.sarif' diff --git a/backend/.env.dev.example b/backend/.env.dev similarity index 90% rename from backend/.env.dev.example rename to backend/.env.dev index a19668e..a1fd6f1 100644 --- a/backend/.env.dev.example +++ b/backend/.env.dev @@ -29,7 +29,6 @@ MAX_RETRIES=5 # E2B_API_KEY= SERVER_HOST=http://localhost:8000 # 使用 email 注册账号从 https://openalex.org/ 文献 -# OPENALEX_EMAIL=example@example.com OPENALEX_EMAIL= LOG_LEVEL=DEBUG diff --git a/backend/.gitignore b/backend/.gitignore index d55a23a..646d9de 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -2,8 +2,6 @@ venv/ _pycache_/ *.pyc .venv/ -.env.dev -.env.prod .idea/ .vscode/ diff --git a/backend/app/main.py b/backend/app/main.py index ef8692d..0c047a0 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -38,7 +38,7 @@ app.include_router(files_router.router) # 跨域 CORS app.add_middleware( CORSMiddleware, - allow_origins=settings.CORS_ALLOW_ORIGINS, + allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], diff --git a/backend/app/routers/files_router.py b/backend/app/routers/files_router.py index de5f42b..6f9cc7b 100644 --- a/backend/app/routers/files_router.py +++ b/backend/app/routers/files_router.py @@ -8,12 +8,27 @@ from fastapi import HTTPException router = APIRouter() +@router.get("/download_url") +async def get_download_url(task_id: str, filename: str): + return {"download_url": f"http://localhost:8000/static/{task_id}/{filename}"} + + +@router.get("/download_all_url") +async def get_download_all_url(task_id: str): + return {"download_url": f"http://localhost:8000/static/{task_id}/all.zip"} + + @router.get("/files") async def get_files(task_id: str): work_dir = get_work_dir(task_id) files = get_current_files(work_dir, "all") + file_all = [] - return {"files": files} + for i in files: + file_type = i.split(".")[-1] + file_all.append({"filename": i, "file_type": file_type}) + + return file_all @router.get("/open_folder") diff --git a/backend/app/routers/modeling_router.py b/backend/app/routers/modeling_router.py index b36210d..d170b57 100644 --- a/backend/app/routers/modeling_router.py +++ b/backend/app/routers/modeling_router.py @@ -19,6 +19,7 @@ from app.schemas.request import ExampleRequest from pydantic import BaseModel import litellm from app.config.setting import settings +import requests router = APIRouter() @@ -29,6 +30,15 @@ class ValidateApiKeyRequest(BaseModel): model_id: str +class ValidateOpenalexEmailRequest(BaseModel): + email: str + + +class ValidateOpenalexEmailResponse(BaseModel): + valid: bool + message: str + + class ValidateApiKeyResponse(BaseModel): valid: bool message: str @@ -39,6 +49,7 @@ class SaveApiConfigRequest(BaseModel): modeler: dict coder: dict writer: dict + openalex_email: str @router.post("/save-api-config") @@ -49,25 +60,28 @@ async def save_api_config(request: SaveApiConfigRequest): try: # 更新各个模块的设置 if request.coordinator: - settings.COORDINATOR_API_KEY = request.coordinator.get('apiKey', '') - settings.COORDINATOR_MODEL = request.coordinator.get('modelId', '') - settings.COORDINATOR_BASE_URL = request.coordinator.get('baseUrl', '') - + settings.COORDINATOR_API_KEY = request.coordinator.get("apiKey", "") + settings.COORDINATOR_MODEL = request.coordinator.get("modelId", "") + settings.COORDINATOR_BASE_URL = request.coordinator.get("baseUrl", "") + if request.modeler: - settings.MODELER_API_KEY = request.modeler.get('apiKey', '') - settings.MODELER_MODEL = request.modeler.get('modelId', '') - settings.MODELER_BASE_URL = request.modeler.get('baseUrl', '') - + settings.MODELER_API_KEY = request.modeler.get("apiKey", "") + settings.MODELER_MODEL = request.modeler.get("modelId", "") + settings.MODELER_BASE_URL = request.modeler.get("baseUrl", "") + if request.coder: - settings.CODER_API_KEY = request.coder.get('apiKey', '') - settings.CODER_MODEL = request.coder.get('modelId', '') - settings.CODER_BASE_URL = request.coder.get('baseUrl', '') - + settings.CODER_API_KEY = request.coder.get("apiKey", "") + settings.CODER_MODEL = request.coder.get("modelId", "") + settings.CODER_BASE_URL = request.coder.get("baseUrl", "") + if request.writer: - settings.WRITER_API_KEY = request.writer.get('apiKey', '') - settings.WRITER_MODEL = request.writer.get('modelId', '') - settings.WRITER_BASE_URL = request.writer.get('baseUrl', '') - + settings.WRITER_API_KEY = request.writer.get("apiKey", "") + settings.WRITER_MODEL = request.writer.get("modelId", "") + settings.WRITER_BASE_URL = request.writer.get("baseUrl", "") + + if request.openalex_email: + settings.OPENALEX_EMAIL = request.openalex_email + return {"success": True, "message": "配置保存成功"} except Exception as e: logger.error(f"保存配置失败: {str(e)}") @@ -86,44 +100,56 @@ async def validate_api_key(request: ValidateApiKeyRequest): messages=[{"role": "user", "content": "Hi"}], max_tokens=1, api_key=request.api_key, - base_url=request.base_url if request.base_url != "https://api.openai.com/v1" else None, - ) - - return ValidateApiKeyResponse( - valid=True, - message="✓ 模型 API 验证成功" + base_url=request.base_url + if request.base_url != "https://api.openai.com/v1" + else None, ) + + return ValidateApiKeyResponse(valid=True, message="✓ 模型 API 验证成功") except Exception as e: error_msg = str(e) - + # 解析不同类型的错误 if "401" in error_msg or "Unauthorized" in error_msg: - return ValidateApiKeyResponse( - valid=False, - message="✗ API Key 无效或已过期" - ) + return ValidateApiKeyResponse(valid=False, message="✗ API Key 无效或已过期") elif "404" in error_msg or "Not Found" in error_msg: return ValidateApiKeyResponse( - valid=False, - message="✗ 模型 ID 不存在或 Base URL 错误" + valid=False, message="✗ 模型 ID 不存在或 Base URL 错误" ) elif "429" in error_msg or "rate limit" in error_msg.lower(): return ValidateApiKeyResponse( - valid=False, - message="✗ 请求过于频繁,请稍后再试" + valid=False, message="✗ 请求过于频繁,请稍后再试" ) elif "403" in error_msg or "Forbidden" in error_msg: return ValidateApiKeyResponse( - valid=False, - message="✗ API 权限不足或账户余额不足" + valid=False, message="✗ API 权限不足或账户余额不足" ) else: return ValidateApiKeyResponse( - valid=False, - message=f"✗ 验证失败: {error_msg[:50]}..." + valid=False, message=f"✗ 验证失败: {error_msg[:50]}..." ) +@router.post("/validate-openalex-email", response_model=ValidateOpenalexEmailResponse) +async def validate_openalex_email(request: ValidateOpenalexEmailRequest): + """ + 验证 OpenAlex Email 的有效性 + """ + try: + response = requests.get( + f"https://api.openalex.org/works?mailto={request.email}" + ) + logger.debug(f"OpenAlex Email 验证响应: {response}") + response.raise_for_status() + return ValidateOpenalexEmailResponse( + valid=True, message="✓ OpenAlex Email 验证成功" + ) + except Exception as e: + return ValidateOpenalexEmailResponse( + valid=False, message=f"✗ OpenAlex Email 验证失败: {str(e)}" + ) + + @router.post("/example") async def exampleModeling( example_request: ExampleRequest, @@ -235,8 +261,8 @@ async def run_modeling_task_async( # 创建任务并等待它完成 task = asyncio.create_task(MathModelWorkFlow().execute(problem)) - # 设置超时时间(比如 60 分钟) - await asyncio.wait_for(task, timeout=3600) + # 设置超时时间(比如 300 分钟) + await asyncio.wait_for(task, timeout=3600 * 5) # 发送任务完成状态 await redis_manager.publish_message( diff --git a/frontend/.env.example b/frontend/.env.development similarity index 100% rename from frontend/.env.example rename to frontend/.env.development diff --git a/frontend/.gitignore b/frontend/.gitignore index cf05ad2..3d9f25a 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -25,7 +25,6 @@ dist-ssr .env .env.local -.env.development .env.production .env.test diff --git a/frontend/src/apis/apiKeyApi.ts b/frontend/src/apis/apiKeyApi.ts index a4a3fd2..5bce79b 100644 --- a/frontend/src/apis/apiKeyApi.ts +++ b/frontend/src/apis/apiKeyApi.ts @@ -19,22 +19,36 @@ export interface SaveApiConfigRequest { apiKey: string; baseUrl: string; modelId: string; + provider: string; }; modeler: { apiKey: string; baseUrl: string; modelId: string; + provider: string; }; coder: { apiKey: string; baseUrl: string; modelId: string; + provider: string; }; writer: { apiKey: string; baseUrl: string; modelId: string; + provider: string; }; + openalex_email: string; +} + +export interface ValidateOpenalexEmailRequest { + email: string; +} + +export interface ValidateOpenalexEmailResponse { + valid: boolean; + message: string; } // 验证 API Key @@ -42,6 +56,10 @@ export function validateApiKey(params: ValidateApiKeyRequest) { return request.post("/validate-api-key", params); } +export function validateOpenalexEmail(params: ValidateOpenalexEmailRequest) { + return request.post("/validate-openalex-email", params); +} + // 保存 API 配置 export function saveApiConfig(params: SaveApiConfigRequest) { return request.post<{ success: boolean; message: string }>("/save-api-config", params); diff --git a/frontend/src/apis/filesApi.ts b/frontend/src/apis/filesApi.ts index 98357af..3586aba 100644 --- a/frontend/src/apis/filesApi.ts +++ b/frontend/src/apis/filesApi.ts @@ -1,7 +1,39 @@ import request from "@/utils/request"; export function getFiles(task_id: string) { - return request.get<{ message: string }>("/files", { + return request.get<{ + files: { + filename: string; + file_type: string; + }[] + }>("/files", { params: { task_id }, }); } + + +/** + * 获取单个文件下载链接 + * @param task_id 任务ID + * @param filename 文件名 + */ +export async function getFileDownloadUrl(task_id: string, filename: string) { + return await request.get<{ download_url: string }>(`/download_url`, { + params: { + task_id, + filename, + } + }) +} + +/** + * 获取所有文件压缩包下载链接 + * @param task_id 任务ID + */ +export async function getAllFilesDownloadUrl(task_id: string) { + return await request.get<{ download_url: string }>(`/download_all_url`, { + params: { + task_id, + } + }) +} \ No newline at end of file diff --git a/frontend/src/components/AppSidebar.vue b/frontend/src/components/AppSidebar.vue index 7986ffd..0226397 100644 --- a/frontend/src/components/AppSidebar.vue +++ b/frontend/src/components/AppSidebar.vue @@ -36,43 +36,7 @@ const data = { title: '历史任务', url: '#', items: [ - { - title: 'Routing', - url: '#', - }, - { - title: 'Data Fetching', - url: '#', - isActive: true, - }, - { - title: 'Rendering', - url: '#', - }, - { - title: 'Caching', - url: '#', - }, - { - title: 'Styling', - url: '#', - }, - { - title: 'Optimizing', - url: '#', - }, - { - title: 'Configuring', - url: '#', - }, - { - title: 'Testing', - url: '#', - }, - { - title: 'Authentication', - url: '#', - }, + ], }, diff --git a/frontend/src/components/NavUser.vue b/frontend/src/components/NavUser.vue index 55b0381..d1d9e82 100644 --- a/frontend/src/components/NavUser.vue +++ b/frontend/src/components/NavUser.vue @@ -24,7 +24,6 @@ import { BadgeCheck, Bell, ChevronsUpDown, - CreditCard, LogOut, KeyRound, } from 'lucide-vue-next' @@ -103,10 +102,6 @@ const openApiKeyDialog = () => { Account - - - Billing - Notifications diff --git a/frontend/src/pages/chat/components/ApiDialog.vue b/frontend/src/pages/chat/components/ApiDialog.vue index 37f72b3..5f1706e 100644 --- a/frontend/src/pages/chat/components/ApiDialog.vue +++ b/frontend/src/pages/chat/components/ApiDialog.vue @@ -7,42 +7,57 @@ import { DialogHeader, DialogTitle, } from '@/components/ui/dialog' +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectTrigger, + SelectValue, +} from "@/components/ui/select" import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { useApiKeyStore } from '@/stores/apiKeys' import { CheckCircle, XCircle } from 'lucide-vue-next' -import { validateApiKey, saveApiConfig } from '@/apis/apiKeyApi' +import { validateApiKey, saveApiConfig, validateOpenalexEmail } from '@/apis/apiKeyApi' const apiKeyStore = useApiKeyStore() // 本地表单数据 const form = ref<{ - coordinator: { apiKey: string; baseUrl: string; modelId: string }; - modeler: { apiKey: string; baseUrl: string; modelId: string }; - coder: { apiKey: string; baseUrl: string; modelId: string }; - writer: { apiKey: string; baseUrl: string; modelId: string }; + coordinator: { apiKey: string; baseUrl: string; modelId: string; provider: string }; + modeler: { apiKey: string; baseUrl: string; modelId: string; provider: string }; + coder: { apiKey: string; baseUrl: string; modelId: string; provider: string }; + writer: { apiKey: string; baseUrl: string; modelId: string; provider: string }; + openalex_email: string; }>({ coordinator: { apiKey: '', baseUrl: '', - modelId: '' + modelId: '', + provider: '' }, modeler: { apiKey: '', baseUrl: '', - modelId: '' + modelId: '', + provider: '' }, coder: { apiKey: '', baseUrl: '', - modelId: '' + modelId: '', + provider: '' }, writer: { apiKey: '', baseUrl: '', - modelId: '' - } + modelId: '', + provider: '' + }, + openalex_email: '' }) // 验证状态 @@ -51,7 +66,8 @@ const validationResults = ref({ coordinator: { valid: false, message: '' }, modeler: { valid: false, message: '' }, coder: { valid: false, message: '' }, - writer: { valid: false, message: '' } + writer: { valid: false, message: '' }, + openalex_email: { valid: false, message: '' } }) // 计算所有验证是否都通过 @@ -73,6 +89,7 @@ const loadFromStore = () => { form.value.modeler = { ...apiKeyStore.modelerConfig } form.value.coder = { ...apiKeyStore.coderConfig } form.value.writer = { ...apiKeyStore.writerConfig } + form.value.openalex_email = apiKeyStore.openalexEmail } // 保存表单数据到 store @@ -82,7 +99,7 @@ const saveToStore = async () => { apiKeyStore.setModelerConfig(form.value.modeler) apiKeyStore.setCoderConfig(form.value.coder) apiKeyStore.setWriterConfig(form.value.writer) - + apiKeyStore.setOpenalexEmail(form.value.openalex_email) // 如果验证成功,也保存到后端设置 if (allValid.value) { try { @@ -90,7 +107,8 @@ const saveToStore = async () => { coordinator: form.value.coordinator, modeler: form.value.modeler, coder: form.value.coder, - writer: form.value.writer + writer: form.value.writer, + openalex_email: form.value.openalex_email }) } catch (error) { console.error('保存配置到后端失败:', error) @@ -159,15 +177,11 @@ const validateAllApiKeys = async () => { coordinator: { valid: false, message: '' }, modeler: { valid: false, message: '' }, coder: { valid: false, message: '' }, - writer: { valid: false, message: '' } + writer: { valid: false, message: '' }, + openalex_email: { valid: false, message: '' } } try { - - - // 在 E2B 验证后等待 500ms - await new Promise(resolve => setTimeout(resolve, 500)) - // 逐个验证各模型 API Keys,避免并发请求 for (const config of modelConfigs.value) { const key = config.key as keyof typeof validationResults.value @@ -182,6 +196,10 @@ const validateAllApiKeys = async () => { // 每次验证后等待 1 秒,避免触发速率限制 await new Promise(resolve => setTimeout(resolve, 1000)) } + + // 验证 OpenAlex Email + validationResults.value.openalex_email = await validateOpenalexEmail({ email: form.value.openalex_email }).then(res => res.data) + } catch (error) { console.error('验证过程中发生错误:', error) // 显示全局错误 @@ -198,30 +216,70 @@ const validateAllApiKeys = async () => { } } -const links = { +const resetAll = () => { + form.value = { + coordinator: { apiKey: '', baseUrl: '', modelId: '', provider: '' }, + modeler: { apiKey: '', baseUrl: '', modelId: '', provider: '' }, + coder: { apiKey: '', baseUrl: '', modelId: '', provider: '' }, + writer: { apiKey: '', baseUrl: '', modelId: '', provider: '' }, + openalex_email: '' + } +} + + +const providers = { "DeepSeek": { "url": "https://platform.deepseek.com/api_keys", "key": "DeepSeek", - "BaseURL": "https://api.deepseek.com", - "ModelID": "deepseek/deepseek-chat" + "baseUrl": "https://api.deepseek.com", + "modelId": "deepseek/deepseek-chat" }, "硅基流动": { "url": "https://cloud.siliconflow.cn/i/UIb4Enf4", "key": "硅基流动", - "BaseURL": "https://api.siliconflow.cn", - "ModelID": "openai/deepseek-ai/DeepSeek-V3" + "baseUrl": "https://api.siliconflow.cn", + "modelId": "openai/deepseek-ai/DeepSeek-V3" }, "Sophnet": { "url": "https://www.sophnet.com/#?code=AZBSFG", "key": "Sophnet", - "BaseURL": "https://www.sophnet.com/api/open-apis", - "ModelID": "openai/DeepSeek-V3-Fast" + "baseUrl": "https://www.sophnet.com/api/open-apis", + "modelId": "openai/DeepSeek-V3-Fast" }, "OpenAI": { "url": "https://platform.openai.com/api-keys", "key": "OpenAI", - "BaseURL": "https://api.openai.com", - "ModelID": "openai/gpt-4o" + "baseUrl": "https://api.openai.com", + "modelId": "openai/gpt-5" + }, + "302.AI": { + "url": "https://302.ai/", + "key": "302.AI", + "baseUrl": "https://api.302.ai", + "modelId": "openai/deepseek-chat" + }, + "OpenAI 兼容": { + "url": "/", + "key": "OpenAI 兼容", + "baseUrl": "basurl", + "modelId": "provider/model_id" + } +} + +// 当供应商选择改变时,自动填写配置 +const onProviderChange = (configKey: string, providerKey: string) => { + const provider = providers[providerKey as keyof typeof providers] + if (provider) { + const formConfig = (form.value as any)[configKey] + formConfig.provider = providerKey + formConfig.baseUrl = provider.baseUrl + formConfig.modelId = provider.modelId + + // 清除之前的验证结果 + validationResults.value[configKey as keyof typeof validationResults.value] = { + valid: false, + message: '' + } } } @@ -231,25 +289,10 @@ const links = { - API Key 设置 + 设置 为每个 Agent 配置合适模型
-
-
-
- - {{ link.key }} - -
- Base URL: {{ link.BaseURL }} -
-
- Model ID: {{ link.ModelID }} -
-
-
-
more details @@ -263,51 +306,105 @@ const links = {

{{ config.label }}

-
+
- -
- - -
-
- - -
-
- - + +
+ + + + + +
+ +
-
- {{ validationResults[config.key as keyof typeof validationResults].message }} + +
+ +
+
+ +
+
+ + +
+
+
+ {{ validationResults[config.key as keyof typeof validationResults].message }}
+ + +
+

其他

+ +
+ 使用 email 注册账号从 OpenAlex 获取访问文献权利 +
+ +
+ {{ validationResults.openalex_email.message }} +
+
+
- +
+ + +
-
diff --git a/frontend/src/pages/task/components/FileSheet.vue b/frontend/src/pages/task/components/FileSheet.vue new file mode 100644 index 0000000..ef19b1b --- /dev/null +++ b/frontend/src/pages/task/components/FileSheet.vue @@ -0,0 +1,242 @@ + + + diff --git a/frontend/src/pages/task/index.vue b/frontend/src/pages/task/index.vue index e313c14..9bd2f59 100644 --- a/frontend/src/pages/task/index.vue +++ b/frontend/src/pages/task/index.vue @@ -18,9 +18,8 @@ import { onMounted, onBeforeUnmount, ref } from 'vue' import { useTaskStore } from '@/stores/task' import { getWriterSeque } from '@/apis/commonApi'; import { Button } from '@/components/ui/button'; -import { openFolderAPI } from '@/apis/commonApi'; import { useToast } from '@/components/ui/toast/use-toast' -import { Folder } from 'lucide-vue-next' +import FilesSheet from '@/pages/task/components/FileSheet.vue' const { toast } = useToast() @@ -69,15 +68,6 @@ onMounted(async () => { updateDuration() // 立即更新一次 }) -const openFolder = async () => { - const res = await openFolderAPI(props.task_id); - console.log(res); - toast({ - title: '打开工作目录成功', - description: res.data.message, - }) -} - onBeforeUnmount(() => { taskStore.closeWebSocket() @@ -125,9 +115,8 @@ onBeforeUnmount(() => { 下载消息 - + +
diff --git a/frontend/src/stores/apiKeys.ts b/frontend/src/stores/apiKeys.ts index b07a45e..ac71834 100644 --- a/frontend/src/stores/apiKeys.ts +++ b/frontend/src/stores/apiKeys.ts @@ -9,31 +9,37 @@ export const useApiKeyStore = defineStore('apiKeys', () => { const coordinatorConfig = ref({ apiKey: '', baseUrl: '', - modelId: '' + modelId: '', + provider: '' }); - + const modelerConfig = ref({ apiKey: '', baseUrl: '', - modelId: '' + modelId: '', + provider: '' }); - + const coderConfig = ref({ apiKey: '', baseUrl: '', - modelId: '' + modelId: '', + provider: '' }); - + const writerConfig = ref({ apiKey: '', baseUrl: '', - modelId: '' + modelId: '', + provider: '' }); + const openalexEmail = ref(''); + const isEmpty = computed(() => { return Object.values(getAllAgentConfigs()).every(config => config.apiKey === '') }) - + // 设置协调者模型配置 function setCoordinatorConfig(config: ModelConfig) { coordinatorConfig.value = { ...config }; @@ -54,6 +60,11 @@ export const useApiKeyStore = defineStore('apiKeys', () => { writerConfig.value = { ...config }; } + function setOpenalexEmail(email: string) { + console.log('setOpenalexEmail', email) + openalexEmail.value = email; + } + // 获取所有 agent 配置 function getAllAgentConfigs() { return { @@ -66,10 +77,11 @@ export const useApiKeyStore = defineStore('apiKeys', () => { // 重置所有配置 function resetAll() { - coordinatorConfig.value = { apiKey: '', baseUrl: '', modelId: '' }; - modelerConfig.value = { apiKey: '', baseUrl: '', modelId: '' }; - coderConfig.value = { apiKey: '', baseUrl: '', modelId: '' }; - writerConfig.value = { apiKey: '', baseUrl: '', modelId: '' }; + coordinatorConfig.value = { apiKey: '', baseUrl: '', modelId: '', provider: '' }; + modelerConfig.value = { apiKey: '', baseUrl: '', modelId: '', provider: '' }; + coderConfig.value = { apiKey: '', baseUrl: '', modelId: '', provider: '' }; + writerConfig.value = { apiKey: '', baseUrl: '', modelId: '', provider: '' }; + openalexEmail.value = ''; } return { @@ -78,13 +90,15 @@ export const useApiKeyStore = defineStore('apiKeys', () => { modelerConfig, coderConfig, writerConfig, + openalexEmail, isEmpty, - + // 方法 setCoordinatorConfig, setModelerConfig, setCoderConfig, setWriterConfig, + setOpenalexEmail, getAllAgentConfigs, resetAll } diff --git a/frontend/src/utils/interface.ts b/frontend/src/utils/interface.ts index d6604f1..f34d770 100644 --- a/frontend/src/utils/interface.ts +++ b/frontend/src/utils/interface.ts @@ -20,4 +20,5 @@ export interface ModelConfig { apiKey: string; baseUrl: string; modelId: string; + provider: string; } \ No newline at end of file diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts index 11f02fe..323c78a 100644 --- a/frontend/src/vite-env.d.ts +++ b/frontend/src/vite-env.d.ts @@ -1 +1,7 @@ /// + +declare module '*.vue' { + import type { DefineComponent } from 'vue' + const component: DefineComponent<{}, {}, any> + export default component +} diff --git a/push-to-dockerhub.sh b/push-to-dockerhub.sh deleted file mode 100755 index dde4cf1..0000000 --- a/push-to-dockerhub.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -# push-to-dockerhub.sh - -set -e - -USERNAME="sanjin66" -BACKEND_TAG="${USERNAME}/mathmodelagent-backend-github:latest" -FRONTEND_TAG="${USERNAME}/mathmodelagent-frontend-github:latest" - -echo "开始构建和推送 MathModelAgent..." - -# 构建并推送后端 -echo "构建后端镜像..." -docker build -t ${BACKEND_TAG} ./backend -echo "推送后端镜像..." -docker push ${BACKEND_TAG} - -# 构建并推送前端 -echo "构建前端镜像..." -docker build -t ${FRONTEND_TAG} ./frontend -echo "推送前端镜像..." -docker push ${FRONTEND_TAG} - -echo "推送完成!" \ No newline at end of file