Spaces:
Sleeping
Sleeping
Sun Jun 8 12:53:28 CST 2025
Browse files- 0-git-all.sh +7 -0
- 1-git-commit-dev.sh +3 -0
- git-push-dev.sh → 2-git-push-dev.sh +0 -0
- git-merge.sh → 3-git-commit-merge copy.sh +3 -0
- app.py +4 -60
0-git-all.sh
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
git checkout dev
|
2 |
+
git add .
|
3 |
+
git commit -m "$(date)"
|
4 |
+
git push
|
5 |
+
git checkout main
|
6 |
+
git merge dev
|
7 |
+
git checkout dev
|
1-git-commit-dev.sh
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
git checkout dev
|
2 |
+
git add .
|
3 |
+
git commit -m "$(date)"
|
git-push-dev.sh → 2-git-push-dev.sh
RENAMED
File without changes
|
git-merge.sh → 3-git-commit-merge copy.sh
RENAMED
@@ -1,3 +1,6 @@
|
|
|
|
|
|
|
|
1 |
git checkout main
|
2 |
git merge dev
|
3 |
git checkout dev
|
|
|
1 |
+
git checkout dev
|
2 |
+
git add .
|
3 |
+
git commit -m "$(date)"
|
4 |
git checkout main
|
5 |
git merge dev
|
6 |
git checkout dev
|
app.py
CHANGED
@@ -4,8 +4,7 @@ import httpx
|
|
4 |
import logging
|
5 |
import os, json
|
6 |
import re # 用于URL路径处理
|
7 |
-
from
|
8 |
-
from key_selector import KeySelector
|
9 |
|
10 |
def get_target_url(url: str) -> str:
|
11 |
"""将url参数变了转换为合法的目标url;from http/ or https/ to http:// or https://"""
|
@@ -20,9 +19,7 @@ logging.basicConfig(level=logging.INFO)
|
|
20 |
logger = logging.getLogger("uvicorn.error")
|
21 |
|
22 |
# 从环境变量获取配置
|
23 |
-
#
|
24 |
-
# AUTH_TOKEN = os.getenv("AUTH_TOKEN", "YIG8ANC8q2QxFV_Gf8qwkPdBj2EpsqGqlfc3qvSdg7ksVkZcokOUtQn43XGK0NK3UUdBlIkYzNWefco_Wu4RcKnB0kpNgtZ2nTeqNum0i3fTUEhEcWSlJtT8FQgRK7bi") # 安全认证令牌
|
25 |
-
X_Goog_Api_Key = os.getenv("X_Goog_Api_Key", "")
|
26 |
|
27 |
@app.get("/")
|
28 |
async def read_root():
|
@@ -32,40 +29,24 @@ async def read_root():
|
|
32 |
async def proxy(request: Request, path: str):
|
33 |
# 添加流式请求判断逻辑
|
34 |
is_streaming = ":streamGenerateContent" in path.lower()
|
35 |
-
print(f"path: {path}")
|
36 |
-
# path = "https/generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent"
|
37 |
-
# target_url = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent"
|
38 |
target_url = get_target_url(path)
|
39 |
-
# target_url = f"{target_url}?key={X_Goog_Api_Key}"
|
40 |
-
print(f"target_url: {target_url}")
|
41 |
method = request.method
|
42 |
headers = {k: v for k, v in request.headers.items()
|
43 |
# if k.lower() not in ["host", "connection", "Postman-Token", "content-length"]}
|
44 |
if k.lower() not in ["host", "content-length"]}
|
45 |
|
46 |
key_selector = KeySelector()
|
47 |
-
|
48 |
-
|
49 |
-
headers["X-Goog-Api-Key"] = key_selector.get_api_key()['key_value']
|
50 |
-
# print(key_selector.api_key_info.key_value)
|
51 |
-
# headers["host"] = "generativelanguage.googleapis.com"
|
52 |
|
53 |
try:
|
54 |
# 关键修复:禁用KeepAlive防止连接冲突
|
55 |
transport = httpx.AsyncHTTPTransport(retries=3, http1=True)
|
56 |
-
|
57 |
async with httpx.AsyncClient(
|
58 |
transport=transport,
|
59 |
timeout=httpx.Timeout(300.0, connect=30.0)
|
60 |
) as client:
|
61 |
# 处理请求体
|
62 |
req_content = await request.body()
|
63 |
-
print(f"Request headers: {headers}")
|
64 |
-
# print(f"req_content: {req_content}")
|
65 |
-
# req_content_str = req_content.decode('utf-8') # 假设内容是 UTF-8 编码
|
66 |
-
# print(f"req_content_str: {req_content_str}")
|
67 |
-
print(f'target_url: {target_url}')
|
68 |
-
print(f'method: {method}')
|
69 |
# 发送请求到上游服务
|
70 |
response = await client.request(
|
71 |
method=method,
|
@@ -74,7 +55,6 @@ async def proxy(request: Request, path: str):
|
|
74 |
content=req_content,
|
75 |
follow_redirects=True # 自动处理重定向
|
76 |
)
|
77 |
-
|
78 |
if is_streaming:
|
79 |
# 流式响应处理
|
80 |
async def stream_generator():
|
@@ -88,7 +68,6 @@ async def proxy(request: Request, path: str):
|
|
88 |
# 移除冲突头部
|
89 |
headers = dict(response.headers)
|
90 |
headers.pop("Content-Length", None)
|
91 |
-
|
92 |
return StreamingResponse(
|
93 |
content=stream_generator(),
|
94 |
status_code=response.status_code,
|
@@ -96,16 +75,12 @@ async def proxy(request: Request, path: str):
|
|
96 |
media_type="application/x-ndjson" # Gemini流式格式
|
97 |
)
|
98 |
else:
|
99 |
-
|
100 |
-
print(f"response.status_code: {response.status_code}")
|
101 |
-
print(f"response.text: {response.text}")
|
102 |
# 解析 JSON 字符串
|
103 |
try:
|
104 |
data = json.loads(response.text)
|
105 |
# 格式化输出 JSON 数据
|
106 |
formatted_json = json.dumps(data, ensure_ascii=False, indent=4)
|
107 |
-
print('formatted_json')
|
108 |
-
print(formatted_json)
|
109 |
# return formatted_json
|
110 |
return Response(
|
111 |
content=formatted_json,
|
@@ -113,23 +88,6 @@ async def proxy(request: Request, path: str):
|
|
113 |
)
|
114 |
except json.JSONDecodeError as e:
|
115 |
print(f"Error decoding JSON: {e}")
|
116 |
-
|
117 |
-
|
118 |
-
# # 详细记录错误响应
|
119 |
-
# if response.status_code >= 400:
|
120 |
-
# error_detail = response.text[:1000] # 增加错误详情长度
|
121 |
-
# logger.error(f"Upstream error {response.status_code}: {error_detail}")
|
122 |
-
# # 可选:记录完整响应到文件
|
123 |
-
# # with open("error.log", "a") as f:
|
124 |
-
# # f.write(f"\n--- {target_url} ---\n{response.text}\n")
|
125 |
-
|
126 |
-
# # 流式传输响应
|
127 |
-
# return StreamingResponse(
|
128 |
-
# content=response.aiter_bytes(),
|
129 |
-
# status_code=response.status_code,
|
130 |
-
# headers=dict(response.headers)
|
131 |
-
# )
|
132 |
-
|
133 |
except httpx.ConnectError as e:
|
134 |
logger.error(f"Connection failed to {target_url}: {e}")
|
135 |
raise HTTPException(502, f"无法连接到上游服务: {target_url}") # Modified error message
|
@@ -137,15 +95,11 @@ async def proxy(request: Request, path: str):
|
|
137 |
logger.error(f"Timeout: {e}")
|
138 |
raise HTTPException(504, "上游服务响应超时")
|
139 |
except httpx.HTTPError as e: # 捕获所有HTTP异常
|
140 |
-
print('111111111111111111111')
|
141 |
-
|
142 |
try:
|
143 |
# 安全地获取异常信息
|
144 |
error_type = type(e).__name__
|
145 |
-
|
146 |
# 尝试获取状态码(如果存在)
|
147 |
status_code = getattr(e, 'response', None) and e.response.status_code
|
148 |
-
|
149 |
# 安全地获取错误详情
|
150 |
error_detail = ""
|
151 |
try:
|
@@ -154,7 +108,6 @@ async def proxy(request: Request, path: str):
|
|
154 |
error_detail = e.response.text[:500] # 只取前500个字符
|
155 |
except Exception as ex:
|
156 |
error_detail = f"无法获取错误详情: {type(ex).__name__}"
|
157 |
-
|
158 |
# 安全地记录日志
|
159 |
logger.error(
|
160 |
"HTTP代理错误 | "
|
@@ -163,13 +116,10 @@ async def proxy(request: Request, path: str):
|
|
163 |
f"目标URL: {target_url} | "
|
164 |
f"详情: {error_detail[:200]}" # 日志中只记录前200字符
|
165 |
)
|
166 |
-
|
167 |
# 打印到控制台以便调试
|
168 |
-
print(f"111111111111111111111 - HTTP代理错误: {error_type}")
|
169 |
print(f"目标URL: {target_url}")
|
170 |
print(f"状态码: {status_code or 'N/A'}")
|
171 |
print(f"错误详情: {error_detail[:500]}")
|
172 |
-
|
173 |
except Exception as ex:
|
174 |
# 如果记录日志本身出错,使用最安全的方式记录
|
175 |
logger.error(f"记录HTTP错误时发生异常: {type(ex).__name__}")
|
@@ -180,12 +130,6 @@ async def proxy(request: Request, path: str):
|
|
180 |
status_code=502,
|
181 |
detail=f"网关服务错误: {error_type} (上游状态: {status_code or '未知'})"
|
182 |
)
|
183 |
-
|
184 |
-
|
185 |
-
print('111111111111111111111')
|
186 |
-
# logger.error(f"HTTP error: {str(e)}")
|
187 |
-
|
188 |
-
raise HTTPException(502, f"网关错误: {str(e)}")
|
189 |
except Exception as e:
|
190 |
logger.exception("Unexpected proxy error")
|
191 |
raise HTTPException(500, f"内部服务器错误: {str(e)}")
|
|
|
4 |
import logging
|
5 |
import os, json
|
6 |
import re # 用于URL路径处理
|
7 |
+
from key_selector import KeySelector # 自动选择key
|
|
|
8 |
|
9 |
def get_target_url(url: str) -> str:
|
10 |
"""将url参数变了转换为合法的目标url;from http/ or https/ to http:// or https://"""
|
|
|
19 |
logger = logging.getLogger("uvicorn.error")
|
20 |
|
21 |
# 从环境变量获取配置
|
22 |
+
# X_Goog_Api_Key = os.getenv("X_Goog_Api_Key", "")
|
|
|
|
|
23 |
|
24 |
@app.get("/")
|
25 |
async def read_root():
|
|
|
29 |
async def proxy(request: Request, path: str):
|
30 |
# 添加流式请求判断逻辑
|
31 |
is_streaming = ":streamGenerateContent" in path.lower()
|
|
|
|
|
|
|
32 |
target_url = get_target_url(path)
|
|
|
|
|
33 |
method = request.method
|
34 |
headers = {k: v for k, v in request.headers.items()
|
35 |
# if k.lower() not in ["host", "connection", "Postman-Token", "content-length"]}
|
36 |
if k.lower() not in ["host", "content-length"]}
|
37 |
|
38 |
key_selector = KeySelector()
|
39 |
+
headers["X-Goog-Api-Key"] = key_selector.get_api_key()['key_value'] # 从数据库获取API密钥
|
|
|
|
|
|
|
|
|
40 |
|
41 |
try:
|
42 |
# 关键修复:禁用KeepAlive防止连接冲突
|
43 |
transport = httpx.AsyncHTTPTransport(retries=3, http1=True)
|
|
|
44 |
async with httpx.AsyncClient(
|
45 |
transport=transport,
|
46 |
timeout=httpx.Timeout(300.0, connect=30.0)
|
47 |
) as client:
|
48 |
# 处理请求体
|
49 |
req_content = await request.body()
|
|
|
|
|
|
|
|
|
|
|
|
|
50 |
# 发送请求到上游服务
|
51 |
response = await client.request(
|
52 |
method=method,
|
|
|
55 |
content=req_content,
|
56 |
follow_redirects=True # 自动处理重定向
|
57 |
)
|
|
|
58 |
if is_streaming:
|
59 |
# 流式响应处理
|
60 |
async def stream_generator():
|
|
|
68 |
# 移除冲突头部
|
69 |
headers = dict(response.headers)
|
70 |
headers.pop("Content-Length", None)
|
|
|
71 |
return StreamingResponse(
|
72 |
content=stream_generator(),
|
73 |
status_code=response.status_code,
|
|
|
75 |
media_type="application/x-ndjson" # Gemini流式格式
|
76 |
)
|
77 |
else:
|
78 |
+
# 非流式响应处理
|
|
|
|
|
79 |
# 解析 JSON 字符串
|
80 |
try:
|
81 |
data = json.loads(response.text)
|
82 |
# 格式化输出 JSON 数据
|
83 |
formatted_json = json.dumps(data, ensure_ascii=False, indent=4)
|
|
|
|
|
84 |
# return formatted_json
|
85 |
return Response(
|
86 |
content=formatted_json,
|
|
|
88 |
)
|
89 |
except json.JSONDecodeError as e:
|
90 |
print(f"Error decoding JSON: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
91 |
except httpx.ConnectError as e:
|
92 |
logger.error(f"Connection failed to {target_url}: {e}")
|
93 |
raise HTTPException(502, f"无法连接到上游服务: {target_url}") # Modified error message
|
|
|
95 |
logger.error(f"Timeout: {e}")
|
96 |
raise HTTPException(504, "上游服务响应超时")
|
97 |
except httpx.HTTPError as e: # 捕获所有HTTP异常
|
|
|
|
|
98 |
try:
|
99 |
# 安全地获取异常信息
|
100 |
error_type = type(e).__name__
|
|
|
101 |
# 尝试获取状态码(如果存在)
|
102 |
status_code = getattr(e, 'response', None) and e.response.status_code
|
|
|
103 |
# 安全地获取错误详情
|
104 |
error_detail = ""
|
105 |
try:
|
|
|
108 |
error_detail = e.response.text[:500] # 只取前500个字符
|
109 |
except Exception as ex:
|
110 |
error_detail = f"无法获取错误详情: {type(ex).__name__}"
|
|
|
111 |
# 安全地记录日志
|
112 |
logger.error(
|
113 |
"HTTP代理错误 | "
|
|
|
116 |
f"目标URL: {target_url} | "
|
117 |
f"详情: {error_detail[:200]}" # 日志中只记录前200字符
|
118 |
)
|
|
|
119 |
# 打印到控制台以便调试
|
|
|
120 |
print(f"目标URL: {target_url}")
|
121 |
print(f"状态码: {status_code or 'N/A'}")
|
122 |
print(f"错误详情: {error_detail[:500]}")
|
|
|
123 |
except Exception as ex:
|
124 |
# 如果记录日志本身出错,使用最安全的方式记录
|
125 |
logger.error(f"记录HTTP错误时发生异常: {type(ex).__name__}")
|
|
|
130 |
status_code=502,
|
131 |
detail=f"网关服务错误: {error_type} (上游状态: {status_code or '未知'})"
|
132 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
133 |
except Exception as e:
|
134 |
logger.exception("Unexpected proxy error")
|
135 |
raise HTTPException(500, f"内部服务器错误: {str(e)}")
|