botrun-flow-lang 5.12.263__py3-none-any.whl → 5.12.264__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. botrun_flow_lang/api/auth_api.py +39 -39
  2. botrun_flow_lang/api/auth_utils.py +183 -183
  3. botrun_flow_lang/api/botrun_back_api.py +65 -65
  4. botrun_flow_lang/api/flow_api.py +3 -3
  5. botrun_flow_lang/api/hatch_api.py +508 -508
  6. botrun_flow_lang/api/langgraph_api.py +811 -811
  7. botrun_flow_lang/api/line_bot_api.py +1484 -1484
  8. botrun_flow_lang/api/model_api.py +300 -300
  9. botrun_flow_lang/api/rate_limit_api.py +32 -32
  10. botrun_flow_lang/api/routes.py +79 -79
  11. botrun_flow_lang/api/search_api.py +53 -53
  12. botrun_flow_lang/api/storage_api.py +395 -395
  13. botrun_flow_lang/api/subsidy_api.py +290 -290
  14. botrun_flow_lang/api/subsidy_api_system_prompt.txt +109 -109
  15. botrun_flow_lang/api/user_setting_api.py +70 -70
  16. botrun_flow_lang/api/version_api.py +31 -31
  17. botrun_flow_lang/api/youtube_api.py +26 -26
  18. botrun_flow_lang/constants.py +13 -13
  19. botrun_flow_lang/langgraph_agents/agents/agent_runner.py +178 -178
  20. botrun_flow_lang/langgraph_agents/agents/agent_tools/step_planner.py +77 -77
  21. botrun_flow_lang/langgraph_agents/agents/checkpointer/firestore_checkpointer.py +666 -666
  22. botrun_flow_lang/langgraph_agents/agents/gov_researcher/GOV_RESEARCHER_PRD.md +192 -192
  23. botrun_flow_lang/langgraph_agents/agents/gov_researcher/gemini_subsidy_graph.py +460 -460
  24. botrun_flow_lang/langgraph_agents/agents/gov_researcher/gov_researcher_2_graph.py +1002 -1002
  25. botrun_flow_lang/langgraph_agents/agents/gov_researcher/gov_researcher_graph.py +822 -822
  26. botrun_flow_lang/langgraph_agents/agents/langgraph_react_agent.py +723 -723
  27. botrun_flow_lang/langgraph_agents/agents/search_agent_graph.py +864 -864
  28. botrun_flow_lang/langgraph_agents/agents/tools/__init__.py +4 -4
  29. botrun_flow_lang/langgraph_agents/agents/tools/gemini_code_execution.py +376 -376
  30. botrun_flow_lang/langgraph_agents/agents/util/gemini_grounding.py +66 -66
  31. botrun_flow_lang/langgraph_agents/agents/util/html_util.py +316 -316
  32. botrun_flow_lang/langgraph_agents/agents/util/img_util.py +294 -294
  33. botrun_flow_lang/langgraph_agents/agents/util/local_files.py +419 -419
  34. botrun_flow_lang/langgraph_agents/agents/util/mermaid_util.py +86 -86
  35. botrun_flow_lang/langgraph_agents/agents/util/model_utils.py +143 -143
  36. botrun_flow_lang/langgraph_agents/agents/util/pdf_analyzer.py +486 -486
  37. botrun_flow_lang/langgraph_agents/agents/util/pdf_cache.py +250 -250
  38. botrun_flow_lang/langgraph_agents/agents/util/pdf_processor.py +204 -204
  39. botrun_flow_lang/langgraph_agents/agents/util/perplexity_search.py +464 -464
  40. botrun_flow_lang/langgraph_agents/agents/util/plotly_util.py +59 -59
  41. botrun_flow_lang/langgraph_agents/agents/util/tavily_search.py +199 -199
  42. botrun_flow_lang/langgraph_agents/agents/util/youtube_util.py +90 -90
  43. botrun_flow_lang/langgraph_agents/cache/langgraph_botrun_cache.py +197 -197
  44. botrun_flow_lang/llm_agent/llm_agent.py +19 -19
  45. botrun_flow_lang/llm_agent/llm_agent_util.py +83 -83
  46. botrun_flow_lang/log/.gitignore +2 -2
  47. botrun_flow_lang/main.py +61 -61
  48. botrun_flow_lang/main_fast.py +51 -51
  49. botrun_flow_lang/mcp_server/__init__.py +10 -10
  50. botrun_flow_lang/mcp_server/default_mcp.py +744 -744
  51. botrun_flow_lang/models/nodes/utils.py +205 -205
  52. botrun_flow_lang/models/token_usage.py +34 -34
  53. botrun_flow_lang/requirements.txt +21 -21
  54. botrun_flow_lang/services/base/firestore_base.py +30 -30
  55. botrun_flow_lang/services/hatch/hatch_factory.py +11 -11
  56. botrun_flow_lang/services/hatch/hatch_fs_store.py +419 -419
  57. botrun_flow_lang/services/storage/storage_cs_store.py +206 -206
  58. botrun_flow_lang/services/storage/storage_factory.py +12 -12
  59. botrun_flow_lang/services/storage/storage_store.py +65 -65
  60. botrun_flow_lang/services/user_setting/user_setting_factory.py +9 -9
  61. botrun_flow_lang/services/user_setting/user_setting_fs_store.py +66 -66
  62. botrun_flow_lang/static/docs/tools/index.html +926 -926
  63. botrun_flow_lang/tests/api_functional_tests.py +1525 -1525
  64. botrun_flow_lang/tests/api_stress_test.py +357 -357
  65. botrun_flow_lang/tests/shared_hatch_tests.py +333 -333
  66. botrun_flow_lang/tests/test_botrun_app.py +46 -46
  67. botrun_flow_lang/tests/test_html_util.py +31 -31
  68. botrun_flow_lang/tests/test_img_analyzer.py +190 -190
  69. botrun_flow_lang/tests/test_img_util.py +39 -39
  70. botrun_flow_lang/tests/test_local_files.py +114 -114
  71. botrun_flow_lang/tests/test_mermaid_util.py +103 -103
  72. botrun_flow_lang/tests/test_pdf_analyzer.py +104 -104
  73. botrun_flow_lang/tests/test_plotly_util.py +151 -151
  74. botrun_flow_lang/tests/test_run_workflow_engine.py +65 -65
  75. botrun_flow_lang/tools/generate_docs.py +133 -133
  76. botrun_flow_lang/tools/templates/tools.html +153 -153
  77. botrun_flow_lang/utils/__init__.py +7 -7
  78. botrun_flow_lang/utils/botrun_logger.py +344 -344
  79. botrun_flow_lang/utils/clients/rate_limit_client.py +209 -209
  80. botrun_flow_lang/utils/clients/token_verify_client.py +153 -153
  81. botrun_flow_lang/utils/google_drive_utils.py +654 -654
  82. botrun_flow_lang/utils/langchain_utils.py +324 -324
  83. botrun_flow_lang/utils/yaml_utils.py +9 -9
  84. {botrun_flow_lang-5.12.263.dist-info → botrun_flow_lang-5.12.264.dist-info}/METADATA +1 -1
  85. botrun_flow_lang-5.12.264.dist-info/RECORD +102 -0
  86. botrun_flow_lang-5.12.263.dist-info/RECORD +0 -102
  87. {botrun_flow_lang-5.12.263.dist-info → botrun_flow_lang-5.12.264.dist-info}/WHEEL +0 -0
@@ -1,300 +1,300 @@
1
- import os
2
- import pandas as pd
3
- from fastapi import APIRouter, HTTPException
4
- from pydantic import BaseModel
5
- from typing import List, Optional
6
- from google.oauth2 import service_account
7
- from googleapiclient.discovery import build
8
-
9
- router = APIRouter()
10
-
11
-
12
- class ModelInfo(BaseModel):
13
- """Model information structure"""
14
-
15
- display_name: str
16
- model_name: str
17
- provider: str
18
-
19
-
20
- class ModelsResponse(BaseModel):
21
- """Response model for listing supported models"""
22
-
23
- models: List[ModelInfo]
24
- total_count: int
25
-
26
-
27
- class AgentModelsResponse(BaseModel):
28
- """Response model for listing supported agent models"""
29
-
30
- models: List[str]
31
- total_count: int
32
-
33
-
34
- def read_google_sheet(
35
- credentials_path: str, spreadsheet_id: str, sheet_name: str
36
- ) -> pd.DataFrame:
37
- """
38
- 讀取 Google Spreadsheet 指定 sheet,回傳 pandas DataFrame。
39
- """
40
- try:
41
- scopes = ["https://www.googleapis.com/auth/spreadsheets.readonly"]
42
- credentials = service_account.Credentials.from_service_account_file(
43
- credentials_path, scopes=scopes
44
- )
45
- service = build("sheets", "v4", credentials=credentials)
46
- sheet = service.spreadsheets()
47
- result = (
48
- sheet.values().get(spreadsheetId=spreadsheet_id, range=sheet_name).execute()
49
- )
50
- values = result.get("values", [])
51
- if not values:
52
- return pd.DataFrame()
53
- # 第一列為欄位名稱
54
- df = pd.DataFrame(values[1:], columns=values[0])
55
- return df
56
- except Exception:
57
- import traceback
58
-
59
- traceback.print_exc()
60
- return pd.DataFrame()
61
-
62
-
63
- def get_models_from_google_sheet() -> List[ModelInfo]:
64
- """
65
- 從 Google Sheets 讀取模型列表
66
- 優先順序:ENV_NAME sheet -> default sheet -> DEFAULT_SUPPORTED_MODELS
67
- """
68
- credentials_path = os.getenv("GOOGLE_APPLICATION_CREDENTIALS_FOR_MODELS_SHEET")
69
- spreadsheet_id = os.getenv("MODELS_GSPREAD_ID")
70
- env_name = os.getenv("ENV_NAME", "")
71
-
72
- if not credentials_path or not spreadsheet_id:
73
- return DEFAULT_SUPPORTED_MODELS
74
-
75
- # 首先嘗試使用 ENV_NAME 作為 sheet_name
76
- if env_name:
77
- df = read_google_sheet(credentials_path, spreadsheet_id, env_name)
78
- if not df.empty and all(
79
- col in df.columns for col in ["display_name", "model_name", "provider"]
80
- ):
81
- try:
82
- return [
83
- ModelInfo(
84
- display_name=row["display_name"],
85
- model_name=row["model_name"],
86
- provider=row["provider"],
87
- )
88
- for _, row in df.iterrows()
89
- ]
90
- except Exception:
91
- pass
92
-
93
- # 如果 ENV_NAME sheet 找不到或有問題,嘗試 default sheet
94
- df = read_google_sheet(credentials_path, spreadsheet_id, "default")
95
- if not df.empty and all(
96
- col in df.columns for col in ["display_name", "model_name", "provider"]
97
- ):
98
- try:
99
- return [
100
- ModelInfo(
101
- display_name=row["display_name"],
102
- model_name=row["model_name"],
103
- provider=row["provider"],
104
- )
105
- for _, row in df.iterrows()
106
- ]
107
- except Exception:
108
- pass
109
-
110
- # 如果都失敗,回退到 DEFAULT_SUPPORTED_MODELS
111
- return DEFAULT_SUPPORTED_MODELS
112
-
113
-
114
- def get_agent_models_from_google_sheet() -> List[str]:
115
- """
116
- 從 Google Sheets 讀取 agent 模型列表
117
- 優先順序:ENV_NAME-agents sheet -> default-agents sheet -> DEFAULT_AGENT_MODELS
118
- """
119
- credentials_path = os.getenv("GOOGLE_APPLICATION_CREDENTIALS_FOR_MODELS_SHEET")
120
- spreadsheet_id = os.getenv("MODELS_GSPREAD_ID")
121
- env_name = os.getenv("ENV_NAME", "")
122
-
123
- if not credentials_path or not spreadsheet_id:
124
- return DEFAULT_AGENT_MODELS
125
-
126
- # 首先嘗試使用 ENV_NAME-agents 作為 sheet_name
127
- if env_name:
128
- sheet_name = f"{env_name}-agents"
129
- df = read_google_sheet(credentials_path, spreadsheet_id, sheet_name)
130
- if not df.empty and "model_name" in df.columns:
131
- try:
132
- return [
133
- row["model_name"] for _, row in df.iterrows() if row["model_name"]
134
- ]
135
- except Exception:
136
- pass
137
-
138
- # 如果 ENV_NAME-agents sheet 找不到或有問題,嘗試 default-agents sheet
139
- df = read_google_sheet(credentials_path, spreadsheet_id, "default-agents")
140
- if not df.empty and "model_name" in df.columns:
141
- try:
142
- return [row["model_name"] for _, row in df.iterrows() if row["model_name"]]
143
- except Exception:
144
- pass
145
-
146
- # 如果都失敗,回退到 DEFAULT_AGENT_MODELS
147
- return DEFAULT_AGENT_MODELS
148
-
149
-
150
- # 定義支援的 LLM models
151
- DEFAULT_SUPPORTED_MODELS = [
152
- ModelInfo(
153
- display_name="gemini-2.5-flash",
154
- model_name="gemini-2.5-flash",
155
- provider="gemini",
156
- ),
157
- ModelInfo(
158
- display_name="claude-sonnet-4-5-20250929",
159
- model_name="claude-sonnet-4-5-20250929",
160
- provider="anthropic",
161
- ),
162
- ModelInfo(
163
- display_name="gemini-2.5-pro",
164
- model_name="gemini-2.5-pro",
165
- provider="gemini",
166
- ),
167
- ModelInfo(
168
- display_name="gpt-4.1-2025-04-14",
169
- model_name="gpt-4.1-2025-04-14",
170
- provider="openai",
171
- ),
172
- ModelInfo(
173
- display_name="o4-mini-2025-04-16",
174
- model_name="o4-mini-2025-04-16",
175
- provider="openai",
176
- ),
177
- ModelInfo(
178
- display_name="claude-3-5-haiku-latest",
179
- model_name="claude-3-5-haiku-latest",
180
- provider="anthropic",
181
- ),
182
- ModelInfo(display_name="gpt-4o-mini", model_name="gpt-4o-mini", provider="openai"),
183
- ModelInfo(
184
- display_name="gpt-4o-2024-08-06",
185
- model_name="gpt-4o-2024-08-06",
186
- provider="openai",
187
- ),
188
- ModelInfo(
189
- display_name="Meta-Llama-3.1-8B-Instruct",
190
- model_name="meta-llama/Meta-Llama-3.1-8B-Instruct",
191
- provider="deepinfra",
192
- ),
193
- ModelInfo(
194
- display_name="Meta-Llama-3.1-70B-Instruct-Turbo",
195
- model_name="meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
196
- provider="deepinfra",
197
- ),
198
- ModelInfo(
199
- display_name="Meta-Llama-3.1-405B-Instruct-Turbo",
200
- model_name="meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo",
201
- provider="together_ai",
202
- ),
203
- ModelInfo(
204
- display_name="Llama-3.1-Nemotron-70B-Instruct",
205
- model_name="nvidia/Llama-3.1-Nemotron-70B-Instruct",
206
- provider="deepinfra",
207
- ),
208
- ModelInfo(
209
- display_name="Llama-3.2-11B-Vision-Instruct-Turbo",
210
- model_name="meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo",
211
- provider="together_ai",
212
- ),
213
- ModelInfo(
214
- display_name="Llama-3.2-90B-Vision-Instruct-Turbo",
215
- model_name="meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo",
216
- provider="together_ai",
217
- ),
218
- ModelInfo(
219
- display_name="gemma-2-9b-it",
220
- model_name="google/gemma-2-9b-it",
221
- provider="deepinfra",
222
- ),
223
- ModelInfo(
224
- display_name="gemma-2-27b-it",
225
- model_name="google/gemma-2-27b-it",
226
- provider="deepinfra",
227
- ),
228
- ModelInfo(
229
- display_name="Mixtral-8x7B-Instruct-v0.1",
230
- model_name="mistralai/Mixtral-8x7B-Instruct-v0.1",
231
- provider="deepinfra",
232
- ),
233
- ModelInfo(
234
- display_name="Mixtral-8x22B-Instruct-v0.1",
235
- model_name="mistralai/Mixtral-8x22B-Instruct-v0.1",
236
- provider="deepinfra",
237
- ),
238
- ModelInfo(
239
- display_name="WizardLM-2-8x22B",
240
- model_name="microsoft/WizardLM-2-8x22B",
241
- provider="deepinfra",
242
- ),
243
- ModelInfo(
244
- display_name="Meta-Llama-Guard-3-8B",
245
- model_name="meta-llama/Meta-Llama-Guard-3-8B",
246
- provider="together_ai",
247
- ),
248
- ModelInfo(display_name="o1-mini", model_name="o1-mini", provider="openai"),
249
- ModelInfo(display_name="o1-preview", model_name="o1-preview", provider="openai"),
250
- ]
251
-
252
- # 定義支援 agent 的 LLM models
253
- DEFAULT_AGENT_MODELS = [
254
- "claude-sonnet-4-5-20250929",
255
- "gemini-2.5-pro",
256
- "gemini-2.5-flash",
257
- ]
258
-
259
-
260
- @router.get("/models", response_model=ModelsResponse)
261
- async def list_models():
262
- """
263
- 列出所有支援的 LLM models
264
- 優先從 Google Sheets 讀取,回退到預設列表
265
-
266
- Args:
267
- provider: 可選的提供商篩選 (openai, anthropic, google, perplexity, etc.)
268
-
269
- Returns:
270
- ModelsResponse: 包含 models 清單和總數
271
- """
272
- try:
273
- # 從 Google Sheets 或預設列表獲取模型
274
- all_models = get_models_from_google_sheet()
275
-
276
- return ModelsResponse(models=all_models, total_count=len(all_models))
277
- except Exception as e:
278
- raise HTTPException(
279
- status_code=500, detail=f"Failed to retrieve models: {str(e)}"
280
- )
281
-
282
-
283
- @router.get("/models/agent-models", response_model=AgentModelsResponse)
284
- async def list_agent_models():
285
- """
286
- 列出所有支援的 agent models
287
- 優先從 Google Sheets 讀取,回退到預設列表
288
-
289
- Returns:
290
- AgentModelsResponse: 包含 models 清單和總數
291
- """
292
- try:
293
- # 從 Google Sheets 或預設列表獲取模型
294
- all_models = get_agent_models_from_google_sheet()
295
-
296
- return AgentModelsResponse(models=all_models, total_count=len(all_models))
297
- except Exception as e:
298
- raise HTTPException(
299
- status_code=500, detail=f"Failed to retrieve agent models: {str(e)}"
300
- )
1
+ import os
2
+ import pandas as pd
3
+ from fastapi import APIRouter, HTTPException
4
+ from pydantic import BaseModel
5
+ from typing import List, Optional
6
+ from google.oauth2 import service_account
7
+ from googleapiclient.discovery import build
8
+
9
+ router = APIRouter()
10
+
11
+
12
+ class ModelInfo(BaseModel):
13
+ """Model information structure"""
14
+
15
+ display_name: str
16
+ model_name: str
17
+ provider: str
18
+
19
+
20
+ class ModelsResponse(BaseModel):
21
+ """Response model for listing supported models"""
22
+
23
+ models: List[ModelInfo]
24
+ total_count: int
25
+
26
+
27
+ class AgentModelsResponse(BaseModel):
28
+ """Response model for listing supported agent models"""
29
+
30
+ models: List[str]
31
+ total_count: int
32
+
33
+
34
+ def read_google_sheet(
35
+ credentials_path: str, spreadsheet_id: str, sheet_name: str
36
+ ) -> pd.DataFrame:
37
+ """
38
+ 讀取 Google Spreadsheet 指定 sheet,回傳 pandas DataFrame。
39
+ """
40
+ try:
41
+ scopes = ["https://www.googleapis.com/auth/spreadsheets.readonly"]
42
+ credentials = service_account.Credentials.from_service_account_file(
43
+ credentials_path, scopes=scopes
44
+ )
45
+ service = build("sheets", "v4", credentials=credentials)
46
+ sheet = service.spreadsheets()
47
+ result = (
48
+ sheet.values().get(spreadsheetId=spreadsheet_id, range=sheet_name).execute()
49
+ )
50
+ values = result.get("values", [])
51
+ if not values:
52
+ return pd.DataFrame()
53
+ # 第一列為欄位名稱
54
+ df = pd.DataFrame(values[1:], columns=values[0])
55
+ return df
56
+ except Exception:
57
+ import traceback
58
+
59
+ traceback.print_exc()
60
+ return pd.DataFrame()
61
+
62
+
63
+ def get_models_from_google_sheet() -> List[ModelInfo]:
64
+ """
65
+ 從 Google Sheets 讀取模型列表
66
+ 優先順序:ENV_NAME sheet -> default sheet -> DEFAULT_SUPPORTED_MODELS
67
+ """
68
+ credentials_path = os.getenv("GOOGLE_APPLICATION_CREDENTIALS_FOR_MODELS_SHEET")
69
+ spreadsheet_id = os.getenv("MODELS_GSPREAD_ID")
70
+ env_name = os.getenv("ENV_NAME", "")
71
+
72
+ if not credentials_path or not spreadsheet_id:
73
+ return DEFAULT_SUPPORTED_MODELS
74
+
75
+ # 首先嘗試使用 ENV_NAME 作為 sheet_name
76
+ if env_name:
77
+ df = read_google_sheet(credentials_path, spreadsheet_id, env_name)
78
+ if not df.empty and all(
79
+ col in df.columns for col in ["display_name", "model_name", "provider"]
80
+ ):
81
+ try:
82
+ return [
83
+ ModelInfo(
84
+ display_name=row["display_name"],
85
+ model_name=row["model_name"],
86
+ provider=row["provider"],
87
+ )
88
+ for _, row in df.iterrows()
89
+ ]
90
+ except Exception:
91
+ pass
92
+
93
+ # 如果 ENV_NAME sheet 找不到或有問題,嘗試 default sheet
94
+ df = read_google_sheet(credentials_path, spreadsheet_id, "default")
95
+ if not df.empty and all(
96
+ col in df.columns for col in ["display_name", "model_name", "provider"]
97
+ ):
98
+ try:
99
+ return [
100
+ ModelInfo(
101
+ display_name=row["display_name"],
102
+ model_name=row["model_name"],
103
+ provider=row["provider"],
104
+ )
105
+ for _, row in df.iterrows()
106
+ ]
107
+ except Exception:
108
+ pass
109
+
110
+ # 如果都失敗,回退到 DEFAULT_SUPPORTED_MODELS
111
+ return DEFAULT_SUPPORTED_MODELS
112
+
113
+
114
+ def get_agent_models_from_google_sheet() -> List[str]:
115
+ """
116
+ 從 Google Sheets 讀取 agent 模型列表
117
+ 優先順序:ENV_NAME-agents sheet -> default-agents sheet -> DEFAULT_AGENT_MODELS
118
+ """
119
+ credentials_path = os.getenv("GOOGLE_APPLICATION_CREDENTIALS_FOR_MODELS_SHEET")
120
+ spreadsheet_id = os.getenv("MODELS_GSPREAD_ID")
121
+ env_name = os.getenv("ENV_NAME", "")
122
+
123
+ if not credentials_path or not spreadsheet_id:
124
+ return DEFAULT_AGENT_MODELS
125
+
126
+ # 首先嘗試使用 ENV_NAME-agents 作為 sheet_name
127
+ if env_name:
128
+ sheet_name = f"{env_name}-agents"
129
+ df = read_google_sheet(credentials_path, spreadsheet_id, sheet_name)
130
+ if not df.empty and "model_name" in df.columns:
131
+ try:
132
+ return [
133
+ row["model_name"] for _, row in df.iterrows() if row["model_name"]
134
+ ]
135
+ except Exception:
136
+ pass
137
+
138
+ # 如果 ENV_NAME-agents sheet 找不到或有問題,嘗試 default-agents sheet
139
+ df = read_google_sheet(credentials_path, spreadsheet_id, "default-agents")
140
+ if not df.empty and "model_name" in df.columns:
141
+ try:
142
+ return [row["model_name"] for _, row in df.iterrows() if row["model_name"]]
143
+ except Exception:
144
+ pass
145
+
146
+ # 如果都失敗,回退到 DEFAULT_AGENT_MODELS
147
+ return DEFAULT_AGENT_MODELS
148
+
149
+
150
+ # 定義支援的 LLM models
151
+ DEFAULT_SUPPORTED_MODELS = [
152
+ ModelInfo(
153
+ display_name="gemini-2.5-flash",
154
+ model_name="gemini-2.5-flash",
155
+ provider="gemini",
156
+ ),
157
+ ModelInfo(
158
+ display_name="claude-sonnet-4-5-20250929",
159
+ model_name="claude-sonnet-4-5-20250929",
160
+ provider="anthropic",
161
+ ),
162
+ ModelInfo(
163
+ display_name="gemini-2.5-pro",
164
+ model_name="gemini-2.5-pro",
165
+ provider="gemini",
166
+ ),
167
+ ModelInfo(
168
+ display_name="gpt-4.1-2025-04-14",
169
+ model_name="gpt-4.1-2025-04-14",
170
+ provider="openai",
171
+ ),
172
+ ModelInfo(
173
+ display_name="o4-mini-2025-04-16",
174
+ model_name="o4-mini-2025-04-16",
175
+ provider="openai",
176
+ ),
177
+ ModelInfo(
178
+ display_name="claude-3-5-haiku-latest",
179
+ model_name="claude-3-5-haiku-latest",
180
+ provider="anthropic",
181
+ ),
182
+ ModelInfo(display_name="gpt-4o-mini", model_name="gpt-4o-mini", provider="openai"),
183
+ ModelInfo(
184
+ display_name="gpt-4o-2024-08-06",
185
+ model_name="gpt-4o-2024-08-06",
186
+ provider="openai",
187
+ ),
188
+ ModelInfo(
189
+ display_name="Meta-Llama-3.1-8B-Instruct",
190
+ model_name="meta-llama/Meta-Llama-3.1-8B-Instruct",
191
+ provider="deepinfra",
192
+ ),
193
+ ModelInfo(
194
+ display_name="Meta-Llama-3.1-70B-Instruct-Turbo",
195
+ model_name="meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
196
+ provider="deepinfra",
197
+ ),
198
+ ModelInfo(
199
+ display_name="Meta-Llama-3.1-405B-Instruct-Turbo",
200
+ model_name="meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo",
201
+ provider="together_ai",
202
+ ),
203
+ ModelInfo(
204
+ display_name="Llama-3.1-Nemotron-70B-Instruct",
205
+ model_name="nvidia/Llama-3.1-Nemotron-70B-Instruct",
206
+ provider="deepinfra",
207
+ ),
208
+ ModelInfo(
209
+ display_name="Llama-3.2-11B-Vision-Instruct-Turbo",
210
+ model_name="meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo",
211
+ provider="together_ai",
212
+ ),
213
+ ModelInfo(
214
+ display_name="Llama-3.2-90B-Vision-Instruct-Turbo",
215
+ model_name="meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo",
216
+ provider="together_ai",
217
+ ),
218
+ ModelInfo(
219
+ display_name="gemma-2-9b-it",
220
+ model_name="google/gemma-2-9b-it",
221
+ provider="deepinfra",
222
+ ),
223
+ ModelInfo(
224
+ display_name="gemma-2-27b-it",
225
+ model_name="google/gemma-2-27b-it",
226
+ provider="deepinfra",
227
+ ),
228
+ ModelInfo(
229
+ display_name="Mixtral-8x7B-Instruct-v0.1",
230
+ model_name="mistralai/Mixtral-8x7B-Instruct-v0.1",
231
+ provider="deepinfra",
232
+ ),
233
+ ModelInfo(
234
+ display_name="Mixtral-8x22B-Instruct-v0.1",
235
+ model_name="mistralai/Mixtral-8x22B-Instruct-v0.1",
236
+ provider="deepinfra",
237
+ ),
238
+ ModelInfo(
239
+ display_name="WizardLM-2-8x22B",
240
+ model_name="microsoft/WizardLM-2-8x22B",
241
+ provider="deepinfra",
242
+ ),
243
+ ModelInfo(
244
+ display_name="Meta-Llama-Guard-3-8B",
245
+ model_name="meta-llama/Meta-Llama-Guard-3-8B",
246
+ provider="together_ai",
247
+ ),
248
+ ModelInfo(display_name="o1-mini", model_name="o1-mini", provider="openai"),
249
+ ModelInfo(display_name="o1-preview", model_name="o1-preview", provider="openai"),
250
+ ]
251
+
252
+ # 定義支援 agent 的 LLM models
253
+ DEFAULT_AGENT_MODELS = [
254
+ "claude-sonnet-4-5-20250929",
255
+ "gemini-2.5-pro",
256
+ "gemini-2.5-flash",
257
+ ]
258
+
259
+
260
+ @router.get("/models", response_model=ModelsResponse)
261
+ async def list_models():
262
+ """
263
+ 列出所有支援的 LLM models
264
+ 優先從 Google Sheets 讀取,回退到預設列表
265
+
266
+ Args:
267
+ provider: 可選的提供商篩選 (openai, anthropic, google, perplexity, etc.)
268
+
269
+ Returns:
270
+ ModelsResponse: 包含 models 清單和總數
271
+ """
272
+ try:
273
+ # 從 Google Sheets 或預設列表獲取模型
274
+ all_models = get_models_from_google_sheet()
275
+
276
+ return ModelsResponse(models=all_models, total_count=len(all_models))
277
+ except Exception as e:
278
+ raise HTTPException(
279
+ status_code=500, detail=f"Failed to retrieve models: {str(e)}"
280
+ )
281
+
282
+
283
+ @router.get("/models/agent-models", response_model=AgentModelsResponse)
284
+ async def list_agent_models():
285
+ """
286
+ 列出所有支援的 agent models
287
+ 優先從 Google Sheets 讀取,回退到預設列表
288
+
289
+ Returns:
290
+ AgentModelsResponse: 包含 models 清單和總數
291
+ """
292
+ try:
293
+ # 從 Google Sheets 或預設列表獲取模型
294
+ all_models = get_agent_models_from_google_sheet()
295
+
296
+ return AgentModelsResponse(models=all_models, total_count=len(all_models))
297
+ except Exception as e:
298
+ raise HTTPException(
299
+ status_code=500, detail=f"Failed to retrieve agent models: {str(e)}"
300
+ )
@@ -1,32 +1,32 @@
1
- from fastapi import APIRouter, HTTPException
2
- from typing import Dict, Any
3
-
4
- from botrun_flow_lang.utils.clients.rate_limit_client import RateLimitClient
5
-
6
- router = APIRouter(prefix="/rate_limit")
7
-
8
-
9
- @router.get("/{username}")
10
- async def get_user_rate_limit(username: str) -> Dict[Any, Any]:
11
- """
12
- 獲取指定用戶的 rate limit 信息。
13
-
14
- Args:
15
- username: 用戶名
16
-
17
- Returns:
18
- 包含用戶 rate limit 信息的字典
19
-
20
- Raises:
21
- HTTPException: 當用戶不存在或後端 API 無法訪問時
22
- """
23
- try:
24
- client = RateLimitClient()
25
- result = await client.get_rate_limit(username)
26
- return result
27
- except ValueError as e:
28
- # 使用者不存在時會回傳 404
29
- if "User not found" in str(e):
30
- raise HTTPException(status_code=404, detail=f"User not found: {username}")
31
- # 其他錯誤回傳 500
32
- raise HTTPException(status_code=500, detail=str(e))
1
+ from fastapi import APIRouter, HTTPException
2
+ from typing import Dict, Any
3
+
4
+ from botrun_flow_lang.utils.clients.rate_limit_client import RateLimitClient
5
+
6
+ router = APIRouter(prefix="/rate_limit")
7
+
8
+
9
+ @router.get("/{username}")
10
+ async def get_user_rate_limit(username: str) -> Dict[Any, Any]:
11
+ """
12
+ 獲取指定用戶的 rate limit 信息。
13
+
14
+ Args:
15
+ username: 用戶名
16
+
17
+ Returns:
18
+ 包含用戶 rate limit 信息的字典
19
+
20
+ Raises:
21
+ HTTPException: 當用戶不存在或後端 API 無法訪問時
22
+ """
23
+ try:
24
+ client = RateLimitClient()
25
+ result = await client.get_rate_limit(username)
26
+ return result
27
+ except ValueError as e:
28
+ # 使用者不存在時會回傳 404
29
+ if "User not found" in str(e):
30
+ raise HTTPException(status_code=404, detail=f"User not found: {username}")
31
+ # 其他錯誤回傳 500
32
+ raise HTTPException(status_code=500, detail=str(e))