tanbushi commited on
Commit
559f768
·
1 Parent(s): fa6601b
app.py CHANGED
@@ -7,6 +7,7 @@ import httpx # 使用httpx替代requests,因为requests是同步的,而FastA
7
  import os, json
8
  from dotenv import load_dotenv
9
  from enum import Enum
 
10
 
11
  # 加载环境变量
12
  load_dotenv()
@@ -14,6 +15,13 @@ load_dotenv()
14
  # 从环境变量获取代理自身的API密钥
15
  proxy_api_key = os.getenv("PROXY_API_KEY")
16
 
 
 
 
 
 
 
 
17
  # 定义HTTPBearer安全方案
18
  security = HTTPBearer()
19
 
@@ -43,23 +51,9 @@ def greet_json():
43
  async def health_check():
44
  return {"status": "ok", "message": "Proxy service is running."}
45
 
46
- gemini_api_keys_str = os.getenv("GEMINI_API_KEYS_STR")
47
- try:
48
- gemini_api_keys_arr = json.loads(gemini_api_keys_str)
49
- except json.JSONDecodeError as e:
50
- print(f"JSON 解析错误:{e}")
51
- gemini_api_keys_arr = [] # 解析失败时设置默认值
52
- except Exception as e:
53
- print(f"其他错误:{e}")
54
- gemini_api_keys_arr = []
55
-
56
- key_index = 0
57
- keys_count = len(gemini_api_keys_arr)
58
-
59
  @app.api_route("/v1/{protocol}/{host}/{path:path}", methods=["GET", "POST", "PUT", "DELETE"])
60
  # @app.api_route("/v1/{protocol}/{host}/{path:path}", methods=["POST"])
61
- async def proxy_gemini(request: Request, protocol: ProtocolType, host:str, path: str, proxy_api_key: str = Depends(verify_proxy_api_key)): # 添加代理认证依赖
62
- global key_index # 声明使用全局变量
63
  real_url = f"{protocol.value}://{host}/{path}"
64
 
65
  # 提取客户端请求的headers和body,透传给Gemini
@@ -67,26 +61,42 @@ async def proxy_gemini(request: Request, protocol: ProtocolType, host:str, path:
67
 
68
  # 移除FastAPI自带的Host头,避免Gemini校验报错
69
  client_headers.pop("host", None)
 
 
 
70
 
71
  # 将User-Agent改为curl/8.7.1,以模拟curl请求
72
  client_headers["User-Agent"] = "curl/8.7.1"
73
-
74
- # 添加Authorization头,用于Gemini的OpenAI兼容API
75
- if gemini_api_keys_arr: # 确保有可用的API密钥
76
- print(f"key_index: {key_index}")
77
- gemini_api_key = gemini_api_keys_arr[key_index]
78
- key_index = (key_index + 1) % keys_count
79
- print(f"next_key_index: {key_index}")
80
- client_headers["Authorization"] = f"Bearer {gemini_api_key}"
81
- elif gemini_api_key: # 如果没有API密钥列表,使用单个API密钥
82
- client_headers["Authorization"] = f"Bearer {gemini_api_key}"
83
- else:
84
- raise HTTPException(status_code=500, detail="No Gemini API keys configured.")
85
-
86
  try:
87
  # 读取客户端请求体
88
  client_body = await request.body()
89
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  # 使用httpx异步客户端发起请求
91
  async with httpx.AsyncClient() as client:
92
  response = await client.request(
@@ -98,6 +108,7 @@ async def proxy_gemini(request: Request, protocol: ProtocolType, host:str, path:
98
  )
99
 
100
  # 将Gemini的响应透传给客户端
 
101
  return Response(
102
  content=response.content,
103
  status_code=response.status_code,
@@ -110,3 +121,26 @@ async def proxy_gemini(request: Request, protocol: ProtocolType, host:str, path:
110
  raise HTTPException(status_code=e.response.status_code, detail=f"目标API返回错误:{e.response.text}")
111
  except Exception as e:
112
  raise HTTPException(status_code=500, detail=f"转发失败:{str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  import os, json
8
  from dotenv import load_dotenv
9
  from enum import Enum
10
+ from supabase import create_client, Client
11
 
12
  # 加载环境变量
13
  load_dotenv()
 
15
  # 从环境变量获取代理自身的API密钥
16
  proxy_api_key = os.getenv("PROXY_API_KEY")
17
 
18
+ # Supabase 配置
19
+ SUPABASE_URL = os.getenv("SUPABASE_URL")
20
+ SUPABASE_KEY = os.getenv("SUPABASE_KEY")
21
+
22
+ # 初始化 Supabase 客户端
23
+ supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
24
+
25
  # 定义HTTPBearer安全方案
26
  security = HTTPBearer()
27
 
 
51
  async def health_check():
52
  return {"status": "ok", "message": "Proxy service is running."}
53
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  @app.api_route("/v1/{protocol}/{host}/{path:path}", methods=["GET", "POST", "PUT", "DELETE"])
55
  # @app.api_route("/v1/{protocol}/{host}/{path:path}", methods=["POST"])
56
+ async def proxy(request: Request, protocol: ProtocolType, host:str, path: str, proxy_api_key: str = Depends(verify_proxy_api_key)): # 添加代理认证依赖
 
57
  real_url = f"{protocol.value}://{host}/{path}"
58
 
59
  # 提取客户端请求的headers和body,透传给Gemini
 
61
 
62
  # 移除FastAPI自带的Host头,避免Gemini校验报错
63
  client_headers.pop("host", None)
64
+ client_headers.pop("authorization", None)
65
+ client_headers.pop("user-agent", None)
66
+ client_headers.pop("content-length", None)
67
 
68
  # 将User-Agent改为curl/8.7.1,以模拟curl请求
69
  client_headers["User-Agent"] = "curl/8.7.1"
70
+
71
+ # 获取 api_key
72
+ api_key = await get_api_key('glm-4-flash')
73
+ print('\n\napi_key', api_key)
74
+ if not api_key:
75
+ raise HTTPException(status_code=500, detail="没有可用的API密钥!")
76
+
77
+ # 添加Authorization
78
+ client_headers["Authorization"] = f"Bearer {api_key}"
79
+
 
 
 
80
  try:
81
  # 读取客户端请求体
82
  client_body = await request.body()
83
 
84
+ # 尝试从请求体中获取model
85
+ model_from_body = None
86
+ try:
87
+ if client_body:
88
+ body_json = json.loads(client_body)
89
+ model_from_body = body_json.get("model")
90
+ except json.JSONDecodeError:
91
+ # 如果请求体不是JSON,则忽略
92
+ pass
93
+
94
+ # 获取 api_key
95
+ api_key = await get_api_key(model_from_body if model_from_body else 'glm-4-flash')
96
+ print('\n\napi_key', api_key)
97
+ if not api_key:
98
+ raise HTTPException(status_code=500, detail="没有可用的API密钥!")
99
+
100
  # 使用httpx异步客户端发起请求
101
  async with httpx.AsyncClient() as client:
102
  response = await client.request(
 
108
  )
109
 
110
  # 将Gemini的响应透传给客户端
111
+ print('444444444444')
112
  return Response(
113
  content=response.content,
114
  status_code=response.status_code,
 
121
  raise HTTPException(status_code=e.response.status_code, detail=f"目标API返回错误:{e.response.text}")
122
  except Exception as e:
123
  raise HTTPException(status_code=500, detail=f"转发失败:{str(e)}")
124
+
125
+ async def get_api_key(model: str = None):
126
+ try:
127
+ # 根据用户提供的SQL语句修改,从 'airs_model_api_keys_view' 视图中获取 'api_key'
128
+ if model:
129
+ print('\n\n1111111')
130
+ response = supabase.from_('airs_model_api_keys_view').select('api_key').eq('model_name', model).execute()
131
+ print('\n\n2222222')
132
+ print(response.data)
133
+ else:
134
+ print('\n\n3333333')
135
+ # 如果没有提供model,则获取所有模型的api_key
136
+ response = supabase.from_('airs_model_api_keys_view').select('api_key').execute()
137
+
138
+ if response.data:
139
+ # 随机选择一个API密钥
140
+ import random
141
+ return random.choice([item['api_key'] for item in response.data])
142
+ else:
143
+ return None
144
+ except Exception as e:
145
+ print(f"从Supabase获取API密钥失败: {e}")
146
+ return None
memory-bank/activeContext.md CHANGED
@@ -15,8 +15,8 @@
15
  * **`.clinerules` 更新**:添加了自动激活 `conda` 环境 `airs` 的自定义指令。
16
 
17
  ## 下一步
18
- * **验证 Postman 兼容性**:用户需要再次尝试使用 Postman 调用部署的代理,以确认 `User-Agent` 头部修改是否解决了“incorrect header check”错误。
19
- * **本地测试环境问题**:如果本地测试仍然遇到“User location is not supported for the API use.”错误,需要明确告知用户这是 Gemini API 的地域限制,而非代理代码问题。
20
 
21
  ## 活跃决策和考虑
22
  * **API 密钥传递方式**:从 URL 查询参数改为 `Authorization` 头是符合 OpenAI 兼容 API 规范的正确做法。
 
15
  * **`.clinerules` 更新**:添加了自动激活 `conda` 环境 `airs` 的自定义指令。
16
 
17
  ## 下一步
18
+ * **验证 Postman 兼容性**:用户已指示暂时搁置此问题。
19
+ * **本地测试环境问题**:已向用户明确告知这是 Gemini API 的地域限制,而非代理代码问题。
20
 
21
  ## 活跃决策和考虑
22
  * **API 密钥传递方式**:从 URL 查询参数改为 `Authorization` 头是符合 OpenAI 兼容 API 规范的正确做法。
memory-bank/progress.md CHANGED
@@ -12,11 +12,11 @@
12
  * **部署环境验证**:通过 `curl` 命令验证了部署在 Hugging Face Spaces 上的代理功能正常。
13
 
14
  ## 待完成的工作
15
- * **Postman 客户端兼容性验证**:用户需要再次尝试使用 Postman 调用部署的代理,以确认 `User-Agent` 头部修改是否解决了“incorrect header check”错误。
16
- * **本地测试地域限制说明**:如果用户在本地测试时仍然遇到“User location is not supported for the API use.”错误,需要明确告知这是 Gemini API 的地域限制,而非代理代码问题。
17
 
18
  ## 当前状态
19
- 代理应用程序的核心功能已完成并部署验证。主要剩余任务是解决特定客户端(Postman)的兼容性问题,并向用户明确本地测试可能遇到的地域限制。
20
 
21
  ## 已知问题
22
  * **本地测试地域限制**:在某些地域,直接从本地机器访问 Gemini API 可能会遇到“User location is not supported for the API use.”错误。
 
12
  * **部署环境验证**:通过 `curl` 命令验证了部署在 Hugging Face Spaces 上的代理功能正常。
13
 
14
  ## 待完成的工作
15
+ * **Postman 客户端兼容性验证**:用户已指示暂时搁置此问题。
16
+ * **本地测试地域限制说明**:已向用户明确告知这是 Gemini API 的地域限制,而非代理代码问题。
17
 
18
  ## 当前状态
19
+ 代理应用程序的核心功能已完成并部署验证。所有待完成的任务已处理或暂时搁置。
20
 
21
  ## 已知问题
22
  * **本地测试地域限制**:在某些地域,直接从本地机器访问 Gemini API 可能会遇到“User location is not supported for the API use.”错误。
read_supabase_data.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from dotenv import load_dotenv
3
+ from supabase import create_client, Client
4
+ import asyncio
5
+
6
+ # 加载环境变量
7
+ load_dotenv()
8
+
9
+ # Supabase 配置
10
+ SUPABASE_URL = os.getenv("SUPABASE_URL")
11
+ SUPABASE_KEY = os.getenv("SUPABASE_KEY")
12
+
13
+ async def read_api_keys_from_supabase(model_name: str = None, limit: int = None):
14
+ """
15
+ 从 Supabase 的 airs_model_api_keys_view 视图中读取 API 密钥。
16
+ """
17
+ if not SUPABASE_URL or not SUPABASE_KEY:
18
+ print("错误:SUPABASE_URL 或 SUPABASE_KEY 未在 .env 文件中配置。")
19
+ return None
20
+
21
+ try:
22
+ supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
23
+ print(f"已连接到 Supabase: {SUPABASE_URL}")
24
+ print('supabase', supabase)
25
+
26
+ print(f"尝试从 Supabase 获取 API 密钥,模型名称: {model_name if model_name else '所有模型'}{f', 限制: {limit}' if limit else ''}")
27
+
28
+ query = supabase.from_('airs_model_api_keys_view').select('api_key,model_name')
29
+
30
+ if model_name:
31
+ query = query.eq('model_name', model_name)
32
+
33
+ if limit:
34
+ query = query.limit(limit)
35
+
36
+ response = query.execute()
37
+
38
+ if response.data:
39
+ print("\n成功从 Supabase 获取数据:")
40
+ for item in response.data:
41
+ print(f" API Key: {item.get('api_key')}, Model Name: {item.get('model_name')}")
42
+ return response.data
43
+ else:
44
+ print("\n从 Supabase 获取数据成功,但未找到匹配的 API 密钥。")
45
+ return None
46
+ except Exception as e:
47
+ print(f"\n从 Supabase 获取 API 密钥失败: {e}")
48
+ return None
49
+
50
+ async def main():
51
+ print("--- 测试获取所有 API 密钥 ---")
52
+ await read_api_keys_from_supabase()
53
+
54
+ print("\n--- 测试获取特定模型 (glm-4-flash) 的 API 密钥 ---")
55
+ await read_api_keys_from_supabase('glm-4-flash')
56
+
57
+ print("\n--- 测试获取特定模型 (gemini-2.5-flash) 的 API 密钥 ---")
58
+ await read_api_keys_from_supabase('gemini-2.5-flash')
59
+
60
+ print("\n--- 测试获取前 5 条 API 密钥 ---")
61
+ await read_api_keys_from_supabase(limit=5)
62
+
63
+ if __name__ == "__main__":
64
+ asyncio.run(main())