-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
140 lines (116 loc) · 5.11 KB
/
main.py
File metadata and controls
140 lines (116 loc) · 5.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
"""
主应用文件 - 重构后的Ollama代理服务器
🎯 核心架构观察和设计理念:
=====================================
1. 前端使用 OpenAI 格式的请求和响应
- 所有客户端都发送标准的 OpenAI API 格式请求
- 响应也保持 OpenAI 格式,确保兼容性
2. 远端 Ollama 原生兼容 OpenAI 格式,无需转换
- 直接转发到 Ollama 服务器
- 保持原始请求格式和响应格式
3. 关键处理点:
○ /api/tags - 返回 Ollama 格式的模型列表
○ /api/show - 返回 Ollama 格式的模型信息
○ /v1/chat/completions - OpenAI 格式,检测虚拟模型:
■ 虚拟模型 → 转发到远程 API
■ 非虚拟模型 → 转发到 Ollama
○ 其他接口 → 直接转发到 Ollama
4. 特殊处理 Claude 模型:
○ OpenAI 请求 → Claude 格式
○ Claude 响应 → OpenAI 格式
○ 重点是工具调用的格式转换
🔧 路由设计原则:
- 精确路径在前,通配符在后
- 虚拟模型检测优先于直接转发
- 保持 API 格式的一致性和兼容性
"""
import uvicorn
from fastapi import FastAPI, Request
from config import SERVER_HOST, SERVER_PORT, ENVIRONMENT
from logger import logger
from model_manager import model_manager
from routes import router_handler
from middleware import request_logging_middleware
def create_app() -> FastAPI:
"""创建FastAPI应用"""
app = FastAPI(
title="Ollama Proxy Server",
version="2.0.0",
description="重构后的Ollama代理服务器,支持多种AI模型API"
)
# 添加中间件
app.middleware("http")(request_logging_middleware)
# 注册路由
register_routes(app)
return app
def register_routes(app: FastAPI):
"""注册所有路由"""
# 🔹 Ollama API 路由 - 标准端点(返回 Ollama 原生格式)
# 这些端点保持 Ollama 原生格式,用于模型管理和信息查询
app.add_api_route("/api/tags", router_handler.list_tags, methods=["GET"])
app.add_api_route("/api/show", router_handler.show_model, methods=["POST"])
app.add_api_route("/api/chat", router_handler.handle_chat, methods=["POST"])
app.add_api_route("/api/generate", router_handler.handle_generate, methods=["POST"])
# 🔹 OpenAI API 路由 - 核心虚拟模型处理点
# ⚠️ 路由顺序很重要:具体路径必须在通用路径之前
app.add_api_route("/v1/models/{model_id}", router_handler.openai_get_model, methods=["GET"])
app.add_api_route("/v1/models", router_handler.openai_list_models, methods=["GET"])
# 🎯 关键路由:/v1/chat/completions - 虚拟模型检测和分发
# 这是整个系统的核心:
# - 虚拟模型 → 转发到对应的远程 API(OpenAI/Claude/DeepSeek)
# - 非虚拟模型 → 转发到 Ollama 服务器
# - 保持 OpenAI 格式的请求和响应
app.add_api_route("/v1/chat/completions", router_handler.openai_chat_completions, methods=["POST"])
app.add_api_route("/v1/completions", router_handler.openai_completions, methods=["POST"])
app.add_api_route("/v1/embeddings", router_handler.openai_embeddings, methods=["POST"])
# 🔹 通配符路由 - 必须放在最后
# 捕获所有其他请求并转发到 Ollama
app.add_api_route("/{path:path}", router_handler.catch_all, methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"])
def print_startup_info():
"""打印启动信息"""
logger.info("=" * 60)
logger.info("🚀 Ollama Proxy Server Starting...")
logger.info("=" * 60)
logger.info(f"Environment: {ENVIRONMENT}")
logger.info(f"Server will run on: http://{SERVER_HOST}:{SERVER_PORT}")
logger.info("=" * 60)
logger.info("📝 Supported APIs:")
logger.info(" 🔹 Ollama API: /api/tags, /api/show, /api/chat, /api/generate")
logger.info(" 🔹 OpenAI API: /v1/models, /v1/chat/completions, /v1/completions, /v1/embeddings")
logger.info("=" * 60)
logger.info("🎯 核心功能:")
logger.info(" ✨ 虚拟模型检测和智能分发")
logger.info(" 🔄 OpenAI ↔ Claude 格式转换")
logger.info(" 🌐 多 API 提供商支持")
logger.info("=" * 60)
logger.info("🤖 Virtual Models:")
virtual_models = model_manager.get_all_virtual_models()
if virtual_models:
for model_name, config in virtual_models.items():
logger.info(f" 🔹 {model_name} -> {config['type']} ({config['model']})")
else:
logger.info(" ⚠️ No virtual models available")
logger.info("=" * 60)
logger.info("Press Ctrl+C to stop the server")
logger.info("=" * 60)
# 创建应用实例
app = create_app()
if __name__ == "__main__":
print_startup_info()
# 根据环境选择运行模式
if ENVIRONMENT == "development":
uvicorn.run(
"main:app",
host=SERVER_HOST,
port=SERVER_PORT,
log_level="warning",
reload=True,
reload_includes=["*.py"]
)
else:
uvicorn.run(
app,
host=SERVER_HOST,
port=SERVER_PORT,
log_level="warning"
)