mirobody 0.0.0.dev0__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 (488) hide show
  1. mirobody/__init__.py +14 -0
  2. mirobody/chat/__init__.py +22 -0
  3. mirobody/chat/adapters/__init__.py +1 -0
  4. mirobody/chat/adapters/base.py +414 -0
  5. mirobody/chat/adapters/http.py +436 -0
  6. mirobody/chat/agent.py +374 -0
  7. mirobody/chat/asr.py +535 -0
  8. mirobody/chat/file.py +263 -0
  9. mirobody/chat/history.py +286 -0
  10. mirobody/chat/mcp_loader.py +116 -0
  11. mirobody/chat/memory.py +570 -0
  12. mirobody/chat/message.py +688 -0
  13. mirobody/chat/model.py +101 -0
  14. mirobody/chat/service.py +580 -0
  15. mirobody/chat/session.py +222 -0
  16. mirobody/chat/session_share.py +291 -0
  17. mirobody/chat/summary.py +147 -0
  18. mirobody/chat/unified_chat_service.py +137 -0
  19. mirobody/chat/user.py +70 -0
  20. mirobody/chat/user_config.py +265 -0
  21. mirobody/chat/user_profile.py +1944 -0
  22. mirobody/indicator/README.md +354 -0
  23. mirobody/indicator/__init__.py +0 -0
  24. mirobody/indicator/__main__.py +3 -0
  25. mirobody/indicator/concept_graph.py +319 -0
  26. mirobody/indicator/embed.py +71 -0
  27. mirobody/indicator/health/__init__.py +0 -0
  28. mirobody/indicator/health/bridge.py +520 -0
  29. mirobody/indicator/health/common.py +347 -0
  30. mirobody/indicator/health/embeddings_db.py +221 -0
  31. mirobody/indicator/health/embeddings_ref.py +475 -0
  32. mirobody/indicator/health/graph_builder.py +180 -0
  33. mirobody/indicator/health/locales/__init__.py +78 -0
  34. mirobody/indicator/health/locales/cn.py +65 -0
  35. mirobody/indicator/health/merge.py +290 -0
  36. mirobody/indicator/health/search.py +320 -0
  37. mirobody/indicator/health/siblings.py +781 -0
  38. mirobody/indicator/health/taxonomy.py +379 -0
  39. mirobody/indicator/health/test.py +223 -0
  40. mirobody/indicator/main.py +275 -0
  41. mirobody/indicator/mapping.py +110 -0
  42. mirobody/indicator/search.py +161 -0
  43. mirobody/indicator/taxonomy.py +260 -0
  44. mirobody/mcp/__init__.py +29 -0
  45. mirobody/mcp/resource.py +146 -0
  46. mirobody/mcp/service.py +730 -0
  47. mirobody/mcp/tool.py +741 -0
  48. mirobody/pub/__init__.py +0 -0
  49. mirobody/pub/agents/README.md +146 -0
  50. mirobody/pub/agents/__init__.py +34 -0
  51. mirobody/pub/agents/baseline_agent.py +1336 -0
  52. mirobody/pub/agents/deep/backend.py +952 -0
  53. mirobody/pub/agents/deep/e2b_backend.py +278 -0
  54. mirobody/pub/agents/deep/errors.py +14 -0
  55. mirobody/pub/agents/deep/middleware/__init__.py +15 -0
  56. mirobody/pub/agents/deep/middleware/prompt_caching.py +423 -0
  57. mirobody/pub/agents/deep/parser.py +228 -0
  58. mirobody/pub/agents/deep/prompt_builder.py +69 -0
  59. mirobody/pub/agents/deep/prompts/theta_health.jinja +290 -0
  60. mirobody/pub/agents/deep/prompts/theta_health_simple.jinja +117 -0
  61. mirobody/pub/agents/deep/store.py +505 -0
  62. mirobody/pub/agents/deep/tool_loader.py +196 -0
  63. mirobody/pub/agents/deep/utils/__init__.py +62 -0
  64. mirobody/pub/agents/deep/utils/constants.py +181 -0
  65. mirobody/pub/agents/deep/utils/funcs.py +167 -0
  66. mirobody/pub/agents/deep/utils/message_converter.py +528 -0
  67. mirobody/pub/agents/deep_agent.py +622 -0
  68. mirobody/pub/agents/mix/__init__.py +14 -0
  69. mirobody/pub/agents/mix/middleware/__init__.py +9 -0
  70. mirobody/pub/agents/mix/middleware/generate_answer.py +124 -0
  71. mirobody/pub/agents/mix/mixin.py +1279 -0
  72. mirobody/pub/agents/mix/prompts/orchestrator.jinja +36 -0
  73. mirobody/pub/agents/mix/prompts/responder.jinja +131 -0
  74. mirobody/pub/agents/mix_agent.py +490 -0
  75. mirobody/pub/agents/utils/__init__.py +11 -0
  76. mirobody/pub/agents/utils/cache_config.py +15 -0
  77. mirobody/pub/agents/utils/file.py +249 -0
  78. mirobody/pub/htdoc/__/auth/experiments.js +2 -0
  79. mirobody/pub/htdoc/__/auth/handler +15 -0
  80. mirobody/pub/htdoc/__/auth/handler.js +564 -0
  81. mirobody/pub/htdoc/__/auth/iframe +13 -0
  82. mirobody/pub/htdoc/__/auth/iframe.js +579 -0
  83. mirobody/pub/htdoc/assets/RenderHistoryList-CLPxljzD.js +1 -0
  84. mirobody/pub/htdoc/assets/RenderHistoryList-Cl2Afq4j.css +1 -0
  85. mirobody/pub/htdoc/assets/RenderHistoryList-legacy-Co4M_wLe.js +1 -0
  86. mirobody/pub/htdoc/assets/account-BG6qgOO9.js +1 -0
  87. mirobody/pub/htdoc/assets/account-legacy-BuPwnMo9.js +1 -0
  88. mirobody/pub/htdoc/assets/file-legacy-B1OPz9YG.js +1 -0
  89. mirobody/pub/htdoc/assets/file-vlwc8DpB.js +1 -0
  90. mirobody/pub/htdoc/assets/index--IFYfMXy.css +1 -0
  91. mirobody/pub/htdoc/assets/index-1_UgC3m0.css +1 -0
  92. mirobody/pub/htdoc/assets/index-5LMo7Lk4.js +1 -0
  93. mirobody/pub/htdoc/assets/index-BDCGenv1.css +1 -0
  94. mirobody/pub/htdoc/assets/index-BWLyl4Ry.js +1 -0
  95. mirobody/pub/htdoc/assets/index-Bivsa4xI.js +1 -0
  96. mirobody/pub/htdoc/assets/index-C32k5tCH.js +1 -0
  97. mirobody/pub/htdoc/assets/index-C3z8gN2O.css +1 -0
  98. mirobody/pub/htdoc/assets/index-CQj7TbUh.css +1 -0
  99. mirobody/pub/htdoc/assets/index-CVBl6l0A.css +1 -0
  100. mirobody/pub/htdoc/assets/index-ClPsPNIh.js +1 -0
  101. mirobody/pub/htdoc/assets/index-DBdtM01z.js +1 -0
  102. mirobody/pub/htdoc/assets/index-DFNSEqZI.js +1 -0
  103. mirobody/pub/htdoc/assets/index-DGSYSAta.js +1 -0
  104. mirobody/pub/htdoc/assets/index-DMNolwBO.css +1 -0
  105. mirobody/pub/htdoc/assets/index-DVDIPMfE.js +1 -0
  106. mirobody/pub/htdoc/assets/index-Du57KEca.js +1 -0
  107. mirobody/pub/htdoc/assets/index-DuTRpth9.js +2 -0
  108. mirobody/pub/htdoc/assets/index-LabuL4U_.js +1 -0
  109. mirobody/pub/htdoc/assets/index-SyEx1qzm.css +1 -0
  110. mirobody/pub/htdoc/assets/index-legacy-1j3_J-0Y.js +1 -0
  111. mirobody/pub/htdoc/assets/index-legacy-BGOD2yAB.js +1 -0
  112. mirobody/pub/htdoc/assets/index-legacy-BSdui2GF.js +1 -0
  113. mirobody/pub/htdoc/assets/index-legacy-C2Fq83tF.js +1 -0
  114. mirobody/pub/htdoc/assets/index-legacy-CHEGF-C1.js +1 -0
  115. mirobody/pub/htdoc/assets/index-legacy-Ck4nK7Ou.js +1 -0
  116. mirobody/pub/htdoc/assets/index-legacy-Cwqts7Oq.js +1 -0
  117. mirobody/pub/htdoc/assets/index-legacy-DhpHlDQT.js +1 -0
  118. mirobody/pub/htdoc/assets/index-legacy-Dl45gcop.js +23 -0
  119. mirobody/pub/htdoc/assets/index-legacy-DpkSGxx1.js +1 -0
  120. mirobody/pub/htdoc/assets/index-legacy-MuQw6dPg.js +1 -0
  121. mirobody/pub/htdoc/assets/index-legacy-Y-c2SyKN.js +1 -0
  122. mirobody/pub/htdoc/assets/index-legacy-ZeQ9g8Jb.js +1 -0
  123. mirobody/pub/htdoc/assets/index-legacy-uIT6h2W_.js +1 -0
  124. mirobody/pub/htdoc/assets/index-mABjyEMg.js +1 -0
  125. mirobody/pub/htdoc/assets/index-qDL7KYga.css +1 -0
  126. mirobody/pub/htdoc/assets/index-tk-1X7yk.js +1 -0
  127. mirobody/pub/htdoc/assets/index-wi8RMjZA.css +1 -0
  128. mirobody/pub/htdoc/assets/index-y-oi3J7w.css +1 -0
  129. mirobody/pub/htdoc/assets/input-BL_pYmlS.js +1 -0
  130. mirobody/pub/htdoc/assets/input-legacy-DqSq0IOV.js +1 -0
  131. mirobody/pub/htdoc/assets/logo-MLEW-e8g.svg +6 -0
  132. mirobody/pub/htdoc/assets/logo-legacy-e9JydjKK.js +1 -0
  133. mirobody/pub/htdoc/assets/logo-wU4jU3J-.js +1 -0
  134. mirobody/pub/htdoc/assets/mcp_feature_eg-CcZqNkG5.png +0 -0
  135. mirobody/pub/htdoc/assets/mcp_feature_eg_cn-CFatLA2c.png +0 -0
  136. mirobody/pub/htdoc/assets/new-CFFfz_aJ.js +1 -0
  137. mirobody/pub/htdoc/assets/new-legacy-CoOAEW9y.js +1 -0
  138. mirobody/pub/htdoc/assets/polyfills-legacy-CtzgJ65B.js +4 -0
  139. mirobody/pub/htdoc/assets/useUpload-CMuIcES4.js +1 -0
  140. mirobody/pub/htdoc/assets/useUpload-legacy-mMCZZnaO.js +1 -0
  141. mirobody/pub/htdoc/assets/vendor-antd-CIj8s3ay.js +1 -0
  142. mirobody/pub/htdoc/assets/vendor-antd-legacy-BpsiSKce.js +15 -0
  143. mirobody/pub/htdoc/assets/vendor-eruda-CwRho7qZ.js +1 -0
  144. mirobody/pub/htdoc/assets/vendor-eruda-legacy-CnYNKfym.js +1 -0
  145. mirobody/pub/htdoc/assets/vendor-firebase-D-efta1e.js +1 -0
  146. mirobody/pub/htdoc/assets/vendor-firebase-legacy-SmJg7pDS.js +1434 -0
  147. mirobody/pub/htdoc/assets/vendor-monaco-Mti7B50H.js +1 -0
  148. mirobody/pub/htdoc/assets/vendor-monaco-legacy-18169DkE.js +1 -0
  149. mirobody/pub/htdoc/github-markdown-light.css +1006 -0
  150. mirobody/pub/htdoc/index.html +34 -0
  151. mirobody/pub/htdoc/mirobody.svg +4 -0
  152. mirobody/pub/prompts/theta_health.jinja +134 -0
  153. mirobody/pub/resources/chart_url.html +102 -0
  154. mirobody/pub/resources/chart_url.json +24 -0
  155. mirobody/pub/resources/upload.html +130 -0
  156. mirobody/pub/resources/upload.json +12 -0
  157. mirobody/pub/tools/README.md +112 -0
  158. mirobody/pub/tools/__init__.py +95 -0
  159. mirobody/pub/tools/_chart_service_schema_loader.py +115 -0
  160. mirobody/pub/tools/chart_schema/area.json +94 -0
  161. mirobody/pub/tools/chart_schema/bar.json +95 -0
  162. mirobody/pub/tools/chart_schema/boxplot.json +88 -0
  163. mirobody/pub/tools/chart_schema/column.json +95 -0
  164. mirobody/pub/tools/chart_schema/district-map.json +115 -0
  165. mirobody/pub/tools/chart_schema/dual-axes.json +96 -0
  166. mirobody/pub/tools/chart_schema/fishbone-diagram.json +85 -0
  167. mirobody/pub/tools/chart_schema/flow-diagram.json +78 -0
  168. mirobody/pub/tools/chart_schema/funnel.json +72 -0
  169. mirobody/pub/tools/chart_schema/histogram.json +77 -0
  170. mirobody/pub/tools/chart_schema/line.json +89 -0
  171. mirobody/pub/tools/chart_schema/liquid.json +64 -0
  172. mirobody/pub/tools/chart_schema/mind-map.json +85 -0
  173. mirobody/pub/tools/chart_schema/network-graph.json +78 -0
  174. mirobody/pub/tools/chart_schema/organization-chart.json +103 -0
  175. mirobody/pub/tools/chart_schema/path-map.json +45 -0
  176. mirobody/pub/tools/chart_schema/pie.json +77 -0
  177. mirobody/pub/tools/chart_schema/pin-map.json +59 -0
  178. mirobody/pub/tools/chart_schema/radar.json +79 -0
  179. mirobody/pub/tools/chart_schema/sankey.json +81 -0
  180. mirobody/pub/tools/chart_schema/scatter.json +86 -0
  181. mirobody/pub/tools/chart_schema/venn.json +81 -0
  182. mirobody/pub/tools/chart_service.py +1051 -0
  183. mirobody/pub/tools/code_service.py +114 -0
  184. mirobody/pub/tools/file_read_service.py +347 -0
  185. mirobody/pub/tools/file_write_service.py +110 -0
  186. mirobody/pub/tools/files_utils/__init__.py +132 -0
  187. mirobody/pub/tools/files_utils/backend.py +160 -0
  188. mirobody/pub/tools/files_utils/compat.py +240 -0
  189. mirobody/pub/tools/files_utils/eviction.py +121 -0
  190. mirobody/pub/tools/files_utils/global_files.py +376 -0
  191. mirobody/pub/tools/memory_service.py +112 -0
  192. mirobody/pub/tools/render-chart.js +106 -0
  193. mirobody/pub/tools/todo_service.py +168 -0
  194. mirobody/pub/tools_health/__init__.py +0 -0
  195. mirobody/pub/tools_health/genetic_service.py +259 -0
  196. mirobody/pub/tools_health/health_indicator_service.py +327 -0
  197. mirobody/pub/tools_health/user_service.py +101 -0
  198. mirobody/pulse/CLAUDE.md +228 -0
  199. mirobody/pulse/README.md +1960 -0
  200. mirobody/pulse/__init__.py +53 -0
  201. mirobody/pulse/apple/INTEGRATION_GUIDE.md +283 -0
  202. mirobody/pulse/apple/README.md +127 -0
  203. mirobody/pulse/apple/__init__.py +24 -0
  204. mirobody/pulse/apple/models.py +269 -0
  205. mirobody/pulse/apple/platform.py +137 -0
  206. mirobody/pulse/apple/provider.py +387 -0
  207. mirobody/pulse/apple/services/__init__.py +0 -0
  208. mirobody/pulse/apple/services/database_service.py +116 -0
  209. mirobody/pulse/apple/statistics_service.py +168 -0
  210. mirobody/pulse/base.py +220 -0
  211. mirobody/pulse/core/README.md +370 -0
  212. mirobody/pulse/core/__init__.py +85 -0
  213. mirobody/pulse/core/aggregate_indicator/README.md +393 -0
  214. mirobody/pulse/core/aggregate_indicator/TEST_README.md +291 -0
  215. mirobody/pulse/core/aggregate_indicator/__init__.py +56 -0
  216. mirobody/pulse/core/aggregate_indicator/aggregators/__init__.py +13 -0
  217. mirobody/pulse/core/aggregate_indicator/aggregators/base.py +84 -0
  218. mirobody/pulse/core/aggregate_indicator/aggregators/sql_aggregator.py +1534 -0
  219. mirobody/pulse/core/aggregate_indicator/database_service.py +85 -0
  220. mirobody/pulse/core/aggregate_indicator/derived_aggregator.py +430 -0
  221. mirobody/pulse/core/aggregate_indicator/derived_task.py +61 -0
  222. mirobody/pulse/core/aggregate_indicator/models.py +154 -0
  223. mirobody/pulse/core/aggregate_indicator/naming.py +69 -0
  224. mirobody/pulse/core/aggregate_indicator/rule_generator.py +174 -0
  225. mirobody/pulse/core/aggregate_indicator/service.py +253 -0
  226. mirobody/pulse/core/aggregate_indicator/startup.py +102 -0
  227. mirobody/pulse/core/aggregate_indicator/task.py +119 -0
  228. mirobody/pulse/core/aggregate_indicator/test_aggregator.py +726 -0
  229. mirobody/pulse/core/aggregate_indicator/test_cgm_indicators.py +427 -0
  230. mirobody/pulse/core/auth.py +47 -0
  231. mirobody/pulse/core/constants.py +266 -0
  232. mirobody/pulse/core/data_quality_service.py +269 -0
  233. mirobody/pulse/core/database.py +600 -0
  234. mirobody/pulse/core/distributed_lock.py +305 -0
  235. mirobody/pulse/core/fhir_mapping.py +253 -0
  236. mirobody/pulse/core/indicators_info.py +3084 -0
  237. mirobody/pulse/core/insight/__init__.py +15 -0
  238. mirobody/pulse/core/insight/baseline_engine.py +250 -0
  239. mirobody/pulse/core/insight/database_service.py +429 -0
  240. mirobody/pulse/core/insight/engine_task.py +457 -0
  241. mirobody/pulse/core/insight/eval_agent.py +220 -0
  242. mirobody/pulse/core/insight/hypothesis_agent.py +197 -0
  243. mirobody/pulse/core/insight/indicator_aliases.py +307 -0
  244. mirobody/pulse/core/insight/insight_agent.py +278 -0
  245. mirobody/pulse/core/insight/models.py +241 -0
  246. mirobody/pulse/core/insight/recipe_registry.py +94 -0
  247. mirobody/pulse/core/insight/recipes/__init__.py +95 -0
  248. mirobody/pulse/core/insight/recipes/r01_multi_signal.py +131 -0
  249. mirobody/pulse/core/insight/recipes/r02_single_sustained.py +116 -0
  250. mirobody/pulse/core/insight/recipes/r03_trend.py +184 -0
  251. mirobody/pulse/core/insight/recipes/r04_recovery.py +110 -0
  252. mirobody/pulse/core/insight/recipes/r05_weekday_pattern.py +76 -0
  253. mirobody/pulse/core/insight/recipes/r06_glucose.py +79 -0
  254. mirobody/pulse/core/insight/startup.py +40 -0
  255. mirobody/pulse/core/manage_service.py +359 -0
  256. mirobody/pulse/core/models.py +151 -0
  257. mirobody/pulse/core/monitor/__init__.py +3 -0
  258. mirobody/pulse/core/monitor/collector_service.py +512 -0
  259. mirobody/pulse/core/monitor/collector_task.py +112 -0
  260. mirobody/pulse/core/monitor/coverage_service.py +282 -0
  261. mirobody/pulse/core/monitor/platform_mapping.py +34 -0
  262. mirobody/pulse/core/monitor/query_service.py +544 -0
  263. mirobody/pulse/core/monitor/startup.py +65 -0
  264. mirobody/pulse/core/monitor/user_profile_service.py +253 -0
  265. mirobody/pulse/core/push_service.py +134 -0
  266. mirobody/pulse/core/scheduler.py +506 -0
  267. mirobody/pulse/core/units.py +856 -0
  268. mirobody/pulse/core/user.py +395 -0
  269. mirobody/pulse/core/user_health_service.py +321 -0
  270. mirobody/pulse/core/value_range_validator.py +172 -0
  271. mirobody/pulse/data_upload/models/__init__.py +3 -0
  272. mirobody/pulse/data_upload/models/requests.py +100 -0
  273. mirobody/pulse/data_upload/repositories/__init__.py +3 -0
  274. mirobody/pulse/data_upload/repositories/health_data.py +110 -0
  275. mirobody/pulse/data_upload/services/__init__.py +18 -0
  276. mirobody/pulse/data_upload/services/base.py +78 -0
  277. mirobody/pulse/data_upload/services/upload_health.py +540 -0
  278. mirobody/pulse/file_parser/FILE_PROCESSING_GUIDE.md +845 -0
  279. mirobody/pulse/file_parser/__init__.py +23 -0
  280. mirobody/pulse/file_parser/config.py +82 -0
  281. mirobody/pulse/file_parser/file_processor.py +160 -0
  282. mirobody/pulse/file_parser/file_upload_manager.py +1510 -0
  283. mirobody/pulse/file_parser/handlers/audio.py +39 -0
  284. mirobody/pulse/file_parser/handlers/base.py +613 -0
  285. mirobody/pulse/file_parser/handlers/csv.py +72 -0
  286. mirobody/pulse/file_parser/handlers/excel.py +52 -0
  287. mirobody/pulse/file_parser/handlers/factory.py +135 -0
  288. mirobody/pulse/file_parser/handlers/genetic.py +113 -0
  289. mirobody/pulse/file_parser/handlers/image.py +65 -0
  290. mirobody/pulse/file_parser/handlers/pdf.py +65 -0
  291. mirobody/pulse/file_parser/handlers/text.py +84 -0
  292. mirobody/pulse/file_parser/services/__init__.py +0 -0
  293. mirobody/pulse/file_parser/services/async_file_processor.py +243 -0
  294. mirobody/pulse/file_parser/services/compressed_file_processor.py +544 -0
  295. mirobody/pulse/file_parser/services/content_extractor.py +165 -0
  296. mirobody/pulse/file_parser/services/content_formatter.py +612 -0
  297. mirobody/pulse/file_parser/services/database_services.py +1441 -0
  298. mirobody/pulse/file_parser/services/db_utils.py +290 -0
  299. mirobody/pulse/file_parser/services/file_abstract_extractor.py +981 -0
  300. mirobody/pulse/file_parser/services/file_db_service.py +962 -0
  301. mirobody/pulse/file_parser/services/file_llm_analyzer.py +685 -0
  302. mirobody/pulse/file_parser/services/file_processing_service.py +1339 -0
  303. mirobody/pulse/file_parser/services/file_uploader.py +212 -0
  304. mirobody/pulse/file_parser/services/food_recognizer.py +271 -0
  305. mirobody/pulse/file_parser/services/genetic_processor.py +693 -0
  306. mirobody/pulse/file_parser/services/indicator_extractor.py +990 -0
  307. mirobody/pulse/file_parser/services/list_my_data.py +47 -0
  308. mirobody/pulse/file_parser/services/pdf_splitter.py +363 -0
  309. mirobody/pulse/file_parser/services/prompts/__init__.py +0 -0
  310. mirobody/pulse/file_parser/services/prompts/file_abstract_prompt.py +98 -0
  311. mirobody/pulse/file_parser/services/prompts/file_indicator_extract.py +321 -0
  312. mirobody/pulse/file_parser/services/prompts/file_original_text_prompt.py +25 -0
  313. mirobody/pulse/file_parser/services/prompts/food_prompts.py +195 -0
  314. mirobody/pulse/file_parser/services/temp_file_manager.py +152 -0
  315. mirobody/pulse/file_parser/tools/__init__.py +1 -0
  316. mirobody/pulse/file_parser/tools/utils_sync_dim_table.py +658 -0
  317. mirobody/pulse/gate_tests/README.md +58 -0
  318. mirobody/pulse/gate_tests/__init__.py +0 -0
  319. mirobody/pulse/gate_tests/conftest.py +12 -0
  320. mirobody/pulse/gate_tests/fixtures/apple_health/body_nutrition_001.json +255 -0
  321. mirobody/pulse/gate_tests/fixtures/apple_health/sleep_001.json +287 -0
  322. mirobody/pulse/gate_tests/fixtures/apple_health/vitals_activity_001.json +372 -0
  323. mirobody/pulse/gate_tests/fixtures/theta_garmin/activities.json +206 -0
  324. mirobody/pulse/gate_tests/fixtures/theta_garmin/bodyComps.json +56 -0
  325. mirobody/pulse/gate_tests/fixtures/theta_garmin/dailies.json +183 -0
  326. mirobody/pulse/gate_tests/fixtures/theta_garmin/pulseOx.json +63 -0
  327. mirobody/pulse/gate_tests/fixtures/theta_garmin/respiration.json +91 -0
  328. mirobody/pulse/gate_tests/fixtures/theta_garmin/sleeps.json +307 -0
  329. mirobody/pulse/gate_tests/fixtures/theta_garmin/stress.json +65 -0
  330. mirobody/pulse/gate_tests/fixtures/theta_garmin/userMetrics.json +56 -0
  331. mirobody/pulse/gate_tests/fixtures/theta_oura/daily_activity.json +317 -0
  332. mirobody/pulse/gate_tests/fixtures/theta_oura/daily_spo2.json +93 -0
  333. mirobody/pulse/gate_tests/fixtures/theta_oura/heartrate.json +134 -0
  334. mirobody/pulse/gate_tests/fixtures/theta_oura/personal_info.json +117 -0
  335. mirobody/pulse/gate_tests/fixtures/theta_oura/sleep.json +366 -0
  336. mirobody/pulse/gate_tests/fixtures/theta_whoop/body_measurements.json +140 -0
  337. mirobody/pulse/gate_tests/fixtures/theta_whoop/cycles.json +158 -0
  338. mirobody/pulse/gate_tests/fixtures/theta_whoop/recoveries.json +185 -0
  339. mirobody/pulse/gate_tests/fixtures/theta_whoop/sleeps.json +294 -0
  340. mirobody/pulse/gate_tests/fixtures/theta_whoop/workouts.json +253 -0
  341. mirobody/pulse/gate_tests/format_tester.py +377 -0
  342. mirobody/pulse/gate_tests/test_format_data.py +24 -0
  343. mirobody/pulse/manager.py +251 -0
  344. mirobody/pulse/router/__init__.py +42 -0
  345. mirobody/pulse/router/apple_router.py +372 -0
  346. mirobody/pulse/router/file_router.py +893 -0
  347. mirobody/pulse/router/food_router.py +466 -0
  348. mirobody/pulse/router/manage_router.py +1126 -0
  349. mirobody/pulse/router/middleware.py +204 -0
  350. mirobody/pulse/router/public_router.py +1252 -0
  351. mirobody/pulse/router/session_share_router.py +166 -0
  352. mirobody/pulse/router/skill_router.py +390 -0
  353. mirobody/pulse/router/user_router.py +515 -0
  354. mirobody/pulse/setup.py +91 -0
  355. mirobody/pulse/theta/README.md +107 -0
  356. mirobody/pulse/theta/__init__.py +41 -0
  357. mirobody/pulse/theta/mirobody_garmin_connect/__init__.py +0 -0
  358. mirobody/pulse/theta/mirobody_garmin_connect/provider_garmin.py +1675 -0
  359. mirobody/pulse/theta/mirobody_oura/DESIGN.md +133 -0
  360. mirobody/pulse/theta/mirobody_oura/__init__.py +1 -0
  361. mirobody/pulse/theta/mirobody_oura/provider_oura.py +589 -0
  362. mirobody/pulse/theta/mirobody_pgsql/__init__.py +8 -0
  363. mirobody/pulse/theta/mirobody_pgsql/provider_pgsql.py +196 -0
  364. mirobody/pulse/theta/mirobody_whoop/__init__.py +0 -0
  365. mirobody/pulse/theta/mirobody_whoop/provider_whoop.py +1219 -0
  366. mirobody/pulse/theta/platform/__init__.py +10 -0
  367. mirobody/pulse/theta/platform/base.py +597 -0
  368. mirobody/pulse/theta/platform/database_service.py +554 -0
  369. mirobody/pulse/theta/platform/oauth2.py +261 -0
  370. mirobody/pulse/theta/platform/platform.py +555 -0
  371. mirobody/pulse/theta/platform/pull_task.py +166 -0
  372. mirobody/pulse/theta/platform/startup.py +50 -0
  373. mirobody/pulse/theta/platform/utils.py +368 -0
  374. mirobody/res/concept_graph.bin +3 -0
  375. mirobody/res/sql/00_init_schema.sql +324 -0
  376. mirobody/res/sql/01_basedata.sql +298 -0
  377. mirobody/res/sql/02_settings.sql +56 -0
  378. mirobody/res/sql/10_health_tab.sql +75 -0
  379. mirobody/res/sql/20_migration_add_reconnect_connect_info.sql +14 -0
  380. mirobody/res/sql/21_health_user_profile_by_system.sql +8 -0
  381. mirobody/res/sql/27_add_tags_to_sessions.sql +13 -0
  382. mirobody/res/sql/28_add_platform_to_series_data.sql +6 -0
  383. mirobody/res/sql/30_add_fhir_mapping_to_th_series_data.sql +4 -0
  384. mirobody/res/sql/31_user_custom_skills.sql +27 -0
  385. mirobody/res/sql/34_add_session_status_fields.sql +6 -0
  386. mirobody/res/sql/36_add_ethnicity_to_health_app_user.sql +2 -0
  387. mirobody/res/sql/39_create_th_files.sql +71 -0
  388. mirobody/res/sql/42_create_unit_type_and_indicator_full_dim.sql +8 -0
  389. mirobody/res/sql/44_th_sessions_add_status.sql +1 -0
  390. mirobody/res/sql/53_add_action_type_to_user_profile.sql +4 -0
  391. mirobody/res/sql/55_health_data_oura.sql +12 -0
  392. mirobody/res/sql/75_2_monitor_tables.sql +8 -0
  393. mirobody/res/sql/75_monitor_tables.sql +48 -0
  394. mirobody/res/sql/76_indicator_valid_rules.sql +156 -0
  395. mirobody/res/sql/77_insight_results.sql +54 -0
  396. mirobody/res/sql/90_deepagents.sql +48 -0
  397. mirobody/res/sql/91_create_webauthn_credentials.sql +20 -0
  398. mirobody/res/sql/92_add_mfa_enabled.sql +2 -0
  399. mirobody/res/sql/93_th_series_data_unmapped_indicator_idx.sql +20 -0
  400. mirobody/res/taxonomy.bin +3 -0
  401. mirobody/server/__init__.py +5 -0
  402. mirobody/server/middlewares.py +219 -0
  403. mirobody/server/server.py +574 -0
  404. mirobody/server/worker.py +75 -0
  405. mirobody/task/__init__.py +25 -0
  406. mirobody/task/base.py +163 -0
  407. mirobody/task/indicator_sync.py +411 -0
  408. mirobody/task/loader.py +87 -0
  409. mirobody/task/profile_refresh.py +66 -0
  410. mirobody/user/__init__.py +14 -0
  411. mirobody/user/apple.py +128 -0
  412. mirobody/user/email.py +514 -0
  413. mirobody/user/firebase.py +108 -0
  414. mirobody/user/google.py +46 -0
  415. mirobody/user/jwt.py +266 -0
  416. mirobody/user/oauth_service.py +823 -0
  417. mirobody/user/sharing.py +2236 -0
  418. mirobody/user/user.py +252 -0
  419. mirobody/user/user_service.py +627 -0
  420. mirobody/user/webauthn.py +924 -0
  421. mirobody/utils/__init__.py +43 -0
  422. mirobody/utils/config/README.md +598 -0
  423. mirobody/utils/config/__init__.py +9 -0
  424. mirobody/utils/config/config.py +1035 -0
  425. mirobody/utils/config/encrypt.py +62 -0
  426. mirobody/utils/config/http.py +60 -0
  427. mirobody/utils/config/llm.py +242 -0
  428. mirobody/utils/config/log.py +22 -0
  429. mirobody/utils/config/postgresql.py +247 -0
  430. mirobody/utils/config/redis.py +131 -0
  431. mirobody/utils/config/redis_compat/README.md +59 -0
  432. mirobody/utils/config/redis_compat/__init__.py +3 -0
  433. mirobody/utils/config/redis_compat/__main__.py +23 -0
  434. mirobody/utils/config/redis_compat/client.py +220 -0
  435. mirobody/utils/config/redis_compat/pubsub.py +110 -0
  436. mirobody/utils/config/redis_compat/resp.py +67 -0
  437. mirobody/utils/config/redis_compat/server.py +360 -0
  438. mirobody/utils/config/redis_compat/store_memory.py +314 -0
  439. mirobody/utils/config/redis_compat/store_pg.py +550 -0
  440. mirobody/utils/config/storage/__init__.py +7 -0
  441. mirobody/utils/config/storage/abstract.py +289 -0
  442. mirobody/utils/config/storage/aliyun.py +306 -0
  443. mirobody/utils/config/storage/aws.py +258 -0
  444. mirobody/utils/config/storage/constants.py +2 -0
  445. mirobody/utils/config/storage/factory.py +44 -0
  446. mirobody/utils/config/storage/local.py +319 -0
  447. mirobody/utils/data.py +124 -0
  448. mirobody/utils/db.py +136 -0
  449. mirobody/utils/distributed_websocket.py +190 -0
  450. mirobody/utils/embedding.py +149 -0
  451. mirobody/utils/http.py +218 -0
  452. mirobody/utils/i18n.py +194 -0
  453. mirobody/utils/llm/__init__.py +156 -0
  454. mirobody/utils/llm/adapters/__init__.py +12 -0
  455. mirobody/utils/llm/adapters/base.py +103 -0
  456. mirobody/utils/llm/adapters/factory.py +77 -0
  457. mirobody/utils/llm/adapters/gemini_adapter.py +175 -0
  458. mirobody/utils/llm/adapters/openai_adapter.py +158 -0
  459. mirobody/utils/llm/adapters/volcengine_adapter.py +135 -0
  460. mirobody/utils/llm/clients.py +258 -0
  461. mirobody/utils/llm/config.py +315 -0
  462. mirobody/utils/llm/file_processors.py +898 -0
  463. mirobody/utils/llm/hipaa_policy.py +124 -0
  464. mirobody/utils/llm/interface.py +112 -0
  465. mirobody/utils/llm/utils.py +615 -0
  466. mirobody/utils/locales/chat_service.json +9 -0
  467. mirobody/utils/locales/content_extractor.json +16 -0
  468. mirobody/utils/locales/file_processor.json +268 -0
  469. mirobody/utils/locales/file_uploader.json +240 -0
  470. mirobody/utils/locales/indicator_extractor.json +136 -0
  471. mirobody/utils/locales/load_genetic_data.json +158 -0
  472. mirobody/utils/locales/temp_file_manager.json +9 -0
  473. mirobody/utils/log.py +280 -0
  474. mirobody/utils/req_ctx.py +24 -0
  475. mirobody/utils/truncate.py +57 -0
  476. mirobody/utils/utils_audio.py +266 -0
  477. mirobody/utils/utils_auth.py +143 -0
  478. mirobody/utils/utils_encrypt.py +181 -0
  479. mirobody/utils/utils_files/__init__.py +0 -0
  480. mirobody/utils/utils_files/utils_oss.py +589 -0
  481. mirobody/utils/utils_files/utils_s3.py +305 -0
  482. mirobody/utils/utils_user.py +201 -0
  483. mirobody-0.0.0.dev0.dist-info/METADATA +698 -0
  484. mirobody-0.0.0.dev0.dist-info/RECORD +488 -0
  485. mirobody-0.0.0.dev0.dist-info/WHEEL +5 -0
  486. mirobody-0.0.0.dev0.dist-info/licenses/LICENSE +192 -0
  487. mirobody-0.0.0.dev0.dist-info/licenses/LICENSE-3RD-PARTY +72 -0
  488. mirobody-0.0.0.dev0.dist-info/top_level.txt +1 -0
mirobody/__init__.py ADDED
@@ -0,0 +1,14 @@
1
+ """Version in YYYYMMDD.HHMMSS format.
2
+
3
+ Resolution order:
4
+ 1. importlib.metadata — works after the package is installed (prod + editable dev).
5
+ 2. MIROBODY_VERSION env var — CI sets this before `python -m build`.
6
+ 3. "0.0.0.dev0" sentinel — running from source tree without installation.
7
+ """
8
+
9
+ try:
10
+ from importlib.metadata import version as _version
11
+ __version__ = _version("mirobody")
12
+ except Exception:
13
+ import os
14
+ __version__ = os.environ.get("MIROBODY_VERSION") or "0.0.0.dev0"
@@ -0,0 +1,22 @@
1
+ from .agent import (
2
+ load_agents_from_module,
3
+ load_agents_from_directory,
4
+ load_agents_from_directories,
5
+
6
+ get_global_agent_count,
7
+ get_global_agents,
8
+
9
+ get_agent,
10
+ get_global_agent,
11
+
12
+ get_llm_client_by_name,
13
+
14
+ detect_language
15
+ )
16
+
17
+ from .service import ChatService
18
+
19
+ from .user_config import (
20
+ get_user_mcps,
21
+ get_user_prompt_by_name
22
+ )
@@ -0,0 +1 @@
1
+ from .http import HTTPChatAdapter
@@ -0,0 +1,414 @@
1
+ """
2
+ Base adapter class for chat protocols
3
+
4
+ Defines the interface and common logic that all protocol adapters share.
5
+ Subclasses implement protocol-specific details (HTTP SSE, WebSocket, etc.).
6
+
7
+ All user_id parameters are consistently typed as str throughout.
8
+ """
9
+
10
+ import asyncio, json, logging
11
+
12
+ from abc import ABC, abstractmethod
13
+ from typing import Any, AsyncGenerator
14
+
15
+ from mirobody.chat.unified_chat_service import UnifiedChatService
16
+
17
+ from ..message import (
18
+ save_message,
19
+ get_last_message,
20
+ chat_list_setter_hollywell
21
+ )
22
+ from ..model import ChatStreamRequest
23
+
24
+ from ...utils import get_query_user_id
25
+
26
+ #-----------------------------------------------------------------------------
27
+ # Constants
28
+ #-----------------------------------------------------------------------------
29
+
30
+ class PERMISSION_ENUM:
31
+ """Permission type identifiers"""
32
+ chat = "chat"
33
+
34
+
35
+ class PERMISSION_LEVEL_ENUM:
36
+ """Permission level values"""
37
+ no_permission = 0
38
+ read = 1
39
+ write = 2
40
+
41
+
42
+ class CHUNK_TYPE_ENUMS:
43
+ """Chunk type identifiers for streaming responses"""
44
+
45
+ reply = "reply"
46
+ queryTitle = "queryTitle"
47
+ queryArguments = "queryArguments"
48
+ queryDetail = "queryDetail"
49
+ thinking = "thinking"
50
+ report = "report"
51
+ image = "image"
52
+ food_snap = "food_snap"
53
+ costStatistics = "costStatistics"
54
+
55
+ non_streaming_types = {
56
+ "food_snap" : 1,
57
+ "report" : 1
58
+ }
59
+
60
+ @classmethod
61
+ def get_streaming_types(cls):
62
+ """Types that should be forwarded to frontend"""
63
+ return [cls.reply, cls.queryTitle, cls.queryArguments, cls.queryDetail, cls.thinking, cls.costStatistics, cls.image]
64
+
65
+ @classmethod
66
+ def get_thinking_types(cls):
67
+ """Types considered as 'thinking' content"""
68
+ return [cls.thinking, cls.queryDetail, cls.queryArguments, cls.queryTitle]
69
+
70
+ #-----------------------------------------------------------------------------
71
+ # Base Adapter
72
+ #-----------------------------------------------------------------------------
73
+
74
+ class ChatProtocolAdapter(ABC):
75
+ """
76
+ Abstract base class for chat protocol adapters.
77
+
78
+ Provides common logic for all protocols:
79
+ - Permission validation
80
+ - Message saving (user question, assistant response)
81
+ - Message history retrieval
82
+ - Chat extraction logging
83
+
84
+ Subclasses must implement:
85
+ - handle_request(): Protocol-specific request handling
86
+ - stream_output(): Protocol-specific output formatting
87
+ - get_session_id(): Protocol-specific session ID extraction
88
+
89
+ Note: All user_id parameters are typed as str for consistency.
90
+ """
91
+
92
+ def __init__(self, chat_service: UnifiedChatService):
93
+ """
94
+ Initialize adapter with the chat service.
95
+
96
+ Args:
97
+ chat_service: Instance of UnifiedChatService
98
+ """
99
+ self.chat_service: UnifiedChatService = chat_service
100
+ self.scene: str = "web" # Default scene, subclasses can override
101
+
102
+ # =========================================================================
103
+ # Abstract methods - must be implemented by subclasses
104
+ # =========================================================================
105
+
106
+ @abstractmethod
107
+ async def handle_request(
108
+ self,
109
+ params: ChatStreamRequest,
110
+ **kwargs
111
+ ) -> AsyncGenerator[str, None]:
112
+ """
113
+ Main entry point for handling a chat request.
114
+
115
+ Args:
116
+ params: Chat request parameters (includes user_id as str)
117
+ **kwargs: Additional protocol-specific arguments
118
+
119
+ Yields:
120
+ Protocol-specific formatted output (e.g., SSE strings for HTTP)
121
+ """
122
+ pass
123
+
124
+ @abstractmethod
125
+ async def stream_output(
126
+ self,
127
+ chunks: AsyncGenerator[dict[str, Any], None],
128
+ context: dict[str, Any]
129
+ ) -> AsyncGenerator[str, None]:
130
+ """
131
+ Convert unified output chunks to protocol-specific format.
132
+
133
+ Args:
134
+ chunks: Raw chunks from UnifiedChatService
135
+ context: Request context for background processing
136
+
137
+ Yields:
138
+ Protocol-specific formatted output
139
+ """
140
+ pass
141
+
142
+ @abstractmethod
143
+ def get_session_id(self, params: ChatStreamRequest) -> str:
144
+ """
145
+ Extract session_id for database storage.
146
+
147
+ Different protocols may use different session ID sources:
148
+ - HTTP: Uses params.session_id
149
+ - WebSocket: May use trace_id
150
+
151
+ Args:
152
+ params: Chat request parameters
153
+
154
+ Returns:
155
+ Session ID string
156
+ """
157
+ pass
158
+
159
+ # =========================================================================
160
+ # Common methods - shared by all protocols
161
+ # =========================================================================
162
+
163
+ def get_scene(self) -> str:
164
+ """
165
+ Get the scene identifier for this adapter.
166
+
167
+ Returns:
168
+ Scene string (e.g., 'web' for HTTP)
169
+ """
170
+ return self.scene
171
+
172
+ #-------------------------------------------------------------------------
173
+
174
+ async def validate_permissions(
175
+ self,
176
+ params: ChatStreamRequest,
177
+ user_id: str,
178
+ **kwargs
179
+ ) -> bool:
180
+ """
181
+ Validate help-ask permissions.
182
+
183
+ In help-ask scenarios, user A can query user B's data if they have
184
+ the appropriate chat permissions.
185
+
186
+ Args:
187
+ params: Chat request parameters
188
+ user_id: Current user ID (str)
189
+ **kwargs: Additional arguments (e.g., token for HTTP)
190
+
191
+ Returns:
192
+ True if permissions are valid, False otherwise
193
+ """
194
+ # If not help-ask scenario (querying own data), always allow
195
+ if not params.query_user_id or params.query_user_id == user_id:
196
+ return True
197
+
198
+ # Validate help-ask permissions
199
+ permission_kwargs = {"permission": [PERMISSION_ENUM.chat]}
200
+ if "token" in kwargs:
201
+ permission_kwargs["token"] = kwargs["token"]
202
+
203
+ permission_result = await get_query_user_id(
204
+ params.query_user_id,
205
+ user_id,
206
+ **permission_kwargs
207
+ )
208
+
209
+ if not permission_result.get("success"):
210
+ logging.error(f"Permission validation failed: {permission_result.get('error')}")
211
+ return False
212
+
213
+ permissions = permission_result.get("permissions", {})
214
+ if permissions.get(PERMISSION_ENUM.chat, 0) < PERMISSION_LEVEL_ENUM.read:
215
+ logging.error(f"Insufficient chat permissions for user {user_id} to query {params.query_user_id}")
216
+ return False
217
+
218
+ return True
219
+
220
+ #-------------------------------------------------------------------------
221
+
222
+ async def save_user_question(
223
+ self,
224
+ params: ChatStreamRequest,
225
+ user_id: str
226
+ ) -> str:
227
+ """
228
+ Save user question to database.
229
+
230
+ Args:
231
+ params: Chat request parameters
232
+ user_id: User ID (str)
233
+
234
+ Returns:
235
+ The saved message ID
236
+ """
237
+ return await save_message(
238
+ user_id=user_id,
239
+ query_user_id=params.query_user_id or user_id,
240
+ content=params.question,
241
+ role='user',
242
+ session_id=self.get_session_id(params),
243
+ scene=self.get_scene(),
244
+ agent=params.agent,
245
+ message_type="text",
246
+ msg_id=params.msg_id,
247
+ provider=params.provider
248
+ )
249
+
250
+ #-------------------------------------------------------------------------
251
+
252
+ async def save_assistant_response(
253
+ self,
254
+ reply_id: str,
255
+ params: ChatStreamRequest,
256
+ user_id: str,
257
+ content: Any,
258
+ question_msg_id: str
259
+ ) -> str:
260
+ """
261
+ Save assistant response to database.
262
+
263
+ Args:
264
+ reply_id: Response message ID
265
+ params: Chat request parameters
266
+ user_id: User ID (str)
267
+ content: Response content (element_list)
268
+ question_msg_id: ID of the question this responds to
269
+
270
+ Returns:
271
+ The saved message ID
272
+ """
273
+ return await save_message(
274
+ user_id=user_id,
275
+ query_user_id=params.query_user_id or user_id,
276
+ content=content,
277
+ role='assistant',
278
+ session_id=self.get_session_id(params),
279
+ scene=self.get_scene(),
280
+ agent=params.agent,
281
+ msg_id=reply_id,
282
+ question_id=question_msg_id,
283
+ message_type="text",
284
+ provider=params.provider
285
+ )
286
+
287
+ #-------------------------------------------------------------------------
288
+
289
+ async def get_message_history(
290
+ self,
291
+ user_id: str,
292
+ query_user_id: str | None = None,
293
+ session_id: str | None = None,
294
+ scene: str | None = None
295
+ ) -> list:
296
+ """
297
+ Get message history for a session.
298
+
299
+ Processes raw messages to:
300
+ 1. Extract file information from JSON content
301
+ 2. Filter consecutive assistant messages
302
+ 3. Remove trailing assistant message for web scene
303
+
304
+ Args:
305
+ user_id: User ID (str)
306
+ query_user_id: Query user ID for help-ask scenarios
307
+ session_id: Session ID
308
+ scene: Scene identifier (uses adapter's scene if not provided)
309
+
310
+ Returns:
311
+ Processed list of messages
312
+ """
313
+ effective_scene = scene or self.get_scene()
314
+ messages = await get_last_message(user_id, query_user_id, session_id, scene=effective_scene)
315
+
316
+ # Process messages to extract files from JSON content
317
+ new_messages = []
318
+ for m in messages:
319
+ content = m.get("content", "")
320
+ role = m.get("role", "user")
321
+
322
+ try:
323
+ data = json.loads(content)
324
+ if role == "user" and "files" in data:
325
+ files = data["files"]
326
+ new_msg = dict(
327
+ role="user",
328
+ files=[dict(
329
+ s3_key=f["file_key"],
330
+ file_name=f["filename"],
331
+ file_type=f["type"]
332
+ ) for f in files],
333
+ type="file"
334
+ )
335
+ new_messages.append(new_msg)
336
+ else:
337
+ new_messages.append(m)
338
+ except (json.JSONDecodeError, TypeError):
339
+ new_messages.append(m)
340
+
341
+ # Filter out consecutive assistant messages, keeping only the last one in each sequence
342
+ filtered_messages = []
343
+ for i, msg in enumerate(new_messages):
344
+ if msg.get("role") != "assistant":
345
+ filtered_messages.append(msg)
346
+ else:
347
+ # Keep this assistant message if it's the last or next is not assistant
348
+ if i == len(new_messages) - 1 or new_messages[i + 1].get("role") != "assistant":
349
+ filtered_messages.append(msg)
350
+ else:
351
+ logging.debug(f"Skipping consecutive assistant message at index {i}")
352
+
353
+ return filtered_messages
354
+
355
+ #-------------------------------------------------------------------------
356
+
357
+ async def log_chat_extraction(
358
+ self,
359
+ params: ChatStreamRequest,
360
+ user_id: str,
361
+ msg_id: str
362
+ ):
363
+ """
364
+ Log chat for extraction (fire-and-forget).
365
+
366
+ Only logs for self-queries (not help-ask scenarios).
367
+
368
+ Args:
369
+ params: Chat request parameters
370
+ user_id: User ID (str)
371
+ msg_id: Message ID
372
+ """
373
+ if params.question and (params.query_user_id == user_id):
374
+ asyncio.create_task(
375
+ chat_list_setter_hollywell(
376
+ user_id=user_id,
377
+ msg_id=msg_id,
378
+ question=params.question
379
+ )
380
+ )
381
+
382
+ #-------------------------------------------------------------------------
383
+ async def prepare_unified_input(
384
+ self,
385
+ params: ChatStreamRequest,
386
+ user_id: int,
387
+ messages: list,
388
+ ) -> dict[str, Any]:
389
+ """
390
+ Prepare input for UnifiedChatService (common logic)
391
+ """
392
+
393
+ messages = messages + [dict(role="user", content=params.question)]
394
+
395
+ return {
396
+ 'user_id': str(user_id),
397
+ 'query_user_id': params.query_user_id or str(user_id),
398
+ 'content': params.question,
399
+ 'agent': params.agent,
400
+ 'messages': messages,
401
+ 'enable_mcp': bool(params.enable_mcp),
402
+ 'trace_id': params.trace_id,
403
+ 'session_id': self.get_session_id(params),
404
+ 'group_id': params.group_id,
405
+ 'file_list': params.file_list,
406
+ 'files_data': getattr(params, 'files_data', None), # Pass downloaded file content
407
+ 'prompt_name': params.prompt_name,
408
+ 'token': params.token or '',
409
+ 'language': params.language or '',
410
+ 'timezone': params.timezone or '',
411
+ 'provider': params.provider
412
+ }
413
+
414
+ #-----------------------------------------------------------------------------