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.
- botrun_flow_lang/api/auth_api.py +39 -39
- botrun_flow_lang/api/auth_utils.py +183 -183
- botrun_flow_lang/api/botrun_back_api.py +65 -65
- botrun_flow_lang/api/flow_api.py +3 -3
- botrun_flow_lang/api/hatch_api.py +508 -508
- botrun_flow_lang/api/langgraph_api.py +811 -811
- botrun_flow_lang/api/line_bot_api.py +1484 -1484
- botrun_flow_lang/api/model_api.py +300 -300
- botrun_flow_lang/api/rate_limit_api.py +32 -32
- botrun_flow_lang/api/routes.py +79 -79
- botrun_flow_lang/api/search_api.py +53 -53
- botrun_flow_lang/api/storage_api.py +395 -395
- botrun_flow_lang/api/subsidy_api.py +290 -290
- botrun_flow_lang/api/subsidy_api_system_prompt.txt +109 -109
- botrun_flow_lang/api/user_setting_api.py +70 -70
- botrun_flow_lang/api/version_api.py +31 -31
- botrun_flow_lang/api/youtube_api.py +26 -26
- botrun_flow_lang/constants.py +13 -13
- botrun_flow_lang/langgraph_agents/agents/agent_runner.py +178 -178
- botrun_flow_lang/langgraph_agents/agents/agent_tools/step_planner.py +77 -77
- botrun_flow_lang/langgraph_agents/agents/checkpointer/firestore_checkpointer.py +666 -666
- botrun_flow_lang/langgraph_agents/agents/gov_researcher/GOV_RESEARCHER_PRD.md +192 -192
- botrun_flow_lang/langgraph_agents/agents/gov_researcher/gemini_subsidy_graph.py +460 -460
- botrun_flow_lang/langgraph_agents/agents/gov_researcher/gov_researcher_2_graph.py +1002 -1002
- botrun_flow_lang/langgraph_agents/agents/gov_researcher/gov_researcher_graph.py +822 -822
- botrun_flow_lang/langgraph_agents/agents/langgraph_react_agent.py +723 -723
- botrun_flow_lang/langgraph_agents/agents/search_agent_graph.py +864 -864
- botrun_flow_lang/langgraph_agents/agents/tools/__init__.py +4 -4
- botrun_flow_lang/langgraph_agents/agents/tools/gemini_code_execution.py +376 -376
- botrun_flow_lang/langgraph_agents/agents/util/gemini_grounding.py +66 -66
- botrun_flow_lang/langgraph_agents/agents/util/html_util.py +316 -316
- botrun_flow_lang/langgraph_agents/agents/util/img_util.py +294 -294
- botrun_flow_lang/langgraph_agents/agents/util/local_files.py +419 -419
- botrun_flow_lang/langgraph_agents/agents/util/mermaid_util.py +86 -86
- botrun_flow_lang/langgraph_agents/agents/util/model_utils.py +143 -143
- botrun_flow_lang/langgraph_agents/agents/util/pdf_analyzer.py +486 -486
- botrun_flow_lang/langgraph_agents/agents/util/pdf_cache.py +250 -250
- botrun_flow_lang/langgraph_agents/agents/util/pdf_processor.py +204 -204
- botrun_flow_lang/langgraph_agents/agents/util/perplexity_search.py +464 -464
- botrun_flow_lang/langgraph_agents/agents/util/plotly_util.py +59 -59
- botrun_flow_lang/langgraph_agents/agents/util/tavily_search.py +199 -199
- botrun_flow_lang/langgraph_agents/agents/util/youtube_util.py +90 -90
- botrun_flow_lang/langgraph_agents/cache/langgraph_botrun_cache.py +197 -197
- botrun_flow_lang/llm_agent/llm_agent.py +19 -19
- botrun_flow_lang/llm_agent/llm_agent_util.py +83 -83
- botrun_flow_lang/log/.gitignore +2 -2
- botrun_flow_lang/main.py +61 -61
- botrun_flow_lang/main_fast.py +51 -51
- botrun_flow_lang/mcp_server/__init__.py +10 -10
- botrun_flow_lang/mcp_server/default_mcp.py +744 -744
- botrun_flow_lang/models/nodes/utils.py +205 -205
- botrun_flow_lang/models/token_usage.py +34 -34
- botrun_flow_lang/requirements.txt +21 -21
- botrun_flow_lang/services/base/firestore_base.py +30 -30
- botrun_flow_lang/services/hatch/hatch_factory.py +11 -11
- botrun_flow_lang/services/hatch/hatch_fs_store.py +419 -419
- botrun_flow_lang/services/storage/storage_cs_store.py +206 -206
- botrun_flow_lang/services/storage/storage_factory.py +12 -12
- botrun_flow_lang/services/storage/storage_store.py +65 -65
- botrun_flow_lang/services/user_setting/user_setting_factory.py +9 -9
- botrun_flow_lang/services/user_setting/user_setting_fs_store.py +66 -66
- botrun_flow_lang/static/docs/tools/index.html +926 -926
- botrun_flow_lang/tests/api_functional_tests.py +1525 -1525
- botrun_flow_lang/tests/api_stress_test.py +357 -357
- botrun_flow_lang/tests/shared_hatch_tests.py +333 -333
- botrun_flow_lang/tests/test_botrun_app.py +46 -46
- botrun_flow_lang/tests/test_html_util.py +31 -31
- botrun_flow_lang/tests/test_img_analyzer.py +190 -190
- botrun_flow_lang/tests/test_img_util.py +39 -39
- botrun_flow_lang/tests/test_local_files.py +114 -114
- botrun_flow_lang/tests/test_mermaid_util.py +103 -103
- botrun_flow_lang/tests/test_pdf_analyzer.py +104 -104
- botrun_flow_lang/tests/test_plotly_util.py +151 -151
- botrun_flow_lang/tests/test_run_workflow_engine.py +65 -65
- botrun_flow_lang/tools/generate_docs.py +133 -133
- botrun_flow_lang/tools/templates/tools.html +153 -153
- botrun_flow_lang/utils/__init__.py +7 -7
- botrun_flow_lang/utils/botrun_logger.py +344 -344
- botrun_flow_lang/utils/clients/rate_limit_client.py +209 -209
- botrun_flow_lang/utils/clients/token_verify_client.py +153 -153
- botrun_flow_lang/utils/google_drive_utils.py +654 -654
- botrun_flow_lang/utils/langchain_utils.py +324 -324
- botrun_flow_lang/utils/yaml_utils.py +9 -9
- {botrun_flow_lang-5.12.263.dist-info → botrun_flow_lang-5.12.264.dist-info}/METADATA +1 -1
- botrun_flow_lang-5.12.264.dist-info/RECORD +102 -0
- botrun_flow_lang-5.12.263.dist-info/RECORD +0 -102
- {botrun_flow_lang-5.12.263.dist-info → botrun_flow_lang-5.12.264.dist-info}/WHEEL +0 -0
|
@@ -1,114 +1,114 @@
|
|
|
1
|
-
import unittest
|
|
2
|
-
import asyncio
|
|
3
|
-
import os
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
from botrun_flow_lang.langgraph_agents.agents.util.local_files import (
|
|
6
|
-
generate_tmp_text_file,
|
|
7
|
-
read_tmp_text_file,
|
|
8
|
-
)
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class TestLocalFiles(unittest.TestCase):
|
|
12
|
-
def setUp(self):
|
|
13
|
-
"""Set up test fixtures"""
|
|
14
|
-
# Test configuration
|
|
15
|
-
self.test_text_content = (
|
|
16
|
-
"This is a test text file content.\n\nLine 2\nLine 3\n測試中文內容"
|
|
17
|
-
)
|
|
18
|
-
self.user_id = "sebastian.hsu@gmail.com"
|
|
19
|
-
|
|
20
|
-
def test_generate_tmp_text_file(self):
|
|
21
|
-
"""Test generate_tmp_text_file function"""
|
|
22
|
-
# Run the async function
|
|
23
|
-
result = asyncio.run(
|
|
24
|
-
generate_tmp_text_file(self.test_text_content, self.user_id)
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
# Print the result for manual verification
|
|
28
|
-
print("\nTest Result:")
|
|
29
|
-
print(f"Generated storage path: {result}")
|
|
30
|
-
|
|
31
|
-
# Basic validation
|
|
32
|
-
if result.startswith("Error:"):
|
|
33
|
-
print(f"Test failed with error: {result}")
|
|
34
|
-
self.fail(f"Test failed with error: {result}")
|
|
35
|
-
else:
|
|
36
|
-
print("Test successful - storage path generated")
|
|
37
|
-
# Validate storage path format
|
|
38
|
-
expected_path_prefix = f"tmp/{self.user_id}/"
|
|
39
|
-
self.assertTrue(
|
|
40
|
-
result.startswith(expected_path_prefix),
|
|
41
|
-
f"Expected storage path to start with '{expected_path_prefix}', got: {result}",
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
# Verify content by reading back the file
|
|
45
|
-
read_content = asyncio.run(read_tmp_text_file(result))
|
|
46
|
-
if read_content.startswith("Error:"):
|
|
47
|
-
self.fail(f"Failed to read back content: {read_content}")
|
|
48
|
-
else:
|
|
49
|
-
self.assertEqual(
|
|
50
|
-
read_content,
|
|
51
|
-
self.test_text_content,
|
|
52
|
-
"Read content doesn't match original content"
|
|
53
|
-
)
|
|
54
|
-
print("Content verification successful - read content matches original")
|
|
55
|
-
|
|
56
|
-
def test_generate_tmp_text_file_with_unicode(self):
|
|
57
|
-
"""Test generate_tmp_text_file function with unicode content"""
|
|
58
|
-
unicode_content = "Unicode test: 你好世界 🌍 ñáéíóú âêîôû àèìòù"
|
|
59
|
-
|
|
60
|
-
result = asyncio.run(
|
|
61
|
-
generate_tmp_text_file(unicode_content, self.user_id)
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
print(f"\nUnicode Test Result: {result}")
|
|
65
|
-
|
|
66
|
-
if result.startswith("Error:"):
|
|
67
|
-
print(f"Unicode test failed: {result}")
|
|
68
|
-
self.fail(f"Unicode test failed: {result}")
|
|
69
|
-
else:
|
|
70
|
-
print("Unicode test successful - storage path generated")
|
|
71
|
-
|
|
72
|
-
# Verify content by reading back the file
|
|
73
|
-
read_content = asyncio.run(read_tmp_text_file(result))
|
|
74
|
-
if read_content.startswith("Error:"):
|
|
75
|
-
self.fail(f"Failed to read back unicode content: {read_content}")
|
|
76
|
-
else:
|
|
77
|
-
self.assertEqual(
|
|
78
|
-
read_content,
|
|
79
|
-
unicode_content,
|
|
80
|
-
"Unicode read content doesn't match original content"
|
|
81
|
-
)
|
|
82
|
-
print("Unicode content verification successful - read content matches original")
|
|
83
|
-
|
|
84
|
-
def test_generate_tmp_text_file_with_empty_content(self):
|
|
85
|
-
"""Test generate_tmp_text_file function with empty content"""
|
|
86
|
-
empty_content = ""
|
|
87
|
-
|
|
88
|
-
result = asyncio.run(
|
|
89
|
-
generate_tmp_text_file(empty_content, self.user_id)
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
print(f"\nEmpty Content Test Result: {result}")
|
|
93
|
-
|
|
94
|
-
if result.startswith("Error:"):
|
|
95
|
-
print(f"Empty content test failed: {result}")
|
|
96
|
-
self.fail(f"Empty content test failed: {result}")
|
|
97
|
-
else:
|
|
98
|
-
print("Empty content test successful - storage path generated")
|
|
99
|
-
|
|
100
|
-
# Verify content by reading back the file
|
|
101
|
-
read_content = asyncio.run(read_tmp_text_file(result))
|
|
102
|
-
if read_content.startswith("Error:"):
|
|
103
|
-
self.fail(f"Failed to read back empty content: {read_content}")
|
|
104
|
-
else:
|
|
105
|
-
self.assertEqual(
|
|
106
|
-
read_content,
|
|
107
|
-
empty_content,
|
|
108
|
-
"Empty read content doesn't match original content"
|
|
109
|
-
)
|
|
110
|
-
print("Empty content verification successful - read content matches original")
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if __name__ == "__main__":
|
|
114
|
-
unittest.main()
|
|
1
|
+
import unittest
|
|
2
|
+
import asyncio
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from botrun_flow_lang.langgraph_agents.agents.util.local_files import (
|
|
6
|
+
generate_tmp_text_file,
|
|
7
|
+
read_tmp_text_file,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestLocalFiles(unittest.TestCase):
|
|
12
|
+
def setUp(self):
|
|
13
|
+
"""Set up test fixtures"""
|
|
14
|
+
# Test configuration
|
|
15
|
+
self.test_text_content = (
|
|
16
|
+
"This is a test text file content.\n\nLine 2\nLine 3\n測試中文內容"
|
|
17
|
+
)
|
|
18
|
+
self.user_id = "sebastian.hsu@gmail.com"
|
|
19
|
+
|
|
20
|
+
def test_generate_tmp_text_file(self):
|
|
21
|
+
"""Test generate_tmp_text_file function"""
|
|
22
|
+
# Run the async function
|
|
23
|
+
result = asyncio.run(
|
|
24
|
+
generate_tmp_text_file(self.test_text_content, self.user_id)
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# Print the result for manual verification
|
|
28
|
+
print("\nTest Result:")
|
|
29
|
+
print(f"Generated storage path: {result}")
|
|
30
|
+
|
|
31
|
+
# Basic validation
|
|
32
|
+
if result.startswith("Error:"):
|
|
33
|
+
print(f"Test failed with error: {result}")
|
|
34
|
+
self.fail(f"Test failed with error: {result}")
|
|
35
|
+
else:
|
|
36
|
+
print("Test successful - storage path generated")
|
|
37
|
+
# Validate storage path format
|
|
38
|
+
expected_path_prefix = f"tmp/{self.user_id}/"
|
|
39
|
+
self.assertTrue(
|
|
40
|
+
result.startswith(expected_path_prefix),
|
|
41
|
+
f"Expected storage path to start with '{expected_path_prefix}', got: {result}",
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# Verify content by reading back the file
|
|
45
|
+
read_content = asyncio.run(read_tmp_text_file(result))
|
|
46
|
+
if read_content.startswith("Error:"):
|
|
47
|
+
self.fail(f"Failed to read back content: {read_content}")
|
|
48
|
+
else:
|
|
49
|
+
self.assertEqual(
|
|
50
|
+
read_content,
|
|
51
|
+
self.test_text_content,
|
|
52
|
+
"Read content doesn't match original content"
|
|
53
|
+
)
|
|
54
|
+
print("Content verification successful - read content matches original")
|
|
55
|
+
|
|
56
|
+
def test_generate_tmp_text_file_with_unicode(self):
|
|
57
|
+
"""Test generate_tmp_text_file function with unicode content"""
|
|
58
|
+
unicode_content = "Unicode test: 你好世界 🌍 ñáéíóú âêîôû àèìòù"
|
|
59
|
+
|
|
60
|
+
result = asyncio.run(
|
|
61
|
+
generate_tmp_text_file(unicode_content, self.user_id)
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
print(f"\nUnicode Test Result: {result}")
|
|
65
|
+
|
|
66
|
+
if result.startswith("Error:"):
|
|
67
|
+
print(f"Unicode test failed: {result}")
|
|
68
|
+
self.fail(f"Unicode test failed: {result}")
|
|
69
|
+
else:
|
|
70
|
+
print("Unicode test successful - storage path generated")
|
|
71
|
+
|
|
72
|
+
# Verify content by reading back the file
|
|
73
|
+
read_content = asyncio.run(read_tmp_text_file(result))
|
|
74
|
+
if read_content.startswith("Error:"):
|
|
75
|
+
self.fail(f"Failed to read back unicode content: {read_content}")
|
|
76
|
+
else:
|
|
77
|
+
self.assertEqual(
|
|
78
|
+
read_content,
|
|
79
|
+
unicode_content,
|
|
80
|
+
"Unicode read content doesn't match original content"
|
|
81
|
+
)
|
|
82
|
+
print("Unicode content verification successful - read content matches original")
|
|
83
|
+
|
|
84
|
+
def test_generate_tmp_text_file_with_empty_content(self):
|
|
85
|
+
"""Test generate_tmp_text_file function with empty content"""
|
|
86
|
+
empty_content = ""
|
|
87
|
+
|
|
88
|
+
result = asyncio.run(
|
|
89
|
+
generate_tmp_text_file(empty_content, self.user_id)
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
print(f"\nEmpty Content Test Result: {result}")
|
|
93
|
+
|
|
94
|
+
if result.startswith("Error:"):
|
|
95
|
+
print(f"Empty content test failed: {result}")
|
|
96
|
+
self.fail(f"Empty content test failed: {result}")
|
|
97
|
+
else:
|
|
98
|
+
print("Empty content test successful - storage path generated")
|
|
99
|
+
|
|
100
|
+
# Verify content by reading back the file
|
|
101
|
+
read_content = asyncio.run(read_tmp_text_file(result))
|
|
102
|
+
if read_content.startswith("Error:"):
|
|
103
|
+
self.fail(f"Failed to read back empty content: {read_content}")
|
|
104
|
+
else:
|
|
105
|
+
self.assertEqual(
|
|
106
|
+
read_content,
|
|
107
|
+
empty_content,
|
|
108
|
+
"Empty read content doesn't match original content"
|
|
109
|
+
)
|
|
110
|
+
print("Empty content verification successful - read content matches original")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
if __name__ == "__main__":
|
|
114
|
+
unittest.main()
|
|
@@ -1,103 +1,103 @@
|
|
|
1
|
-
import unittest
|
|
2
|
-
import requests
|
|
3
|
-
from botrun_flow_lang.langgraph_agents.agents.util.mermaid_util import (
|
|
4
|
-
generate_mermaid_files,
|
|
5
|
-
)
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class TestMermaidUtil(unittest.TestCase):
|
|
9
|
-
def setUp(self):
|
|
10
|
-
"""Set up test fixtures"""
|
|
11
|
-
self.botrun_flow_lang_url = (
|
|
12
|
-
"https://botrun-flow-lang-fastapi-dev-36186877499.asia-east1.run.app"
|
|
13
|
-
)
|
|
14
|
-
self.user_id = "sebastian.hsu@gmail.com"
|
|
15
|
-
|
|
16
|
-
def verify_mermaid_content(self, html_url: str):
|
|
17
|
-
"""Helper method to verify the mermaid HTML content"""
|
|
18
|
-
# Verify URL format
|
|
19
|
-
self.assertTrue(html_url.endswith(".html"), "URL should end with .html")
|
|
20
|
-
|
|
21
|
-
# Get the HTML content
|
|
22
|
-
response = requests.get(html_url)
|
|
23
|
-
self.assertEqual(response.status_code, 200, "Failed to fetch HTML content")
|
|
24
|
-
|
|
25
|
-
html_content = response.text
|
|
26
|
-
|
|
27
|
-
# Check if it's a mermaid diagram
|
|
28
|
-
self.assertTrue(
|
|
29
|
-
"mermaid" in html_content, "HTML content should contain 'mermaid'"
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
# Check if it contains the required elements
|
|
33
|
-
self.assertTrue(
|
|
34
|
-
'<div class="mermaid"' in html_content,
|
|
35
|
-
"HTML content should contain mermaid div",
|
|
36
|
-
)
|
|
37
|
-
self.assertTrue(
|
|
38
|
-
"mermaid.min.js" in html_content,
|
|
39
|
-
"HTML content should contain mermaid.js script",
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
def test_generate_flowchart(self):
|
|
43
|
-
"""Test generating a flowchart"""
|
|
44
|
-
# Test data for flowchart
|
|
45
|
-
flowchart_data = """
|
|
46
|
-
graph TD
|
|
47
|
-
A[開始] --> B{是否有資料?}
|
|
48
|
-
B -->|是| C[處理資料]
|
|
49
|
-
B -->|否| D[取得資料]
|
|
50
|
-
C --> E[結束]
|
|
51
|
-
D --> B
|
|
52
|
-
"""
|
|
53
|
-
|
|
54
|
-
# Execute test
|
|
55
|
-
html_url = generate_mermaid_files(
|
|
56
|
-
mermaid_data=flowchart_data,
|
|
57
|
-
botrun_flow_lang_url=self.botrun_flow_lang_url,
|
|
58
|
-
user_id=self.user_id,
|
|
59
|
-
title="互動式流程圖",
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
# Verify results
|
|
63
|
-
self.verify_mermaid_content(html_url)
|
|
64
|
-
|
|
65
|
-
def test_generate_sequence_diagram(self):
|
|
66
|
-
"""Test generating a sequence diagram"""
|
|
67
|
-
# Test data for sequence diagram
|
|
68
|
-
sequence_data = """
|
|
69
|
-
sequenceDiagram
|
|
70
|
-
participant 使用者
|
|
71
|
-
participant 系統
|
|
72
|
-
participant 資料庫
|
|
73
|
-
使用者->>系統: 請求資料
|
|
74
|
-
系統->>資料庫: 查詢資料
|
|
75
|
-
資料庫-->>系統: 返回結果
|
|
76
|
-
系統-->>使用者: 顯示資料
|
|
77
|
-
"""
|
|
78
|
-
|
|
79
|
-
# Execute test
|
|
80
|
-
html_url = generate_mermaid_files(
|
|
81
|
-
mermaid_data=sequence_data,
|
|
82
|
-
botrun_flow_lang_url=self.botrun_flow_lang_url,
|
|
83
|
-
user_id=self.user_id,
|
|
84
|
-
title="互動式序列圖",
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
# Verify results
|
|
88
|
-
self.verify_mermaid_content(html_url)
|
|
89
|
-
|
|
90
|
-
def test_generate_mermaid_files_error(self):
|
|
91
|
-
"""Test error handling with invalid data"""
|
|
92
|
-
result = generate_mermaid_files(
|
|
93
|
-
mermaid_data="invalid mermaid syntax",
|
|
94
|
-
botrun_flow_lang_url=self.botrun_flow_lang_url,
|
|
95
|
-
user_id=self.user_id,
|
|
96
|
-
)
|
|
97
|
-
self.assertFalse(
|
|
98
|
-
result.startswith("Error:")
|
|
99
|
-
) # Mermaid will handle syntax errors client-side
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if __name__ == "__main__":
|
|
103
|
-
unittest.main()
|
|
1
|
+
import unittest
|
|
2
|
+
import requests
|
|
3
|
+
from botrun_flow_lang.langgraph_agents.agents.util.mermaid_util import (
|
|
4
|
+
generate_mermaid_files,
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TestMermaidUtil(unittest.TestCase):
|
|
9
|
+
def setUp(self):
|
|
10
|
+
"""Set up test fixtures"""
|
|
11
|
+
self.botrun_flow_lang_url = (
|
|
12
|
+
"https://botrun-flow-lang-fastapi-dev-36186877499.asia-east1.run.app"
|
|
13
|
+
)
|
|
14
|
+
self.user_id = "sebastian.hsu@gmail.com"
|
|
15
|
+
|
|
16
|
+
def verify_mermaid_content(self, html_url: str):
|
|
17
|
+
"""Helper method to verify the mermaid HTML content"""
|
|
18
|
+
# Verify URL format
|
|
19
|
+
self.assertTrue(html_url.endswith(".html"), "URL should end with .html")
|
|
20
|
+
|
|
21
|
+
# Get the HTML content
|
|
22
|
+
response = requests.get(html_url)
|
|
23
|
+
self.assertEqual(response.status_code, 200, "Failed to fetch HTML content")
|
|
24
|
+
|
|
25
|
+
html_content = response.text
|
|
26
|
+
|
|
27
|
+
# Check if it's a mermaid diagram
|
|
28
|
+
self.assertTrue(
|
|
29
|
+
"mermaid" in html_content, "HTML content should contain 'mermaid'"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# Check if it contains the required elements
|
|
33
|
+
self.assertTrue(
|
|
34
|
+
'<div class="mermaid"' in html_content,
|
|
35
|
+
"HTML content should contain mermaid div",
|
|
36
|
+
)
|
|
37
|
+
self.assertTrue(
|
|
38
|
+
"mermaid.min.js" in html_content,
|
|
39
|
+
"HTML content should contain mermaid.js script",
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def test_generate_flowchart(self):
|
|
43
|
+
"""Test generating a flowchart"""
|
|
44
|
+
# Test data for flowchart
|
|
45
|
+
flowchart_data = """
|
|
46
|
+
graph TD
|
|
47
|
+
A[開始] --> B{是否有資料?}
|
|
48
|
+
B -->|是| C[處理資料]
|
|
49
|
+
B -->|否| D[取得資料]
|
|
50
|
+
C --> E[結束]
|
|
51
|
+
D --> B
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
# Execute test
|
|
55
|
+
html_url = generate_mermaid_files(
|
|
56
|
+
mermaid_data=flowchart_data,
|
|
57
|
+
botrun_flow_lang_url=self.botrun_flow_lang_url,
|
|
58
|
+
user_id=self.user_id,
|
|
59
|
+
title="互動式流程圖",
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Verify results
|
|
63
|
+
self.verify_mermaid_content(html_url)
|
|
64
|
+
|
|
65
|
+
def test_generate_sequence_diagram(self):
|
|
66
|
+
"""Test generating a sequence diagram"""
|
|
67
|
+
# Test data for sequence diagram
|
|
68
|
+
sequence_data = """
|
|
69
|
+
sequenceDiagram
|
|
70
|
+
participant 使用者
|
|
71
|
+
participant 系統
|
|
72
|
+
participant 資料庫
|
|
73
|
+
使用者->>系統: 請求資料
|
|
74
|
+
系統->>資料庫: 查詢資料
|
|
75
|
+
資料庫-->>系統: 返回結果
|
|
76
|
+
系統-->>使用者: 顯示資料
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
# Execute test
|
|
80
|
+
html_url = generate_mermaid_files(
|
|
81
|
+
mermaid_data=sequence_data,
|
|
82
|
+
botrun_flow_lang_url=self.botrun_flow_lang_url,
|
|
83
|
+
user_id=self.user_id,
|
|
84
|
+
title="互動式序列圖",
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Verify results
|
|
88
|
+
self.verify_mermaid_content(html_url)
|
|
89
|
+
|
|
90
|
+
def test_generate_mermaid_files_error(self):
|
|
91
|
+
"""Test error handling with invalid data"""
|
|
92
|
+
result = generate_mermaid_files(
|
|
93
|
+
mermaid_data="invalid mermaid syntax",
|
|
94
|
+
botrun_flow_lang_url=self.botrun_flow_lang_url,
|
|
95
|
+
user_id=self.user_id,
|
|
96
|
+
)
|
|
97
|
+
self.assertFalse(
|
|
98
|
+
result.startswith("Error:")
|
|
99
|
+
) # Mermaid will handle syntax errors client-side
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
if __name__ == "__main__":
|
|
103
|
+
unittest.main()
|
|
@@ -1,104 +1,104 @@
|
|
|
1
|
-
import unittest
|
|
2
|
-
import os
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
from botrun_flow_lang.langgraph_agents.agents.util.local_files import (
|
|
5
|
-
upload_and_get_tmp_public_url,
|
|
6
|
-
)
|
|
7
|
-
from botrun_flow_lang.langgraph_agents.agents.util.pdf_analyzer import analyze_pdf
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class TestPDFAnalyzer(unittest.TestCase):
|
|
11
|
-
def setUp(self):
|
|
12
|
-
# Get the path to the test PDF file
|
|
13
|
-
current_dir = Path(__file__).parent
|
|
14
|
-
self.pdf_path = (
|
|
15
|
-
current_dir
|
|
16
|
-
/ "test_files"
|
|
17
|
-
/ "1120701A海廣離岸風力發電計畫環境影響說明書-C04.PDF"
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
# Ensure the test file exists
|
|
21
|
-
self.assertTrue(
|
|
22
|
-
os.path.exists(self.pdf_path), f"Test file not found at {self.pdf_path}"
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
def test_pdf_exists(self):
|
|
26
|
-
"""Test if the PDF file exists"""
|
|
27
|
-
self.assertEqual(True, os.path.exists(self.pdf_path))
|
|
28
|
-
|
|
29
|
-
def test_pdf_readable(self):
|
|
30
|
-
"""Test if the PDF file is readable"""
|
|
31
|
-
self.assertEqual(True, os.access(self.pdf_path, os.R_OK))
|
|
32
|
-
|
|
33
|
-
def test_pdf_not_empty(self):
|
|
34
|
-
"""Test if the PDF file is not empty"""
|
|
35
|
-
self.assertEqual(True, os.path.getsize(self.pdf_path) > 0)
|
|
36
|
-
|
|
37
|
-
def test_pdf_extension(self):
|
|
38
|
-
"""Test if the file has a PDF extension"""
|
|
39
|
-
self.assertEqual(True, self.pdf_path.suffix.lower() == ".pdf")
|
|
40
|
-
|
|
41
|
-
def test_analyze_table_4_3_1(self):
|
|
42
|
-
"""Test analyzing table 4.3-1 from the PDF"""
|
|
43
|
-
query = "請你幫我找出在報告書中的「表 4.3-1 環境敏感地區調查表-第一級環境敏感地區」表格中的所有項目的「查詢結果及限制內容」幫我列出是或否?請全部列出來,不要遺漏"
|
|
44
|
-
pdf_url = upload_and_get_tmp_public_url(
|
|
45
|
-
self.pdf_path,
|
|
46
|
-
"https://botrun-flow-lang-fastapi-dev-36186877499.asia-east1.run.app",
|
|
47
|
-
"sebastian.hsu@gmail.com",
|
|
48
|
-
)
|
|
49
|
-
result = analyze_pdf(pdf_url, query)
|
|
50
|
-
|
|
51
|
-
# Print the result for inspection
|
|
52
|
-
print("\nAnalyze PDF Result:")
|
|
53
|
-
print("-" * 50)
|
|
54
|
-
print(result)
|
|
55
|
-
print("-" * 50)
|
|
56
|
-
|
|
57
|
-
# 確保結果不為空
|
|
58
|
-
self.assertIsNotNone(result)
|
|
59
|
-
self.assertNotEqual("", result)
|
|
60
|
-
|
|
61
|
-
# 檢查每個項目的結果
|
|
62
|
-
self.assertTrue(
|
|
63
|
-
"活動斷層兩側一定範圍: 否" in result
|
|
64
|
-
or "活動斷層兩側一定範圍:否" in result,
|
|
65
|
-
)
|
|
66
|
-
self.assertTrue(
|
|
67
|
-
"特定水土保持區: 否" in result or "特定水土保持區:否" in result
|
|
68
|
-
)
|
|
69
|
-
self.assertTrue("河川區域: 否" in result or "河川區域:否" in result)
|
|
70
|
-
self.assertTrue(
|
|
71
|
-
"洪氾區一級管制區及洪水平原一級管制區: 否" in result
|
|
72
|
-
or "洪氾區一級管制區及洪水平原一級管制區:否" in result
|
|
73
|
-
)
|
|
74
|
-
self.assertTrue(
|
|
75
|
-
"區域排水設施範圍: 是" in result or "區域排水設施範圍:是" in result
|
|
76
|
-
)
|
|
77
|
-
self.assertTrue(
|
|
78
|
-
"國家公園區內之特別景觀區、生態保護區: 否" in result
|
|
79
|
-
or "國家公園區內之特別景觀區、生態保護區:否" in result
|
|
80
|
-
)
|
|
81
|
-
self.assertTrue("自然保留區: 否" in result or "自然保留區:否" in result)
|
|
82
|
-
self.assertTrue(
|
|
83
|
-
"野生動物保護區: 否" in result or "野生動物保護區:否" in result
|
|
84
|
-
)
|
|
85
|
-
self.assertTrue(
|
|
86
|
-
"野生動物重要棲息環境: 是" in result or "野生動物重要棲息環境:是" in result
|
|
87
|
-
)
|
|
88
|
-
self.assertTrue("自然保護區: 否" in result or "自然保護區:否" in result)
|
|
89
|
-
self.assertTrue(
|
|
90
|
-
"一級海岸保護區: 是" in result or "一級海岸保護區:是" in result
|
|
91
|
-
)
|
|
92
|
-
self.assertTrue(
|
|
93
|
-
"國際級重要濕地、國家級重要濕地之核心保育區及生態復育區: 否" in result
|
|
94
|
-
or "國際級重要濕地、國家級重要濕地之核心保育區及生態復育區:否" in result
|
|
95
|
-
)
|
|
96
|
-
self.assertTrue("古蹟保存區: 否" in result or "古蹟保存區:否" in result)
|
|
97
|
-
self.assertTrue("考古遺址: 否" in result or "考古遺址:否" in result)
|
|
98
|
-
self.assertTrue(
|
|
99
|
-
"重要聚落建築群: 否" in result or "重要聚落建築群:否" in result
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if __name__ == "__main__":
|
|
104
|
-
unittest.main()
|
|
1
|
+
import unittest
|
|
2
|
+
import os
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from botrun_flow_lang.langgraph_agents.agents.util.local_files import (
|
|
5
|
+
upload_and_get_tmp_public_url,
|
|
6
|
+
)
|
|
7
|
+
from botrun_flow_lang.langgraph_agents.agents.util.pdf_analyzer import analyze_pdf
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TestPDFAnalyzer(unittest.TestCase):
|
|
11
|
+
def setUp(self):
|
|
12
|
+
# Get the path to the test PDF file
|
|
13
|
+
current_dir = Path(__file__).parent
|
|
14
|
+
self.pdf_path = (
|
|
15
|
+
current_dir
|
|
16
|
+
/ "test_files"
|
|
17
|
+
/ "1120701A海廣離岸風力發電計畫環境影響說明書-C04.PDF"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# Ensure the test file exists
|
|
21
|
+
self.assertTrue(
|
|
22
|
+
os.path.exists(self.pdf_path), f"Test file not found at {self.pdf_path}"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
def test_pdf_exists(self):
|
|
26
|
+
"""Test if the PDF file exists"""
|
|
27
|
+
self.assertEqual(True, os.path.exists(self.pdf_path))
|
|
28
|
+
|
|
29
|
+
def test_pdf_readable(self):
|
|
30
|
+
"""Test if the PDF file is readable"""
|
|
31
|
+
self.assertEqual(True, os.access(self.pdf_path, os.R_OK))
|
|
32
|
+
|
|
33
|
+
def test_pdf_not_empty(self):
|
|
34
|
+
"""Test if the PDF file is not empty"""
|
|
35
|
+
self.assertEqual(True, os.path.getsize(self.pdf_path) > 0)
|
|
36
|
+
|
|
37
|
+
def test_pdf_extension(self):
|
|
38
|
+
"""Test if the file has a PDF extension"""
|
|
39
|
+
self.assertEqual(True, self.pdf_path.suffix.lower() == ".pdf")
|
|
40
|
+
|
|
41
|
+
def test_analyze_table_4_3_1(self):
|
|
42
|
+
"""Test analyzing table 4.3-1 from the PDF"""
|
|
43
|
+
query = "請你幫我找出在報告書中的「表 4.3-1 環境敏感地區調查表-第一級環境敏感地區」表格中的所有項目的「查詢結果及限制內容」幫我列出是或否?請全部列出來,不要遺漏"
|
|
44
|
+
pdf_url = upload_and_get_tmp_public_url(
|
|
45
|
+
self.pdf_path,
|
|
46
|
+
"https://botrun-flow-lang-fastapi-dev-36186877499.asia-east1.run.app",
|
|
47
|
+
"sebastian.hsu@gmail.com",
|
|
48
|
+
)
|
|
49
|
+
result = analyze_pdf(pdf_url, query)
|
|
50
|
+
|
|
51
|
+
# Print the result for inspection
|
|
52
|
+
print("\nAnalyze PDF Result:")
|
|
53
|
+
print("-" * 50)
|
|
54
|
+
print(result)
|
|
55
|
+
print("-" * 50)
|
|
56
|
+
|
|
57
|
+
# 確保結果不為空
|
|
58
|
+
self.assertIsNotNone(result)
|
|
59
|
+
self.assertNotEqual("", result)
|
|
60
|
+
|
|
61
|
+
# 檢查每個項目的結果
|
|
62
|
+
self.assertTrue(
|
|
63
|
+
"活動斷層兩側一定範圍: 否" in result
|
|
64
|
+
or "活動斷層兩側一定範圍:否" in result,
|
|
65
|
+
)
|
|
66
|
+
self.assertTrue(
|
|
67
|
+
"特定水土保持區: 否" in result or "特定水土保持區:否" in result
|
|
68
|
+
)
|
|
69
|
+
self.assertTrue("河川區域: 否" in result or "河川區域:否" in result)
|
|
70
|
+
self.assertTrue(
|
|
71
|
+
"洪氾區一級管制區及洪水平原一級管制區: 否" in result
|
|
72
|
+
or "洪氾區一級管制區及洪水平原一級管制區:否" in result
|
|
73
|
+
)
|
|
74
|
+
self.assertTrue(
|
|
75
|
+
"區域排水設施範圍: 是" in result or "區域排水設施範圍:是" in result
|
|
76
|
+
)
|
|
77
|
+
self.assertTrue(
|
|
78
|
+
"國家公園區內之特別景觀區、生態保護區: 否" in result
|
|
79
|
+
or "國家公園區內之特別景觀區、生態保護區:否" in result
|
|
80
|
+
)
|
|
81
|
+
self.assertTrue("自然保留區: 否" in result or "自然保留區:否" in result)
|
|
82
|
+
self.assertTrue(
|
|
83
|
+
"野生動物保護區: 否" in result or "野生動物保護區:否" in result
|
|
84
|
+
)
|
|
85
|
+
self.assertTrue(
|
|
86
|
+
"野生動物重要棲息環境: 是" in result or "野生動物重要棲息環境:是" in result
|
|
87
|
+
)
|
|
88
|
+
self.assertTrue("自然保護區: 否" in result or "自然保護區:否" in result)
|
|
89
|
+
self.assertTrue(
|
|
90
|
+
"一級海岸保護區: 是" in result or "一級海岸保護區:是" in result
|
|
91
|
+
)
|
|
92
|
+
self.assertTrue(
|
|
93
|
+
"國際級重要濕地、國家級重要濕地之核心保育區及生態復育區: 否" in result
|
|
94
|
+
or "國際級重要濕地、國家級重要濕地之核心保育區及生態復育區:否" in result
|
|
95
|
+
)
|
|
96
|
+
self.assertTrue("古蹟保存區: 否" in result or "古蹟保存區:否" in result)
|
|
97
|
+
self.assertTrue("考古遺址: 否" in result or "考古遺址:否" in result)
|
|
98
|
+
self.assertTrue(
|
|
99
|
+
"重要聚落建築群: 否" in result or "重要聚落建築群:否" in result
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
if __name__ == "__main__":
|
|
104
|
+
unittest.main()
|