botrun-flow-lang 5.10.291__tar.gz → 5.11.11__tar.gz

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 (149) hide show
  1. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/CHANGELOG.md +15 -0
  2. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/PKG-INFO +1 -1
  3. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/api/storage_api.py +79 -0
  4. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/util/local_files.py +77 -3
  5. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/mcp_server/default_mcp.py +37 -13
  6. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/services/storage/storage_cs_store.py +1 -1
  7. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/pyproject.toml +1 -1
  8. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/.env_template +0 -0
  9. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/.gcloudignore +0 -0
  10. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/.gitignore +0 -0
  11. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/.vscode/launch.json +0 -0
  12. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/CLAUDE.md +0 -0
  13. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/Dockerfile.template +0 -0
  14. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/README.md +0 -0
  15. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/__init__.py +0 -0
  16. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/api/__init__.py +0 -0
  17. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/api/auth_api.py +0 -0
  18. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/api/auth_utils.py +0 -0
  19. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/api/botrun_back_api.py +0 -0
  20. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/api/flow_api.py +0 -0
  21. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/api/hatch_api.py +0 -0
  22. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/api/langgraph_api.py +0 -0
  23. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/api/line_bot_api.py +0 -0
  24. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/api/model_api.py +0 -0
  25. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/api/rate_limit_api.py +0 -0
  26. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/api/routes.py +0 -0
  27. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/api/search_api.py +0 -0
  28. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/api/subsidy_api.py +0 -0
  29. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/api/subsidy_api_system_prompt.txt +0 -0
  30. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/api/user_setting_api.py +0 -0
  31. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/api/version_api.py +0 -0
  32. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/api/youtube_api.py +0 -0
  33. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/constants.py +0 -0
  34. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/__init__.py +0 -0
  35. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/__init__.py +0 -0
  36. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/agent_runner.py +0 -0
  37. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/agent_tools/__init__.py +0 -0
  38. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/agent_tools/step_planner.py +0 -0
  39. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/checkpointer/__init__.py +0 -0
  40. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/checkpointer/firestore_checkpointer.py +0 -0
  41. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/gov_researcher/GOV_RESEARCHER_PRD.md +0 -0
  42. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/gov_researcher/__init__.py +0 -0
  43. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/gov_researcher/gemini_subsidy_graph.py +0 -0
  44. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/gov_researcher/gov_researcher_2_graph.py +0 -0
  45. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/gov_researcher/gov_researcher_graph.py +0 -0
  46. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/langgraph_react_agent.py +0 -0
  47. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/search_agent_graph.py +0 -0
  48. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/tools/__init__.py +0 -0
  49. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/tools/gemini_code_execution.py +0 -0
  50. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/util/__init__.py +0 -0
  51. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/util/gemini_grounding.py +0 -0
  52. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/util/html_util.py +0 -0
  53. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/util/img_util.py +0 -0
  54. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/util/mermaid_util.py +0 -0
  55. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/util/model_utils.py +0 -0
  56. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/util/pdf_analyzer.py +0 -0
  57. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/util/perplexity_search.py +0 -0
  58. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/util/plotly_util.py +0 -0
  59. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/util/tavily_search.py +0 -0
  60. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/agents/util/youtube_util.py +0 -0
  61. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/cache/__init__.py +0 -0
  62. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/langgraph_agents/cache/langgraph_botrun_cache.py +0 -0
  63. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/llm_agent/__init__.py +0 -0
  64. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/llm_agent/llm_agent.py +0 -0
  65. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/llm_agent/llm_agent_util.py +0 -0
  66. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/log/.gitignore +0 -0
  67. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/main.py +0 -0
  68. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/main_fast.py +0 -0
  69. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/mcp_server/__init__.py +0 -0
  70. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/models/__init__.py +0 -0
  71. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/models/nodes/utils.py +0 -0
  72. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/models/token_usage.py +0 -0
  73. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/requirements.txt +0 -0
  74. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/services/__init__.py +0 -0
  75. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/services/base/firestore_base.py +0 -0
  76. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/services/hatch/__init__.py +0 -0
  77. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/services/hatch/hatch_factory.py +0 -0
  78. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/services/hatch/hatch_fs_store.py +0 -0
  79. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/services/storage/__init__.py +0 -0
  80. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/services/storage/storage_factory.py +0 -0
  81. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/services/storage/storage_store.py +0 -0
  82. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/services/user_setting/__init__.py +0 -0
  83. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/services/user_setting/user_setting_factory.py +0 -0
  84. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/services/user_setting/user_setting_fs_store.py +0 -0
  85. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/static/docs/tools/index.html +0 -0
  86. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/tests/api_functional_tests.py +0 -0
  87. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/tests/api_stress_test.py +0 -0
  88. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/tests/shared_hatch_tests.py +0 -0
  89. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/tests/test_botrun_app.py +0 -0
  90. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/tests/test_files/(/346/272/253/351/246/250/346/210/220/346/236/234 /350/241/214/346/224/277/350/253/213/347/244/272/345/214/257/347/270/275)20250210/345/220/221 /344/270/212/344/272/272/345/240/261/345/221/212/347/260/241/345/240/261 (1).pdf" +0 -0
  91. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/tests/test_files/11206_10808/344/272/272/345/217/243/346/225/270(3/346/256/265/345/271/264/351/275/241/347/265/204+/346/257/224/347/216/207)/345/244/251/344/270/213/351/233/234/350/252/2141.pdf" +0 -0
  92. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/tests/test_files/1120701A/346/265/267/345/273/243/351/233/242/345/262/270/351/242/250/345/212/233/347/231/274/351/233/273/350/250/210/347/225/253/347/222/260/345/242/203/345/275/261/351/237/277/350/252/252/346/230/216/346/233/270-C04.PDF" +0 -0
  93. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/tests/test_files/ImportedPhoto.760363950.029251.jpeg +0 -0
  94. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/tests/test_files/ImportedPhoto.760363950.030446.jpeg +0 -0
  95. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/tests/test_files/ImportedPhoto.760363950.031127.jpeg +0 -0
  96. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/tests/test_files/d5712343.jpg +0 -0
  97. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/tests/test_files/spot_difference_1.png +0 -0
  98. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/tests/test_files/spot_difference_2.png +0 -0
  99. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/tests/test_html_util.py +0 -0
  100. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/tests/test_img_analyzer.py +0 -0
  101. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/tests/test_img_util.py +0 -0
  102. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/tests/test_local_files.py +0 -0
  103. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/tests/test_mermaid_util.py +0 -0
  104. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/tests/test_pdf_analyzer.py +0 -0
  105. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/tests/test_plotly_util.py +0 -0
  106. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/tests/test_run_workflow_engine.py +0 -0
  107. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/tools/generate_docs.py +0 -0
  108. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/tools/templates/tools.html +0 -0
  109. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/utils/__init__.py +0 -0
  110. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/utils/botrun_logger.py +0 -0
  111. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/utils/clients/__init__.py +0 -0
  112. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/utils/clients/rate_limit_client.py +0 -0
  113. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/utils/clients/token_verify_client.py +0 -0
  114. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/utils/google_drive_utils.py +0 -0
  115. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/utils/langchain_utils.py +0 -0
  116. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/botrun_flow_lang/utils/yaml_utils.py +0 -0
  117. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/cloudbuild_template.yaml +0 -0
  118. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/convert_newlines.sh +0 -0
  119. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/deploy.sh +0 -0
  120. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/langgraph.json +0 -0
  121. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/poetry.lock +0 -0
  122. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/poetry.toml +0 -0
  123. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/requirements.txt +0 -0
  124. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/requirements_fast.txt +0 -0
  125. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/sh/deploy_bigline.sh +0 -0
  126. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/sh/deploy_dev.sh +0 -0
  127. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/sh/deploy_evaldev.sh +0 -0
  128. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/sh/deploy_fast.sh +0 -0
  129. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/sh/deploy_modaline.sh +0 -0
  130. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/sh/deploy_prod.sh +0 -0
  131. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/sh/deploy_sebaline.sh +0 -0
  132. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/sh/firestore_checkpointer_delete_thread.py +0 -0
  133. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/sh/generate_docs.sh +0 -0
  134. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/sh/llm_time_test.py +0 -0
  135. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/sh/run_langgraph.py +0 -0
  136. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/sh/run_langgraph_react.py +0 -0
  137. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/sh/run_youtube_summary.py +0 -0
  138. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/sh/scrape_pdf.py +0 -0
  139. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/sh/seba_pypi.sh +0 -0
  140. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/sh/subsidy_test.py +0 -0
  141. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/specs/gov-search/agent-architecture-upgrade.excalidraw +0 -0
  142. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/specs/gov-search/line_dev.excalidraw +0 -0
  143. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/specs/gov-search/subsidy_agent.excalidraw +0 -0
  144. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/specs/gov-search/subsidy_agent_dev.excalidraw +0 -0
  145. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/specs/gov-search/temp_txt.md +0 -0
  146. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/specs/gov-search/temp_txt2.md +0 -0
  147. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/specs/gov-search//346/264/245/350/262/274line/345/276/205/350/250/216/350/253/226.excalidraw" +0 -0
  148. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/specs/system_thinking.excalidraw +0 -0
  149. {botrun_flow_lang-5.10.291 → botrun_flow_lang-5.11.11}/uv.lock +0 -0
@@ -1,3 +1,18 @@
1
+ ## [5.11.11]
2
+ ### Added
3
+ - DALL-E 圖片永久化儲存功能 [參考 specs/gen-img/design.md]
4
+ - Storage API 新增 `/api/img-files/{user_id}` endpoint,支援圖片永久儲存到 GCS
5
+ - 新增 `_upload_img_file_internal` 內部函數處理圖片上傳
6
+ - local_files.py 新增 `download_image_from_url` 函數從 URL 下載圖片到記憶體
7
+ - local_files.py 新增 `upload_image_and_get_public_url` 函數處理圖片下載與上傳流程
8
+ - MCP generate_image 工具現在會將 DALL-E 生成的圖片自動上傳到 GCS,回傳永久 URL
9
+ - 圖片儲存路徑:`img/{user_id}/dalle_{timestamp}_{random_id}.png`
10
+ - 支援 fallback 機制:上傳失敗時回傳臨時 URL(1小時有效)
11
+
12
+ ### Updated
13
+ - GCS bucket lifecycle rules:將 tmp/ 目錄檔案的自動刪除期限從 7 天延長至 365 天(1年)
14
+ - local_files.py 重新命名 `_perform_upload` → `_perform_tmp_file_upload`,明確表示暫存檔案上傳
15
+
1
16
  ## [5.10.291]
2
17
  ### Updated
3
18
  - line bot api ,呼叫 cbh 做的 api
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: botrun-flow-lang
3
- Version: 5.10.291
3
+ Version: 5.11.11
4
4
  Summary: A flow language for botrun
5
5
  Author-email: sebastian-hsu <sebastian.hsu@gmail.com>
6
6
  License: MIT
@@ -202,6 +202,43 @@ async def _upload_html_file_internal(
202
202
  return public_url
203
203
 
204
204
 
205
+ async def _upload_img_file_internal(
206
+ user_id: str, file_content: bytes, file_name: str, content_type: str = "image/png"
207
+ ) -> str:
208
+ """
209
+ Internal function to upload image file to GCS
210
+
211
+ Args:
212
+ user_id: User ID
213
+ file_content: File content as bytes
214
+ file_name: File name
215
+ content_type: MIME type of the file
216
+
217
+ Returns:
218
+ str: Public URL of the uploaded file
219
+
220
+ Raises:
221
+ Exception: If upload fails
222
+ """
223
+ storage = storage_store_factory()
224
+
225
+ # Create file object from bytes
226
+ file_object = BytesIO(file_content)
227
+
228
+ # Build storage path - use img directory for permanent storage
229
+ storage_path = f"img/{user_id}/{file_name}"
230
+
231
+ # Store file with public access and content type
232
+ success, public_url = await storage.store_file(
233
+ storage_path, file_object, public=True, content_type=content_type
234
+ )
235
+
236
+ if not success:
237
+ raise Exception("Failed to store image file")
238
+
239
+ return public_url
240
+
241
+
205
242
  @router.post("/html-files/{user_id}")
206
243
  async def upload_html_file(
207
244
  user_id: str,
@@ -244,6 +281,48 @@ async def upload_html_file(
244
281
  raise HTTPException(status_code=500, detail=str(e))
245
282
 
246
283
 
284
+ @router.post("/img-files/{user_id}")
285
+ async def upload_img_file(
286
+ user_id: str,
287
+ file: FastAPIUploadFile = File(...),
288
+ file_name: str = Form(...),
289
+ content_type: str = Form(None),
290
+ # current_user: CurrentUser = Depends(verify_jwt_token)
291
+ ) -> dict:
292
+ """
293
+ 儲存圖片檔案到 GCS,檔案會是公開可存取且永久保存
294
+
295
+ Args:
296
+ user_id: 使用者 ID
297
+ file: 上傳的檔案
298
+ file_name: 檔案名稱
299
+ content_type: 檔案的 MIME type,如果沒有提供則使用檔案的 content_type
300
+ """
301
+ # Verify user permission
302
+ # verify_user_permission(current_user, user_id)
303
+
304
+ try:
305
+ # 讀取上傳的檔案內容
306
+ contents = await file.read()
307
+
308
+ # 如果沒有提供 content_type,使用檔案的 content_type
309
+ if not content_type:
310
+ content_type = file.content_type
311
+
312
+ # Use internal function to upload file
313
+ public_url = await _upload_img_file_internal(
314
+ user_id, contents, file_name, content_type
315
+ )
316
+
317
+ return {
318
+ "message": "Image file uploaded successfully",
319
+ "success": True,
320
+ "url": public_url,
321
+ }
322
+ except Exception as e:
323
+ raise HTTPException(status_code=500, detail=str(e))
324
+
325
+
247
326
  @router.get("/directory-sizes")
248
327
  async def get_directory_sizes(current_user: CurrentUser = Depends(verify_jwt_token)):
249
328
  """
@@ -52,7 +52,7 @@ def upload_and_get_tmp_public_url(
52
52
  """
53
53
  # 外層 try-except: 處理第一次嘗試與重試邏輯
54
54
  try:
55
- return _perform_upload(file_path, botrun_flow_lang_url, user_id)
55
+ return _perform_tmp_file_upload(file_path, botrun_flow_lang_url, user_id)
56
56
  except Exception as e:
57
57
  import traceback
58
58
 
@@ -68,7 +68,7 @@ def upload_and_get_tmp_public_url(
68
68
  # 第二次嘗試
69
69
  try:
70
70
  print("Retry attempt...")
71
- return _perform_upload(file_path, botrun_flow_lang_url, user_id)
71
+ return _perform_tmp_file_upload(file_path, botrun_flow_lang_url, user_id)
72
72
  except Exception as retry_e:
73
73
  # 第二次嘗試也失敗,記錄錯誤並返回錯誤訊息
74
74
  print(f"Retry attempt failed: {str(retry_e)}")
@@ -76,7 +76,7 @@ def upload_and_get_tmp_public_url(
76
76
  return "Error uploading file"
77
77
 
78
78
 
79
- def _perform_upload(
79
+ def _perform_tmp_file_upload(
80
80
  file_path: str, botrun_flow_lang_url: str = "", user_id: str = ""
81
81
  ) -> str:
82
82
  """執行實際的上傳操作
@@ -343,3 +343,77 @@ async def _perform_html_upload(
343
343
  traceback.print_exc()
344
344
  # 這裡把異常往上拋,讓外層的重試邏輯處理
345
345
  raise e
346
+
347
+
348
+ async def download_image_from_url(image_url: str) -> tuple[BytesIO, str]:
349
+ """
350
+ 從 URL 下載圖片到記憶體
351
+
352
+ Args:
353
+ image_url: 圖片的 URL
354
+
355
+ Returns:
356
+ tuple[BytesIO, str]: (圖片內容, content_type)
357
+
358
+ Raises:
359
+ Exception: 下載失敗時拋出例外
360
+ """
361
+ try:
362
+ import httpx
363
+
364
+ async with httpx.AsyncClient(timeout=30.0) as client:
365
+ response = await client.get(image_url, follow_redirects=True)
366
+ response.raise_for_status()
367
+
368
+ # 從 response headers 取得 content type
369
+ content_type = response.headers.get("content-type", "image/png")
370
+
371
+ # 將圖片內容存入 BytesIO
372
+ image_data = BytesIO(response.content)
373
+
374
+ return image_data, content_type
375
+
376
+ except Exception as e:
377
+ raise Exception(f"Failed to download image from URL: {str(e)}")
378
+
379
+
380
+ async def upload_image_and_get_public_url(
381
+ image_url: str, botrun_flow_lang_url: str = "", user_id: str = ""
382
+ ) -> str:
383
+ """
384
+ 從 URL 下載圖片並上傳到 GCS /img 目錄,取得永久公開 URL
385
+
386
+ Args:
387
+ image_url: 圖片來源 URL(如 DALL-E 生成的臨時 URL)
388
+ botrun_flow_lang_url: botrun flow lang API 的 URL
389
+ user_id: 使用者 ID
390
+
391
+ Returns:
392
+ str: 上傳後的永久公開存取 URL
393
+ """
394
+ try:
395
+ # 1. 從 URL 下載圖片
396
+ image_data, content_type = await download_image_from_url(image_url)
397
+
398
+ # 2. 生成唯一檔案名稱
399
+ import uuid
400
+ from datetime import datetime
401
+
402
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
403
+ file_name = f"dalle_{timestamp}_{uuid.uuid4().hex[:8]}.png"
404
+
405
+ # 3. 使用內部函數上傳
406
+ from botrun_flow_lang.api.storage_api import _upload_img_file_internal
407
+
408
+ public_url = await _upload_img_file_internal(
409
+ user_id, image_data.getvalue(), file_name, content_type
410
+ )
411
+
412
+ return public_url
413
+
414
+ except Exception as e:
415
+ import traceback
416
+
417
+ print(f"Error uploading image: {str(e)}")
418
+ traceback.print_exc()
419
+ raise e
@@ -128,9 +128,11 @@ async def chat_with_imgs(
128
128
 
129
129
 
130
130
  @mcp.tool()
131
- async def generate_image(user_input: str, user_id: str = "") -> str:
131
+ async def generate_image(
132
+ user_input: str, user_id: str = "", botrun_flow_lang_url: str = ""
133
+ ) -> str:
132
134
  """
133
- Generate high-quality images using DALL-E 3.
135
+ Generate high-quality images using DALL-E 3 and store permanently in GCS.
134
136
 
135
137
  When using generate_image tool, you must include the image URL in your response.
136
138
  You MUST respond using this format (from @begin img to @end, including the image URL):
@@ -161,17 +163,22 @@ async def generate_image(user_input: str, user_id: str = "") -> str:
161
163
  Args:
162
164
  user_input: Detailed description of the image you want to generate.
163
165
  Be specific about style, content, and composition.
164
- user_id: REQUIRED - User ID for rate limit checking (LLM can get this from system prompt)
166
+ user_id: REQUIRED - User ID for rate limit and file storage (LLM can get this from system prompt)
167
+ botrun_flow_lang_url: REQUIRED - URL for the botrun flow lang API (LLM can get this from system prompt)
165
168
 
166
169
  Returns:
167
- str: URL to the generated image, or error message if generation fails
170
+ str: Permanent URL to the generated image stored in GCS, or error message if generation fails
168
171
  """
169
172
  try:
170
173
  logger.info(f"generate_image user_input: {user_input}")
171
174
 
175
+ # 驗證必要參數
172
176
  if not user_id:
173
- logger.error("User ID not available for rate limit check")
174
- raise Exception("User ID not available for rate limit check")
177
+ logger.error("User ID not available")
178
+ raise Exception("User ID not available")
179
+ if not botrun_flow_lang_url:
180
+ logger.error("botrun_flow_lang_url not available")
181
+ raise Exception("botrun_flow_lang_url not available")
175
182
 
176
183
  # Check rate limit before generating image
177
184
  rate_limit_client = RateLimitClient()
@@ -193,25 +200,42 @@ async def generate_image(user_input: str, user_id: str = "") -> str:
193
200
  f"Current usage: {current_usage}. Please try again tomorrow."
194
201
  )
195
202
 
196
- # Proceed with image generation using DALL-E API Wrapper
203
+ # 2. 使用 DALL-E 生成圖片
197
204
  dalle_wrapper = DallEAPIWrapper(
198
205
  api_key=os.getenv("OPENAI_API_KEY"), model="dall-e-3"
199
206
  )
200
207
 
201
208
  # Generate image with token usage tracking
202
209
  with get_openai_callback() as cb:
203
- image_url = dalle_wrapper.run(user_input)
210
+ temp_image_url = dalle_wrapper.run(user_input)
204
211
  logger.info(
205
- f"generate_image=======> Estimated prompt tokens: {cb.prompt_tokens}, "
212
+ f"DALL-E generated temporary URL: {temp_image_url}, "
213
+ f"prompt tokens: {cb.prompt_tokens}, "
206
214
  f"completion tokens: {cb.completion_tokens}"
207
215
  )
208
216
 
209
- logger.info(f"generate_image generated============> {image_url}")
217
+ # 3. 下載並上傳到 GCS,取得永久 URL
218
+ from botrun_flow_lang.langgraph_agents.agents.util.local_files import (
219
+ upload_image_and_get_public_url,
220
+ )
210
221
 
211
- # Update usage counter after successful generation
212
- await rate_limit_client.update_drawing_usage(user_id)
222
+ try:
223
+ permanent_url = await upload_image_and_get_public_url(
224
+ temp_image_url, botrun_flow_lang_url, user_id
225
+ )
226
+ logger.info(f"Image uploaded to GCS: {permanent_url}")
213
227
 
214
- return image_url
228
+ # 4. 更新使用計數
229
+ await rate_limit_client.update_drawing_usage(user_id)
230
+
231
+ return permanent_url
232
+ except Exception as upload_error:
233
+ logger.error(
234
+ f"Failed to upload to GCS, returning temporary URL: {upload_error}"
235
+ )
236
+ # Fallback: 回傳臨時 URL
237
+ await rate_limit_client.update_drawing_usage(user_id)
238
+ return temp_image_url
215
239
 
216
240
  except Exception as e:
217
241
  logger.error(f"generate_image error: {e}", error=str(e), exc_info=True)
@@ -38,7 +38,7 @@ class StorageCsStore(StorageStore):
38
38
  desired_rules = [
39
39
  {
40
40
  "action": {"type": "Delete"},
41
- "condition": {"age": 7, "matchesPrefix": ["tmp/"]},
41
+ "condition": {"age": 365, "matchesPrefix": ["tmp/"]},
42
42
  }
43
43
  ]
44
44
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "botrun-flow-lang"
3
- version = "5.10.291"
3
+ version = "5.11.11"
4
4
  description = "A flow language for botrun"
5
5
  authors = [
6
6
  { name = "sebastian-hsu", email = "sebastian.hsu@gmail.com" }