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,206 +1,206 @@
1
- from google.cloud import storage
2
- from google.cloud.exceptions import NotFound
3
- from google.oauth2 import service_account
4
- from io import BytesIO
5
- import os
6
- from typing import Optional, Tuple
7
- from datetime import datetime, timedelta, UTC
8
-
9
- from botrun_flow_lang.constants import HATCH_BUCKET_NAME
10
- from botrun_flow_lang.services.storage.storage_store import StorageStore
11
- from botrun_flow_lang.utils.botrun_logger import get_default_botrun_logger
12
-
13
- logger = get_default_botrun_logger()
14
-
15
-
16
- class StorageCsStore(StorageStore):
17
- def __init__(self, env_name: str):
18
- google_service_account_key_path = os.getenv(
19
- "GOOGLE_APPLICATION_CREDENTIALS_FOR_FASTAPI",
20
- "/app/keys/scoop-386004-d22d99a7afd9.json",
21
- )
22
- credentials = service_account.Credentials.from_service_account_file(
23
- google_service_account_key_path,
24
- scopes=["https://www.googleapis.com/auth/devstorage.full_control"],
25
- )
26
-
27
- self.storage_client = storage.Client(credentials=credentials)
28
- self.bucket_name = f"{HATCH_BUCKET_NAME}-{env_name}"
29
- self.bucket = self.create_bucket(self.bucket_name)
30
- if not self.bucket:
31
- raise Exception(f"Failed to create or get bucket: {self.bucket_name}")
32
-
33
- def create_bucket(self, bucket_name: str) -> Optional[storage.Bucket]:
34
- """創建新的 bucket,如果已存在則返回現有的,並確保 lifecycle rules 正確設定"""
35
- try:
36
- bucket = self.storage_client.bucket(bucket_name)
37
-
38
- desired_rules = [
39
- {
40
- "action": {"type": "Delete"},
41
- "condition": {"age": 365, "matchesPrefix": ["tmp/"]},
42
- },
43
- {
44
- "action": {"type": "Delete"},
45
- "condition": {"age": 7, "matchesPrefix": ["pdf-cache/"]},
46
- },
47
- ]
48
-
49
- if not bucket.exists():
50
- logger.info(f"Creating new bucket: {bucket_name}")
51
- # 設定 lifecycle rules
52
- bucket.lifecycle_rules = desired_rules
53
- # 創建 bucket
54
- bucket = self.storage_client.create_bucket(
55
- bucket, location="asia-east1"
56
- )
57
- logger.info(
58
- f"Created bucket {bucket_name} in asia-east1 and set lifecycle rules."
59
- )
60
- else:
61
- logger.info(
62
- f"Bucket {bucket_name} already exists. Checking lifecycle rules."
63
- )
64
- bucket.reload() # 獲取最新的 bucket metadata
65
-
66
- # google-cloud-storage 回傳的 rule 是 frozenset of dicts,需要轉換
67
- current_rules = [dict(rule) for rule in bucket.lifecycle_rules]
68
-
69
- if current_rules != desired_rules:
70
- logger.info(f"Updating lifecycle rules for bucket {bucket_name}")
71
- bucket.lifecycle_rules = desired_rules
72
- bucket.patch()
73
- logger.info(
74
- f"Successfully updated lifecycle rules for bucket {bucket_name}"
75
- )
76
- else:
77
- logger.info(
78
- f"Lifecycle rules for bucket {bucket_name} are already up-to-date."
79
- )
80
-
81
- return bucket
82
- except Exception as e:
83
- logger.error(f"Error creating or updating bucket {bucket_name}: {str(e)}")
84
- return None
85
-
86
- async def get_directory_sizes(self) -> dict:
87
- """
88
- 計算 bucket 中每個目錄的總檔案大小 (bytes) 與檔案數量,排除 tmp, html 目錄
89
-
90
- Returns:
91
- dict: 包含每個目錄資訊的字典,格式為 {directory_name: {"size": total_size_in_bytes, "file_count": count}}
92
- """
93
- try:
94
- # 初始化結果字典
95
- directory_info = {}
96
-
97
- # 列出所有 blobs
98
- blobs = list(self.bucket.list_blobs())
99
-
100
- # 計算每個目錄的大小和檔案數量
101
- for blob in blobs:
102
- # 跳過 tmp 目錄
103
- if blob.name.startswith("tmp/"):
104
- continue
105
- # 跳過 html 目錄
106
- if blob.name.startswith("html/"):
107
- continue
108
-
109
- # 從 blob 名稱中提取目錄名稱 (第一層目錄)
110
- parts = blob.name.split("/")
111
- if len(parts) >= 1:
112
- directory = parts[0]
113
-
114
- # 如果這是一個新目錄,初始化其資訊
115
- if directory not in directory_info:
116
- directory_info[directory] = {"size": 0, "file_count": 0}
117
-
118
- # 加上此 blob 的大小
119
- directory_info[directory]["size"] += blob.size
120
- # 增加檔案計數
121
- directory_info[directory]["file_count"] += 1
122
-
123
- return directory_info
124
- except Exception as e:
125
- logger.error(f"Error calculating directory sizes: {e}")
126
- return {}
127
-
128
- async def store_file(
129
- self,
130
- filepath: str,
131
- file_object: BytesIO,
132
- public: bool = False,
133
- content_type: str = None,
134
- ) -> Tuple[bool, Optional[str]]:
135
- try:
136
- blob = self.bucket.blob(filepath)
137
-
138
- # 設定 content_type 和其他 metadata
139
- if content_type:
140
- blob.content_type = content_type
141
- # 如果是圖片,設定為 inline 顯示並加入 cache control
142
- if content_type.startswith("image/"):
143
- blob.content_disposition = (
144
- 'inline; filename="' + filepath.split("/")[-1] + '"'
145
- )
146
- blob.cache_control = "public, max-age=3600, no-transform"
147
-
148
- # 上傳檔案
149
- blob.upload_from_file(file_object, rewind=True)
150
-
151
- # 確保 metadata 更新
152
- blob.patch()
153
-
154
- # 如果需要公開存取
155
- if public:
156
- blob.make_public()
157
- return True, blob.public_url
158
-
159
- return True, None
160
- except Exception as e:
161
- logger.error(f"Error storing file in Cloud Storage: {e}")
162
- return False, None
163
-
164
- async def get_public_url(self, filepath: str) -> Optional[str]:
165
- try:
166
- blob = self.bucket.blob(filepath)
167
- if blob.exists():
168
- return blob.public_url
169
- return None
170
- except Exception as e:
171
- logger.error(f"Error getting public URL: {e}")
172
- return None
173
-
174
- async def retrieve_file(self, filepath: str) -> Optional[BytesIO]:
175
- try:
176
- blob = self.bucket.blob(filepath)
177
- file_object = BytesIO()
178
- blob.download_to_file(file_object)
179
- file_object.seek(0) # Rewind the file object to the beginning
180
- return file_object
181
- except NotFound:
182
- logger.error(f"File not found in Cloud Storage: {filepath}")
183
- return None
184
- except Exception as e:
185
- logger.error(f"Error retrieving file from Cloud Storage: {e}")
186
- return None
187
-
188
- async def delete_file(self, filepath: str) -> bool:
189
- try:
190
- blob = self.bucket.blob(filepath)
191
- blob.delete()
192
- return True
193
- except NotFound:
194
- logger.error(f"File not found in Cloud Storage: {filepath}")
195
- return False
196
- except Exception as e:
197
- logger.error(f"Error deleting file from Cloud Storage: {e}")
198
- return False
199
-
200
- async def file_exists(self, filepath: str) -> bool:
201
- try:
202
- blob = self.bucket.blob(filepath)
203
- return blob.exists()
204
- except Exception as e:
205
- logger.error(f"Error checking file existence in Cloud Storage: {e}")
206
- return False
1
+ from google.cloud import storage
2
+ from google.cloud.exceptions import NotFound
3
+ from google.oauth2 import service_account
4
+ from io import BytesIO
5
+ import os
6
+ from typing import Optional, Tuple
7
+ from datetime import datetime, timedelta, UTC
8
+
9
+ from botrun_flow_lang.constants import HATCH_BUCKET_NAME
10
+ from botrun_flow_lang.services.storage.storage_store import StorageStore
11
+ from botrun_flow_lang.utils.botrun_logger import get_default_botrun_logger
12
+
13
+ logger = get_default_botrun_logger()
14
+
15
+
16
+ class StorageCsStore(StorageStore):
17
+ def __init__(self, env_name: str):
18
+ google_service_account_key_path = os.getenv(
19
+ "GOOGLE_APPLICATION_CREDENTIALS_FOR_FASTAPI",
20
+ "/app/keys/scoop-386004-d22d99a7afd9.json",
21
+ )
22
+ credentials = service_account.Credentials.from_service_account_file(
23
+ google_service_account_key_path,
24
+ scopes=["https://www.googleapis.com/auth/devstorage.full_control"],
25
+ )
26
+
27
+ self.storage_client = storage.Client(credentials=credentials)
28
+ self.bucket_name = f"{HATCH_BUCKET_NAME}-{env_name}"
29
+ self.bucket = self.create_bucket(self.bucket_name)
30
+ if not self.bucket:
31
+ raise Exception(f"Failed to create or get bucket: {self.bucket_name}")
32
+
33
+ def create_bucket(self, bucket_name: str) -> Optional[storage.Bucket]:
34
+ """創建新的 bucket,如果已存在則返回現有的,並確保 lifecycle rules 正確設定"""
35
+ try:
36
+ bucket = self.storage_client.bucket(bucket_name)
37
+
38
+ desired_rules = [
39
+ {
40
+ "action": {"type": "Delete"},
41
+ "condition": {"age": 365, "matchesPrefix": ["tmp/"]},
42
+ },
43
+ {
44
+ "action": {"type": "Delete"},
45
+ "condition": {"age": 7, "matchesPrefix": ["pdf-cache/"]},
46
+ },
47
+ ]
48
+
49
+ if not bucket.exists():
50
+ logger.info(f"Creating new bucket: {bucket_name}")
51
+ # 設定 lifecycle rules
52
+ bucket.lifecycle_rules = desired_rules
53
+ # 創建 bucket
54
+ bucket = self.storage_client.create_bucket(
55
+ bucket, location="asia-east1"
56
+ )
57
+ logger.info(
58
+ f"Created bucket {bucket_name} in asia-east1 and set lifecycle rules."
59
+ )
60
+ else:
61
+ logger.info(
62
+ f"Bucket {bucket_name} already exists. Checking lifecycle rules."
63
+ )
64
+ bucket.reload() # 獲取最新的 bucket metadata
65
+
66
+ # google-cloud-storage 回傳的 rule 是 frozenset of dicts,需要轉換
67
+ current_rules = [dict(rule) for rule in bucket.lifecycle_rules]
68
+
69
+ if current_rules != desired_rules:
70
+ logger.info(f"Updating lifecycle rules for bucket {bucket_name}")
71
+ bucket.lifecycle_rules = desired_rules
72
+ bucket.patch()
73
+ logger.info(
74
+ f"Successfully updated lifecycle rules for bucket {bucket_name}"
75
+ )
76
+ else:
77
+ logger.info(
78
+ f"Lifecycle rules for bucket {bucket_name} are already up-to-date."
79
+ )
80
+
81
+ return bucket
82
+ except Exception as e:
83
+ logger.error(f"Error creating or updating bucket {bucket_name}: {str(e)}")
84
+ return None
85
+
86
+ async def get_directory_sizes(self) -> dict:
87
+ """
88
+ 計算 bucket 中每個目錄的總檔案大小 (bytes) 與檔案數量,排除 tmp, html 目錄
89
+
90
+ Returns:
91
+ dict: 包含每個目錄資訊的字典,格式為 {directory_name: {"size": total_size_in_bytes, "file_count": count}}
92
+ """
93
+ try:
94
+ # 初始化結果字典
95
+ directory_info = {}
96
+
97
+ # 列出所有 blobs
98
+ blobs = list(self.bucket.list_blobs())
99
+
100
+ # 計算每個目錄的大小和檔案數量
101
+ for blob in blobs:
102
+ # 跳過 tmp 目錄
103
+ if blob.name.startswith("tmp/"):
104
+ continue
105
+ # 跳過 html 目錄
106
+ if blob.name.startswith("html/"):
107
+ continue
108
+
109
+ # 從 blob 名稱中提取目錄名稱 (第一層目錄)
110
+ parts = blob.name.split("/")
111
+ if len(parts) >= 1:
112
+ directory = parts[0]
113
+
114
+ # 如果這是一個新目錄,初始化其資訊
115
+ if directory not in directory_info:
116
+ directory_info[directory] = {"size": 0, "file_count": 0}
117
+
118
+ # 加上此 blob 的大小
119
+ directory_info[directory]["size"] += blob.size
120
+ # 增加檔案計數
121
+ directory_info[directory]["file_count"] += 1
122
+
123
+ return directory_info
124
+ except Exception as e:
125
+ logger.error(f"Error calculating directory sizes: {e}")
126
+ return {}
127
+
128
+ async def store_file(
129
+ self,
130
+ filepath: str,
131
+ file_object: BytesIO,
132
+ public: bool = False,
133
+ content_type: str = None,
134
+ ) -> Tuple[bool, Optional[str]]:
135
+ try:
136
+ blob = self.bucket.blob(filepath)
137
+
138
+ # 設定 content_type 和其他 metadata
139
+ if content_type:
140
+ blob.content_type = content_type
141
+ # 如果是圖片,設定為 inline 顯示並加入 cache control
142
+ if content_type.startswith("image/"):
143
+ blob.content_disposition = (
144
+ 'inline; filename="' + filepath.split("/")[-1] + '"'
145
+ )
146
+ blob.cache_control = "public, max-age=3600, no-transform"
147
+
148
+ # 上傳檔案
149
+ blob.upload_from_file(file_object, rewind=True)
150
+
151
+ # 確保 metadata 更新
152
+ blob.patch()
153
+
154
+ # 如果需要公開存取
155
+ if public:
156
+ blob.make_public()
157
+ return True, blob.public_url
158
+
159
+ return True, None
160
+ except Exception as e:
161
+ logger.error(f"Error storing file in Cloud Storage: {e}")
162
+ return False, None
163
+
164
+ async def get_public_url(self, filepath: str) -> Optional[str]:
165
+ try:
166
+ blob = self.bucket.blob(filepath)
167
+ if blob.exists():
168
+ return blob.public_url
169
+ return None
170
+ except Exception as e:
171
+ logger.error(f"Error getting public URL: {e}")
172
+ return None
173
+
174
+ async def retrieve_file(self, filepath: str) -> Optional[BytesIO]:
175
+ try:
176
+ blob = self.bucket.blob(filepath)
177
+ file_object = BytesIO()
178
+ blob.download_to_file(file_object)
179
+ file_object.seek(0) # Rewind the file object to the beginning
180
+ return file_object
181
+ except NotFound:
182
+ logger.error(f"File not found in Cloud Storage: {filepath}")
183
+ return None
184
+ except Exception as e:
185
+ logger.error(f"Error retrieving file from Cloud Storage: {e}")
186
+ return None
187
+
188
+ async def delete_file(self, filepath: str) -> bool:
189
+ try:
190
+ blob = self.bucket.blob(filepath)
191
+ blob.delete()
192
+ return True
193
+ except NotFound:
194
+ logger.error(f"File not found in Cloud Storage: {filepath}")
195
+ return False
196
+ except Exception as e:
197
+ logger.error(f"Error deleting file from Cloud Storage: {e}")
198
+ return False
199
+
200
+ async def file_exists(self, filepath: str) -> bool:
201
+ try:
202
+ blob = self.bucket.blob(filepath)
203
+ return blob.exists()
204
+ except Exception as e:
205
+ logger.error(f"Error checking file existence in Cloud Storage: {e}")
206
+ return False
@@ -1,12 +1,12 @@
1
- import os
2
- from dotenv import load_dotenv
3
-
4
- from botrun_flow_lang.services.storage.storage_cs_store import StorageCsStore
5
- from botrun_flow_lang.services.storage.storage_store import StorageStore
6
-
7
- load_dotenv()
8
-
9
-
10
- def storage_store_factory() -> StorageStore:
11
- env_name = os.getenv("HATCH_ENV_NAME", "botrun-hatch-dev")
12
- return StorageCsStore(env_name)
1
+ import os
2
+ from dotenv import load_dotenv
3
+
4
+ from botrun_flow_lang.services.storage.storage_cs_store import StorageCsStore
5
+ from botrun_flow_lang.services.storage.storage_store import StorageStore
6
+
7
+ load_dotenv()
8
+
9
+
10
+ def storage_store_factory() -> StorageStore:
11
+ env_name = os.getenv("HATCH_ENV_NAME", "botrun-hatch-dev")
12
+ return StorageCsStore(env_name)
@@ -1,65 +1,65 @@
1
- from abc import ABC, abstractmethod
2
- from typing import Optional, Tuple
3
- from io import BytesIO
4
-
5
-
6
- class StorageStore(ABC):
7
-
8
- @abstractmethod
9
- async def store_file(
10
- self,
11
- filepath: str,
12
- file_object: BytesIO,
13
- public: bool = False,
14
- content_type: str = None,
15
- ) -> Tuple[bool, Optional[str]]:
16
- """
17
- Store a file in the storage.
18
-
19
- :param filepath: The path where the file should be stored
20
- :param file_object: The file object to be stored (BytesIO)
21
- :param public: Whether the file should be publicly accessible
22
- :param content_type: The MIME type of the file
23
- :return: Tuple of (success, public_url if public=True else None)
24
- """
25
- pass
26
-
27
- @abstractmethod
28
- async def get_public_url(self, filepath: str) -> Optional[str]:
29
- """
30
- Get the public URL for a file.
31
-
32
- :param filepath: The path of the file
33
- :return: Public URL if the file exists and is public, None otherwise
34
- """
35
- pass
36
-
37
- @abstractmethod
38
- async def retrieve_file(self, filepath: str) -> Optional[BytesIO]:
39
- """
40
- Retrieve a file from the storage.
41
-
42
- :param filepath: The path of the file to retrieve
43
- :return: BytesIO object containing the file data if found, None otherwise
44
- """
45
- pass
46
-
47
- @abstractmethod
48
- async def delete_file(self, filepath: str) -> bool:
49
- """
50
- Delete a file from the storage.
51
-
52
- :param filepath: The path of the file to delete
53
- :return: True if the file was successfully deleted, False otherwise
54
- """
55
- pass
56
-
57
- @abstractmethod
58
- async def file_exists(self, filepath: str) -> bool:
59
- """
60
- Check if a file exists in the storage.
61
-
62
- :param filepath: The path of the file to check
63
- :return: True if the file exists, False otherwise
64
- """
65
- pass
1
+ from abc import ABC, abstractmethod
2
+ from typing import Optional, Tuple
3
+ from io import BytesIO
4
+
5
+
6
+ class StorageStore(ABC):
7
+
8
+ @abstractmethod
9
+ async def store_file(
10
+ self,
11
+ filepath: str,
12
+ file_object: BytesIO,
13
+ public: bool = False,
14
+ content_type: str = None,
15
+ ) -> Tuple[bool, Optional[str]]:
16
+ """
17
+ Store a file in the storage.
18
+
19
+ :param filepath: The path where the file should be stored
20
+ :param file_object: The file object to be stored (BytesIO)
21
+ :param public: Whether the file should be publicly accessible
22
+ :param content_type: The MIME type of the file
23
+ :return: Tuple of (success, public_url if public=True else None)
24
+ """
25
+ pass
26
+
27
+ @abstractmethod
28
+ async def get_public_url(self, filepath: str) -> Optional[str]:
29
+ """
30
+ Get the public URL for a file.
31
+
32
+ :param filepath: The path of the file
33
+ :return: Public URL if the file exists and is public, None otherwise
34
+ """
35
+ pass
36
+
37
+ @abstractmethod
38
+ async def retrieve_file(self, filepath: str) -> Optional[BytesIO]:
39
+ """
40
+ Retrieve a file from the storage.
41
+
42
+ :param filepath: The path of the file to retrieve
43
+ :return: BytesIO object containing the file data if found, None otherwise
44
+ """
45
+ pass
46
+
47
+ @abstractmethod
48
+ async def delete_file(self, filepath: str) -> bool:
49
+ """
50
+ Delete a file from the storage.
51
+
52
+ :param filepath: The path of the file to delete
53
+ :return: True if the file was successfully deleted, False otherwise
54
+ """
55
+ pass
56
+
57
+ @abstractmethod
58
+ async def file_exists(self, filepath: str) -> bool:
59
+ """
60
+ Check if a file exists in the storage.
61
+
62
+ :param filepath: The path of the file to check
63
+ :return: True if the file exists, False otherwise
64
+ """
65
+ pass
@@ -1,9 +1,9 @@
1
- import os
2
- from botrun_flow_lang.services.user_setting.user_setting_fs_store import (
3
- UserSettingFsStore,
4
- )
5
-
6
-
7
- def user_setting_store_factory():
8
- env_name = os.getenv("ENV_NAME", "dev")
9
- return UserSettingFsStore(env_name)
1
+ import os
2
+ from botrun_flow_lang.services.user_setting.user_setting_fs_store import (
3
+ UserSettingFsStore,
4
+ )
5
+
6
+
7
+ def user_setting_store_factory():
8
+ env_name = os.getenv("ENV_NAME", "dev")
9
+ return UserSettingFsStore(env_name)