botrun-flow-lang 5.10.32__py3-none-any.whl → 5.10.82__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 (84) 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 +481 -481
  6. botrun_flow_lang/api/langgraph_api.py +796 -796
  7. botrun_flow_lang/api/line_bot_api.py +1357 -1357
  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 +316 -316
  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 +174 -174
  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/gov_researcher_2_graph.py +1002 -1002
  24. botrun_flow_lang/langgraph_agents/agents/gov_researcher/gov_researcher_graph.py +822 -822
  25. botrun_flow_lang/langgraph_agents/agents/langgraph_react_agent.py +548 -548
  26. botrun_flow_lang/langgraph_agents/agents/search_agent_graph.py +864 -864
  27. botrun_flow_lang/langgraph_agents/agents/tools/__init__.py +4 -4
  28. botrun_flow_lang/langgraph_agents/agents/tools/gemini_code_execution.py +376 -376
  29. botrun_flow_lang/langgraph_agents/agents/util/gemini_grounding.py +66 -66
  30. botrun_flow_lang/langgraph_agents/agents/util/html_util.py +316 -316
  31. botrun_flow_lang/langgraph_agents/agents/util/img_util.py +294 -294
  32. botrun_flow_lang/langgraph_agents/agents/util/local_files.py +345 -345
  33. botrun_flow_lang/langgraph_agents/agents/util/mermaid_util.py +86 -86
  34. botrun_flow_lang/langgraph_agents/agents/util/model_utils.py +143 -143
  35. botrun_flow_lang/langgraph_agents/agents/util/pdf_analyzer.py +160 -160
  36. botrun_flow_lang/langgraph_agents/agents/util/perplexity_search.py +464 -464
  37. botrun_flow_lang/langgraph_agents/agents/util/plotly_util.py +59 -59
  38. botrun_flow_lang/langgraph_agents/agents/util/tavily_search.py +199 -199
  39. botrun_flow_lang/langgraph_agents/agents/util/youtube_util.py +90 -90
  40. botrun_flow_lang/langgraph_agents/cache/langgraph_botrun_cache.py +197 -197
  41. botrun_flow_lang/llm_agent/llm_agent.py +19 -19
  42. botrun_flow_lang/llm_agent/llm_agent_util.py +83 -83
  43. botrun_flow_lang/log/.gitignore +2 -2
  44. botrun_flow_lang/main.py +61 -61
  45. botrun_flow_lang/main_fast.py +51 -51
  46. botrun_flow_lang/mcp_server/__init__.py +10 -10
  47. botrun_flow_lang/mcp_server/default_mcp.py +711 -711
  48. botrun_flow_lang/models/nodes/utils.py +205 -205
  49. botrun_flow_lang/models/token_usage.py +34 -34
  50. botrun_flow_lang/requirements.txt +21 -21
  51. botrun_flow_lang/services/base/firestore_base.py +30 -30
  52. botrun_flow_lang/services/hatch/hatch_factory.py +11 -11
  53. botrun_flow_lang/services/hatch/hatch_fs_store.py +372 -372
  54. botrun_flow_lang/services/storage/storage_cs_store.py +202 -202
  55. botrun_flow_lang/services/storage/storage_factory.py +12 -12
  56. botrun_flow_lang/services/storage/storage_store.py +65 -65
  57. botrun_flow_lang/services/user_setting/user_setting_factory.py +9 -9
  58. botrun_flow_lang/services/user_setting/user_setting_fs_store.py +66 -66
  59. botrun_flow_lang/static/docs/tools/index.html +926 -926
  60. botrun_flow_lang/tests/api_functional_tests.py +1525 -1525
  61. botrun_flow_lang/tests/api_stress_test.py +357 -357
  62. botrun_flow_lang/tests/shared_hatch_tests.py +333 -333
  63. botrun_flow_lang/tests/test_botrun_app.py +46 -46
  64. botrun_flow_lang/tests/test_html_util.py +31 -31
  65. botrun_flow_lang/tests/test_img_analyzer.py +190 -190
  66. botrun_flow_lang/tests/test_img_util.py +39 -39
  67. botrun_flow_lang/tests/test_local_files.py +114 -114
  68. botrun_flow_lang/tests/test_mermaid_util.py +103 -103
  69. botrun_flow_lang/tests/test_pdf_analyzer.py +104 -104
  70. botrun_flow_lang/tests/test_plotly_util.py +151 -151
  71. botrun_flow_lang/tests/test_run_workflow_engine.py +65 -65
  72. botrun_flow_lang/tools/generate_docs.py +133 -133
  73. botrun_flow_lang/tools/templates/tools.html +153 -153
  74. botrun_flow_lang/utils/__init__.py +7 -7
  75. botrun_flow_lang/utils/botrun_logger.py +344 -344
  76. botrun_flow_lang/utils/clients/rate_limit_client.py +209 -209
  77. botrun_flow_lang/utils/clients/token_verify_client.py +153 -153
  78. botrun_flow_lang/utils/google_drive_utils.py +654 -654
  79. botrun_flow_lang/utils/langchain_utils.py +324 -324
  80. botrun_flow_lang/utils/yaml_utils.py +9 -9
  81. {botrun_flow_lang-5.10.32.dist-info → botrun_flow_lang-5.10.82.dist-info}/METADATA +2 -2
  82. botrun_flow_lang-5.10.82.dist-info/RECORD +99 -0
  83. botrun_flow_lang-5.10.32.dist-info/RECORD +0 -99
  84. {botrun_flow_lang-5.10.32.dist-info → botrun_flow_lang-5.10.82.dist-info}/WHEEL +0 -0
@@ -1,202 +1,202 @@
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": 7, "matchesPrefix": ["tmp/"]},
42
- }
43
- ]
44
-
45
- if not bucket.exists():
46
- logger.info(f"Creating new bucket: {bucket_name}")
47
- # 設定 lifecycle rules
48
- bucket.lifecycle_rules = desired_rules
49
- # 創建 bucket
50
- bucket = self.storage_client.create_bucket(
51
- bucket, location="asia-east1"
52
- )
53
- logger.info(
54
- f"Created bucket {bucket_name} in asia-east1 and set lifecycle rules."
55
- )
56
- else:
57
- logger.info(
58
- f"Bucket {bucket_name} already exists. Checking lifecycle rules."
59
- )
60
- bucket.reload() # 獲取最新的 bucket metadata
61
-
62
- # google-cloud-storage 回傳的 rule 是 frozenset of dicts,需要轉換
63
- current_rules = [dict(rule) for rule in bucket.lifecycle_rules]
64
-
65
- if current_rules != desired_rules:
66
- logger.info(f"Updating lifecycle rules for bucket {bucket_name}")
67
- bucket.lifecycle_rules = desired_rules
68
- bucket.patch()
69
- logger.info(
70
- f"Successfully updated lifecycle rules for bucket {bucket_name}"
71
- )
72
- else:
73
- logger.info(
74
- f"Lifecycle rules for bucket {bucket_name} are already up-to-date."
75
- )
76
-
77
- return bucket
78
- except Exception as e:
79
- logger.error(f"Error creating or updating bucket {bucket_name}: {str(e)}")
80
- return None
81
-
82
- async def get_directory_sizes(self) -> dict:
83
- """
84
- 計算 bucket 中每個目錄的總檔案大小 (bytes) 與檔案數量,排除 tmp, html 目錄
85
-
86
- Returns:
87
- dict: 包含每個目錄資訊的字典,格式為 {directory_name: {"size": total_size_in_bytes, "file_count": count}}
88
- """
89
- try:
90
- # 初始化結果字典
91
- directory_info = {}
92
-
93
- # 列出所有 blobs
94
- blobs = list(self.bucket.list_blobs())
95
-
96
- # 計算每個目錄的大小和檔案數量
97
- for blob in blobs:
98
- # 跳過 tmp 目錄
99
- if blob.name.startswith("tmp/"):
100
- continue
101
- # 跳過 html 目錄
102
- if blob.name.startswith("html/"):
103
- continue
104
-
105
- # 從 blob 名稱中提取目錄名稱 (第一層目錄)
106
- parts = blob.name.split("/")
107
- if len(parts) >= 1:
108
- directory = parts[0]
109
-
110
- # 如果這是一個新目錄,初始化其資訊
111
- if directory not in directory_info:
112
- directory_info[directory] = {"size": 0, "file_count": 0}
113
-
114
- # 加上此 blob 的大小
115
- directory_info[directory]["size"] += blob.size
116
- # 增加檔案計數
117
- directory_info[directory]["file_count"] += 1
118
-
119
- return directory_info
120
- except Exception as e:
121
- logger.error(f"Error calculating directory sizes: {e}")
122
- return {}
123
-
124
- async def store_file(
125
- self,
126
- filepath: str,
127
- file_object: BytesIO,
128
- public: bool = False,
129
- content_type: str = None,
130
- ) -> Tuple[bool, Optional[str]]:
131
- try:
132
- blob = self.bucket.blob(filepath)
133
-
134
- # 設定 content_type 和其他 metadata
135
- if content_type:
136
- blob.content_type = content_type
137
- # 如果是圖片,設定為 inline 顯示並加入 cache control
138
- if content_type.startswith("image/"):
139
- blob.content_disposition = (
140
- 'inline; filename="' + filepath.split("/")[-1] + '"'
141
- )
142
- blob.cache_control = "public, max-age=3600, no-transform"
143
-
144
- # 上傳檔案
145
- blob.upload_from_file(file_object, rewind=True)
146
-
147
- # 確保 metadata 更新
148
- blob.patch()
149
-
150
- # 如果需要公開存取
151
- if public:
152
- blob.make_public()
153
- return True, blob.public_url
154
-
155
- return True, None
156
- except Exception as e:
157
- logger.error(f"Error storing file in Cloud Storage: {e}")
158
- return False, None
159
-
160
- async def get_public_url(self, filepath: str) -> Optional[str]:
161
- try:
162
- blob = self.bucket.blob(filepath)
163
- if blob.exists():
164
- return blob.public_url
165
- return None
166
- except Exception as e:
167
- logger.error(f"Error getting public URL: {e}")
168
- return None
169
-
170
- async def retrieve_file(self, filepath: str) -> Optional[BytesIO]:
171
- try:
172
- blob = self.bucket.blob(filepath)
173
- file_object = BytesIO()
174
- blob.download_to_file(file_object)
175
- file_object.seek(0) # Rewind the file object to the beginning
176
- return file_object
177
- except NotFound:
178
- logger.error(f"File not found in Cloud Storage: {filepath}")
179
- return None
180
- except Exception as e:
181
- logger.error(f"Error retrieving file from Cloud Storage: {e}")
182
- return None
183
-
184
- async def delete_file(self, filepath: str) -> bool:
185
- try:
186
- blob = self.bucket.blob(filepath)
187
- blob.delete()
188
- return True
189
- except NotFound:
190
- logger.error(f"File not found in Cloud Storage: {filepath}")
191
- return False
192
- except Exception as e:
193
- logger.error(f"Error deleting file from Cloud Storage: {e}")
194
- return False
195
-
196
- async def file_exists(self, filepath: str) -> bool:
197
- try:
198
- blob = self.bucket.blob(filepath)
199
- return blob.exists()
200
- except Exception as e:
201
- logger.error(f"Error checking file existence in Cloud Storage: {e}")
202
- 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": 7, "matchesPrefix": ["tmp/"]},
42
+ }
43
+ ]
44
+
45
+ if not bucket.exists():
46
+ logger.info(f"Creating new bucket: {bucket_name}")
47
+ # 設定 lifecycle rules
48
+ bucket.lifecycle_rules = desired_rules
49
+ # 創建 bucket
50
+ bucket = self.storage_client.create_bucket(
51
+ bucket, location="asia-east1"
52
+ )
53
+ logger.info(
54
+ f"Created bucket {bucket_name} in asia-east1 and set lifecycle rules."
55
+ )
56
+ else:
57
+ logger.info(
58
+ f"Bucket {bucket_name} already exists. Checking lifecycle rules."
59
+ )
60
+ bucket.reload() # 獲取最新的 bucket metadata
61
+
62
+ # google-cloud-storage 回傳的 rule 是 frozenset of dicts,需要轉換
63
+ current_rules = [dict(rule) for rule in bucket.lifecycle_rules]
64
+
65
+ if current_rules != desired_rules:
66
+ logger.info(f"Updating lifecycle rules for bucket {bucket_name}")
67
+ bucket.lifecycle_rules = desired_rules
68
+ bucket.patch()
69
+ logger.info(
70
+ f"Successfully updated lifecycle rules for bucket {bucket_name}"
71
+ )
72
+ else:
73
+ logger.info(
74
+ f"Lifecycle rules for bucket {bucket_name} are already up-to-date."
75
+ )
76
+
77
+ return bucket
78
+ except Exception as e:
79
+ logger.error(f"Error creating or updating bucket {bucket_name}: {str(e)}")
80
+ return None
81
+
82
+ async def get_directory_sizes(self) -> dict:
83
+ """
84
+ 計算 bucket 中每個目錄的總檔案大小 (bytes) 與檔案數量,排除 tmp, html 目錄
85
+
86
+ Returns:
87
+ dict: 包含每個目錄資訊的字典,格式為 {directory_name: {"size": total_size_in_bytes, "file_count": count}}
88
+ """
89
+ try:
90
+ # 初始化結果字典
91
+ directory_info = {}
92
+
93
+ # 列出所有 blobs
94
+ blobs = list(self.bucket.list_blobs())
95
+
96
+ # 計算每個目錄的大小和檔案數量
97
+ for blob in blobs:
98
+ # 跳過 tmp 目錄
99
+ if blob.name.startswith("tmp/"):
100
+ continue
101
+ # 跳過 html 目錄
102
+ if blob.name.startswith("html/"):
103
+ continue
104
+
105
+ # 從 blob 名稱中提取目錄名稱 (第一層目錄)
106
+ parts = blob.name.split("/")
107
+ if len(parts) >= 1:
108
+ directory = parts[0]
109
+
110
+ # 如果這是一個新目錄,初始化其資訊
111
+ if directory not in directory_info:
112
+ directory_info[directory] = {"size": 0, "file_count": 0}
113
+
114
+ # 加上此 blob 的大小
115
+ directory_info[directory]["size"] += blob.size
116
+ # 增加檔案計數
117
+ directory_info[directory]["file_count"] += 1
118
+
119
+ return directory_info
120
+ except Exception as e:
121
+ logger.error(f"Error calculating directory sizes: {e}")
122
+ return {}
123
+
124
+ async def store_file(
125
+ self,
126
+ filepath: str,
127
+ file_object: BytesIO,
128
+ public: bool = False,
129
+ content_type: str = None,
130
+ ) -> Tuple[bool, Optional[str]]:
131
+ try:
132
+ blob = self.bucket.blob(filepath)
133
+
134
+ # 設定 content_type 和其他 metadata
135
+ if content_type:
136
+ blob.content_type = content_type
137
+ # 如果是圖片,設定為 inline 顯示並加入 cache control
138
+ if content_type.startswith("image/"):
139
+ blob.content_disposition = (
140
+ 'inline; filename="' + filepath.split("/")[-1] + '"'
141
+ )
142
+ blob.cache_control = "public, max-age=3600, no-transform"
143
+
144
+ # 上傳檔案
145
+ blob.upload_from_file(file_object, rewind=True)
146
+
147
+ # 確保 metadata 更新
148
+ blob.patch()
149
+
150
+ # 如果需要公開存取
151
+ if public:
152
+ blob.make_public()
153
+ return True, blob.public_url
154
+
155
+ return True, None
156
+ except Exception as e:
157
+ logger.error(f"Error storing file in Cloud Storage: {e}")
158
+ return False, None
159
+
160
+ async def get_public_url(self, filepath: str) -> Optional[str]:
161
+ try:
162
+ blob = self.bucket.blob(filepath)
163
+ if blob.exists():
164
+ return blob.public_url
165
+ return None
166
+ except Exception as e:
167
+ logger.error(f"Error getting public URL: {e}")
168
+ return None
169
+
170
+ async def retrieve_file(self, filepath: str) -> Optional[BytesIO]:
171
+ try:
172
+ blob = self.bucket.blob(filepath)
173
+ file_object = BytesIO()
174
+ blob.download_to_file(file_object)
175
+ file_object.seek(0) # Rewind the file object to the beginning
176
+ return file_object
177
+ except NotFound:
178
+ logger.error(f"File not found in Cloud Storage: {filepath}")
179
+ return None
180
+ except Exception as e:
181
+ logger.error(f"Error retrieving file from Cloud Storage: {e}")
182
+ return None
183
+
184
+ async def delete_file(self, filepath: str) -> bool:
185
+ try:
186
+ blob = self.bucket.blob(filepath)
187
+ blob.delete()
188
+ return True
189
+ except NotFound:
190
+ logger.error(f"File not found in Cloud Storage: {filepath}")
191
+ return False
192
+ except Exception as e:
193
+ logger.error(f"Error deleting file from Cloud Storage: {e}")
194
+ return False
195
+
196
+ async def file_exists(self, filepath: str) -> bool:
197
+ try:
198
+ blob = self.bucket.blob(filepath)
199
+ return blob.exists()
200
+ except Exception as e:
201
+ logger.error(f"Error checking file existence in Cloud Storage: {e}")
202
+ 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)