pygeai 0.1.51b3__py3-none-any.whl → 0.6.0b15__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 (648) hide show
  1. pygeai/__init__.py +9 -1
  2. pygeai/_docs/Makefile +20 -0
  3. pygeai/_docs/make.bat +35 -0
  4. pygeai/_docs/source/conf.py +117 -0
  5. pygeai/_docs/source/content/ai_lab/cli.rst +747 -0
  6. pygeai/_docs/source/content/ai_lab/models.rst +1734 -0
  7. pygeai/_docs/source/content/ai_lab/runner.rst +253 -0
  8. pygeai/_docs/source/content/ai_lab/spec.rst +431 -0
  9. pygeai/_docs/source/content/ai_lab/usage.rst +1011 -0
  10. pygeai/_docs/source/content/ai_lab.rst +102 -0
  11. pygeai/_docs/source/content/analytics.rst +598 -0
  12. pygeai/_docs/source/content/api_reference/admin.rst +161 -0
  13. pygeai/_docs/source/content/api_reference/assistant.rst +326 -0
  14. pygeai/_docs/source/content/api_reference/auth.rst +379 -0
  15. pygeai/_docs/source/content/api_reference/chat.rst +754 -0
  16. pygeai/_docs/source/content/api_reference/embeddings.rst +154 -0
  17. pygeai/_docs/source/content/api_reference/evaluation.rst +590 -0
  18. pygeai/_docs/source/content/api_reference/feedback.rst +237 -0
  19. pygeai/_docs/source/content/api_reference/files.rst +592 -0
  20. pygeai/_docs/source/content/api_reference/gam.rst +401 -0
  21. pygeai/_docs/source/content/api_reference/health.rst +58 -0
  22. pygeai/_docs/source/content/api_reference/project.rst +738 -0
  23. pygeai/_docs/source/content/api_reference/proxy.rst +318 -0
  24. pygeai/_docs/source/content/api_reference/rag.rst +710 -0
  25. pygeai/_docs/source/content/api_reference/rerank.rst +94 -0
  26. pygeai/_docs/source/content/api_reference/secrets.rst +495 -0
  27. pygeai/_docs/source/content/api_reference/usage_limits.rst +390 -0
  28. pygeai/_docs/source/content/api_reference.rst +58 -0
  29. pygeai/_docs/source/content/authentication.rst +295 -0
  30. pygeai/_docs/source/content/chat_gui.rst +121 -0
  31. pygeai/_docs/source/content/cli.rst +203 -0
  32. pygeai/_docs/source/content/debugger.rst +651 -0
  33. pygeai/_docs/source/content/intro.rst +67 -0
  34. pygeai/_docs/source/content/migration.rst +929 -0
  35. pygeai/_docs/source/content/modules.rst +7 -0
  36. pygeai/_docs/source/content/quickstart.rst +143 -0
  37. pygeai/_docs/source/content/samples.rst +394 -0
  38. pygeai/_docs/source/index.rst +75 -0
  39. pygeai/_docs/source/modules.rst +7 -0
  40. pygeai/_docs/source/pygeai.admin.rst +29 -0
  41. pygeai/_docs/source/pygeai.analytics.rst +53 -0
  42. pygeai/_docs/source/pygeai.assistant.data.rst +21 -0
  43. pygeai/_docs/source/pygeai.assistant.data_analyst.rst +29 -0
  44. pygeai/_docs/source/pygeai.assistant.rag.rst +53 -0
  45. pygeai/_docs/source/pygeai.assistant.rst +55 -0
  46. pygeai/_docs/source/pygeai.auth.rst +29 -0
  47. pygeai/_docs/source/pygeai.chat.rst +69 -0
  48. pygeai/_docs/source/pygeai.cli.commands.flows.rst +10 -0
  49. pygeai/_docs/source/pygeai.cli.commands.lab.rst +53 -0
  50. pygeai/_docs/source/pygeai.cli.commands.rst +222 -0
  51. pygeai/_docs/source/pygeai.cli.rst +62 -0
  52. pygeai/_docs/source/pygeai.cli.texts.rst +21 -0
  53. pygeai/_docs/source/pygeai.core.base.rst +53 -0
  54. pygeai/_docs/source/pygeai.core.common.rst +37 -0
  55. pygeai/_docs/source/pygeai.core.embeddings.rst +61 -0
  56. pygeai/_docs/source/pygeai.core.feedback.rst +37 -0
  57. pygeai/_docs/source/pygeai.core.files.rst +61 -0
  58. pygeai/_docs/source/pygeai.core.llm.rst +29 -0
  59. pygeai/_docs/source/pygeai.core.plugins.rst +37 -0
  60. pygeai/_docs/source/pygeai.core.rerank.rst +53 -0
  61. pygeai/_docs/source/pygeai.core.rst +63 -0
  62. pygeai/_docs/source/pygeai.core.secrets.rst +29 -0
  63. pygeai/_docs/source/pygeai.core.services.llm.rst +29 -0
  64. pygeai/_docs/source/pygeai.core.services.rst +37 -0
  65. pygeai/_docs/source/pygeai.core.utils.rst +37 -0
  66. pygeai/_docs/source/pygeai.dbg.rst +21 -0
  67. pygeai/_docs/source/pygeai.evaluation.dataset.rst +29 -0
  68. pygeai/_docs/source/pygeai.evaluation.plan.rst +29 -0
  69. pygeai/_docs/source/pygeai.evaluation.result.rst +29 -0
  70. pygeai/_docs/source/pygeai.evaluation.rst +31 -0
  71. pygeai/_docs/source/pygeai.flows.rst +29 -0
  72. pygeai/_docs/source/pygeai.gam.rst +29 -0
  73. pygeai/_docs/source/pygeai.health.rst +29 -0
  74. pygeai/_docs/source/pygeai.lab.agents.rst +37 -0
  75. pygeai/_docs/source/pygeai.lab.processes.rst +37 -0
  76. pygeai/_docs/source/pygeai.lab.rst +65 -0
  77. pygeai/_docs/source/pygeai.lab.spec.rst +29 -0
  78. pygeai/_docs/source/pygeai.lab.strategies.rst +37 -0
  79. pygeai/_docs/source/pygeai.lab.tools.rst +37 -0
  80. pygeai/_docs/source/pygeai.man.man1.rst +10 -0
  81. pygeai/_docs/source/pygeai.man.rst +18 -0
  82. pygeai/_docs/source/pygeai.migration.rst +29 -0
  83. pygeai/_docs/source/pygeai.organization.limits.rst +45 -0
  84. pygeai/_docs/source/pygeai.organization.rst +61 -0
  85. pygeai/_docs/source/pygeai.proxy.rst +53 -0
  86. pygeai/_docs/source/pygeai.rst +35 -0
  87. pygeai/_docs/source/pygeai.tests.admin.rst +21 -0
  88. pygeai/_docs/source/pygeai.tests.analytics.rst +45 -0
  89. pygeai/_docs/source/pygeai.tests.assistants.rag.rst +37 -0
  90. pygeai/_docs/source/pygeai.tests.assistants.rst +45 -0
  91. pygeai/_docs/source/pygeai.tests.auth.rst +29 -0
  92. pygeai/_docs/source/pygeai.tests.chat.rst +45 -0
  93. pygeai/_docs/source/pygeai.tests.cli.commands.lab.rst +37 -0
  94. pygeai/_docs/source/pygeai.tests.cli.commands.rst +165 -0
  95. pygeai/_docs/source/pygeai.tests.cli.docker.rst +10 -0
  96. pygeai/_docs/source/pygeai.tests.cli.rst +46 -0
  97. pygeai/_docs/source/pygeai.tests.core.base.data.rst +29 -0
  98. pygeai/_docs/source/pygeai.tests.core.base.rst +45 -0
  99. pygeai/_docs/source/pygeai.tests.core.common.data.rst +10 -0
  100. pygeai/_docs/source/pygeai.tests.core.common.rst +37 -0
  101. pygeai/_docs/source/pygeai.tests.core.embeddings.rst +37 -0
  102. pygeai/_docs/source/pygeai.tests.core.feedback.rst +21 -0
  103. pygeai/_docs/source/pygeai.tests.core.files.rst +53 -0
  104. pygeai/_docs/source/pygeai.tests.core.llm.rst +21 -0
  105. pygeai/_docs/source/pygeai.tests.core.plugins.rst +21 -0
  106. pygeai/_docs/source/pygeai.tests.core.rerank.rst +37 -0
  107. pygeai/_docs/source/pygeai.tests.core.rst +39 -0
  108. pygeai/_docs/source/pygeai.tests.core.secrets.rst +21 -0
  109. pygeai/_docs/source/pygeai.tests.core.services.rst +21 -0
  110. pygeai/_docs/source/pygeai.tests.core.utils.rst +21 -0
  111. pygeai/_docs/source/pygeai.tests.dbg.rst +21 -0
  112. pygeai/_docs/source/pygeai.tests.evaluation.dataset.rst +21 -0
  113. pygeai/_docs/source/pygeai.tests.evaluation.plan.rst +21 -0
  114. pygeai/_docs/source/pygeai.tests.evaluation.result.rst +21 -0
  115. pygeai/_docs/source/pygeai.tests.evaluation.rst +20 -0
  116. pygeai/_docs/source/pygeai.tests.gam.rst +21 -0
  117. pygeai/_docs/source/pygeai.tests.health.rst +21 -0
  118. pygeai/_docs/source/pygeai.tests.integration.assistants.rag.rst +21 -0
  119. pygeai/_docs/source/pygeai.tests.integration.assistants.rst +18 -0
  120. pygeai/_docs/source/pygeai.tests.integration.chat.rst +21 -0
  121. pygeai/_docs/source/pygeai.tests.integration.lab.agents.rst +69 -0
  122. pygeai/_docs/source/pygeai.tests.integration.lab.processes.rst +77 -0
  123. pygeai/_docs/source/pygeai.tests.integration.lab.reasoning_strategies.rst +37 -0
  124. pygeai/_docs/source/pygeai.tests.integration.lab.rst +21 -0
  125. pygeai/_docs/source/pygeai.tests.integration.lab.tools.rst +77 -0
  126. pygeai/_docs/source/pygeai.tests.integration.rst +20 -0
  127. pygeai/_docs/source/pygeai.tests.lab.agents.rst +29 -0
  128. pygeai/_docs/source/pygeai.tests.lab.processes.rst +29 -0
  129. pygeai/_docs/source/pygeai.tests.lab.rst +49 -0
  130. pygeai/_docs/source/pygeai.tests.lab.spec.rst +29 -0
  131. pygeai/_docs/source/pygeai.tests.lab.strategies.rst +29 -0
  132. pygeai/_docs/source/pygeai.tests.lab.tools.rst +29 -0
  133. pygeai/_docs/source/pygeai.tests.migration.rst +29 -0
  134. pygeai/_docs/source/pygeai.tests.organization.limits.rst +29 -0
  135. pygeai/_docs/source/pygeai.tests.organization.rst +53 -0
  136. pygeai/_docs/source/pygeai.tests.proxy.rst +61 -0
  137. pygeai/_docs/source/pygeai.tests.rst +33 -0
  138. pygeai/admin/clients.py +14 -11
  139. pygeai/admin/endpoints.py +2 -2
  140. pygeai/analytics/clients.py +505 -0
  141. pygeai/analytics/endpoints.py +35 -0
  142. pygeai/analytics/managers.py +606 -0
  143. pygeai/analytics/mappers.py +207 -0
  144. pygeai/analytics/responses.py +240 -0
  145. pygeai/assistant/clients.py +18 -45
  146. pygeai/assistant/data/clients.py +1 -0
  147. pygeai/assistant/data_analyst/clients.py +8 -12
  148. pygeai/assistant/managers.py +195 -157
  149. pygeai/assistant/mappers.py +4 -2
  150. pygeai/assistant/rag/clients.py +27 -67
  151. pygeai/assistant/rag/mappers.py +12 -6
  152. pygeai/assistant/rag/models.py +301 -159
  153. pygeai/auth/__init__.py +0 -0
  154. pygeai/auth/clients.py +129 -0
  155. pygeai/auth/endpoints.py +6 -0
  156. pygeai/chat/clients.py +308 -37
  157. pygeai/chat/endpoints.py +3 -0
  158. pygeai/chat/iris.py +2 -8
  159. pygeai/chat/managers.py +9 -6
  160. pygeai/chat/session.py +38 -0
  161. pygeai/chat/settings.py +6 -0
  162. pygeai/chat/ui.py +678 -0
  163. pygeai/cli/__init__.py +0 -1
  164. pygeai/cli/commands/admin.py +7 -10
  165. pygeai/cli/commands/analytics.py +533 -0
  166. pygeai/cli/commands/assistant.py +9 -9
  167. pygeai/cli/commands/auth.py +299 -0
  168. pygeai/cli/commands/base.py +71 -9
  169. pygeai/cli/commands/chat.py +676 -19
  170. pygeai/cli/commands/common.py +28 -24
  171. pygeai/cli/commands/configuration.py +66 -13
  172. pygeai/cli/commands/docs.py +105 -0
  173. pygeai/cli/commands/embeddings.py +58 -11
  174. pygeai/cli/commands/evaluation.py +38 -38
  175. pygeai/cli/commands/feedback.py +3 -4
  176. pygeai/cli/commands/files.py +7 -8
  177. pygeai/cli/commands/gam.py +85 -10
  178. pygeai/cli/commands/lab/ai_lab.py +340 -484
  179. pygeai/cli/commands/lab/options.py +8 -0
  180. pygeai/cli/commands/lab/spec.py +273 -0
  181. pygeai/cli/commands/lab/utils.py +13 -0
  182. pygeai/cli/commands/llm.py +6 -7
  183. pygeai/cli/commands/migrate.py +1064 -436
  184. pygeai/cli/commands/organization.py +516 -11
  185. pygeai/cli/commands/rag.py +13 -14
  186. pygeai/cli/commands/rerank.py +3 -5
  187. pygeai/cli/commands/secrets.py +8 -9
  188. pygeai/cli/commands/usage_limits.py +18 -20
  189. pygeai/cli/commands/validators.py +144 -1
  190. pygeai/cli/commands/version.py +4 -5
  191. pygeai/cli/error_handler.py +151 -0
  192. pygeai/cli/geai.py +170 -31
  193. pygeai/cli/geai_proxy.py +86 -25
  194. pygeai/cli/install_man.py +93 -22
  195. pygeai/cli/parsers.py +75 -25
  196. pygeai/cli/texts/help.py +265 -28
  197. pygeai/core/base/clients.py +53 -12
  198. pygeai/core/base/mappers.py +11 -2
  199. pygeai/core/base/session.py +95 -11
  200. pygeai/core/common/config.py +78 -14
  201. pygeai/core/common/exceptions.py +96 -6
  202. pygeai/core/embeddings/__init__.py +19 -0
  203. pygeai/core/embeddings/clients.py +23 -5
  204. pygeai/core/embeddings/managers.py +9 -4
  205. pygeai/core/embeddings/mappers.py +16 -2
  206. pygeai/core/embeddings/responses.py +9 -2
  207. pygeai/core/feedback/clients.py +8 -3
  208. pygeai/core/files/clients.py +23 -24
  209. pygeai/core/files/managers.py +121 -30
  210. pygeai/core/files/responses.py +4 -3
  211. pygeai/core/handlers.py +10 -1
  212. pygeai/core/llm/clients.py +22 -29
  213. pygeai/core/models.py +127 -11
  214. pygeai/core/plugins/clients.py +6 -6
  215. pygeai/core/rerank/clients.py +9 -3
  216. pygeai/core/rerank/managers.py +22 -5
  217. pygeai/core/secrets/clients.py +16 -37
  218. pygeai/core/services/response.py +18 -0
  219. pygeai/core/services/rest.py +159 -49
  220. pygeai/core/utils/__init__.py +0 -0
  221. pygeai/core/utils/console.py +83 -0
  222. pygeai/core/utils/parsers.py +32 -0
  223. pygeai/core/utils/validators.py +10 -0
  224. pygeai/dbg/__init__.py +3 -0
  225. pygeai/dbg/debugger.py +854 -14
  226. pygeai/evaluation/clients.py +7 -4
  227. pygeai/evaluation/dataset/clients.py +46 -44
  228. pygeai/evaluation/plan/clients.py +28 -26
  229. pygeai/evaluation/result/clients.py +38 -5
  230. pygeai/gam/clients.py +50 -28
  231. pygeai/gam/endpoints.py +2 -1
  232. pygeai/health/__init__.py +0 -0
  233. pygeai/health/clients.py +24 -0
  234. pygeai/health/endpoints.py +1 -0
  235. pygeai/lab/__init__.py +0 -90
  236. pygeai/lab/agents/clients.py +203 -162
  237. pygeai/lab/agents/endpoints.py +4 -0
  238. pygeai/lab/agents/mappers.py +57 -7
  239. pygeai/lab/clients.py +24 -0
  240. pygeai/lab/constants.py +3 -0
  241. pygeai/lab/managers.py +571 -541
  242. pygeai/lab/models.py +108 -19
  243. pygeai/lab/processes/clients.py +332 -340
  244. pygeai/lab/processes/mappers.py +3 -3
  245. pygeai/lab/runners.py +90 -0
  246. pygeai/lab/spec/__init__.py +0 -0
  247. pygeai/lab/spec/loader.py +24 -0
  248. pygeai/lab/spec/parsers.py +39 -0
  249. pygeai/lab/strategies/clients.py +67 -63
  250. pygeai/lab/strategies/mappers.py +1 -1
  251. pygeai/lab/tools/clients.py +85 -118
  252. pygeai/lab/tools/endpoints.py +4 -0
  253. pygeai/lab/tools/mappers.py +5 -5
  254. pygeai/man/man1/geai-proxy.1 +116 -0
  255. pygeai/man/man1/geai.1 +2580 -66
  256. pygeai/migration/__init__.py +33 -0
  257. pygeai/migration/strategies.py +468 -146
  258. pygeai/migration/tools.py +170 -3
  259. pygeai/organization/clients.py +245 -50
  260. pygeai/organization/endpoints.py +17 -8
  261. pygeai/organization/limits/clients.py +34 -32
  262. pygeai/organization/limits/managers.py +108 -49
  263. pygeai/organization/managers.py +347 -53
  264. pygeai/organization/mappers.py +102 -2
  265. pygeai/organization/responses.py +58 -1
  266. pygeai/proxy/clients.py +6 -3
  267. pygeai/proxy/config.py +14 -1
  268. pygeai/proxy/managers.py +61 -33
  269. pygeai/proxy/servers.py +196 -51
  270. pygeai/proxy/tool.py +33 -16
  271. pygeai/tests/admin/__init__.py +0 -0
  272. pygeai/tests/admin/test_clients.py +148 -0
  273. pygeai/tests/analytics/__init__.py +0 -0
  274. pygeai/tests/analytics/test_clients.py +86 -0
  275. pygeai/tests/analytics/test_managers.py +94 -0
  276. pygeai/tests/analytics/test_mappers.py +84 -0
  277. pygeai/tests/analytics/test_responses.py +73 -0
  278. pygeai/tests/assistants/rag/test_clients.py +346 -0
  279. pygeai/tests/assistants/rag/test_models.py +292 -0
  280. pygeai/tests/assistants/test_clients.py +176 -82
  281. pygeai/tests/assistants/test_managers.py +191 -57
  282. pygeai/tests/auth/__init__.py +0 -0
  283. pygeai/tests/auth/test_clients.py +289 -0
  284. pygeai/tests/auth/test_oauth.py +172 -0
  285. pygeai/tests/auth/test_session_logging.py +150 -0
  286. pygeai/tests/chat/__init__.py +0 -0
  287. pygeai/tests/chat/test_clients.py +393 -0
  288. pygeai/tests/chat/test_iris.py +38 -0
  289. pygeai/tests/chat/test_session.py +62 -0
  290. pygeai/tests/chat/test_ui.py +224 -0
  291. pygeai/tests/cli/commands/__init__.py +0 -0
  292. pygeai/tests/cli/commands/lab/__init__.py +0 -0
  293. pygeai/tests/cli/commands/lab/test_ai_lab.py +786 -0
  294. pygeai/tests/cli/commands/lab/test_common.py +208 -0
  295. pygeai/tests/cli/commands/lab/test_spec.py +246 -0
  296. pygeai/tests/cli/commands/test_assistant.py +202 -0
  297. pygeai/tests/cli/commands/test_chat.py +130 -0
  298. pygeai/tests/cli/commands/test_common.py +350 -0
  299. pygeai/tests/cli/commands/test_embeddings.py +132 -0
  300. pygeai/tests/cli/commands/test_evaluation.py +656 -0
  301. pygeai/tests/cli/commands/test_feedback.py +65 -0
  302. pygeai/tests/cli/commands/test_files.py +161 -0
  303. pygeai/tests/cli/commands/test_gam.py +201 -0
  304. pygeai/tests/cli/commands/test_llm.py +114 -0
  305. pygeai/tests/cli/commands/test_migrate.py +176 -0
  306. pygeai/tests/cli/commands/test_organization.py +276 -0
  307. pygeai/tests/cli/commands/test_rag.py +266 -0
  308. pygeai/tests/cli/commands/test_rerank.py +110 -0
  309. pygeai/tests/cli/commands/test_secrets.py +171 -0
  310. pygeai/tests/cli/commands/test_show_help.py +41 -0
  311. pygeai/tests/cli/commands/test_usage_limits.py +412 -0
  312. pygeai/tests/cli/commands/test_validators.py +160 -0
  313. pygeai/tests/cli/commands/test_version.py +81 -0
  314. pygeai/tests/cli/docker/__init__.py +0 -0
  315. pygeai/tests/cli/test_credentials_flag.py +316 -0
  316. pygeai/tests/cli/test_error_handler.py +225 -0
  317. pygeai/tests/cli/test_geai_driver.py +154 -0
  318. pygeai/tests/cli/test_parsers.py +5 -5
  319. pygeai/tests/core/base/data/models.py +7 -0
  320. pygeai/tests/core/base/test_mappers.py +43 -11
  321. pygeai/tests/core/base/test_models.py +3 -1
  322. pygeai/tests/core/base/test_responses.py +53 -0
  323. pygeai/tests/core/common/__init__.py +0 -0
  324. pygeai/tests/core/common/data/__init__.py +0 -0
  325. pygeai/tests/core/common/test_config.py +186 -0
  326. pygeai/tests/core/common/test_decorators.py +69 -0
  327. pygeai/tests/core/embeddings/__init__.py +0 -0
  328. pygeai/tests/core/embeddings/test_clients.py +225 -0
  329. pygeai/tests/core/embeddings/test_managers.py +171 -0
  330. pygeai/tests/core/embeddings/test_mappers.py +142 -0
  331. pygeai/tests/core/feedback/__init__.py +0 -0
  332. pygeai/tests/core/feedback/test_clients.py +64 -0
  333. pygeai/tests/core/files/test_clients.py +128 -0
  334. pygeai/tests/core/files/test_managers.py +124 -78
  335. pygeai/tests/core/files/test_mappers.py +137 -0
  336. pygeai/tests/core/files/test_models.py +103 -0
  337. pygeai/tests/core/files/test_responses.py +122 -0
  338. pygeai/tests/core/llm/__init__.py +0 -0
  339. pygeai/tests/core/llm/test_clients.py +142 -0
  340. pygeai/tests/core/plugins/__init__.py +0 -0
  341. pygeai/tests/core/plugins/test_clients.py +66 -0
  342. pygeai/tests/core/rerank/test_clients.py +76 -0
  343. pygeai/tests/core/rerank/test_managers.py +61 -39
  344. pygeai/tests/core/secrets/__init__.py +0 -0
  345. pygeai/tests/core/secrets/test_clients.py +264 -0
  346. pygeai/tests/core/services/__init__.py +0 -0
  347. pygeai/tests/core/services/test_rest.py +273 -0
  348. pygeai/tests/core/test_handlers.py +66 -0
  349. pygeai/tests/core/utils/__init__.py +0 -0
  350. pygeai/tests/core/utils/test_console.py +80 -0
  351. pygeai/tests/dbg/__init__.py +0 -0
  352. pygeai/tests/dbg/test_debugger.py +591 -0
  353. pygeai/tests/evaluation/__init__.py +0 -0
  354. pygeai/tests/evaluation/dataset/__init__.py +0 -0
  355. pygeai/tests/evaluation/dataset/test_clients.py +265 -0
  356. pygeai/tests/evaluation/plan/__init__.py +0 -0
  357. pygeai/tests/evaluation/plan/test_clients.py +195 -0
  358. pygeai/tests/evaluation/result/__init__.py +0 -0
  359. pygeai/tests/evaluation/result/test_clients.py +66 -0
  360. pygeai/tests/gam/__init__.py +0 -0
  361. pygeai/tests/gam/test_clients.py +195 -0
  362. pygeai/tests/health/__init__.py +0 -0
  363. pygeai/tests/health/test_clients.py +41 -0
  364. pygeai/tests/integration/__init__.py +0 -0
  365. pygeai/tests/integration/assistants/__init__.py +0 -0
  366. pygeai/tests/integration/assistants/rag/__init__.py +0 -0
  367. pygeai/tests/integration/assistants/rag/test_create_rag.py +91 -0
  368. pygeai/tests/integration/chat/__init__.py +0 -0
  369. pygeai/tests/integration/chat/test_generate_image.py +158 -0
  370. pygeai/tests/integration/lab/__init__.py +0 -0
  371. pygeai/tests/integration/lab/agents/__init__.py +0 -0
  372. pygeai/tests/integration/lab/agents/test_agents_list.py +106 -0
  373. pygeai/tests/integration/lab/agents/test_create_agent.py +319 -0
  374. pygeai/tests/integration/lab/agents/test_create_sharing_link.py +70 -0
  375. pygeai/tests/integration/lab/agents/test_delete_agent.py +75 -0
  376. pygeai/tests/integration/lab/agents/test_get_agent.py +94 -0
  377. pygeai/tests/integration/lab/agents/test_publish_agent_revision.py +127 -0
  378. pygeai/tests/integration/lab/agents/test_update_agent.py +250 -0
  379. pygeai/tests/integration/lab/processes/__init__.py +0 -0
  380. pygeai/tests/integration/lab/processes/test_create_process.py +345 -0
  381. pygeai/tests/integration/lab/processes/test_create_task.py +211 -0
  382. pygeai/tests/integration/lab/processes/test_delete_process.py +111 -0
  383. pygeai/tests/integration/lab/processes/test_get_process.py +201 -0
  384. pygeai/tests/integration/lab/processes/test_list_process_instances.py +91 -0
  385. pygeai/tests/integration/lab/processes/test_list_processes.py +138 -0
  386. pygeai/tests/integration/lab/processes/test_publish_process_revision.py +232 -0
  387. pygeai/tests/integration/lab/processes/test_update_process.py +289 -0
  388. pygeai/tests/integration/lab/reasoning_strategies/__init__.py +0 -0
  389. pygeai/tests/integration/lab/reasoning_strategies/test_get_reasoning_strategy.py +70 -0
  390. pygeai/tests/integration/lab/reasoning_strategies/test_list_reasoning_strategies.py +93 -0
  391. pygeai/tests/integration/lab/reasoning_strategies/test_update_reasoning_strategy.py +149 -0
  392. pygeai/tests/integration/lab/tools/__init__.py +0 -0
  393. pygeai/tests/integration/lab/tools/test_create_tool.py +288 -0
  394. pygeai/tests/integration/lab/tools/test_delete_tool.py +87 -0
  395. pygeai/tests/integration/lab/tools/test_get_parameter.py +98 -0
  396. pygeai/tests/integration/lab/tools/test_get_tool.py +91 -0
  397. pygeai/tests/integration/lab/tools/test_list_tools.py +106 -0
  398. pygeai/tests/integration/lab/tools/test_publish_tool_revision.py +119 -0
  399. pygeai/tests/integration/lab/tools/test_set_parameter.py +114 -0
  400. pygeai/tests/integration/lab/tools/test_update_tool.py +267 -0
  401. pygeai/tests/lab/agents/__init__.py +0 -0
  402. pygeai/tests/lab/agents/test_clients.py +481 -0
  403. pygeai/tests/lab/agents/test_mappers.py +440 -0
  404. pygeai/tests/lab/processes/__init__.py +0 -0
  405. pygeai/tests/lab/processes/test_clients.py +1416 -0
  406. pygeai/tests/lab/processes/test_mappers.py +1092 -0
  407. pygeai/tests/lab/spec/__init__.py +0 -0
  408. pygeai/tests/lab/spec/test_loader.py +59 -0
  409. pygeai/tests/lab/spec/test_parsers.py +182 -0
  410. pygeai/tests/lab/strategies/__init__.py +0 -0
  411. pygeai/tests/lab/strategies/test_clients.py +241 -0
  412. pygeai/tests/lab/strategies/test_mappers.py +132 -0
  413. pygeai/tests/lab/test_managers.py +553 -0
  414. pygeai/tests/lab/test_models.py +500 -3
  415. pygeai/tests/lab/tools/__init__.py +0 -0
  416. pygeai/tests/lab/tools/test_clients.py +521 -0
  417. pygeai/tests/lab/tools/test_mappers.py +198 -0
  418. pygeai/tests/migration/__init__.py +0 -0
  419. pygeai/tests/migration/test_strategies.py +405 -0
  420. pygeai/tests/migration/test_tools.py +159 -0
  421. pygeai/tests/organization/limits/test_clients.py +567 -0
  422. pygeai/tests/organization/limits/test_managers.py +298 -56
  423. pygeai/tests/organization/test_clients.py +600 -30
  424. pygeai/tests/organization/test_managers.py +424 -0
  425. pygeai/tests/organization/test_mappers.py +153 -0
  426. pygeai/tests/organization/test_responses.py +137 -0
  427. pygeai/tests/proxy/__init__.py +1 -0
  428. pygeai/tests/proxy/test_clients.py +397 -0
  429. pygeai/tests/proxy/test_config.py +171 -0
  430. pygeai/tests/proxy/test_integration.py +305 -0
  431. pygeai/tests/proxy/test_managers.py +312 -0
  432. pygeai/tests/proxy/test_servers.py +387 -0
  433. pygeai/tests/proxy/test_tool.py +176 -0
  434. pygeai/tests/snippets/analytics/__init__.py +0 -0
  435. pygeai/tests/snippets/analytics/get_agent_usage_per_user.py +16 -0
  436. pygeai/tests/snippets/analytics/get_agents_created_and_modified.py +11 -0
  437. pygeai/tests/snippets/analytics/get_average_cost_per_request.py +10 -0
  438. pygeai/tests/snippets/analytics/get_overall_error_rate.py +10 -0
  439. pygeai/tests/snippets/analytics/get_top_10_agents_by_requests.py +12 -0
  440. pygeai/tests/snippets/analytics/get_total_active_users.py +10 -0
  441. pygeai/tests/snippets/analytics/get_total_cost.py +10 -0
  442. pygeai/tests/snippets/analytics/get_total_requests_per_day.py +12 -0
  443. pygeai/tests/snippets/analytics/get_total_tokens.py +12 -0
  444. pygeai/tests/snippets/auth/__init__.py +0 -0
  445. pygeai/tests/snippets/chat/chat_completion_with_reasoning_effort.py +18 -0
  446. pygeai/tests/snippets/chat/get_response.py +15 -0
  447. pygeai/tests/snippets/chat/get_response_complete_example.py +67 -0
  448. pygeai/tests/snippets/chat/get_response_streaming.py +20 -0
  449. pygeai/tests/snippets/chat/get_response_with_files.py +16 -0
  450. pygeai/tests/snippets/chat/get_response_with_instructions.py +19 -0
  451. pygeai/tests/snippets/chat/get_response_with_metadata.py +24 -0
  452. pygeai/tests/snippets/chat/get_response_with_parallel_tools.py +58 -0
  453. pygeai/tests/snippets/chat/get_response_with_reasoning.py +21 -0
  454. pygeai/tests/snippets/chat/get_response_with_store.py +38 -0
  455. pygeai/tests/snippets/chat/get_response_with_tools.py +36 -0
  456. pygeai/tests/snippets/chat/get_response_with_truncation.py +24 -0
  457. pygeai/tests/snippets/dbg/__init__.py +0 -0
  458. pygeai/tests/snippets/dbg/basic_debugging.py +32 -0
  459. pygeai/tests/snippets/dbg/breakpoint_management.py +48 -0
  460. pygeai/tests/snippets/dbg/file_debugging.py +72 -0
  461. pygeai/tests/snippets/dbg/module_debugging.py +61 -0
  462. pygeai/tests/snippets/dbg/stack_navigation.py +45 -0
  463. pygeai/tests/snippets/dbg/stepping_example.py +40 -0
  464. pygeai/tests/snippets/embeddings/cache_example.py +31 -0
  465. pygeai/tests/snippets/embeddings/cohere_example.py +41 -0
  466. pygeai/tests/snippets/embeddings/openai_base64_example.py +27 -0
  467. pygeai/tests/snippets/embeddings/openai_example.py +30 -0
  468. pygeai/tests/snippets/embeddings/similarity_example.py +42 -0
  469. pygeai/tests/snippets/evaluation/dataset/__init__.py +0 -0
  470. pygeai/tests/snippets/evaluation/dataset/complete_workflow_example.py +195 -0
  471. pygeai/tests/snippets/evaluation/dataset/create_dataset.py +26 -0
  472. pygeai/tests/snippets/evaluation/dataset/create_dataset_from_file.py +11 -0
  473. pygeai/tests/snippets/evaluation/dataset/create_dataset_row.py +17 -0
  474. pygeai/tests/snippets/evaluation/dataset/create_expected_source.py +18 -0
  475. pygeai/tests/snippets/evaluation/dataset/create_filter_variable.py +19 -0
  476. pygeai/tests/snippets/evaluation/dataset/delete_dataset.py +9 -0
  477. pygeai/tests/snippets/evaluation/dataset/delete_dataset_row.py +10 -0
  478. pygeai/tests/snippets/evaluation/dataset/delete_expected_source.py +15 -0
  479. pygeai/tests/snippets/evaluation/dataset/delete_filter_variable.py +15 -0
  480. pygeai/tests/snippets/evaluation/dataset/get_dataset.py +9 -0
  481. pygeai/tests/snippets/evaluation/dataset/get_dataset_row.py +10 -0
  482. pygeai/tests/snippets/evaluation/dataset/get_expected_source.py +15 -0
  483. pygeai/tests/snippets/evaluation/dataset/get_filter_variable.py +15 -0
  484. pygeai/tests/snippets/evaluation/dataset/list_dataset_rows.py +9 -0
  485. pygeai/tests/snippets/evaluation/dataset/list_datasets.py +6 -0
  486. pygeai/tests/snippets/evaluation/dataset/list_expected_sources.py +10 -0
  487. pygeai/tests/snippets/evaluation/dataset/list_filter_variables.py +10 -0
  488. pygeai/tests/snippets/evaluation/dataset/update_dataset.py +15 -0
  489. pygeai/tests/snippets/evaluation/dataset/update_dataset_row.py +20 -0
  490. pygeai/tests/snippets/evaluation/dataset/update_expected_source.py +18 -0
  491. pygeai/tests/snippets/evaluation/dataset/update_filter_variable.py +19 -0
  492. pygeai/tests/snippets/evaluation/dataset/upload_dataset_rows_file.py +10 -0
  493. pygeai/tests/snippets/evaluation/plan/__init__.py +0 -0
  494. pygeai/tests/snippets/evaluation/plan/add_plan_system_metric.py +13 -0
  495. pygeai/tests/snippets/evaluation/plan/complete_workflow_example.py +136 -0
  496. pygeai/tests/snippets/evaluation/plan/create_evaluation_plan.py +24 -0
  497. pygeai/tests/snippets/evaluation/plan/create_rag_evaluation_plan.py +22 -0
  498. pygeai/tests/snippets/evaluation/plan/delete_evaluation_plan.py +9 -0
  499. pygeai/tests/snippets/evaluation/plan/delete_plan_system_metric.py +13 -0
  500. pygeai/tests/snippets/evaluation/plan/execute_evaluation_plan.py +11 -0
  501. pygeai/tests/snippets/evaluation/plan/get_evaluation_plan.py +9 -0
  502. pygeai/tests/snippets/evaluation/plan/get_plan_system_metric.py +13 -0
  503. pygeai/tests/snippets/evaluation/plan/get_system_metric.py +9 -0
  504. pygeai/tests/snippets/evaluation/plan/list_evaluation_plans.py +7 -0
  505. pygeai/tests/snippets/evaluation/plan/list_plan_system_metrics.py +9 -0
  506. pygeai/tests/snippets/evaluation/plan/list_system_metrics.py +7 -0
  507. pygeai/tests/snippets/evaluation/plan/update_evaluation_plan.py +22 -0
  508. pygeai/tests/snippets/evaluation/plan/update_plan_system_metric.py +14 -0
  509. pygeai/tests/snippets/evaluation/result/__init__.py +0 -0
  510. pygeai/tests/snippets/evaluation/result/complete_workflow_example.py +150 -0
  511. pygeai/tests/snippets/evaluation/result/get_evaluation_result.py +26 -0
  512. pygeai/tests/snippets/evaluation/result/list_evaluation_results.py +17 -0
  513. pygeai/tests/snippets/files/delete_file.py +1 -4
  514. pygeai/tests/snippets/files/get_file_content.py +2 -4
  515. pygeai/tests/snippets/files/get_file_data.py +1 -4
  516. pygeai/tests/snippets/files/get_file_list.py +1 -6
  517. pygeai/tests/snippets/files/upload_file.py +1 -5
  518. pygeai/tests/snippets/gam/gam_access_token.py +87 -0
  519. pygeai/tests/snippets/lab/agentic_flow_example_1.py +25 -23
  520. pygeai/tests/snippets/lab/agentic_flow_example_4.py +23 -23
  521. pygeai/tests/snippets/lab/agents/create_agent.py +5 -8
  522. pygeai/tests/snippets/lab/agents/create_agent_2.py +1 -5
  523. pygeai/tests/snippets/lab/agents/create_agent_edge_case.py +48 -0
  524. pygeai/tests/snippets/lab/agents/create_agent_with_permissions.py +39 -0
  525. pygeai/tests/snippets/lab/agents/create_agent_with_properties.py +46 -0
  526. pygeai/tests/snippets/lab/agents/create_agent_without_instructions.py +48 -0
  527. pygeai/tests/snippets/lab/agents/delete_agent.py +1 -5
  528. pygeai/tests/snippets/lab/agents/get_agent.py +2 -11
  529. pygeai/tests/snippets/lab/agents/get_agent_with_new_fields.py +62 -0
  530. pygeai/tests/snippets/lab/agents/get_sharing_link.py +2 -7
  531. pygeai/tests/snippets/lab/agents/list_agents.py +4 -7
  532. pygeai/tests/snippets/lab/agents/publish_agent_revision.py +2 -6
  533. pygeai/tests/snippets/lab/agents/update_agent.py +1 -5
  534. pygeai/tests/snippets/lab/agents/update_agent_properties.py +50 -0
  535. pygeai/tests/snippets/lab/assistant_to_agent.py +191 -0
  536. pygeai/tests/snippets/lab/crud_ui.py +462 -0
  537. pygeai/tests/snippets/lab/processes/create_process.py +3 -5
  538. pygeai/tests/snippets/lab/processes/create_task.py +3 -5
  539. pygeai/tests/snippets/lab/processes/jobs/list_jobs.py +10 -19
  540. pygeai/tests/snippets/lab/processes/kbs/create_kb.py +2 -5
  541. pygeai/tests/snippets/lab/processes/kbs/get_kb.py +10 -16
  542. pygeai/tests/snippets/lab/processes/kbs/list_kbs.py +13 -20
  543. pygeai/tests/snippets/lab/processes/kbs/try_all.py +5 -7
  544. pygeai/tests/snippets/lab/processes/list_processes.py +5 -7
  545. pygeai/tests/snippets/lab/runner_1.py +1 -1
  546. pygeai/tests/snippets/lab/samples/summarize_files.py +3 -3
  547. pygeai/tests/snippets/lab/strategies/create_reasoning_strategy.py +2 -5
  548. pygeai/tests/snippets/lab/strategies/get_reasoning_strategy.py +2 -5
  549. pygeai/tests/snippets/lab/strategies/list_reasoning_strategies.py +3 -6
  550. pygeai/tests/snippets/lab/strategies/update_reasoning_strategy.py +2 -5
  551. pygeai/tests/snippets/lab/tools/create_tool.py +4 -10
  552. pygeai/tests/snippets/lab/tools/create_tool_edge_case.py +50 -0
  553. pygeai/tests/snippets/lab/tools/delete_tool.py +2 -6
  554. pygeai/tests/snippets/lab/tools/get_parameter.py +5 -7
  555. pygeai/tests/snippets/lab/tools/get_tool.py +5 -7
  556. pygeai/tests/snippets/lab/tools/list_tools.py +3 -7
  557. pygeai/tests/snippets/lab/tools/publish_tool_revision.py +3 -5
  558. pygeai/tests/snippets/lab/tools/set_parameters.py +4 -9
  559. pygeai/tests/snippets/lab/tools/update_tool.py +4 -8
  560. pygeai/tests/snippets/lab/use_cases/__init__.py +0 -0
  561. pygeai/tests/snippets/lab/use_cases/create_cli_expert.py +1640 -0
  562. pygeai/tests/snippets/lab/use_cases/create_lab_expert.py +4541 -0
  563. pygeai/tests/snippets/lab/use_cases/create_tool_headless_web_browser.py +133 -0
  564. pygeai/tests/snippets/lab/use_cases/create_web_designer.py +189 -0
  565. pygeai/tests/snippets/lab/use_cases/create_web_reader.py +185 -0
  566. pygeai/tests/snippets/lab/{file_summarizer_example.py → use_cases/file_summarizer_example.py} +3 -3
  567. pygeai/tests/snippets/lab/{file_summarizer_example_2.py → use_cases/file_summarizer_example_2.py} +12 -12
  568. pygeai/tests/snippets/lab/use_cases/update_cli_expert.py +1773 -0
  569. pygeai/tests/snippets/lab/use_cases/update_lab_expert.py +4541 -0
  570. pygeai/tests/snippets/lab/use_cases/update_web_designer.py +188 -0
  571. pygeai/tests/snippets/lab/use_cases/update_web_reader.py +195 -0
  572. pygeai/tests/snippets/lab/use_cases/update_web_reader_with_tool.py +210 -0
  573. pygeai/tests/snippets/migrate/__init__.py +45 -0
  574. pygeai/tests/snippets/migrate/agent_migration.py +110 -0
  575. pygeai/tests/snippets/migrate/assistant_migration.py +64 -0
  576. pygeai/tests/snippets/migrate/orchestrator_examples.py +179 -0
  577. pygeai/tests/snippets/migrate/process_migration.py +64 -0
  578. pygeai/tests/snippets/migrate/project_migration.py +42 -0
  579. pygeai/tests/snippets/migrate/tool_migration.py +64 -0
  580. pygeai/tests/snippets/organization/add_project_member.py +10 -0
  581. pygeai/tests/snippets/organization/add_project_member_batch.py +44 -0
  582. pygeai/tests/snippets/organization/create_project.py +2 -2
  583. pygeai/tests/snippets/organization/get_memberships.py +12 -0
  584. pygeai/tests/snippets/organization/get_organization_members.py +6 -0
  585. pygeai/tests/snippets/organization/get_project_members.py +6 -0
  586. pygeai/tests/snippets/organization/get_project_memberships.py +12 -0
  587. pygeai/tests/snippets/organization/get_project_roles.py +6 -0
  588. pygeai/vendor/a2a/__init__.py +1 -0
  589. pygeai/vendor/a2a/auth/__init__.py +0 -0
  590. pygeai/vendor/a2a/auth/user.py +31 -0
  591. pygeai/vendor/a2a/client/__init__.py +19 -0
  592. pygeai/vendor/a2a/client/client.py +425 -0
  593. pygeai/vendor/a2a/client/errors.py +33 -0
  594. pygeai/vendor/a2a/client/helpers.py +22 -0
  595. pygeai/vendor/a2a/py.typed +0 -0
  596. pygeai/vendor/a2a/server/__init__.py +1 -0
  597. pygeai/vendor/a2a/server/agent_execution/__init__.py +18 -0
  598. pygeai/vendor/a2a/server/agent_execution/agent_executor.py +44 -0
  599. pygeai/vendor/a2a/server/agent_execution/context.py +155 -0
  600. pygeai/vendor/a2a/server/agent_execution/request_context_builder.py +20 -0
  601. pygeai/vendor/a2a/server/agent_execution/simple_request_context_builder.py +77 -0
  602. pygeai/vendor/a2a/server/apps/__init__.py +16 -0
  603. pygeai/vendor/a2a/server/apps/jsonrpc/__init__.py +16 -0
  604. pygeai/vendor/a2a/server/apps/jsonrpc/fastapi_app.py +88 -0
  605. pygeai/vendor/a2a/server/apps/jsonrpc/jsonrpc_app.py +426 -0
  606. pygeai/vendor/a2a/server/apps/jsonrpc/starlette_app.py +123 -0
  607. pygeai/vendor/a2a/server/context.py +23 -0
  608. pygeai/vendor/a2a/server/events/__init__.py +21 -0
  609. pygeai/vendor/a2a/server/events/event_consumer.py +149 -0
  610. pygeai/vendor/a2a/server/events/event_queue.py +156 -0
  611. pygeai/vendor/a2a/server/events/in_memory_queue_manager.py +85 -0
  612. pygeai/vendor/a2a/server/events/queue_manager.py +35 -0
  613. pygeai/vendor/a2a/server/request_handlers/__init__.py +20 -0
  614. pygeai/vendor/a2a/server/request_handlers/default_request_handler.py +435 -0
  615. pygeai/vendor/a2a/server/request_handlers/jsonrpc_handler.py +327 -0
  616. pygeai/vendor/a2a/server/request_handlers/request_handler.py +161 -0
  617. pygeai/vendor/a2a/server/request_handlers/response_helpers.py +133 -0
  618. pygeai/vendor/a2a/server/tasks/__init__.py +20 -0
  619. pygeai/vendor/a2a/server/tasks/inmemory_push_notifier.py +62 -0
  620. pygeai/vendor/a2a/server/tasks/inmemory_task_store.py +51 -0
  621. pygeai/vendor/a2a/server/tasks/push_notifier.py +25 -0
  622. pygeai/vendor/a2a/server/tasks/result_aggregator.py +151 -0
  623. pygeai/vendor/a2a/server/tasks/task_manager.py +253 -0
  624. pygeai/vendor/a2a/server/tasks/task_store.py +22 -0
  625. pygeai/vendor/a2a/server/tasks/task_updater.py +155 -0
  626. pygeai/vendor/a2a/types.py +1624 -0
  627. pygeai/vendor/a2a/utils/__init__.py +40 -0
  628. pygeai/vendor/a2a/utils/artifact.py +72 -0
  629. pygeai/vendor/a2a/utils/errors.py +69 -0
  630. pygeai/vendor/a2a/utils/helpers.py +176 -0
  631. pygeai/vendor/a2a/utils/message.py +83 -0
  632. pygeai/vendor/a2a/utils/task.py +57 -0
  633. pygeai/vendor/a2a/utils/telemetry.py +299 -0
  634. pygeai-0.6.0b15.dist-info/METADATA +205 -0
  635. pygeai-0.6.0b15.dist-info/RECORD +799 -0
  636. {pygeai-0.1.51b3.dist-info → pygeai-0.6.0b15.dist-info}/WHEEL +1 -1
  637. {pygeai-0.1.51b3.dist-info → pygeai-0.6.0b15.dist-info}/entry_points.txt +2 -1
  638. {pygeai-0.1.51b3.dist-info → pygeai-0.6.0b15.dist-info}/licenses/LICENSE +13 -1
  639. pygeai-0.6.0b15.dist-info/top_level.txt +1 -0
  640. docs/geai-proxy/README.md +0 -145
  641. docs/source/conf.py +0 -45
  642. pygeai/tests/core/test_managers.py +0 -233
  643. pygeai-0.1.51b3.dist-info/METADATA +0 -130
  644. pygeai-0.1.51b3.dist-info/RECORD +0 -324
  645. pygeai-0.1.51b3.dist-info/top_level.txt +0 -3
  646. scripts/bump_beta_version.py +0 -56
  647. {scripts → pygeai/analytics}/__init__.py +0 -0
  648. /pygeai/tests/snippets/lab/{c_code_fixer_agent_flow.py → use_cases/c_code_fixer_agent_flow.py} +0 -0
pygeai/chat/ui.py ADDED
@@ -0,0 +1,678 @@
1
+ import logging
2
+
3
+ import streamlit as st
4
+ import argparse
5
+ import json
6
+ import os
7
+ from datetime import datetime
8
+ from pygeai.chat.session import AgentChatSession
9
+ from pygeai.core.utils.console import Console
10
+ from pygeai.core.common.config import get_settings
11
+ from pygeai.lab.managers import AILabManager
12
+ from pygeai.lab.models import FilterSettings, AgentList
13
+ import sys
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ global SESSION_FILE_PATH
19
+
20
+
21
+ def parse_args():
22
+ """Parse command-line arguments."""
23
+ parser = argparse.ArgumentParser(description="Streamlit chat interface for pygeai agent")
24
+ parser.add_argument("--agent-name", "-n", required=True, help="Name of the agent to interact with")
25
+ args, unknown = parser.parse_known_args() # Ignore Streamlit's args
26
+ return args
27
+
28
+
29
+ def save_session_to_file(messages, file_path):
30
+ """Helper function to save session to a server-side file."""
31
+ try:
32
+ os.makedirs(os.path.dirname(file_path), exist_ok=True)
33
+ with open(file_path, 'w') as f:
34
+ json.dump(messages, f, indent=2)
35
+ logger.info(f"Session automatically saved to {file_path}")
36
+ return True
37
+ except Exception as e:
38
+ logger.error(f"Error saving session to {file_path}: {e}")
39
+ st.error(f"Failed to auto-save session: {e}")
40
+ return False
41
+
42
+
43
+ def get_unique_file_path(base_path):
44
+ """Generate a unique file path by appending a numeric suffix if the file exists."""
45
+ if not os.path.exists(base_path):
46
+ return base_path
47
+
48
+ directory, filename = os.path.split(base_path)
49
+ name, ext = os.path.splitext(filename)
50
+ counter = 1
51
+
52
+ new_path = base_path
53
+ while os.path.exists(new_path):
54
+ new_filename = f"{name}_{counter}{ext}"
55
+ new_path = os.path.join(directory, new_filename)
56
+ counter += 1
57
+
58
+ return new_path
59
+
60
+
61
+ def get_session_file_path(agent_name, custom_filename=None):
62
+ """Generate the session file path with date and agent name, or use a custom filename."""
63
+ if custom_filename:
64
+ if not custom_filename.endswith('.json'):
65
+ custom_filename += '.json'
66
+ return os.path.join("chats", custom_filename)
67
+ current_date = datetime.now().strftime("%Y-%m-%d")
68
+ return os.path.join("chats", f"chat_session_{agent_name}_{current_date}.json")
69
+
70
+
71
+ def list_session_files():
72
+ """List all JSON files in the 'chats' directory."""
73
+ chats_dir = "chats"
74
+ if not os.path.exists(chats_dir):
75
+ return []
76
+ try:
77
+ return [f for f in os.listdir(chats_dir) if f.endswith('.json')]
78
+ except Exception as e:
79
+ logger.error(f"Error listing session files in {chats_dir}: {e}")
80
+ return []
81
+
82
+
83
+ def load_session_from_file(file_path):
84
+ """Load session data from a specified file."""
85
+ try:
86
+ with open(file_path, 'r') as f:
87
+ data = json.load(f)
88
+ if isinstance(data, list):
89
+ return data, True
90
+ else:
91
+ return None, False
92
+ except Exception as e:
93
+ logger.error(f"Error loading session from {file_path}: {e}")
94
+ return None, False
95
+
96
+
97
+ def delete_session_file(file_path):
98
+ """Helper function to delete a session file from the file system."""
99
+ try:
100
+ if os.path.exists(file_path):
101
+ os.remove(file_path)
102
+ logger.info(f"Session file deleted: {file_path}")
103
+ return True, f"Session file {os.path.basename(file_path)} deleted successfully."
104
+ else:
105
+ logger.warning(f"Session file not found: {file_path}")
106
+ return False, f"Session file {os.path.basename(file_path)} not found."
107
+ except Exception as e:
108
+ logger.error(f"Error deleting session file {file_path}: {e}")
109
+ return False, f"Error deleting session file {os.path.basename(file_path)}: {str(e)}"
110
+
111
+
112
+ def get_alias_list():
113
+ """Get list of available aliases from settings."""
114
+ try:
115
+ settings = get_settings()
116
+ aliases = list(settings.list_aliases().keys())
117
+ return ["-"] + aliases # Add "-" as default no-selection option
118
+ except Exception as e:
119
+ logger.error(f"Error fetching alias list: {e}")
120
+ st.error(f"Failed to fetch alias list: {e}")
121
+ return ["-"]
122
+
123
+
124
+ def get_agent_list(alias, project_id):
125
+ """Get list of agents for a given alias and project ID."""
126
+ try:
127
+ if alias == "-" or not project_id:
128
+ return ["-"]
129
+ ai_lab_manager = AILabManager(alias=alias)
130
+ filter_settings = FilterSettings(
131
+ allow_external=False,
132
+ allow_drafts=True,
133
+ access_scope="private"
134
+ )
135
+ result = ai_lab_manager.get_agent_list(
136
+ project_id=project_id,
137
+ filter_settings=filter_settings
138
+ )
139
+ if isinstance(result, AgentList) and result.agents:
140
+ # Store full agent data for preview
141
+ st.session_state.agent_data = {f"{agent.name} (ID: {agent.id})": agent for agent in result.agents}
142
+ return ["-"] + [f"{agent.name} (ID: {agent.id})" for agent in result.agents]
143
+ else:
144
+ st.error(f"No agents found for project ID {project_id} or errors occurred: {result.errors if hasattr(result, 'errors') else 'Unknown error'}")
145
+ return ["-"]
146
+ except Exception as e:
147
+ logger.error(f"Error fetching agents for project ID {project_id} with alias {alias}: {e}")
148
+ st.error(f"Failed to fetch agents for project: {e}")
149
+ return ["-"]
150
+
151
+
152
+ def save_recent_agents(agent_name):
153
+ """Save a recently used agent to session state or file."""
154
+ if "recent_agents" not in st.session_state:
155
+ st.session_state.recent_agents = []
156
+ if agent_name and agent_name not in st.session_state.recent_agents:
157
+ st.session_state.recent_agents = [agent_name] + st.session_state.recent_agents[:4] # Keep top 5 recent
158
+ # Optionally save to a file for persistence
159
+ try:
160
+ with open("recent_agents.json", "w") as f:
161
+ json.dump(st.session_state.recent_agents, f)
162
+ except Exception as e:
163
+ logger.error(f"Error saving recent agents: {e}")
164
+
165
+
166
+ def load_recent_agents():
167
+ """Load recent agents from session state or file."""
168
+ if "recent_agents" not in st.session_state:
169
+ try:
170
+ with open("recent_agents.json", "r") as f:
171
+ st.session_state.recent_agents = json.load(f)
172
+ except Exception:
173
+ st.session_state.recent_agents = []
174
+ return st.session_state.recent_agents
175
+
176
+
177
+ def run_streamlit_chat():
178
+ """Run a Streamlit chat interface for the specified agent."""
179
+ args = parse_args()
180
+ initial_agent_name = args.agent_name
181
+ try:
182
+ # Initialize session state for multiple tabs and current agent
183
+ if "sessions" not in st.session_state:
184
+ st.session_state.sessions = {
185
+ initial_agent_name: {
186
+ "messages": [],
187
+ "chat_session": AgentChatSession(initial_agent_name),
188
+ "custom_filename": "",
189
+ "active": True
190
+ }
191
+ }
192
+ if "active_tab" not in st.session_state:
193
+ st.session_state.active_tab = initial_agent_name
194
+ if "agent_data" not in st.session_state:
195
+ st.session_state.agent_data = {}
196
+ if "search_term" not in st.session_state:
197
+ st.session_state.search_term = ""
198
+ # if "editing_message" not in st.session_state:
199
+ # st.session_state.editing_message = None
200
+ # if "edit_content" not in st.session_state:
201
+ # st.session_state.edit_content = ""
202
+ # if "regenerate_response" not in st.session_state:
203
+ # st.session_state.regenerate_response = False
204
+
205
+ # Load recent agents
206
+ load_recent_agents()
207
+
208
+ # Load global vars
209
+ global current_session
210
+ global SESSION_FILE_PATH
211
+
212
+ # Get current session for active tab
213
+ current_session = st.session_state.sessions[st.session_state.active_tab]
214
+ if "editing_message" not in current_session:
215
+ current_session["editing_message"] = None
216
+ if "edit_content" not in current_session:
217
+ current_session["edit_content"] = ""
218
+ if "regenerate_response" not in current_session:
219
+ current_session["regenerate_response"] = False
220
+ SESSION_FILE_PATH = get_session_file_path(st.session_state.active_tab, current_session.get("custom_filename", ""))
221
+
222
+ # Initialize messages for new session if not already set
223
+ if not current_session["messages"]:
224
+ if os.path.exists(SESSION_FILE_PATH):
225
+ try:
226
+ with open(SESSION_FILE_PATH, 'r') as f:
227
+ restored_data = json.load(f)
228
+ if isinstance(restored_data, list):
229
+ current_session["messages"] = restored_data
230
+ st.success(f"Session automatically restored from {SESSION_FILE_PATH}")
231
+ else:
232
+ logger.warning(f"Invalid session data in {SESSION_FILE_PATH}. Starting fresh.")
233
+ intro = current_session["chat_session"].get_answer(
234
+ ["You're about to speak to a user. Introduce yourself in a clear and concise manner, "
235
+ "stating who you are and what you do. Nothing else."]
236
+ )
237
+ if "Agent not found" in str(intro):
238
+ st.error("The specified agent doesn't seem to exist. Please review the name and try again.")
239
+ logger.error("The specified agent doesn't seem to exist. Please review the name and try again.")
240
+ return
241
+ current_session["messages"] = [{"role": "assistant", "content": intro}]
242
+ except Exception as e:
243
+ logger.error(f"Error auto-restoring session from {SESSION_FILE_PATH}: {e}")
244
+ intro = current_session["chat_session"].get_answer(
245
+ ["You're about to speak to a user. Introduce yourself in a clear and concise manner, "
246
+ "stating who you are and what you do. Nothing else."]
247
+ )
248
+ if "Agent not found" in str(intro):
249
+ st.error("The specified agent doesn't seem to exist. Please review the name and try again.")
250
+ logger.error("The specified agent doesn't seem to exist. Please review the name and try again.")
251
+ return
252
+ current_session["messages"] = [{"role": "assistant", "content": intro}]
253
+ else:
254
+ intro = current_session["chat_session"].get_answer(
255
+ ["You're about to speak to a user. Introduce yourself in a clear and concise manner, "
256
+ "stating who you are and what you do. Nothing else."]
257
+ )
258
+ if "Agent not found" in str(intro):
259
+ st.error("The specified agent doesn't seem to exist. Please review the name and try again.")
260
+ logger.error("The specified agent doesn't seem to exist. Please review the name and try again.")
261
+ return
262
+ current_session["messages"] = [{"role": "assistant", "content": intro}]
263
+ save_session_to_file(current_session["messages"], SESSION_FILE_PATH)
264
+ st.rerun()
265
+
266
+ # Set page title
267
+ st.title(f"Chat with {st.session_state.active_tab}")
268
+
269
+ # Display the session save path in the main area for better readability
270
+ st.info(f"Session will be saved as: {SESSION_FILE_PATH}", icon="ℹ️")
271
+
272
+ # Chat History Search
273
+ search_term = st.text_input("Search Chat History", value=st.session_state.search_term, placeholder="Enter keyword to filter messages")
274
+ if search_term != st.session_state.search_term:
275
+ st.session_state.search_term = search_term
276
+ st.rerun()
277
+
278
+ # Single Session Chat
279
+ current_session = st.session_state.sessions[st.session_state.active_tab]
280
+ if "editing_message" not in current_session:
281
+ current_session["editing_message"] = None
282
+ if "edit_content" not in current_session:
283
+ current_session["edit_content"] = ""
284
+ render_chat_history(
285
+ current_session["messages"],
286
+ search_term,
287
+ tab_key_prefix=st.session_state.active_tab,
288
+ session=current_session
289
+ )
290
+
291
+ # Sidebar for session management and agent selection
292
+ with st.sidebar:
293
+ st.header("Session Management")
294
+
295
+ with st.expander("Help: Session Saving & Restoring", expanded=False):
296
+ st.markdown("""
297
+ **Session Saving & Restoring Explained:**
298
+ - **Auto-Save Session**: When toggled on, your chat history is automatically saved to a server-side file in the 'chats' directory after each message or action.
299
+ - **Custom Session Filename**: Enter a custom name for the session file to save it with a specific identifier.
300
+ - **Save Session (JSON)**: Download a local copy of your chat history.
301
+ - **Restore Session (JSON)**: Upload a previously saved JSON file to load a specific chat history.
302
+ - **Available Sessions**: List of saved session files with previews. Click to load into the current tab.
303
+ - **Reset Chat**: Clears the current session and starts fresh with the agent's introduction.
304
+ """)
305
+
306
+ # Custom filename input for the current session
307
+ custom_filename = st.text_input("Custom Session Filename (optional)", value=current_session.get("custom_filename", ""), placeholder="e.g., my_custom_session")
308
+ if custom_filename != current_session.get("custom_filename", ""):
309
+ current_session["custom_filename"] = custom_filename
310
+ SESSION_FILE_PATH = get_session_file_path(st.session_state.active_tab, custom_filename)
311
+ if current_session["messages"]:
312
+ save_session_to_file(current_session["messages"], SESSION_FILE_PATH)
313
+ st.rerun()
314
+
315
+ uploaded_file = st.file_uploader("Restore Session (JSON)", type=["json"])
316
+ if uploaded_file is not None and "session_restored" not in st.session_state:
317
+ try:
318
+ restored_data = json.load(uploaded_file)
319
+ if isinstance(restored_data, list):
320
+ current_session["messages"] = restored_data
321
+ st.session_state.session_restored = True
322
+ save_session_to_file(current_session["messages"], SESSION_FILE_PATH)
323
+ st.success(f"Session restored from {uploaded_file.name}")
324
+ st.rerun()
325
+ else:
326
+ st.error("Invalid session file: Must contain a list of messages in JSON format.")
327
+ except json.JSONDecodeError:
328
+ st.error("Invalid JSON format in uploaded file.")
329
+ except Exception as e:
330
+ st.error(f"Error restoring session: {e}")
331
+ logger.error(f"Error restoring session: {e}")
332
+
333
+ if current_session["messages"]:
334
+ session_json = json.dumps(current_session["messages"], indent=2)
335
+ current_date = datetime.now().strftime("%Y-%m-%d")
336
+ st.download_button(
337
+ label="Save Session (JSON)",
338
+ data=session_json,
339
+ file_name=f"chat_session_{st.session_state.active_tab}_{current_date}.json",
340
+ mime="application/json"
341
+ )
342
+
343
+ auto_save = st.toggle("Auto-Save Session", value=st.session_state.get("auto_save", True))
344
+ st.session_state.auto_save = auto_save
345
+
346
+ # Available Sessions with Preview
347
+ st.subheader("Available Sessions")
348
+ session_files = list_session_files()
349
+ session_feedback_placeholder = st.empty()
350
+ if session_files:
351
+ st.markdown("Click a file to load the session, or use the trash icon to delete:")
352
+ for file in session_files:
353
+ col1, col2 = st.columns([3, 1])
354
+ with col1:
355
+ if st.button(file, key=f"load_{file}", use_container_width=True):
356
+ file_path = os.path.join("chats", file)
357
+ loaded_data, success = load_session_from_file(file_path)
358
+ if success:
359
+ current_session["messages"] = loaded_data
360
+ st.success(f"Session loaded from {file}")
361
+ if st.session_state.auto_save:
362
+ save_session_to_file(current_session["messages"], SESSION_FILE_PATH)
363
+ st.rerun()
364
+ else:
365
+ st.error(f"Failed to load session from {file}")
366
+ with col2:
367
+ if st.button("🗑️", key=f"delete_{file}", help=f"Delete {file}"):
368
+ file_path = os.path.join("chats", file)
369
+ if os.path.abspath(file_path) == os.path.abspath(SESSION_FILE_PATH):
370
+ with session_feedback_placeholder.container():
371
+ st.markdown("---")
372
+ st.error(
373
+ "Cannot delete the currently active session file. Please switch to another session or reset the chat first.",
374
+ icon="🚫")
375
+ st.markdown("---")
376
+ else:
377
+ try:
378
+ os.remove(file_path)
379
+ logger.info(f"Deleted session file: {file_path}")
380
+ with session_feedback_placeholder.container():
381
+ st.markdown("---")
382
+ st.success(f"Deleted {file}", icon="✅")
383
+ st.markdown("---")
384
+ st.rerun()
385
+ except Exception as e:
386
+ logger.error(f"Error deleting session file {file_path}: {e}")
387
+ with session_feedback_placeholder.container():
388
+ st.markdown("---")
389
+ st.error(f"Failed to delete {file}: {e}", icon="❌")
390
+ st.markdown("---")
391
+ else:
392
+ st.markdown("No saved sessions found in 'chats' directory.")
393
+
394
+ # New Feature: Agent Selection
395
+ st.header("Switch Agent")
396
+ with st.expander("Help: Switching Agents", expanded=False):
397
+ st.markdown("""
398
+ **Switching Agents Explained:**
399
+ - **Select Alias**: Choose a profile (alias) to access specific API configurations.
400
+ - **Enter Project ID**: Provide the ID of the project to list available agents.
401
+ - **Select Agent**: Pick an agent to chat with. You'll be prompted to confirm before switching.
402
+ - Switching to a new agent starts a fresh session in a new tab.
403
+ """)
404
+
405
+ # Initialize session state for alias, project ID, and agent selection
406
+ if "selected_alias" not in st.session_state:
407
+ st.session_state.selected_alias = "-"
408
+ if "project_id_input" not in st.session_state:
409
+ st.session_state.project_id_input = ""
410
+ if "selected_agent" not in st.session_state:
411
+ st.session_state.selected_agent = "-"
412
+ if "confirm_switch" not in st.session_state:
413
+ st.session_state.confirm_switch = False
414
+
415
+ # Alias Selection
416
+ alias_list = get_alias_list()
417
+ selected_alias = st.selectbox("Select Alias (Profile)", alias_list, index=alias_list.index(st.session_state.selected_alias))
418
+ if selected_alias != st.session_state.selected_alias:
419
+ st.session_state.selected_alias = selected_alias
420
+ st.session_state.selected_agent = "-"
421
+ st.rerun()
422
+
423
+ # Project ID Input
424
+ project_id_input = st.text_input("Enter Project ID", value=st.session_state.project_id_input, placeholder="e.g., 2ca6883f-6778-40bb-bcc1-85451fb11107")
425
+ if project_id_input != st.session_state.project_id_input:
426
+ st.session_state.project_id_input = project_id_input
427
+ st.session_state.selected_agent = "-"
428
+ st.rerun()
429
+
430
+ # Agent Selection
431
+ agent_list = get_agent_list(st.session_state.selected_alias, st.session_state.project_id_input) if st.session_state.selected_alias != "-" and st.session_state.project_id_input else ["-"]
432
+ selected_agent = st.selectbox("Select Agent", agent_list, index=agent_list.index(st.session_state.selected_agent) if st.session_state.selected_agent in agent_list else 0)
433
+ if selected_agent != st.session_state.selected_agent:
434
+ st.session_state.selected_agent = selected_agent
435
+ st.session_state.confirm_switch = False
436
+
437
+ # Agent Info Preview
438
+ if st.session_state.selected_agent != "-" and st.session_state.selected_agent in st.session_state.agent_data:
439
+ agent_obj = st.session_state.agent_data.get(st.session_state.selected_agent)
440
+ if agent_obj and getattr(agent_obj, 'description', None):
441
+ st.markdown("**Agent Description:**")
442
+ st.markdown(f"{agent_obj.description[:200]}{'...' if len(agent_obj.description) > 200 else ''}")
443
+
444
+ # Favorite/Recent Agents List
445
+ st.subheader("Recent Agents")
446
+ recent_agents = load_recent_agents()
447
+ if recent_agents:
448
+ for agent in recent_agents:
449
+ if st.button(f"Switch to {agent}", key=f"recent_{agent}"):
450
+ if agent != st.session_state.active_tab:
451
+ if agent not in st.session_state.sessions:
452
+ st.session_state.sessions[agent] = {
453
+ "messages": [],
454
+ "chat_session": AgentChatSession(agent),
455
+ "custom_filename": "",
456
+ "active": True
457
+ }
458
+ st.session_state.active_tab = agent
459
+ save_recent_agents(agent)
460
+ st.rerun()
461
+ else:
462
+ st.markdown("No recent agents found.")
463
+
464
+ # Confirmation for switching agent
465
+ if st.session_state.selected_agent != "-" and st.session_state.selected_agent.split(" (ID: ")[0] != st.session_state.active_tab:
466
+ if not st.session_state.confirm_switch:
467
+ st.warning(f"Do you wish to chat with {st.session_state.selected_agent.split(' (ID: ')[0]}? This will start a new session in a new tab.", icon="⚠️")
468
+ col1, col2 = st.columns(2)
469
+ with col1:
470
+ if st.button("Confirm Switch"):
471
+ st.session_state.confirm_switch = True
472
+ new_agent_name = st.session_state.selected_agent.split(" (ID: ")[0]
473
+ if new_agent_name not in st.session_state.sessions:
474
+ st.session_state.sessions[new_agent_name] = {
475
+ "messages": [],
476
+ "chat_session": AgentChatSession(new_agent_name),
477
+ "custom_filename": "",
478
+ "active": True
479
+ }
480
+ st.session_state.active_tab = new_agent_name
481
+ save_recent_agents(new_agent_name)
482
+ current_session = st.session_state.sessions[new_agent_name]
483
+ if not current_session["messages"]:
484
+ intro = current_session["chat_session"].get_answer(
485
+ ["You're about to speak to a user. Introduce yourself in a clear and concise manner, "
486
+ "stating who you are and what you do. Nothing else."]
487
+ )
488
+ if "Agent not found" in str(intro):
489
+ st.error("The specified agent doesn't seem to exist. Please review the name and try again.")
490
+ logger.error("The specified agent doesn't seem to exist. Please review the name and try again.")
491
+ return
492
+ current_session["messages"] = [{"role": "assistant", "content": intro}]
493
+ SESSION_FILE_PATH = get_session_file_path(new_agent_name, current_session.get("custom_filename", ""))
494
+ if st.session_state.auto_save:
495
+ save_session_to_file(current_session["messages"], SESSION_FILE_PATH)
496
+ st.rerun()
497
+ with col2:
498
+ if st.button("Cancel"):
499
+ st.session_state.selected_agent = "-"
500
+ st.session_state.confirm_switch = False
501
+ st.rerun()
502
+ else:
503
+ st.success(f"Switched to agent {st.session_state.selected_agent.split(' (ID: ')[0]}!", icon="✅")
504
+
505
+ # Reset chat button for current tab
506
+ if st.button("Reset Chat"):
507
+ current_session["messages"] = []
508
+ intro = current_session["chat_session"].get_answer(
509
+ ["You're about to speak to a user. Introduce yourself in a clear and concise manner, "
510
+ "stating who you are and what you do. Nothing else."]
511
+ )
512
+ if "Agent not found" in str(intro):
513
+ st.error("The specified agent doesn't seem to exist. Please review the name and try again.")
514
+ logger.error("The specified agent doesn't seem to exist. Please review the name and try again.")
515
+ return
516
+ current_session["messages"] = [{"role": "assistant", "content": intro}]
517
+ if st.session_state.auto_save:
518
+ save_session_to_file(current_session["messages"], SESSION_FILE_PATH)
519
+ st.rerun()
520
+
521
+ error_container = st.empty()
522
+
523
+ # "Complete Answer" button logic
524
+ if (current_session["messages"] and
525
+ current_session["messages"][-1]["role"] == "assistant" and
526
+ "complete_answer_triggered" not in st.session_state):
527
+ if st.button("Complete Answer"):
528
+ st.session_state.complete_answer_triggered = True
529
+ last_assistant_message = current_session["messages"][-1]["content"]
530
+ continuation_prompt = (
531
+ f"The previous answer was: '{last_assistant_message}'. "
532
+ "It seems incomplete. Please continue and complete the answer."
533
+ )
534
+ current_session["messages"].append({"role": "user", "content": continuation_prompt})
535
+ with st.chat_message("user"):
536
+ st.markdown(continuation_prompt)
537
+ with st.chat_message("assistant"):
538
+ with st.spinner("Continuing answer..."):
539
+ response_placeholder = st.empty()
540
+ continued_answer = ""
541
+ result = current_session["chat_session"].stream_answer(current_session["messages"])
542
+ for chunk in result:
543
+ continued_answer += chunk
544
+ sanitized_answer = continued_answer
545
+ response_placeholder.markdown(f'{sanitized_answer}')
546
+ current_session["messages"].append({"role": "assistant", "content": continued_answer})
547
+ if st.session_state.auto_save:
548
+ save_session_to_file(current_session["messages"], SESSION_FILE_PATH)
549
+ del st.session_state.complete_answer_triggered
550
+ st.rerun()
551
+
552
+ # Chat input for current tab
553
+ if user_input := st.chat_input(f"Ask {st.session_state.active_tab}"):
554
+ if not user_input.strip():
555
+ logger.warning(f"Empty input submitted for agent {st.session_state.active_tab}")
556
+ with error_container.container():
557
+ st.error(f"Unable to communicate with the agent {st.session_state.active_tab}")
558
+ return
559
+ error_container.empty()
560
+
561
+ # Append user message to session
562
+ with st.chat_message("user"):
563
+ st.markdown(f"{user_input}")
564
+ current_session["messages"].append({"role": "user", "content": user_input})
565
+
566
+ # Save session if auto_save is enabled
567
+ if st.session_state.auto_save:
568
+ save_session_to_file(current_session["messages"], SESSION_FILE_PATH)
569
+
570
+ # Stream assistant response
571
+ with st.chat_message("assistant"):
572
+ with st.spinner("Processing..."):
573
+ response_placeholder = st.empty()
574
+ answer = ""
575
+ result = current_session["chat_session"].stream_answer(current_session["messages"])
576
+ for chunk in result:
577
+ answer += chunk
578
+ sanitized_answer = answer
579
+ response_placeholder.markdown(f'{sanitized_answer}')
580
+
581
+ # Append the complete response to session
582
+ current_session["messages"].append({"role": "assistant", "content": answer})
583
+
584
+ # Save session again after assistant response
585
+ if st.session_state.auto_save:
586
+ save_session_to_file(current_session["messages"], SESSION_FILE_PATH)
587
+ st.rerun()
588
+ except Exception as e:
589
+ st.error(f"An unexpected error occurred: {e}")
590
+ logger.error(f"An unexpected error occurred: {e}")
591
+ Console.write_stderr("An unexpected error has occurred. Please contact the developers.")
592
+ sys.exit(1)
593
+
594
+
595
+ def render_chat_history(messages, search_term="", tab_key_prefix="", session=None):
596
+ """Render chat history with search filter, copy, and edit buttons."""
597
+
598
+ global auto_save
599
+
600
+ filtered_messages = []
601
+ if search_term:
602
+ search_term = search_term.lower()
603
+ filtered_messages = [msg for msg in messages if search_term in msg["content"].lower()]
604
+ else:
605
+ filtered_messages = messages
606
+
607
+ if search_term and not filtered_messages:
608
+ st.info("No messages match your search term.", icon="ℹ️")
609
+ elif search_term:
610
+ st.info(f"Showing {len(filtered_messages)} messages matching '{search_term}'", icon="🔍")
611
+
612
+ # Find the index of the last user message in the filtered list for edit button display
613
+ last_user_msg_index = None
614
+ for i, msg in enumerate(filtered_messages):
615
+ if msg["role"] == "user":
616
+ last_user_msg_index = i
617
+
618
+ for i, message in enumerate(filtered_messages):
619
+ with st.chat_message(message["role"]):
620
+ col1, col2 = st.columns([5, 1])
621
+ with col1:
622
+ st.markdown(message["content"])
623
+ with col2:
624
+ # Edit Button - Only for the last user message
625
+ if message["role"] == "user" and i == last_user_msg_index:
626
+ if st.button("✏️", key=f"{tab_key_prefix}_edit_{i}", help="Edit this message"):
627
+ session["editing_message"] = i
628
+ session["edit_content"] = message["content"]
629
+ st.rerun()
630
+
631
+ # Handle Message Editing
632
+ if session and session.get("editing_message") is not None:
633
+ with st.container():
634
+ st.markdown("**Editing Message:**")
635
+ new_content = st.text_area(
636
+ "Edit your message:",
637
+ value=session.get("edit_content", ""),
638
+ key=f"{tab_key_prefix}_edit_area"
639
+ )
640
+ col1, col2 = st.columns(2)
641
+ with col1:
642
+ if st.button("Save Edit", key=f"{tab_key_prefix}_save_edit"):
643
+ # Update the message content
644
+ messages[session["editing_message"]]["content"] = new_content
645
+ # Check if there's a subsequent agent response to remove
646
+ if session["editing_message"] + 1 < len(messages) and messages[session["editing_message"] + 1]["role"] == "assistant":
647
+ messages.pop(session["editing_message"] + 1) # Remove the old response
648
+ session["regenerate_response"] = True # Flag to regenerate response
649
+ session["editing_message"] = None
650
+ session["edit_content"] = ""
651
+ st.rerun()
652
+ with col2:
653
+ if st.button("Cancel Edit", key=f"{tab_key_prefix}_cancel_edit"):
654
+ session["editing_message"] = None
655
+ session["edit_content"] = ""
656
+ session["regenerate_response"] = False
657
+ st.rerun()
658
+
659
+ # Regenerate agent response if needed after edit
660
+ if session and session.get("regenerate_response"):
661
+ with st.chat_message("assistant"):
662
+ with st.spinner("Regenerating response..."):
663
+ response_placeholder = st.empty()
664
+ answer = ""
665
+ result = current_session["chat_session"].stream_answer(messages)
666
+ for chunk in result:
667
+ answer += chunk
668
+ sanitized_answer = answer
669
+ response_placeholder.markdown(f'{sanitized_answer}')
670
+ messages.append({"role": "assistant", "content": answer})
671
+ if st.session_state.auto_save:
672
+ save_session_to_file(messages, SESSION_FILE_PATH)
673
+ session["regenerate_response"] = False
674
+ st.rerun()
675
+
676
+
677
+ if __name__ == "__main__":
678
+ run_streamlit_chat()