feat: 新增Python服务器。准备逐步替换node服务器

This commit is contained in:
肖应宇 2026-03-03 13:58:13 +08:00
parent a9b31f540f
commit 485b07c12f
8 changed files with 934 additions and 1 deletions

2
.gitignore vendored
View File

@ -13,6 +13,8 @@ dist-ssr
*.local
uploads
.env
.venv
__pycache__
# Editor directories and files
.vscode/*

106
server_python/README.md Normal file
View File

@ -0,0 +1,106 @@
# Python AI Chat Server
这是原有Node.js服务器的Python替代版本使用FastAPI和DashScope Python SDK连接阿里云百炼平台API。
## 特性
- 基于FastAPI的高性能异步服务器
- 支持流式和非流式对话
- 完全兼容前端API端点
- 支持多模态输入(文本+图像)
- 集成阿里云百炼API
- 文件上传功能
- 对话历史管理
## 安装要求
- Python 3.8+
- pip包管理器
## 安装步骤
1. **克隆或复制代码**
2. **安装Python依赖**
```bash
cd server_python
pip install -r requirements.txt
```
3. **配置环境变量**
```bash
cp .env.example .env
```
编辑`.env`文件填入您的阿里云百炼API密钥
```
ALIYUN_API_KEY=your_actual_api_key_here
```
## 启动服务器
### 方法一:直接运行
```bash
python run_server.py
```
### 方法二使用uvicorn
```bash
uvicorn app:app --host 0.0.0.0 --port 8000 --reload
```
## API端点
服务器提供了与原Node.js版本完全相同的API端点
- `POST /api/chat-ui/chat` - 聊天接口(支持流式和非流式)
- `GET /api/chat-ui/models` - 获取模型列表
- `GET /api/chat-ui/conversations` - 获取所有对话
- `GET /api/chat-ui/conversations/{id}` - 获取特定对话
- `POST /api/chat-ui/conversations` - 保存/更新对话
- `DELETE /api/chat-ui/conversations/{id}` - 删除对话
- `POST /api/chat-ui/upload` - 文件上传
- `POST /api/chat-ui/stop` - 停止生成
- `POST /api/chat-ui/stop/{id}` - 按ID停止生成
- `GET /health` - 健康检查
## 前端配置
修改Vite配置`vite.config.ts`)中的代理目标:
```typescript
server: {
proxy: {
"/api/chat-ui": {
target: "http://localhost:8000", // 修改为Python服务器端口
changeOrigin: true,
},
},
},
```
## 环境变量
- `ALIYUN_API_KEY`: 阿里云百炼API密钥必填
- `PORT`: 服务器端口默认8000
## 依赖说明
- `fastapi`: 现代高性能web框架
- `uvicorn`: ASGI服务器
- `dashscope`: 阿里云百炼SDK
- `python-multipart`: 处理文件上传
- `python-dotenv`: 环境变量管理
## 错误排查
1. **API密钥错误**: 确保在`.env`文件中正确设置了`ALIYUN_API_KEY`
2. **端口冲突**: 检查8000端口是否被占用可以修改`.env`中的`PORT`变量
3. **依赖问题**: 确保已正确安装所有依赖
## 注意事项
- Python服务器提供了与Node.js服务器相同的功能和API接口
- 保留了原有的日志记录机制
- 对话数据仍存储在内存中,生产环境建议使用数据库
- 支持与原前端应用无缝集成

434
server_python/app.py Normal file
View File

@ -0,0 +1,434 @@
"""
改进版Python FastAPI服务器实现使用DashScope Python SDK连接阿里云百炼平台API
"""
import os
import json
import uuid
import asyncio
from datetime import datetime
from typing import Dict, List, Optional, Any
from pathlib import Path
from enum import Enum
import dashscope
from dashscope import Generation
from dotenv import load_dotenv
from fastapi import FastAPI, HTTPException, File, UploadFile, Request
from fastapi.responses import JSONResponse, StreamingResponse
from pydantic import BaseModel, Field
import uvicorn
# 加载环境变量
load_dotenv()
# 设置 DashScope API 密钥
api_key = os.getenv("ALIYUN_API_KEY")
if not api_key:
raise ValueError("请在环境变量中设置 ALIYUN_API_KEY")
dashscope.api_key = api_key
# 创建 FastAPI 应用
app = FastAPI(title="AI Chat API Server (Python)", version="2.0.0")
# 数据模型定义
class ChatMessage(BaseModel):
role: str
content: str
images: Optional[List[str]] = None
files: Optional[List[str]] = None
class ChatRequest(BaseModel):
model: str = "qwen-plus"
messages: List[Dict[str, Any]]
stream: bool = True
temperature: Optional[float] = 0.7
max_tokens: Optional[int] = 2000
class ModelInfo(BaseModel):
id: str
name: str
description: str
maxTokens: int
provider: str
# 模拟数据库 - 实际应用中应使用持久化存储
conversations_db: Dict[str, dict] = {}
# 配置上传目录
upload_dir = Path("uploads")
upload_dir.mkdir(exist_ok=True)
@app.middleware("http")
async def logging_middleware(request: Request, call_next):
"""中间件:记录请求日志"""
start_time = datetime.utcnow()
# 记录请求信息
print(f"[INFO] {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - "
f"HTTP {request.method} {request.url.path} - "
f"IP: {request.client.host if request.client else 'unknown'}")
response = await call_next(request)
# 计算处理时间
process_time = (datetime.utcnow() - start_time).total_seconds() * 1000
# 记录响应信息
print(f"[INFO] {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - "
f"Response {response.status_code}, Process Time: {process_time:.2f}ms")
# 在响应头中添加处理时间
response.headers["X-Process-Time"] = f"{process_time:.2f}ms"
return response
@app.get("/health")
async def health_check():
"""健康检查端点"""
return {"status": "healthy", "timestamp": datetime.utcnow().isoformat()}
@app.post("/api/chat-ui/chat")
async def chat_endpoint(request: Request):
"""
聊天接口 - 与阿里云百炼API兼容的接口
这个端点会接收前端的聊天请求并转发到阿里云百炼API
"""
try:
# 获取请求体数据
body = await request.json()
# 检查请求格式并适配
# 如果是OpenAI兼容格式 (来自streamChat)
if 'messages' in body:
messages = body.get('messages', [])
model = body.get('model', 'qwen-plus')
stream = body.get('stream', True)
temperature = body.get('temperature', 0.7)
max_tokens = body.get('max_tokens', 2000)
else:
# 否则是前端简化格式 (来自chat函数)
# 需要将其转换为OpenAI兼容格式
message_text = body.get('message', '')
# 检查message是否已经是格式化的列表带图片的情况
if isinstance(message_text, list):
user_content = message_text
else:
# 如果是字符串,转换为标准格式
user_content = [{"type": "text", "text": message_text}]
messages = [
{"role": "system", "content": body.get('systemPrompt', '你是一个支持视觉理解的助手。')},
{"role": "user", "content": user_content}
]
model = body.get('model', 'qwen-plus')
stream = body.get('stream', False) # 默认为非流式
temperature = body.get('temperature', 0.7)
max_tokens = body.get('maxTokens', 2000)
if stream:
# 流式响应
async def event_generator():
try:
responses = Generation.call(
model=model,
messages=messages,
stream=True,
max_tokens=max_tokens,
temperature=temperature
)
for idx, response in enumerate(responses):
if response.status_code == 200:
# 检查响应是否包含预期的内容
# DashScope API的响应结构可能是 output.choices 或 output.text
content = None
# 尝试从 output.choices 获取内容
if (hasattr(response, 'output') and
response.output and
hasattr(response.output, 'choices') and
response.output.choices is not None and
len(response.output.choices) > 0 and
'message' in response.output.choices[0] and
'content' in response.output.choices[0]['message']):
content = response.output.choices[0]['message']['content']
# 否则尝试从 output.text 获取内容DashScope特定格式
elif (hasattr(response, 'output') and
response.output and
'text' in response.output):
content = response.output.get('text')
if content:
# 构建 SSE 数据块
data = {
"id": f"chatcmpl-{uuid.uuid4()}",
"object": "chat.completion.chunk",
"created": int(datetime.utcnow().timestamp()),
"model": model,
"choices": [
{
"index": 0,
"delta": {"content": content} if content else {},
"finish_reason": None
}
]
}
yield f"data: {json.dumps(data)}\n\n"
else:
# 如果响应中没有内容,跳过
continue
else:
# 错误处理
error_data = {
"error": {
"message": f"API Error: {response.code} - {response.message}",
"type": "api_error",
"param": None,
"code": response.code
}
}
yield f"data: {json.dumps(error_data)}\n\n"
break
# 发送结束信号
finish_data = {
"id": f"chatcmpl-{uuid.uuid4()}",
"object": "chat.completion.chunk",
"created": int(datetime.utcnow().timestamp()),
"model": model,
"choices": [
{
"index": 0,
"delta": {},
"finish_reason": "stop"
}
]
}
yield f"data: {json.dumps(finish_data)}\n\n"
yield "data: [DONE]\n\n"
except Exception as e:
error_data = {
"error": {
"message": str(e),
"type": "server_error"
}
}
yield f"data: {json.dumps(error_data)}\n\n"
return StreamingResponse(event_generator(), media_type="text/event-stream")
else:
# 非流式响应
response = Generation.call(
model=model,
messages=messages,
stream=False,
max_tokens=max_tokens,
temperature=temperature
)
if response.status_code == 200:
# 检查响应是否包含预期的内容
# DashScope API的响应结构可能是 output.choices 或 output.text
content = None
# 尝试从 output.choices 获取内容
if (hasattr(response, 'output') and
response.output and
hasattr(response.output, 'choices') and
response.output.choices is not None and
len(response.output.choices) > 0 and
'message' in response.output.choices[0] and
'content' in response.output.choices[0]['message']):
content = response.output.choices[0]['message']['content']
# 否则尝试从 output.text 获取内容DashScope特定格式
elif (hasattr(response, 'output') and
response.output and
'text' in response.output):
content = response.output.get('text')
if content:
# 构建前端期望的响应格式
chat_response = {
"id": str(uuid.uuid4()),
"conversationId": body.get('conversationId', str(uuid.uuid4())),
"content": content,
"model": model,
"createdAt": int(datetime.utcnow().timestamp())
}
if hasattr(response, 'usage') and response.usage:
chat_response["usage"] = {
"promptTokens": response.usage.input_tokens,
"completionTokens": response.usage.output_tokens,
"totalTokens": response.usage.total_tokens
}
return JSONResponse(content=chat_response)
else:
raise HTTPException(
status_code=500,
detail="API Response does not contain expected content"
)
else:
raise HTTPException(
status_code=500,
detail=f"API Error: {response.code} - {response.message}"
)
except Exception as e:
print(f"[ERROR] Error in chat endpoint: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
@app.get("/api/chat-ui/models")
async def get_models():
"""获取模型列表"""
models = [
ModelInfo(
id="qwen-max",
name="通义千问 Max",
description="最强大的模型",
maxTokens=8192,
provider="Aliyun"
),
ModelInfo(
id="qwen-plus",
name="通义千问 Plus",
description="能力均衡",
maxTokens=8192,
provider="Aliyun"
),
ModelInfo(
id="qwen-turbo",
name="通义千问 Turbo",
description="速度更快、成本更低",
maxTokens=8192,
provider="Aliyun"
)
]
return [model.dict() for model in models]
@app.get("/api/chat-ui/conversations")
async def get_conversations():
"""获取所有对话"""
return list(conversations_db.values())
@app.get("/api/chat-ui/conversations/{conversation_id}")
async def get_conversation(conversation_id: str):
"""获取特定对话"""
conversation = conversations_db.get(conversation_id)
if not conversation:
raise HTTPException(status_code=404, detail="对话不存在")
return conversation
@app.post("/api/chat-ui/conversations")
async def save_conversation(request: Request):
"""保存或更新对话"""
try:
data = await request.json()
conversation_id = data.get('id') or str(uuid.uuid4())
conversation = {
"id": conversation_id,
"title": data.get('title', '新对话'),
"messages": data.get('messages', []),
"updatedAt": datetime.utcnow().isoformat(),
"createdAt": data.get('createdAt', datetime.utcnow().isoformat())
}
conversations_db[conversation_id] = conversation
return conversation
except Exception as e:
print(f"[ERROR] Error saving conversation: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
@app.delete("/api/chat-ui/conversations/{conversation_id}")
async def delete_conversation(conversation_id: str):
"""删除对话"""
if conversation_id in conversations_db:
del conversations_db[conversation_id]
return {"success": True, "message": "删除成功"}
else:
raise HTTPException(status_code=404, detail="对话不存在")
@app.post("/api/chat-ui/upload")
async def upload_file(file: UploadFile = File(...)):
"""文件上传接口"""
try:
# 检查文件类型
allowed_types = ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'text/plain', 'application/pdf']
if file.content_type not in allowed_types:
raise HTTPException(status_code=400, detail=f"不支持的文件类型: {file.content_type}")
# 生成唯一文件名
file_extension = Path(file.filename).suffix.lower()
unique_filename = f"{int(datetime.utcnow().timestamp())}_{uuid.uuid4()}{file_extension}"
file_path = upload_dir / unique_filename
# 保存文件
with open(file_path, "wb") as f:
content = await file.read()
f.write(content)
# 返回文件信息
file_url = f"http://localhost:8000/uploads/{unique_filename}"
result = {
"url": file_url,
"name": file.filename,
"size": len(content),
"mimeType": file.content_type
}
print(f"[INFO] File uploaded: {result}")
return result
except Exception as e:
print(f"[ERROR] Upload error: {str(e)}")
raise HTTPException(status_code=500, detail=f"上传失败: {str(e)}")
@app.get("/uploads/{filename}")
async def serve_upload(filename: str):
"""提供上传文件的访问"""
file_path = upload_dir / filename
if not file_path.exists():
raise HTTPException(status_code=404, detail="文件不存在")
from fastapi.responses import FileResponse
return FileResponse(str(file_path))
@app.post("/api/chat-ui/stop")
async def stop_generation():
"""停止生成接口"""
# 在实际实现中这里可能需要维护正在运行的任务ID列表
# 目前只是返回成功消息
return {"success": True, "message": "已发出停止指令"}
@app.post("/api/chat-ui/stop/{message_id}")
async def stop_generation_by_id(message_id: str):
"""根据消息ID停止生成"""
return {"success": True, "message": "已发出停止指令消息ID: " + message_id}
if __name__ == "__main__":
port = int(os.getenv("PORT", 8000))
print("="*50)
print(f"Python AI Chat Server 启动中...")
print(f"监听端口: {port}")
print(f"API Key 状态: {'已配置' if api_key else '未配置'}")
print("="*50)
if not api_key:
print("警告: 未在环境变量中检测到 ALIYUN_API_KEY!")
print("请在 .env 文件中添加您的百炼 API Key。")
else:
print("API Key 已检测到。")
uvicorn.run(app, host="0.0.0.0", port=port)

343
server_python/main.py Normal file
View File

@ -0,0 +1,343 @@
"""
Python Flask/FastAPI 服务器实现用于替代 Node.js 服务器
使用 DashScope Python SDK 连接阿里云百炼平台 API
"""
import os
import json
import uuid
import asyncio
from datetime import datetime
from typing import Dict, List, Optional
from pathlib import Path
import dashscope
from dashscope import Generation
from dotenv import load_dotenv
from fastapi import FastAPI, HTTPException, File, UploadFile, Form
from fastapi.responses import JSONResponse, StreamingResponse
from pydantic import BaseModel, Field
import uvicorn
# 加载环境变量
load_dotenv()
# 设置 DashScope API 密钥
dashscope.api_key = os.getenv("ALIYUN_API_KEY")
# 创建 FastAPI 应用
app = FastAPI(title="AI Chat API Server", version="1.0.0")
# 数据模型定义
class ChatMessage(BaseModel):
role: str
content: str
images: Optional[List[str]] = None
files: Optional[List[str]] = None
class ChatRequest(BaseModel):
conversationId: Optional[str] = None
message: str
images: Optional[List[str]] = None
files: Optional[List[str]] = None
model: Optional[str] = "qwen-plus"
temperature: Optional[float] = 0.7
maxTokens: Optional[int] = 2000
systemPrompt: Optional[str] = "你是一个支持视觉理解的助手。"
stream: Optional[bool] = True
# 扩展选项
deepSearch: Optional[bool] = False
webSearch: Optional[bool] = False
deepThinking: Optional[bool] = False
class ModelInfo(BaseModel):
id: str
name: str
description: str
maxTokens: int
provider: str
# 模拟数据库 - 实际应用中应使用持久化存储
conversations_db: Dict[str, dict] = {}
# 配置上传目录
upload_dir = Path("uploads")
upload_dir.mkdir(exist_ok=True)
@app.middleware("http")
async def add_process_time_header(request, call_next):
"""中间件:记录请求处理时间"""
start_time = datetime.utcnow()
response = await call_next(request)
# 计算处理时间
process_time = (datetime.utcnow() - start_time).total_seconds() * 1000
# 在响应头中添加处理时间
response.headers["X-Process-Time"] = f"{process_time:.2f}ms"
# 记录请求信息
print(f"HTTP {request.method} {request.url.path} {response.status_code} {process_time:.2f}ms")
return response
@app.get("/health")
async def health_check():
"""健康检查端点"""
return {"status": "healthy", "timestamp": datetime.utcnow().isoformat()}
@app.post("/api/chat-ui/chat")
async def chat_endpoint(request: ChatRequest):
"""聊天接口 - 处理普通请求"""
try:
# 构建消息数组,考虑是否包含图片
user_content = []
# 添加用户消息文本
user_content.append({"type": "text", "text": request.message})
# 如果有图片,则添加到内容中
if request.images and len(request.images) > 0:
for image_url in request.images:
user_content.append({
"type": "image_url",
"image_url": image_url
})
# 构建请求给百炼的消息列表
messages = [
{"role": "system", "content": request.systemPrompt},
{"role": "user", "content": user_content}
]
# 调用 DashScope API
response = Generation.call(
model=request.model,
messages=messages,
stream=False, # 非流式响应
max_tokens=request.maxTokens,
temperature=request.temperature
)
if response.status_code == 200:
content = response.output.choices[0]['message']['content']
# 构建响应
result = {
"id": str(uuid.uuid4()),
"conversationId": request.conversationId or str(uuid.uuid4()),
"content": content,
"model": request.model,
"createdAt": int(datetime.utcnow().timestamp())
}
if hasattr(response, 'usage'):
result["usage"] = {
"promptTokens": response.usage.input_tokens,
"completionTokens": response.usage.output_tokens,
"totalTokens": response.usage.total_tokens
}
return JSONResponse(content=result)
else:
raise HTTPException(status_code=500, detail=f"API Error: {response.code} - {response.message}")
except Exception as e:
print(f"Error in chat endpoint: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/chat-ui/chat/stream")
async def chat_stream_endpoint(request: ChatRequest):
"""流式聊天接口 - 处理流式请求"""
async def event_generator():
try:
# 构建消息数组,考虑是否包含图片
user_content = []
# 添加用户消息文本
user_content.append({"type": "text", "text": request.message})
# 如果有图片,则添加到内容中
if request.images and len(request.images) > 0:
for image_url in request.images:
user_content.append({
"type": "image_url",
"image_url": image_url
})
# 构建请求给百炼的消息列表
messages = [
{"role": "system", "content": request.systemPrompt},
{"role": "user", "content": user_content}
]
# 调用 DashScope API流式
responses = Generation.call(
model=request.model,
messages=messages,
stream=True, # 流式响应
max_tokens=request.maxTokens,
temperature=request.temperature
)
for response in responses:
if response.status_code == 200:
content = response.output.choices[0]['message']['content']
if content:
# 发送流式数据
data = {
"choices": [
{
"delta": {"content": content},
"index": 0,
"finish_reason": None
}
]
}
yield f"data: {json.dumps(data)}\n\n"
else:
error_data = {
"error": {
"message": f"API Error: {response.code} - {response.message}",
"type": "api_error",
"param": None,
"code": response.code
}
}
yield f"data: {json.dumps(error_data)}\n\n"
break
# 发送结束信号
yield "data: [DONE]\n\n"
except Exception as e:
error_data = {
"error": {
"message": str(e),
"type": "server_error"
}
}
yield f"data: {json.dumps(error_data)}\n\n"
return StreamingResponse(event_generator(), media_type="text/event-stream")
@app.get("/api/chat-ui/models")
async def get_models():
"""获取模型列表"""
models = [
ModelInfo(
id="qwen-max",
name="通义千问 Max",
description="最强大的模型",
maxTokens=8192,
provider="Aliyun"
),
ModelInfo(
id="qwen-plus",
name="通义千问 Plus",
description="能力均衡",
maxTokens=8192,
provider="Aliyun"
)
]
return [model.dict() for model in models]
@app.get("/api/chat-ui/conversations")
async def get_conversations():
"""获取所有对话"""
return list(conversations_db.values())
@app.get("/api/chat-ui/conversations/{conversation_id}")
async def get_conversation(conversation_id: str):
"""获取特定对话"""
conversation = conversations_db.get(conversation_id)
if not conversation:
raise HTTPException(status_code=404, detail="对话不存在")
return conversation
@app.post("/api/chat-ui/conversations")
async def save_conversation(
id: str = Form(None),
title: str = Form(...),
messages: str = Form(...)
):
"""保存或更新对话"""
# 解析 messages JSON 字符串
try:
parsed_messages = json.loads(messages)
except json.JSONDecodeError:
raise HTTPException(status_code=400, detail="Invalid messages JSON")
conversation_id = id or str(uuid.uuid4())
conversation = {
"id": conversation_id,
"title": title,
"messages": parsed_messages,
"updatedAt": datetime.utcnow().isoformat()
}
conversations_db[conversation_id] = conversation
return conversation
@app.delete("/api/chat-ui/conversations/{conversation_id}")
async def delete_conversation(conversation_id: str):
"""删除对话"""
if conversation_id in conversations_db:
del conversations_db[conversation_id]
return {"success": True, "message": "删除成功"}
else:
raise HTTPException(status_code=404, detail="对话不存在")
@app.post("/api/chat-ui/upload")
async def upload_file(file: UploadFile = File(...)):
"""文件上传接口"""
try:
# 生成唯一文件名
file_extension = Path(file.filename).suffix
unique_filename = f"{int(datetime.utcnow().timestamp())}-{uuid.uuid4()}{file_extension}"
file_path = upload_dir / unique_filename
# 保存文件
with open(file_path, "wb") as f:
content = await file.read()
f.write(content)
# 返回文件信息
file_url = f"http://localhost:8000/uploads/{unique_filename}"
return {
"url": file_url,
"name": file.filename,
"size": len(content),
"mimeType": file.content_type
}
except Exception as e:
print(f"Upload error: {str(e)}")
raise HTTPException(status_code=500, detail=f"上传失败: {str(e)}")
@app.get("/uploads/{filename}")
async def serve_upload(filename: str):
"""提供上传文件的访问"""
file_path = upload_dir / filename
if not file_path.exists():
raise HTTPException(status_code=404, detail="文件不存在")
from fastapi.responses import FileResponse
return FileResponse(file_path)
@app.post("/api/chat-ui/stop")
async def stop_generation():
"""停止生成接口"""
# 在实际实现中这里可能需要维护正在运行的任务ID列表
# 目前只是返回成功消息
return {"success": True, "message": "已发出停止指令"}
@app.post("/api/chat-ui/stop/{message_id}")
async def stop_generation_by_id(message_id: str):
"""根据消息ID停止生成"""
return {"success": True, "message": "已发出停止指令"}
if __name__ == "__main__":
port = int(os.getenv("PORT", 8000))
uvicorn.run(app, host="0.0.0.0", port=port)

View File

@ -0,0 +1,8 @@
fastapi==0.115.4
uvicorn==0.32.0
dashscope==1.20.12
python-multipart==0.0.18
python-dotenv==1.0.1
aiofiles==24.1.0
pydantic==2.9.2
typing-extensions==4.12.2

27
start_python_server.sh Normal file
View File

@ -0,0 +1,27 @@
#!/bin/bash
# 启动Python服务器的增强脚本
echo "启动Python AI Chat服务器..."
# 检查是否有服务器已经在8000端口运行
if lsof -Pi :8000 -sTCP:LISTEN -t >/dev/null; then
echo "错误: 端口8000已被占用。请先停止占用该端口的进程。"
exit 1
fi
# 切换到服务器目录
cd /home/mt/project/ai-chat-ui/server_python
# 检查虚拟环境是否存在
if [ ! -d ".venv" ]; then
echo "错误: 虚拟环境不存在。请先创建虚拟环境:"
echo "python3 -m venv .venv"
echo "source .venv/bin/activate"
echo "pip install -r requirements.txt"
exit 1
fi
echo "虚拟环境已找到,正在激活..."
# 激活虚拟环境并启动服务器
source .venv/bin/activate && python3 app.py

13
stop_python_server.sh Normal file
View File

@ -0,0 +1,13 @@
#!/bin/bash
# 停止Python服务器的脚本
echo "正在停止Python AI Chat服务器..."
# 查找并终止在8000端口运行的Python进程
PID=$(lsof -t -i:8000)
if [ -n "$PID" ]; then
kill $PID
echo "服务器进程 (PID: $PID) 已停止"
else
echo "在端口8000上没有找到运行的服务器"
fi

View File

@ -18,7 +18,7 @@ export default defineConfig({
host: "0.0.0.0",
proxy: {
"/api/chat-ui": {
target: "http://localhost:3000",
target: "http://localhost:8000", // Python服务器端口
changeOrigin: true,
},
},