This commit is contained in:
2025-08-30 16:18:32 +08:00
parent a94e656acc
commit 9bf3ca79b0
3 changed files with 2631 additions and 0 deletions

View File

@@ -0,0 +1,356 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
OpenAI兼容接口到阿里云百炼平台智能体应用的转发服务
"""
import os
import json
import logging
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse
import requests
import threading
import time
import uuid
from dotenv import load_dotenv
# 尝试加载.env文件
load_dotenv()
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler("proxy.log", encoding='utf-8'),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
# 从环境变量获取配置
DASHSCOPE_API_KEY = os.getenv("DASHSCOPE_API_KEY")
APP_ID = os.getenv("DASHSCOPE_APP_ID")
# 检查必要配置
if not DASHSCOPE_API_KEY:
raise ValueError("请设置环境变量 DASHSCOPE_API_KEY")
if not APP_ID:
raise ValueError("请设置环境变量 DASHSCOPE_APP_ID")
# 阿里云百炼平台智能体应用的URL
DASHSCOPE_APP_URL = f"https://dashscope.aliyuncs.com/api/v1/apps/{APP_ID}/completion"
class OpenAIProxyHandler(BaseHTTPRequestHandler):
"""处理OpenAI兼容请求并转发到阿里云百炼平台"""
def log_message(self, format, *args):
"""重写日志方法,使用我们的日志配置"""
logger.info("%s - - [%s] %s" % (self.address_string(), self.log_date_time_string(), format % args))
def do_POST(self):
"""处理POST请求"""
try:
# 解析请求路径
parsed_path = urlparse(self.path)
# 只处理聊天完成接口
if parsed_path.path == "/v1/chat/completions":
self.handle_chat_completions()
else:
self.send_error(404, "接口未找到")
except Exception as e:
logger.error(f"处理POST请求时出错: {e}")
try:
self.send_error(500, "内部服务器错误")
except:
pass # 客户端可能已经断开连接
def do_GET(self):
"""处理GET请求"""
try:
# 解析请求路径
parsed_path = urlparse(self.path)
# 处理模型列表请求
if parsed_path.path == "/v1/models":
self.handle_list_models()
else:
self.send_error(404, "接口未找到")
except Exception as e:
logger.error(f"处理GET请求时出错: {e}")
try:
self.send_error(500, "内部服务器错误")
except:
pass # 客户端可能已经断开连接
def handle_list_models(self):
"""处理模型列表请求"""
try:
# 构造OpenAI兼容的模型列表响应
models_response = {
"object": "list",
"data": [
{
"id": "dashscope-app",
"object": "model",
"created": int(time.time()),
"owned_by": "dashscope"
}
]
}
# 发送响应
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.send_header('Access-Control-Allow-Origin', '*')
self.end_headers()
response_json = json.dumps(models_response, ensure_ascii=False)
self.wfile.write(response_json.encode('utf-8'))
except Exception as e:
logger.error(f"处理模型列表请求时出错: {e}")
try:
self.send_error(500, "内部服务器错误")
except:
pass # 客户端可能已经断开连接
def handle_chat_completions(self):
"""处理聊天完成请求"""
try:
# 读取请求内容
content_length = int(self.headers.get('Content-Length', 0))
post_data = self.rfile.read(content_length)
# 解析JSON数据
request_data = json.loads(post_data.decode('utf-8'))
# 记录接收到的请求
logger.info(f"收到OpenAI兼容请求: {json.dumps(request_data, ensure_ascii=False)}")
# 提取用户消息
messages = request_data.get('messages', [])
if not messages:
self.send_error(400, "缺少消息内容")
return
# 获取最后一条用户消息作为提示词
prompt = ""
conversation_history = []
for message in messages:
role = message.get('role')
content = message.get('content', '')
conversation_history.append(f"{role}: {content}")
if role == 'user':
prompt = content
if not prompt:
self.send_error(400, "未找到用户消息")
return
# 记录提取的提示词和对话历史
logger.info(f"提取的用户提示词: {prompt}")
logger.info(f"完整对话历史: {' | '.join(conversation_history)}")
# 构造阿里云百炼平台请求
dashscope_request = {
"input": {
"prompt": prompt
},
"parameters": {},
"debug": {}
}
# 处理流式请求
stream = request_data.get('stream', False)
# 设置请求头
headers = {
"Authorization": f"Bearer {DASHSCOPE_API_KEY}",
"Content-Type": "application/json"
}
if stream:
headers["Accept"] = "text/event-stream"
headers["X-DashScope-SSE"] = "enable"
dashscope_request["parameters"]["stream"] = True
dashscope_request["parameters"]["incremental_output"] = True
# 记录发送给阿里云百炼平台的请求
logger.info(f"发送到阿里云百炼平台的请求: {json.dumps(dashscope_request, ensure_ascii=False)}")
# 转发请求到阿里云百炼平台
response = requests.post(
DASHSCOPE_APP_URL,
headers=headers,
json=dashscope_request,
stream=stream
)
# 记录阿里云百炼平台的响应状态
logger.info(f"阿里云百炼平台响应状态: {response.status_code}")
# 设置响应头
self.send_response(response.status_code)
# 复制响应头
for key, value in response.headers.items():
# 过滤掉一些不需要的头
if key.lower() not in ['connection', 'transfer-encoding']:
self.send_header(key, value)
self.end_headers()
# 转发响应内容
if stream:
logger.info("处理流式响应")
# 流式响应
try:
for chunk in response.iter_content(chunk_size=1024):
if chunk:
self.wfile.write(chunk)
self.wfile.flush()
except ConnectionAbortedError:
logger.warning("客户端中断了连接")
else:
# 普通响应
# 读取响应内容
response_content = response.text
# 记录响应内容
logger.info(f"阿里云百炼平台响应内容: {response_content[:200]}...") # 只记录前200个字符
# 尝试解析并转换为OpenAI格式
try:
dashscope_response = json.loads(response_content)
openai_response = self.convert_to_openai_format(dashscope_response, request_data)
response_json = json.dumps(openai_response, ensure_ascii=False)
logger.info(f"转换后的OpenAI格式响应: {response_json[:200]}...") # 只记录前200个字符
self.wfile.write(response_json.encode('utf-8'))
except json.JSONDecodeError:
# 如果不是JSON格式构造一个标准的OpenAI格式响应
logger.warning("响应不是JSON格式构造标准OpenAI格式响应")
openai_response = {
"id": f"chatcmpl-{uuid.uuid4().hex}",
"object": "chat.completion",
"created": int(time.time()),
"model": request_data.get("model", "dashscope-app"),
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": response_content,
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": -1,
"completion_tokens": -1,
"total_tokens": -1
}
}
response_json = json.dumps(openai_response, ensure_ascii=False)
self.wfile.write(response_json.encode('utf-8'))
except Exception as e:
logger.error(f"处理聊天完成请求时出错: {e}")
try:
self.send_error(500, "处理请求时出错")
except:
pass # 如果无法发送错误响应,则忽略
def convert_to_openai_format(self, dashscope_response, request_data):
"""将阿里云百炼平台响应转换为OpenAI格式"""
try:
# 提取文本内容
text_content = dashscope_response.get("output", {}).get("text", "")
finish_reason = dashscope_response.get("output", {}).get("finish_reason", "stop")
# 构造OpenAI格式响应
openai_response = {
"id": f"chatcmpl-{uuid.uuid4().hex}",
"object": "chat.completion",
"created": int(time.time()),
"model": request_data.get("model", "dashscope-app"),
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": text_content,
},
"finish_reason": finish_reason
}
],
"usage": {
"prompt_tokens": -1,
"completion_tokens": -1,
"total_tokens": -1
}
}
# 如果有使用情况信息,添加到响应中
if "usage" in dashscope_response:
openai_response["usage"] = dashscope_response["usage"]
return openai_response
except Exception as e:
logger.error(f"转换响应格式时出错: {e}")
# 如果转换失败构造标准OpenAI格式响应
openai_response = {
"id": f"chatcmpl-{uuid.uuid4().hex}",
"object": "chat.completion",
"created": int(time.time()),
"model": request_data.get("model", "dashscope-app"),
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": str(dashscope_response),
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": -1,
"completion_tokens": -1,
"total_tokens": -1
}
}
return openai_response
def do_OPTIONS(self):
"""处理CORS预检请求"""
self.send_response(200)
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
self.send_header('Access-Control-Allow-Headers', '*')
self.end_headers()
def run_server(port=8000):
"""启动HTTP服务器"""
server_address = ('', port)
httpd = HTTPServer(server_address, OpenAIProxyHandler)
logger.info(f"OpenAI到阿里云百炼平台转发服务启动监听端口 {port}")
logger.info(f"Base URL: http://localhost:{port}/v1")
logger.info(f"模型名称: dashscope-app")
try:
httpd.serve_forever()
except KeyboardInterrupt:
logger.info("服务已停止")
httpd.server_close()
if __name__ == '__main__':
# 可以通过环境变量设置端口默认为8000
port = int(os.getenv("PROXY_PORT", 8000))
run_server(port)

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,207 @@
# OpenAI兼容接口到阿里云百炼平台智能体应用转发服务使用指南
本文档介绍了如何使用Python编写的转发服务将OpenAI兼容的API请求转换为阿里云百炼平台智能体应用的请求。
## 工作原理
该转发服务作为一个代理服务器运行接收OpenAI兼容的API请求并将其转换为阿里云百炼平台智能体应用的请求格式然后将响应转换回OpenAI格式返回给客户端。
```
客户端 -> OpenAI兼容请求 -> 转发服务 -> 阿里云百炼平台 -> 转发服务 -> OpenAI兼容响应 -> 客户端
```
## 配置要求
### 环境变量设置
需要设置以下环境变量:
1. `DASHSCOPE_API_KEY` - 阿里云百炼平台的API Key
2. `DASHSCOPE_APP_ID` - 智能体应用的APP ID
3. `PROXY_PORT` (可选) - 转发服务监听的端口默认为8000
您可以在项目目录下的 [.env](file://c:\Steam\steamapps\common\RimWorld\Mods\3516260226\MCP\.env) 文件中配置这些变量:
```env
DASHSCOPE_API_KEY="sk-xxxxxxxx"
DASHSCOPE_APP_ID="app-xxxxxxxx"
PROXY_PORT=8000
```
在Windows系统中设置环境变量的示例
```cmd
set DASHSCOPE_API_KEY=your_api_key_here
set DASHSCOPE_APP_ID=your_app_id_here
set PROXY_PORT=8000
```
在Linux/macOS系统中设置环境变量的示例
```bash
export DASHSCOPE_API_KEY=your_api_key_here
export DASHSCOPE_APP_ID=your_app_id_here
export PROXY_PORT=8000
```
## 启动服务
运行转发服务:
```bash
python openai_to_dashscope_proxy.py
```
服务启动后,将显示以下信息:
```
OpenAI到阿里云百炼平台转发服务启动监听端口 8000
Base URL: http://localhost:8000/v1
模型名称: dashscope-app
```
## 在Kilo Code中配置
在Kilo Code中按以下步骤配置
1. 打开Kilo Code设置面板
2. 选择"API Provider"为"OpenAI Compatible"
3. 设置Base URL为: `http://localhost:8000/v1` (如果使用默认端口)
4. API Key可以任意填写(服务不会验证)
5. 模型名称填写: `dashscope-app`
## 支持的功能
### 1. 基本文本对话
发送聊天完成请求:
```json
{
"model": "dashscope-app",
"messages": [
{
"role": "user",
"content": "你好,你是谁?"
}
]
}
```
### 2. 流式输出
启用流式输出:
```json
{
"model": "dashscope-app",
"messages": [
{
"role": "user",
"content": "讲一个有趣的故事"
}
],
"stream": true
}
```
## 技术细节
### 请求转换
OpenAI兼容请求格式
```json
{
"model": "dashscope-app",
"messages": [
{"role": "user", "content": "提示词"}
]
}
```
转换为阿里云百炼平台请求格式:
```json
{
"input": {
"prompt": "提示词"
},
"parameters": {},
"debug": {}
}
```
### 响应转换
阿里云百炼平台响应格式:
```json
{
"output": {
"text": "响应内容",
"finish_reason": "stop"
}
}
```
转换为OpenAI兼容响应格式
```json
{
"id": "chatcmpl-request_id",
"object": "chat.completion",
"created": 1234567890,
"model": "dashscope-app",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "响应内容"
},
"finish_reason": "stop"
}
]
}
```
## 故障排除
### 1. 环境变量未设置
错误信息:
```
ValueError: 请设置环境变量 DASHSCOPE_API_KEY
```
解决方案:
确保已正确设置环境变量 `DASHSCOPE_API_KEY``DASHSCOPE_APP_ID`
### 2. 网络连接问题
错误信息:
```
requests.exceptions.ConnectionError
```
解决方案:
检查网络连接,确保可以访问阿里云百炼平台。
### 3. API Key或APP ID错误
错误信息:
```
401 Unauthorized
```
解决方案:
检查API Key和APP ID是否正确。
### 4. 端口被占用
错误信息:
```
OSError: [Errno 98] Address already in use
```
解决方案:
更改端口号,通过设置 `PROXY_PORT` 环境变量或修改代码中的默认端口。
## 最佳实践
1. **安全性**:在生产环境中,建议将转发服务部署在安全的网络环境中
2. **监控**:启用日志记录以便监控服务运行状态
3. **错误处理**:确保正确处理各种错误情况
4. **性能**对于高并发场景考虑使用异步框架如FastAPI替代内置的HTTP服务器