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.

Potentially problematic release.


This version of pygeai might be problematic. Click here for more details.

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
@@ -1,516 +1,1181 @@
1
- import sys
2
-
1
+ from typing import Tuple, Dict, Any, Optional, List
2
+ from pygeai import logger
3
3
  from pygeai.cli.commands import Command, Option, ArgumentsEnum
4
4
  from pygeai.cli.commands.builders import build_help_text
5
5
  from pygeai.cli.texts.help import MIGRATE_HELP_TEXT
6
6
  from pygeai.core.common.exceptions import MissingRequirementException
7
- from pygeai.migration.strategies import ProjectMigrationStrategy, AgentMigrationStrategy, ToolMigrationStrategy, \
8
- AgenticProcessMigrationStrategy, TaskMigrationStrategy
9
- from pygeai.migration.tools import MigrationTool
7
+ from pygeai.core.utils.console import Console
8
+ from pygeai.lab.managers import AILabManager
9
+ from pygeai.lab.models import FilterSettings
10
+ from pygeai.assistant.managers import AssistantManager
11
+ from pygeai.assistant.rag.clients import RAGAssistantClient
12
+ from pygeai.assistant.rag.mappers import RAGAssistantMapper
13
+ from pygeai.core.files.managers import FileManager
14
+ from pygeai.core.secrets.clients import SecretClient
15
+ from pygeai.migration.strategies import (
16
+ ProjectMigrationStrategy,
17
+ AgentMigrationStrategy,
18
+ ToolMigrationStrategy,
19
+ AgenticProcessMigrationStrategy,
20
+ TaskMigrationStrategy,
21
+ UsageLimitMigrationStrategy,
22
+ RAGAssistantMigrationStrategy,
23
+ FileMigrationStrategy,
24
+ SecretMigrationStrategy
25
+ )
26
+ from pygeai.migration.tools import MigrationTool, MigrationPlan, MigrationOrchestrator
27
+ from pygeai.admin.clients import AdminClient
10
28
 
11
29
 
12
- def show_help():
30
+ def show_help() -> None:
13
31
  """
14
- Displays help text in stdout
32
+ Displays help text in stdout.
15
33
  """
16
34
  help_text = build_help_text(migrate_commands, MIGRATE_HELP_TEXT)
17
- sys.stdout.write(help_text)
35
+ Console.write_stdout(help_text)
18
36
 
19
37
 
20
- def migrate_base(option_list: list):
21
- from_api_key = None
22
- from_instance = None
23
- to_api_key = None
24
- to_instance = None
38
+ def prompt_with_retry(
39
+ prompt_message: str,
40
+ valid_choices: Optional[list] = None,
41
+ allow_empty: bool = False
42
+ ) -> str:
43
+ """
44
+ Prompt user for input with validation and retry logic.
45
+
46
+ :param prompt_message: Message to display when prompting
47
+ :param valid_choices: Optional list of valid input choices
48
+ :param allow_empty: Whether to allow empty input
49
+ :return: User's validated input string
50
+ """
51
+ while True:
52
+ user_input = input(prompt_message).strip()
53
+
54
+ if not user_input and not allow_empty:
55
+ Console.write_stdout("Error: Input cannot be empty. Please try again.")
56
+ continue
57
+
58
+ if valid_choices and user_input not in valid_choices:
59
+ Console.write_stdout(f"Error: Invalid choice '{user_input}'. Valid options: {', '.join(valid_choices)}")
60
+ continue
61
+
62
+ return user_input
25
63
 
26
- for option_flag, option_arg in option_list:
27
- if option_flag.name == "from_api_key":
28
- from_api_key = option_arg
29
- if option_flag.name == "from_instance":
30
- from_instance = option_arg
31
- if option_flag.name == "to_api_key":
32
- to_api_key = option_arg
33
- if option_flag.name == "to_instance":
34
- to_instance = option_arg
35
64
 
36
- if not (from_api_key and from_instance):
37
- raise MissingRequirementException("Cannot migrate resources without indicating source: API key and instance")
65
+ def prompt_resource_selection(
66
+ resource_type: str,
67
+ items: list,
68
+ id_field: str = "id",
69
+ name_field: str = "name"
70
+ ) -> Optional[str]:
71
+ """
72
+ Display a list of resources and let the user select which ones to migrate.
73
+
74
+ :param resource_type: Type of resource being selected (for display purposes)
75
+ :param items: List of resource items to display
76
+ :param id_field: Name of the attribute containing the resource ID
77
+ :param name_field: Name of the attribute containing the resource name
78
+ :return: Comma-separated string of selected IDs, 'all' for all resources, or None to cancel
79
+ """
80
+ if not items:
81
+ Console.write_stdout(f"No {resource_type} found.")
82
+ return None
83
+
84
+ Console.write_stdout(f"\nAvailable {resource_type}:")
85
+ Console.write_stdout(" 0. Cancel (don't migrate this resource type)")
86
+
87
+ for idx, item in enumerate(items, 1):
88
+ item_id = getattr(item, id_field, None)
89
+ item_name = getattr(item, name_field, None) if hasattr(item, name_field) else None
90
+ if item_name:
91
+ Console.write_stdout(f" {idx}. {item_name} (ID: {item_id})")
92
+ else:
93
+ Console.write_stdout(f" {idx}. {item_id}")
94
+
95
+ while True:
96
+ selection = input(f"\nSelect {resource_type} (comma-separated numbers, or empty for all): ").strip()
97
+
98
+ if not selection:
99
+ return "all"
100
+
101
+ if selection == "0":
102
+ return None
103
+
104
+ try:
105
+ indices = [int(x.strip()) for x in selection.split(",")]
106
+ if any(i < 0 or i > len(items) for i in indices):
107
+ Console.write_stdout(f"Error: Invalid selection. Numbers must be between 0 and {len(items)}.")
108
+ continue
109
+
110
+ if 0 in indices:
111
+ return None
112
+
113
+ selected_ids = [getattr(items[i-1], id_field) for i in indices]
114
+ return ",".join(str(sid) for sid in selected_ids)
115
+ except (ValueError, IndexError) as e:
116
+ Console.write_stdout(f"Error: Invalid input format. Please enter comma-separated numbers.")
117
+ continue
38
118
 
39
- return from_api_key, from_instance, to_api_key, to_instance
40
119
 
120
+ def get_source_configuration() -> Tuple[str, str, str, Optional[str]]:
121
+ """
122
+ Prompt user for source configuration and retrieve organization ID.
123
+
124
+ :return: Tuple of (api_key, instance_url, project_id, organization_id)
125
+ """
126
+ Console.write_stdout("\n--- Source Configuration ---")
127
+ from_api_key = prompt_with_retry("Source API key: ")
128
+ from_instance = prompt_with_retry("Source instance URL: ")
129
+ from_project_id = prompt_with_retry("Source project ID: ")
130
+
131
+ admin_client = AdminClient(api_key=from_api_key, base_url=from_instance)
132
+ source_token_info = admin_client.validate_api_token()
133
+ from_organization_id = source_token_info.get("organizationId")
134
+
135
+ return from_api_key, from_instance, from_project_id, from_organization_id
41
136
 
42
- migrate_base_options = [
43
- Option(
44
- "from_api_key",
45
- ["--from-api-key", "--fak"],
46
- "API key for the source instance",
47
- True
48
- ),
49
- Option(
50
- "from_instance",
51
- ["--from-instance", "--fi"],
52
- "Source instance to migrate from",
53
- True
54
- ),
55
- Option(
56
- "to_api_key",
57
- ["--to-api-key", "--tak"],
58
- "API key for the destination instance",
59
- True
60
- ),
61
- Option(
62
- "to_instance",
63
- ["--to-instance", "--ti"],
64
- "Destination instance to migrate to",
65
- True
66
- ),
67
- ]
137
+
138
+ def get_destination_configuration(
139
+ same_instance: bool,
140
+ from_instance: str,
141
+ from_api_key: str,
142
+ from_organization_id: Optional[str],
143
+ creating_project: bool
144
+ ) -> Tuple[str, Optional[str], Optional[str]]:
145
+ """
146
+ Configure destination instance settings based on migration type.
147
+
148
+ :param same_instance: Whether migration is within the same instance
149
+ :param from_instance: Source instance URL
150
+ :param from_api_key: Source API key
151
+ :param from_organization_id: Source organization ID
152
+ :param creating_project: Whether a new project will be created
153
+ :return: Tuple of (instance_url, api_key, organization_id)
154
+ """
155
+ if same_instance:
156
+ to_instance = from_instance
157
+ to_organization_id = from_organization_id
158
+ Console.write_stdout(f"Destination instance: {to_instance} (same as source)")
159
+ Console.write_stdout(f"Destination organization ID: {to_organization_id} (same as source)")
160
+
161
+ if creating_project:
162
+ to_api_key = None
163
+ Console.write_stdout("Destination API key: (will be created after project creation)")
164
+ else:
165
+ to_api_key = prompt_with_retry("Destination API key: ")
166
+ else:
167
+ Console.write_stdout("\n--- Destination Configuration ---")
168
+ to_instance = prompt_with_retry("Destination instance URL: ")
169
+
170
+ if creating_project:
171
+ to_api_key = None
172
+ to_organization_id = None
173
+ Console.write_stdout("Destination API key: (will be created after project creation)")
174
+ Console.write_stdout("Destination organization ID: (will be retrieved after project creation)")
175
+ else:
176
+ to_api_key = prompt_with_retry("Destination API key: ")
177
+ dest_admin_client = AdminClient(api_key=to_api_key, base_url=to_instance)
178
+ dest_token_info = dest_admin_client.validate_api_token()
179
+ to_organization_id = dest_token_info.get("organizationId")
180
+
181
+ return to_instance, to_api_key, to_organization_id
68
182
 
69
183
 
70
- def clone_project(option_list: list):
71
- from_api_key = None
72
- from_project_id = None
73
- from_instance = None
74
- to_api_key = None
184
+ def get_project_creation_info(same_instance: bool) -> Tuple[Optional[str], Optional[str], Optional[str], Optional[str], Optional[str]]:
185
+ """
186
+ Prompt user for project creation information.
187
+
188
+ :param same_instance: Whether migration is within the same instance
189
+ :return: Tuple of (from_org_api_key, to_org_api_key, project_name, admin_email, project_id)
190
+ """
191
+ create_project = prompt_with_retry(
192
+ "Create new destination project? (y/n): ",
193
+ valid_choices=["y", "n"]
194
+ )
195
+
196
+ from_organization_api_key = None
197
+ to_organization_api_key = None
75
198
  to_project_name = None
76
- to_instance = None
77
199
  admin_email = None
200
+ to_project_id = None
201
+
202
+ if create_project == "y":
203
+ from_organization_api_key = prompt_with_retry("Source organization API key: ")
204
+ if same_instance:
205
+ to_organization_api_key = from_organization_api_key
206
+ Console.write_stdout("Destination organization API key: (same as source)")
207
+ else:
208
+ to_organization_api_key = prompt_with_retry("Destination organization API key: ")
209
+ to_project_name = prompt_with_retry("New project name: ")
210
+ admin_email = prompt_with_retry("Admin email: ")
211
+ else:
212
+ to_project_id = prompt_with_retry("Destination project ID: ")
213
+
214
+ return from_organization_api_key, to_organization_api_key, to_project_name, admin_email, to_project_id
78
215
 
79
- for option_flag, option_arg in option_list:
80
- if option_flag.name == "from_api_key":
81
- from_api_key = option_arg
82
- if option_flag.name == "from_project_id":
83
- from_project_id = option_arg
84
- if option_flag.name == "from_instance":
85
- from_instance = option_arg
86
- if option_flag.name == "to_api_key":
87
- to_api_key = option_arg
88
- if option_flag.name == "to_project_name":
89
- to_project_name = option_arg
90
- if option_flag.name == "to_instance":
91
- to_instance = option_arg
92
- if option_flag.name == "admin_email":
93
- admin_email = option_arg
94
216
 
95
- if not (from_project_id and from_instance):
96
- raise MissingRequirementException("Cannot migrate resources without indicating source: project and instance")
217
+ def select_resource_types() -> List[int]:
218
+ """
219
+ Prompt user to select which resource types to migrate.
220
+
221
+ :return: List of integers representing selected resource types (1-8)
222
+ """
223
+ Console.write_stdout("\n--- Resource Type Selection ---")
224
+ Console.write_stdout("Which resource types do you want to migrate?")
225
+ Console.write_stdout(" 1. Agents")
226
+ Console.write_stdout(" 2. Tools")
227
+ Console.write_stdout(" 3. Agentic Processes")
228
+ Console.write_stdout(" 4. Tasks")
229
+ Console.write_stdout(" 5. RAG Assistants")
230
+ Console.write_stdout(" 6. Files")
231
+ Console.write_stdout(" 7. Usage Limits")
232
+ Console.write_stdout(" 8. Secrets")
233
+
234
+ while True:
235
+ resource_choice = input("\nSelect resource types (comma-separated numbers, or empty for all): ").strip()
236
+ if not resource_choice:
237
+ return [1, 2, 3, 4, 5, 6, 7, 8]
238
+ try:
239
+ resource_types = [int(x.strip()) for x in resource_choice.split(",")]
240
+ if any(i < 1 or i > 8 for i in resource_types):
241
+ Console.write_stdout("Error: Invalid selection. Numbers must be between 1 and 8.")
242
+ continue
243
+ return resource_types
244
+ except ValueError:
245
+ Console.write_stdout("Error: Invalid input format. Please enter comma-separated numbers.")
246
+ continue
97
247
 
98
- if not admin_email:
99
- raise MissingRequirementException("Admin email for new project must be defined.")
100
248
 
101
- migration_strategy = ProjectMigrationStrategy(
102
- from_api_key=from_api_key,
103
- from_project_id=from_project_id,
104
- from_instance=from_instance,
105
- to_api_key=to_api_key,
106
- to_project_name=to_project_name,
107
- to_instance=to_instance,
108
- admin_email=admin_email
109
- )
110
- tool = MigrationTool(migration_strategy)
111
- response = tool.run_migration()
249
+ def fetch_and_select_agents(lab_manager: AILabManager) -> Optional[str]:
250
+ """
251
+ Fetch and prompt user to select agents for migration.
252
+
253
+ :param lab_manager: AI Lab manager instance
254
+ :return: Comma-separated IDs, 'all', or None
255
+ """
256
+ try:
257
+ agent_list = lab_manager.get_agent_list(FilterSettings(count=1000))
258
+ agents = [a for a in agent_list.agents if a.id]
259
+ if agents:
260
+ selection = prompt_resource_selection("agents", agents, id_field="id", name_field="name")
261
+ return selection
262
+ except Exception as e:
263
+ Console.write_stdout(f"Warning: Could not retrieve agents: {e}")
264
+ return None
112
265
 
113
- sys.stdout.write(f"Migration result: \n{response}\n")
114
266
 
267
+ def fetch_and_select_tools(lab_manager: AILabManager) -> Optional[str]:
268
+ """
269
+ Fetch and prompt user to select tools for migration.
270
+
271
+ :param lab_manager: AI Lab manager instance
272
+ :return: Comma-separated IDs, 'all', or None
273
+ """
274
+ try:
275
+ tool_list = lab_manager.list_tools(FilterSettings(count=1000))
276
+ tools = [t for t in tool_list.tools if t.id]
277
+ if tools:
278
+ selection = prompt_resource_selection("tools", tools, id_field="id", name_field="name")
279
+ return selection
280
+ except Exception as e:
281
+ Console.write_stdout(f"Warning: Could not retrieve tools: {e}")
282
+ return None
115
283
 
116
- clone_project_options = [
117
- Option(
118
- "from_api_key",
119
- ["--from-api-key", "--fak"],
120
- "API key for the source instance",
121
- True
122
- ),
123
- Option(
124
- "from_project_id",
125
- ["--from-project-id", "--fpid"],
126
- "ID of the source project to migrate from",
127
- True
128
- ),
129
- Option(
130
- "from_instance",
131
- ["--from-instance", "--fi"],
132
- "URL from the source instance to migrate from",
133
- True
134
- ),
135
- Option(
136
- "to_api_key",
137
- ["--to-api-key", "--tak"],
138
- "API key for the destination instance. If not specified, the same instance's API key will be used",
139
- True
140
- ),
141
- Option(
142
- "to_project_name",
143
- ["--to-project-name", "--tpn"],
144
- "Name of the destination project to migrate to",
145
- True
146
- ),
147
- Option(
148
- "to_instance",
149
- ["--to-instance", "--ti"],
150
- "URL from the destination instance to migrate to. If not specified, the same instance's URL will be used",
151
- True
152
- ),
153
- Option(
154
- "admin_email",
155
- ["--admin-email", "--ae"],
156
- "Email from destination project's administrator",
157
- True
158
- ),
159
- ]
160
284
 
285
+ def fetch_and_select_processes(lab_manager: AILabManager) -> Optional[str]:
286
+ """
287
+ Fetch and prompt user to select processes for migration.
288
+
289
+ :param lab_manager: AI Lab manager instance
290
+ :return: Comma-separated IDs, 'all', or None
291
+ """
292
+ try:
293
+ process_list = lab_manager.list_processes(FilterSettings(count=1000))
294
+ processes = [p for p in process_list.processes if p.id]
295
+ if processes:
296
+ selection = prompt_resource_selection("agentic processes", processes, id_field="id", name_field="name")
297
+ return selection
298
+ except Exception as e:
299
+ Console.write_stdout(f"Warning: Could not retrieve agentic processes: {e}")
300
+ return None
161
301
 
162
- def clone_agent(option_list: list):
163
- from_api_key = None
164
- from_project_id = None
165
- from_instance = None
166
- to_api_key = None
167
- to_project_id = None
168
- to_instance = None
169
- agent_id = None
170
302
 
171
- for option_flag, option_arg in option_list:
172
- if option_flag.name == "from_api_key":
173
- from_api_key = option_arg
174
- if option_flag.name == "from_project_id":
175
- from_project_id = option_arg
176
- if option_flag.name == "from_instance":
177
- from_instance = option_arg
178
- if option_flag.name == "to_api_key":
179
- to_api_key = option_arg
180
- if option_flag.name == "to_project_id":
181
- to_project_id = option_arg
182
- if option_flag.name == "to_instance":
183
- to_instance = option_arg
184
- if option_flag.name == "agent_id":
185
- agent_id = option_arg
186
-
187
- if not (from_project_id and from_instance and agent_id):
188
- raise MissingRequirementException("Cannot migrate resources without indicating source: project, instance and agent id")
189
-
190
- migration_strategy = AgentMigrationStrategy(
191
- from_api_key=from_api_key,
192
- from_project_id=from_project_id,
193
- from_instance=from_instance,
194
- to_api_key=to_api_key,
195
- to_project_id=to_project_id,
196
- to_instance=to_instance,
197
- agent_id=agent_id
198
- )
199
- tool = MigrationTool(migration_strategy)
200
- response = tool.run_migration()
303
+ def fetch_and_select_tasks(lab_manager: AILabManager) -> Optional[str]:
304
+ """
305
+ Fetch and prompt user to select tasks for migration.
306
+
307
+ :param lab_manager: AI Lab manager instance
308
+ :return: Comma-separated IDs, 'all', or None
309
+ """
310
+ try:
311
+ task_list = lab_manager.list_tasks(FilterSettings(count=1000))
312
+ tasks = [t for t in task_list.tasks if t.id]
313
+ if tasks:
314
+ selection = prompt_resource_selection("tasks", tasks, id_field="id", name_field="name")
315
+ return selection
316
+ except Exception as e:
317
+ Console.write_stdout(f"Warning: Could not retrieve tasks: {e}")
318
+ return None
201
319
 
202
320
 
203
- clone_agent_options = [
204
- Option(
205
- "from_api_key",
206
- ["--from-api-key", "--fak"],
207
- "API key for the source instance",
208
- True
209
- ),
210
- Option(
211
- "from_project_id",
212
- ["--from-project-id", "--fpid"],
213
- "ID of the source project to migrate from",
214
- True
215
- ),
216
- Option(
217
- "from_instance",
218
- ["--from-instance", "--fi"],
219
- "URL from the source instance to migrate from",
220
- True
221
- ),
222
- Option(
223
- "to_api_key",
224
- ["--to-api-key", "--tak"],
225
- "API key for the destination instance. If not specified, the same instance's API key will be used",
226
- True
227
- ),
228
- Option(
229
- "to_project_id",
230
- ["--to-project-id", "--tpid"],
231
- "ID of the destination project to migrate to",
232
- True
233
- ),
234
- Option(
235
- "to_instance",
236
- ["--to-instance", "--ti"],
237
- "URL from the destination instance to migrate to. If not specified, the same instance's URL will be used",
238
- True
239
- ),
240
- Option(
241
- "agent_id",
242
- ["--agent-id", "--aid"],
243
- "Unique identifier from the agent to be migrated",
244
- True
245
- ),
321
+ def fetch_and_select_rag_assistants(from_api_key: str, from_instance: str) -> Optional[str]:
322
+ """
323
+ Fetch and prompt user to select RAG assistants for migration.
324
+
325
+ :param from_api_key: Source API key
326
+ :param from_instance: Source instance URL
327
+ :return: Comma-separated names, 'all', or None
328
+ """
329
+ try:
330
+ rag_client = RAGAssistantClient(api_key=from_api_key, base_url=from_instance)
331
+ assistant_data = rag_client.get_assistants_from_project()
332
+ assistants_raw = assistant_data.get("assistants", [])
333
+ assistant_list = [RAGAssistantMapper.map_to_rag_assistant(a) for a in assistants_raw] if assistants_raw else []
334
+ if assistant_list:
335
+ selection = prompt_resource_selection("RAG assistants", assistant_list, id_field="name", name_field="name")
336
+ return selection
337
+ except Exception as e:
338
+ Console.write_stdout(f"Warning: Could not retrieve RAG assistants: {e}")
339
+ return None
246
340
 
247
- ]
341
+
342
+ def fetch_and_select_files(
343
+ from_api_key: str,
344
+ from_instance: str,
345
+ from_organization_id: str,
346
+ from_project_id: str
347
+ ) -> Optional[str]:
348
+ """
349
+ Fetch and prompt user to select files for migration.
350
+
351
+ :param from_api_key: Source API key
352
+ :param from_instance: Source instance URL
353
+ :param from_organization_id: Source organization ID
354
+ :param from_project_id: Source project ID
355
+ :return: Comma-separated file IDs, 'all', or None
356
+ """
357
+ try:
358
+ file_manager = FileManager(
359
+ api_key=from_api_key,
360
+ base_url=from_instance,
361
+ organization_id=from_organization_id,
362
+ project_id=from_project_id
363
+ )
364
+ file_list_response = file_manager.get_file_list()
365
+ files = [f for f in file_list_response.files if f.id]
366
+ if files:
367
+ selection = prompt_resource_selection("files", files, id_field="id", name_field="filename")
368
+ return selection
369
+ except Exception as e:
370
+ Console.write_stdout(f"Warning: Could not retrieve files: {e}")
371
+ return None
372
+
373
+
374
+ def fetch_and_select_secrets(from_api_key: str, from_instance: str) -> Optional[str]:
375
+ """
376
+ Fetch and prompt user to select secrets for migration.
377
+
378
+ :param from_api_key: Source API key
379
+ :param from_instance: Source instance URL
380
+ :return: Comma-separated secret IDs, 'all', or None
381
+ """
382
+ try:
383
+ secret_client = SecretClient(api_key=from_api_key, base_url=from_instance)
384
+ secrets_data = secret_client.list_secrets(count=1000)
385
+ secrets_list = secrets_data.get("secrets", []) if isinstance(secrets_data, dict) else []
386
+
387
+ if secrets_list:
388
+ secrets_objects = [type('obj', (object,), {'id': s.get('id'), 'name': s.get('name')})()
389
+ for s in secrets_list if s.get('id')]
390
+ selection = prompt_resource_selection("secrets", secrets_objects, id_field="id", name_field="name")
391
+ return selection
392
+ except Exception as e:
393
+ Console.write_stdout(f"Warning: Could not retrieve secrets: {e}")
394
+ return None
248
395
 
249
396
 
250
- def clone_tool(option_list: list):
397
+ def handle_usage_limits_keys(
398
+ same_instance: bool,
399
+ from_organization_api_key: Optional[str],
400
+ to_organization_api_key: Optional[str]
401
+ ) -> Tuple[str, str]:
402
+ """
403
+ Ensure organization API keys are available for usage limits migration.
404
+
405
+ :param same_instance: Whether migration is within the same instance
406
+ :param from_organization_api_key: Source organization API key (may be None)
407
+ :param to_organization_api_key: Destination organization API key (may be None)
408
+ :return: Tuple of (from_org_api_key, to_org_api_key)
409
+ """
410
+ if not from_organization_api_key:
411
+ from_organization_api_key = prompt_with_retry("Source organization API key (required for usage limits): ")
412
+ if same_instance:
413
+ to_organization_api_key = from_organization_api_key
414
+ elif not to_organization_api_key:
415
+ to_organization_api_key = prompt_with_retry("Destination organization API key (required for usage limits): ")
416
+
417
+ return from_organization_api_key, to_organization_api_key
418
+
419
+
420
+ def show_summary_and_confirm(
421
+ from_instance: str,
422
+ from_project_id: str,
423
+ to_instance: str,
424
+ to_project_name: Optional[str],
425
+ to_project_id: Optional[str],
426
+ selected_resources: Dict[str, Any]
427
+ ) -> Tuple[bool, bool]:
428
+ """
429
+ Display migration summary and prompt user for confirmation and error handling preference.
430
+
431
+ :param from_instance: Source instance URL
432
+ :param from_project_id: Source project ID
433
+ :param to_instance: Destination instance URL
434
+ :param to_project_name: Destination project name (if creating new project)
435
+ :param to_project_id: Destination project ID (if using existing project)
436
+ :param selected_resources: Dictionary of selected resources to migrate
437
+ :return: Tuple of (confirmation, stop_on_error) - whether user confirmed migration and whether to stop on errors
438
+ """
439
+ Console.write_stdout("\n--- Migration Summary ---")
440
+ Console.write_stdout(f"Source: {from_instance} / Project: {from_project_id}")
441
+ Console.write_stdout(f"Destination: {to_instance} / Project: {to_project_name or to_project_id}")
442
+ Console.write_stdout(f"Resources: {', '.join(selected_resources.keys()) if selected_resources else 'None'}")
443
+ Console.write_stdout("")
444
+
445
+ stop_on_error_response = prompt_with_retry("Stop migration on first error? (Y/n): ", valid_choices=["y", "n", "Y", "N", ""])
446
+ stop_on_error = stop_on_error_response.lower() != "n"
447
+
448
+ confirm = prompt_with_retry("Proceed with migration? (y/n): ", valid_choices=["y", "n"])
449
+ return confirm == "y", stop_on_error
450
+
451
+
452
+ def build_option_list_and_execute(
453
+ from_api_key: str,
454
+ from_instance: str,
455
+ from_project_id: str,
456
+ from_organization_id: Optional[str],
457
+ from_organization_api_key: Optional[str],
458
+ to_api_key: Optional[str],
459
+ to_instance: str,
460
+ to_project_id: Optional[str],
461
+ to_organization_id: Optional[str],
462
+ to_organization_api_key: Optional[str],
463
+ to_project_name: Optional[str],
464
+ admin_email: Optional[str],
465
+ selected_resources: Dict[str, Any],
466
+ stop_on_error: bool
467
+ ) -> None:
468
+ """
469
+ Build option list from interactive mode selections and execute migration.
470
+
471
+ :param from_api_key: Source API key
472
+ :param from_instance: Source instance URL
473
+ :param from_project_id: Source project ID
474
+ :param from_organization_id: Source organization ID
475
+ :param from_organization_api_key: Source organization API key
476
+ :param to_api_key: Destination API key
477
+ :param to_instance: Destination instance URL
478
+ :param to_project_id: Destination project ID
479
+ :param to_organization_id: Destination organization ID
480
+ :param to_organization_api_key: Destination organization API key
481
+ :param to_project_name: New project name (if creating)
482
+ :param admin_email: Admin email (if creating project)
483
+ :param selected_resources: Dictionary of selected resources to migrate
484
+ :param stop_on_error: Whether to stop migration on first error
485
+ """
486
+ option_list = []
487
+ option_list.append((type('obj', (object,), {'name': 'from_api_key'})(), from_api_key))
488
+ option_list.append((type('obj', (object,), {'name': 'from_instance'})(), from_instance))
489
+ option_list.append((type('obj', (object,), {'name': 'from_project_id'})(), from_project_id))
490
+ option_list.append((type('obj', (object,), {'name': 'from_organization_api_key'})(), from_organization_api_key))
491
+
492
+ if from_organization_id:
493
+ option_list.append((type('obj', (object,), {'name': 'from_organization_id'})(), from_organization_id))
494
+
495
+ option_list.append((type('obj', (object,), {'name': 'to_api_key'})(), to_api_key))
496
+ option_list.append((type('obj', (object,), {'name': 'to_instance'})(), to_instance))
497
+ option_list.append((type('obj', (object,), {'name': 'to_organization_api_key'})(), to_organization_api_key))
498
+
499
+ if to_project_id:
500
+ option_list.append((type('obj', (object,), {'name': 'to_project_id'})(), to_project_id))
501
+ if to_project_name:
502
+ option_list.append((type('obj', (object,), {'name': 'to_project_name'})(), to_project_name))
503
+ if admin_email:
504
+ option_list.append((type('obj', (object,), {'name': 'admin_email'})(), admin_email))
505
+ if to_organization_id:
506
+ option_list.append((type('obj', (object,), {'name': 'to_organization_id'})(), to_organization_id))
507
+
508
+ for resource_type, value in selected_resources.items():
509
+ option_list.append((type('obj', (object,), {'name': resource_type})(), value))
510
+
511
+ option_list.append((type('obj', (object,), {'name': 'stop_on_error'})(), "1" if stop_on_error else "0"))
512
+
513
+ clone_project(option_list)
514
+
515
+
516
+ def clone_project_interactively() -> None:
517
+ """
518
+ Run interactive migration wizard with step-by-step prompts for all configuration.
519
+ """
520
+ Console.write_stdout("")
521
+ Console.write_stdout("=" * 80)
522
+ Console.write_stdout("PROJECT MIGRATION ASSISTANT")
523
+ Console.write_stdout("=" * 80)
524
+ Console.write_stdout("")
525
+
526
+ migration_type = prompt_with_retry(
527
+ "Migration type (1=same instance, 2=cross instance): ",
528
+ valid_choices=["1", "2"]
529
+ )
530
+ same_instance = migration_type == "1"
531
+
532
+ from_api_key, from_instance, from_project_id, from_organization_id = get_source_configuration()
533
+ from_organization_api_key, to_organization_api_key, to_project_name, admin_email, to_project_id = get_project_creation_info(same_instance)
534
+
535
+ creating_project = bool(to_project_name and admin_email)
536
+ to_instance, to_api_key, to_organization_id = get_destination_configuration(
537
+ same_instance, from_instance, from_api_key, from_organization_id, creating_project
538
+ )
539
+
540
+ resource_types_to_migrate = select_resource_types()
541
+
542
+ Console.write_stdout("\n--- Retrieving Available Resources ---")
543
+ lab_manager = AILabManager(api_key=from_api_key, base_url=from_instance)
544
+
545
+ selected_resources = {}
546
+
547
+ if 1 in resource_types_to_migrate:
548
+ selection = fetch_and_select_agents(lab_manager)
549
+ if selection:
550
+ selected_resources["agents"] = selection
551
+
552
+ if 2 in resource_types_to_migrate:
553
+ selection = fetch_and_select_tools(lab_manager)
554
+ if selection:
555
+ selected_resources["tools"] = selection
556
+
557
+ if 3 in resource_types_to_migrate:
558
+ selection = fetch_and_select_processes(lab_manager)
559
+ if selection:
560
+ selected_resources["agentic_processes"] = selection
561
+
562
+ if 4 in resource_types_to_migrate:
563
+ selection = fetch_and_select_tasks(lab_manager)
564
+ if selection:
565
+ selected_resources["tasks"] = selection
566
+
567
+ if 5 in resource_types_to_migrate:
568
+ selection = fetch_and_select_rag_assistants(from_api_key, from_instance)
569
+ if selection:
570
+ selected_resources["rag_assistants"] = selection
571
+
572
+ if 6 in resource_types_to_migrate:
573
+ selection = fetch_and_select_files(from_api_key, from_instance, from_organization_id, from_project_id)
574
+ if selection:
575
+ selected_resources["files"] = selection
576
+
577
+ if 7 in resource_types_to_migrate:
578
+ from_organization_api_key, to_organization_api_key = handle_usage_limits_keys(
579
+ same_instance, from_organization_api_key, to_organization_api_key
580
+ )
581
+ selected_resources["usage_limits"] = True
582
+
583
+ if 8 in resource_types_to_migrate:
584
+ selection = fetch_and_select_secrets(from_api_key, from_instance)
585
+ if selection:
586
+ selected_resources["secrets"] = selection
587
+
588
+ confirmed, stop_on_error = show_summary_and_confirm(from_instance, from_project_id, to_instance, to_project_name, to_project_id, selected_resources)
589
+ if not confirmed:
590
+ Console.write_stdout("Migration cancelled.")
591
+ return
592
+
593
+ build_option_list_and_execute(
594
+ from_api_key, from_instance, from_project_id, from_organization_id, from_organization_api_key,
595
+ to_api_key, to_instance, to_project_id, to_organization_id, to_organization_api_key,
596
+ to_project_name, admin_email, selected_resources, stop_on_error
597
+ )
598
+
599
+
600
+ def clone_project(option_list: list) -> None:
601
+ """
602
+ Clone a project with selected components from source to destination instance.
603
+
604
+ Supports migration of agents, tools, agentic processes, tasks, usage limits,
605
+ RAG assistants, files, and secrets between GEAI instances.
606
+
607
+ :param option_list: List of (option_flag, option_value) tuples from CLI parsing
608
+ """
609
+ interactive_mode = False
610
+ for option_flag, option_arg in option_list:
611
+ if option_flag.name == "interactive":
612
+ interactive_mode = True
613
+ break
614
+
615
+ if interactive_mode:
616
+ clone_project_interactively()
617
+ return
618
+
251
619
  from_api_key = None
252
- from_project_id = None
620
+ from_organization_api_key = None
253
621
  from_instance = None
622
+ from_project_id = None
623
+ from_organization_id = None
254
624
  to_api_key = None
255
- to_project_id = None
625
+ to_organization_api_key = None
256
626
  to_instance = None
257
- tool_id = None
627
+ to_project_id = None
628
+ to_organization_id = None
629
+ to_project_name = None
630
+ admin_email = None
631
+
632
+ migrate_all = False
633
+ migrate_agents = False
634
+ migrate_tools = False
635
+ migrate_processes = False
636
+ migrate_tasks = False
637
+ migrate_usage_limits = False
638
+ migrate_rag_assistants = False
639
+ migrate_files = False
640
+ migrate_secrets = False
641
+
642
+ agent_ids = None
643
+ tool_ids = None
644
+ process_ids = None
645
+ task_ids = None
646
+ assistant_names = None
647
+ file_ids = None
648
+ secret_ids = None
649
+
650
+ stop_on_error = True
258
651
 
259
652
  for option_flag, option_arg in option_list:
260
653
  if option_flag.name == "from_api_key":
261
654
  from_api_key = option_arg
262
- if option_flag.name == "from_project_id":
263
- from_project_id = option_arg
264
- if option_flag.name == "from_instance":
655
+ elif option_flag.name == "from_organization_api_key":
656
+ from_organization_api_key = option_arg
657
+ elif option_flag.name == "from_instance":
265
658
  from_instance = option_arg
266
- if option_flag.name == "to_api_key":
659
+ elif option_flag.name == "from_project_id":
660
+ from_project_id = option_arg
661
+ elif option_flag.name == "from_organization_id":
662
+ from_organization_id = option_arg
663
+ elif option_flag.name == "to_api_key":
267
664
  to_api_key = option_arg
268
- if option_flag.name == "to_project_id":
269
- to_project_id = option_arg
270
- if option_flag.name == "to_instance":
665
+ elif option_flag.name == "to_organization_api_key":
666
+ to_organization_api_key = option_arg
667
+ elif option_flag.name == "to_instance":
271
668
  to_instance = option_arg
272
- if option_flag.name == "tool_id":
273
- tool_id = option_arg
274
-
275
- if not (from_project_id and from_instance and tool_id):
276
- raise MissingRequirementException("Cannot migrate resources without indicating source: project, instance, and tool ID")
277
-
278
- migration_strategy = ToolMigrationStrategy(
279
- from_api_key=from_api_key,
280
- from_project_id=from_project_id,
281
- from_instance=from_instance,
282
- to_api_key=to_api_key,
283
- to_project_id=to_project_id,
284
- to_instance=to_instance,
285
- tool_id=tool_id
286
- )
287
- tool = MigrationTool(migration_strategy)
288
- tool.run_migration()
669
+ elif option_flag.name == "to_project_id":
670
+ to_project_id = option_arg
671
+ elif option_flag.name == "to_organization_id":
672
+ to_organization_id = option_arg
673
+ elif option_flag.name == "to_project_name":
674
+ to_project_name = option_arg
675
+ elif option_flag.name == "admin_email":
676
+ admin_email = option_arg
677
+ elif option_flag.name == "all":
678
+ migrate_all = True
679
+ elif option_flag.name == "agents":
680
+ migrate_agents = True
681
+ agent_ids = option_arg if option_arg else "all"
682
+ elif option_flag.name == "tools":
683
+ migrate_tools = True
684
+ tool_ids = option_arg if option_arg else "all"
685
+ elif option_flag.name == "agentic_processes":
686
+ migrate_processes = True
687
+ process_ids = option_arg if option_arg else "all"
688
+ elif option_flag.name == "tasks":
689
+ migrate_tasks = True
690
+ task_ids = option_arg if option_arg else "all"
691
+ elif option_flag.name == "usage_limits":
692
+ migrate_usage_limits = True
693
+ elif option_flag.name == "rag_assistants":
694
+ migrate_rag_assistants = True
695
+ assistant_names = option_arg if option_arg else "all"
696
+ elif option_flag.name == "files":
697
+ migrate_files = True
698
+ file_ids = option_arg if option_arg else "all"
699
+ elif option_flag.name == "secrets":
700
+ migrate_secrets = True
701
+ secret_ids = option_arg if option_arg else "all"
702
+ elif option_flag.name == "stop_on_error":
703
+ from pygeai.cli.commands.common import get_boolean_value
704
+ stop_on_error = get_boolean_value(option_arg)
705
+
706
+ if not all([from_api_key, from_instance, from_project_id]):
707
+ raise MissingRequirementException("Source API key, instance, and project ID are required")
708
+
709
+ if (to_project_name or admin_email) and not (to_project_name and admin_email):
710
+ raise MissingRequirementException(
711
+ "Both --to-project-name and --admin-email are required when creating a new project"
712
+ )
713
+
714
+ if to_project_id and (to_project_name or admin_email):
715
+ raise MissingRequirementException(
716
+ "Cannot specify both --to-project-id and project creation parameters (--to-project-name, --admin-email)"
717
+ )
718
+
719
+ if not to_project_id and not (to_project_name and admin_email):
720
+ raise MissingRequirementException(
721
+ "Must specify either --to-project-id (for existing project) or both --to-project-name and --admin-email (to create new project)"
722
+ )
723
+
724
+ if to_project_id and not to_api_key:
725
+ raise MissingRequirementException(
726
+ "Destination project API key (--to-api-key) is required when migrating to an existing project (--to-project-id)"
727
+ )
728
+
729
+ if to_project_name and admin_email:
730
+ if not from_organization_api_key:
731
+ raise MissingRequirementException(
732
+ "Source organization scope API key (--from-org-key) is required for project creation"
733
+ )
734
+ if not (to_organization_api_key or from_organization_api_key):
735
+ raise MissingRequirementException(
736
+ "Destination organization scope API key (--to-org-key) is required for project creation in a different "
737
+ "instance. Alternatively source organization scope (--from-org-key) can be used if project needs to be "
738
+ "created in the same instance."
739
+ )
740
+
741
+ # Validate organization scope keys for usage limits migration
742
+ if migrate_usage_limits:
743
+ if not from_organization_api_key:
744
+ raise MissingRequirementException(
745
+ "Source organization scope API key (--from-org-key) is required for usage limits migration"
746
+ )
747
+ if not (to_organization_api_key or from_organization_api_key):
748
+ raise MissingRequirementException(
749
+ "Destination organization scope API key (--to-org-key) is required for usage limits migration in a "
750
+ "different instance. Alternatively source organization scope (--from-org-key) can be used if limits "
751
+ "need to be migrated in the same instance."
752
+ )
753
+
754
+ if to_project_name and admin_email:
755
+ Console.write_stdout(f"Creating new project '{to_project_name}'...")
756
+
757
+ org_key_to_use = to_organization_api_key or from_organization_api_key
758
+ logger.debug(f"DEBUG: Preparing to create project with organization API key")
759
+ logger.debug(f" - to_organization_api_key exists: {to_organization_api_key is not None}")
760
+ logger.debug(f" - from_organization_api_key exists: {from_organization_api_key is not None}")
761
+ logger.debug(f" - Using key (first 20 chars): {org_key_to_use[:20] if org_key_to_use else 'None'}...")
762
+ Console.write_stderr(f"DEBUG: Using org key for project creation (first 20 chars): {org_key_to_use[:20] if org_key_to_use else 'None'}...")
763
+
764
+ project_strategy = ProjectMigrationStrategy(
765
+ from_api_key=from_organization_api_key,
766
+ from_instance=from_instance,
767
+ from_project_id=from_project_id,
768
+ to_project_name=to_project_name,
769
+ admin_email=admin_email,
770
+ to_api_key=org_key_to_use,
771
+ to_instance=to_instance
772
+ )
773
+ project_tool = MigrationTool(project_strategy)
774
+ to_project_id = project_tool.run_migration()
775
+
776
+ if not to_project_id:
777
+ raise ValueError("Project creation did not return a project ID")
778
+
779
+ Console.write_stdout(f"Project '{to_project_name}' created successfully with ID: {to_project_id}")
780
+
781
+ from pygeai.auth.clients import AuthClient
782
+ Console.write_stdout(f"Creating project API key for new project...")
783
+
784
+ org_key_for_token_creation = to_organization_api_key or from_organization_api_key
785
+ auth_client = AuthClient(
786
+ api_key=org_key_for_token_creation,
787
+ base_url=to_instance or from_instance
788
+ )
789
+
790
+ token_response = auth_client.create_project_api_token(
791
+ project_id=to_project_id,
792
+ name=f"Migration API Key for {to_project_name}",
793
+ description=f"Auto-generated API key for project migration to {to_project_name}"
794
+ )
795
+
796
+ if not token_response or 'id' not in token_response:
797
+ raise ValueError("Failed to create project API key")
798
+
799
+ to_api_key = token_response['id']
800
+ Console.write_stdout(f"Project API key created successfully")
801
+
802
+ if not to_organization_id:
803
+ Console.write_stdout(f"Retrieving destination organization ID...")
804
+ dest_admin_client = AdminClient(api_key=to_api_key, base_url=to_instance or from_instance)
805
+ dest_token_info = dest_admin_client.validate_api_token()
806
+ to_organization_id = dest_token_info.get("organizationId")
807
+ Console.write_stdout(f"Destination organization ID: {to_organization_id}")
808
+
809
+ if migrate_all:
810
+ migrate_agents = True
811
+ migrate_tools = True
812
+ migrate_processes = True
813
+ migrate_tasks = True
814
+ migrate_usage_limits = True if from_organization_id and to_organization_id else False
815
+ migrate_rag_assistants = True
816
+ migrate_files = True if from_organization_id and to_organization_id else False
817
+ migrate_secrets = True
818
+ agent_ids = "all"
819
+ tool_ids = "all"
820
+ process_ids = "all"
821
+ task_ids = "all"
822
+ assistant_names = "all"
823
+ file_ids = "all"
824
+ secret_ids = "all"
825
+
826
+ strategies = []
827
+
828
+ lab_manager = AILabManager(api_key=from_api_key, base_url=from_instance)
829
+
830
+ if migrate_agents:
831
+ if agent_ids == "all":
832
+ agent_list = lab_manager.get_agent_list(FilterSettings(count=1000))
833
+ discovered_agents = [agent.id for agent in agent_list.agents if agent.id]
834
+ Console.write_stdout(f"Discovered {len(discovered_agents)} agents")
835
+ for agent_id in discovered_agents:
836
+ strategies.append(AgentMigrationStrategy(
837
+ from_api_key=from_api_key,
838
+ from_instance=from_instance,
839
+ agent_id=agent_id,
840
+ to_api_key=to_api_key,
841
+ to_instance=to_instance
842
+ ))
843
+ elif agent_ids:
844
+ for agent_id in agent_ids.split(','):
845
+ strategies.append(AgentMigrationStrategy(
846
+ from_api_key=from_api_key,
847
+ from_instance=from_instance,
848
+ agent_id=agent_id.strip(),
849
+ to_api_key=to_api_key,
850
+ to_instance=to_instance
851
+ ))
852
+
853
+ if migrate_tools:
854
+ if tool_ids == "all":
855
+ tool_list = lab_manager.list_tools(FilterSettings(count=1000))
856
+ discovered_tools = [tool.id for tool in tool_list.tools if tool.id]
857
+ Console.write_stdout(f"Discovered {len(discovered_tools)} tools")
858
+ for tool_id in discovered_tools:
859
+ strategies.append(ToolMigrationStrategy(
860
+ from_api_key=from_api_key,
861
+ from_instance=from_instance,
862
+ tool_id=tool_id,
863
+ to_api_key=to_api_key,
864
+ to_instance=to_instance
865
+ ))
866
+ elif tool_ids:
867
+ for tool_id in tool_ids.split(','):
868
+ strategies.append(ToolMigrationStrategy(
869
+ from_api_key=from_api_key,
870
+ from_instance=from_instance,
871
+ tool_id=tool_id.strip(),
872
+ to_api_key=to_api_key,
873
+ to_instance=to_instance
874
+ ))
875
+
876
+ if migrate_processes:
877
+ if process_ids == "all":
878
+ process_list = lab_manager.list_processes(FilterSettings(count=1000))
879
+ discovered_processes = [proc.id for proc in process_list.processes if proc.id]
880
+ Console.write_stdout(f"Discovered {len(discovered_processes)} agentic processes")
881
+ for process_id in discovered_processes:
882
+ strategies.append(AgenticProcessMigrationStrategy(
883
+ from_api_key=from_api_key,
884
+ from_instance=from_instance,
885
+ process_id=process_id,
886
+ to_api_key=to_api_key,
887
+ to_instance=to_instance
888
+ ))
889
+ elif process_ids:
890
+ for process_id in process_ids.split(','):
891
+ strategies.append(AgenticProcessMigrationStrategy(
892
+ from_api_key=from_api_key,
893
+ from_instance=from_instance,
894
+ process_id=process_id.strip(),
895
+ to_api_key=to_api_key,
896
+ to_instance=to_instance
897
+ ))
898
+
899
+ if migrate_tasks:
900
+ if task_ids == "all":
901
+ task_list = lab_manager.list_tasks(FilterSettings(count=1000))
902
+ discovered_tasks = [task.id for task in task_list.tasks if task.id]
903
+ Console.write_stdout(f"Discovered {len(discovered_tasks)} tasks")
904
+ for task_id in discovered_tasks:
905
+ strategies.append(TaskMigrationStrategy(
906
+ from_api_key=from_api_key,
907
+ from_instance=from_instance,
908
+ task_id=task_id,
909
+ to_api_key=to_api_key,
910
+ to_instance=to_instance
911
+ ))
912
+ elif task_ids:
913
+ for task_id in task_ids.split(','):
914
+ strategies.append(TaskMigrationStrategy(
915
+ from_api_key=from_api_key,
916
+ from_instance=from_instance,
917
+ task_id=task_id.strip(),
918
+ to_api_key=to_api_key,
919
+ to_instance=to_instance
920
+ ))
921
+
922
+ if migrate_usage_limits and from_organization_id and to_organization_id:
923
+ strategies.append(UsageLimitMigrationStrategy(
924
+ from_api_key=from_organization_api_key,
925
+ from_instance=from_instance,
926
+ from_organization_id=from_organization_id,
927
+ to_organization_id=to_organization_id,
928
+ to_api_key=to_organization_api_key or from_organization_api_key,
929
+ to_instance=to_instance
930
+ ))
931
+
932
+ if migrate_rag_assistants:
933
+ rag_client = RAGAssistantClient(api_key=from_api_key, base_url=from_instance)
934
+ if assistant_names == "all":
935
+ assistant_data = rag_client.get_assistants_from_project()
936
+ assistants_raw = assistant_data.get("assistants", [])
937
+ assistant_list = [RAGAssistantMapper.map_to_rag_assistant(a) for a in assistants_raw] if assistants_raw else []
938
+ discovered_assistants = [assistant.name for assistant in assistant_list if assistant.name]
939
+ Console.write_stdout(f"Discovered {len(discovered_assistants)} RAG assistants")
940
+ for assistant_name in discovered_assistants:
941
+ strategies.append(RAGAssistantMigrationStrategy(
942
+ from_api_key=from_api_key,
943
+ from_instance=from_instance,
944
+ assistant_name=assistant_name,
945
+ to_api_key=to_api_key,
946
+ to_instance=to_instance
947
+ ))
948
+ elif assistant_names:
949
+ for assistant_name in assistant_names.split(','):
950
+ strategies.append(RAGAssistantMigrationStrategy(
951
+ from_api_key=from_api_key,
952
+ from_instance=from_instance,
953
+ assistant_name=assistant_name.strip(),
954
+ to_api_key=to_api_key,
955
+ to_instance=to_instance
956
+ ))
957
+
958
+ if migrate_files and from_organization_id and from_project_id and to_organization_id and to_project_id:
959
+ file_manager = FileManager(
960
+ api_key=from_api_key,
961
+ base_url=from_instance,
962
+ organization_id=from_organization_id,
963
+ project_id=from_project_id
964
+ )
965
+ if file_ids == "all":
966
+ file_list_response = file_manager.get_file_list()
967
+ discovered_files = [f.id for f in file_list_response.files if f.id]
968
+ Console.write_stdout(f"Discovered {len(discovered_files)} files")
969
+ for file_id in discovered_files:
970
+ strategies.append(FileMigrationStrategy(
971
+ from_api_key=from_api_key,
972
+ from_instance=from_instance,
973
+ from_organization_id=from_organization_id,
974
+ from_project_id=from_project_id,
975
+ to_organization_id=to_organization_id,
976
+ to_project_id=to_project_id,
977
+ file_id=file_id,
978
+ to_api_key=to_api_key,
979
+ to_instance=to_instance
980
+ ))
981
+ elif file_ids:
982
+ for file_id in file_ids.split(','):
983
+ strategies.append(FileMigrationStrategy(
984
+ from_api_key=from_api_key,
985
+ from_instance=from_instance,
986
+ from_organization_id=from_organization_id,
987
+ from_project_id=from_project_id,
988
+ to_organization_id=to_organization_id,
989
+ to_project_id=to_project_id,
990
+ file_id=file_id.strip(),
991
+ to_api_key=to_api_key,
992
+ to_instance=to_instance
993
+ ))
994
+
995
+ if migrate_secrets:
996
+ secret_client = SecretClient(api_key=from_api_key, base_url=from_instance)
997
+ if secret_ids == "all":
998
+ secrets_data = secret_client.list_secrets(count=1000)
999
+ secrets_list = secrets_data.get("secrets", []) if isinstance(secrets_data, dict) else []
1000
+ discovered_secrets = [s.get('id') for s in secrets_list if s.get('id')]
1001
+ Console.write_stdout(f"Discovered {len(discovered_secrets)} secrets")
1002
+ for secret_id in discovered_secrets:
1003
+ strategies.append(SecretMigrationStrategy(
1004
+ from_api_key=from_api_key,
1005
+ from_instance=from_instance,
1006
+ secret_id=secret_id,
1007
+ to_api_key=to_api_key,
1008
+ to_instance=to_instance
1009
+ ))
1010
+ elif secret_ids:
1011
+ for secret_id in secret_ids.split(','):
1012
+ strategies.append(SecretMigrationStrategy(
1013
+ from_api_key=from_api_key,
1014
+ from_instance=from_instance,
1015
+ secret_id=secret_id.strip(),
1016
+ to_api_key=to_api_key,
1017
+ to_instance=to_instance
1018
+ ))
289
1019
 
1020
+ if not strategies:
1021
+ Console.write_stdout("No migration strategies configured. Use flags like --agents, --tools, --all, etc.")
1022
+ return
290
1023
 
291
- clone_tool_options = [
1024
+ plan = MigrationPlan(strategies=strategies, stop_on_error=stop_on_error)
1025
+ orchestrator = MigrationOrchestrator(plan)
1026
+
1027
+ try:
1028
+ result = orchestrator.execute()
1029
+ Console.write_stdout(f"Migration completed: {result['completed']}/{result['total']} successful")
1030
+ logger.info(f"Project cloning completed: {result}")
1031
+ except Exception as e:
1032
+ Console.write_stderr(f"Migration failed: {e}")
1033
+ logger.error(f"Project cloning failed: {e}")
1034
+ raise
1035
+
1036
+
1037
+ clone_project_options = [
1038
+ Option(
1039
+ "interactive",
1040
+ ["-i", "--interactive"],
1041
+ "Interactive mode: guided step-by-step migration wizard",
1042
+ False
1043
+ ),
292
1044
  Option(
293
1045
  "from_api_key",
294
- ["--from-api-key", "--fak"],
295
- "API key for the source instance",
1046
+ ["--from-api-key", "--from-key"],
1047
+ "Source instance project scope API key (required)",
296
1048
  True
297
1049
  ),
298
1050
  Option(
299
- "from_project_id",
300
- ["--from-project-id", "--fpid"],
301
- "ID of the source project to migrate from",
1051
+ "from_organization_api_key",
1052
+ ["--from-org-key", "--from-organization-key"],
1053
+ "Source instance organization scope API key (optional, for project creation)",
302
1054
  True
303
1055
  ),
304
1056
  Option(
305
1057
  "from_instance",
306
- ["--from-instance", "--fi"],
307
- "URL from the source instance to migrate from",
1058
+ ["--from-instance", "--from-url"],
1059
+ "Source instance URL (required)",
308
1060
  True
309
1061
  ),
310
1062
  Option(
311
- "to_api_key",
312
- ["--to-api-key", "--tak"],
313
- "API key for the destination instance. If not specified, the same instance's API key will be used",
1063
+ "from_project_id",
1064
+ ["--from-project-id", "--from-pid"],
1065
+ "Source project ID (required)",
314
1066
  True
315
1067
  ),
316
1068
  Option(
317
- "to_project_id",
318
- ["--to-project-id", "--tpid"],
319
- "ID of the destination project to migrate to",
1069
+ "from_organization_id",
1070
+ ["--from-organization-id", "--from-oid"],
1071
+ "Source organization ID (required for usage limits and files)",
320
1072
  True
321
1073
  ),
322
1074
  Option(
323
- "to_instance",
324
- ["--to-instance", "--ti"],
325
- "URL from the destination instance to migrate to. If not specified, the same instance's URL will be used",
1075
+ "to_api_key",
1076
+ ["--to-api-key", "--to-key"],
1077
+ "Destination instance project scope API key (optional, defaults to source key)",
326
1078
  True
327
1079
  ),
328
1080
  Option(
329
- "tool_id",
330
- ["--tool-id", "--tid"],
331
- "Unique identifier from the tool to be migrated",
1081
+ "to_organization_api_key",
1082
+ ["--to-org-key", "--to-organization-key"],
1083
+ "Destination instance organization scope API key (optional, for project creation)",
332
1084
  True
333
1085
  ),
334
- ]
335
-
336
-
337
- def clone_process(option_list: list):
338
- from_api_key = None
339
- from_project_id = None
340
- from_instance = None
341
- to_api_key = None
342
- to_project_id = None
343
- to_instance = None
344
- process_id = None
345
-
346
- for option_flag, option_arg in option_list:
347
- if option_flag.name == "from_api_key":
348
- from_api_key = option_arg
349
- if option_flag.name == "from_project_id":
350
- from_project_id = option_arg
351
- if option_flag.name == "from_instance":
352
- from_instance = option_arg
353
- if option_flag.name == "to_api_key":
354
- to_api_key = option_arg
355
- if option_flag.name == "to_project_id":
356
- to_project_id = option_arg
357
- if option_flag.name == "to_instance":
358
- to_instance = option_arg
359
- if option_flag.name == "process_id":
360
- process_id = option_arg
361
-
362
- if not (from_project_id and from_instance and process_id):
363
- raise MissingRequirementException("Cannot migrate resources without indicating source: project, instance, and process ID")
364
-
365
- migration_strategy = AgenticProcessMigrationStrategy(
366
- from_api_key=from_api_key,
367
- from_project_id=from_project_id,
368
- from_instance=from_instance,
369
- to_api_key=to_api_key,
370
- to_project_id=to_project_id,
371
- to_instance=to_instance,
372
- process_id=process_id
373
- )
374
- tool = MigrationTool(migration_strategy)
375
- tool.run_migration()
376
-
377
-
378
- clone_process_options = [
379
1086
  Option(
380
- "from_api_key",
381
- ["--from-api-key", "--fak"],
382
- "API key for the source instance",
1087
+ "to_instance",
1088
+ ["--to-instance", "--to-url"],
1089
+ "Destination instance URL (optional, defaults to source URL)",
383
1090
  True
384
1091
  ),
385
1092
  Option(
386
- "from_project_id",
387
- ["--from-project-id", "--fpid"],
388
- "ID of the source project to migrate from",
1093
+ "to_project_name",
1094
+ ["--to-project-name", "--to-name"],
1095
+ "Name for the new destination project (creates new project if specified with --admin-email)",
389
1096
  True
390
1097
  ),
391
1098
  Option(
392
- "from_instance",
393
- ["--from-instance", "--fi"],
394
- "URL from the source instance to migrate from",
1099
+ "admin_email",
1100
+ ["--admin-email"],
1101
+ "Admin email for new project (required when creating new project)",
395
1102
  True
396
1103
  ),
397
1104
  Option(
398
- "to_api_key",
399
- ["--to-api-key", "--tak"],
400
- "API key for the destination instance. If not specified, the same instance's API key will be used",
1105
+ "to_project_id",
1106
+ ["--to-project-id", "--to-pid"],
1107
+ "Destination project ID (optional for files)",
401
1108
  True
402
1109
  ),
403
1110
  Option(
404
- "to_project_id",
405
- ["--to-project-id", "--tpid"],
406
- "ID of the destination project to migrate to",
1111
+ "to_organization_id",
1112
+ ["--to-organization-id", "--to-oid"],
1113
+ "Destination organization ID (optional for usage limits and files)",
407
1114
  True
408
1115
  ),
409
1116
  Option(
410
- "to_instance",
411
- ["--to-instance", "--ti"],
412
- "URL from the destination instance to migrate to. If not specified, the same instance's URL will be used",
413
- True
1117
+ "all",
1118
+ ["--all"],
1119
+ "Migrate all available components",
1120
+ False
414
1121
  ),
415
1122
  Option(
416
- "process_id",
417
- ["--process-id", "--pid"],
418
- "Unique identifier from the process to be migrated",
1123
+ "agents",
1124
+ ["--agents"],
1125
+ "Agent IDs to migrate: comma-separated IDs or 'all'",
419
1126
  True
420
1127
  ),
421
- ]
422
-
423
-
424
- def clone_task(option_list: list):
425
- from_api_key = None
426
- from_project_id = None
427
- from_instance = None
428
- to_api_key = None
429
- to_project_id = None
430
- to_instance = None
431
- task_id = None
432
-
433
- for option_flag, option_arg in option_list:
434
- if option_flag.name == "from_api_key":
435
- from_api_key = option_arg
436
- if option_flag.name == "from_project_id":
437
- from_project_id = option_arg
438
- if option_flag.name == "from_instance":
439
- from_instance = option_arg
440
- if option_flag.name == "to_api_key":
441
- to_api_key = option_arg
442
- if option_flag.name == "to_project_id":
443
- to_project_id = option_arg
444
- if option_flag.name == "to_instance":
445
- to_instance = option_arg
446
- if option_flag.name == "task_id":
447
- task_id = option_arg
448
-
449
- if not (from_project_id and from_instance and task_id):
450
- raise MissingRequirementException("Cannot migrate resources without indicating source: project, instance, and task ID")
451
-
452
- migration_strategy = TaskMigrationStrategy(
453
- from_api_key=from_api_key,
454
- from_project_id=from_project_id,
455
- from_instance=from_instance,
456
- to_api_key=to_api_key,
457
- to_project_id=to_project_id,
458
- to_instance=to_instance,
459
- task_id=task_id
460
- )
461
- tool = MigrationTool(migration_strategy)
462
- response = tool.run_migration()
463
-
464
- sys.stdout.write(f"Migration result: \n{response}\n")
465
-
466
-
467
- clone_task_options = [
468
1128
  Option(
469
- "from_api_key",
470
- ["--from-api-key", "--fak"],
471
- "API key for the source instance",
1129
+ "tools",
1130
+ ["--tools"],
1131
+ "Tool IDs to migrate: comma-separated IDs or 'all'",
472
1132
  True
473
1133
  ),
474
1134
  Option(
475
- "from_project_id",
476
- ["--from-project-id", "--fpid"],
477
- "ID of the source project to migrate from",
1135
+ "agentic_processes",
1136
+ ["--agentic-processes", "--processes"],
1137
+ "Agentic process IDs to migrate: comma-separated IDs or 'all'",
478
1138
  True
479
1139
  ),
480
1140
  Option(
481
- "from_instance",
482
- ["--from-instance", "--fi"],
483
- "URL from the source instance to migrate from",
1141
+ "tasks",
1142
+ ["--tasks"],
1143
+ "Task IDs to migrate: comma-separated IDs or 'all'",
484
1144
  True
485
1145
  ),
486
1146
  Option(
487
- "to_api_key",
488
- ["--to-api-key", "--tak"],
489
- "API key for the destination instance. If not specified, the same instance's API key will be used",
1147
+ "usage_limits",
1148
+ ["--usage-limits"],
1149
+ "Migrate usage limits (requires organization IDs)",
1150
+ False
1151
+ ),
1152
+ Option(
1153
+ "rag_assistants",
1154
+ ["--rag-assistants"],
1155
+ "RAG assistant names to migrate: comma-separated names or 'all'",
490
1156
  True
491
1157
  ),
492
1158
  Option(
493
- "to_project_id",
494
- ["--to-project-id", "--tpid"],
495
- "ID of the destination project to migrate to",
1159
+ "files",
1160
+ ["--files"],
1161
+ "File IDs to migrate: comma-separated IDs or 'all' (requires org/project IDs)",
496
1162
  True
497
1163
  ),
498
1164
  Option(
499
- "to_instance",
500
- ["--to-instance", "--ti"],
501
- "URL from the destination instance to migrate to. If not specified, the same instance's URL will be used",
1165
+ "secrets",
1166
+ ["--secrets"],
1167
+ "Secret IDs to migrate: comma-separated IDs or 'all'",
502
1168
  True
503
1169
  ),
504
1170
  Option(
505
- "task_id",
506
- ["--task-id", "--tid"],
507
- "Unique identifier from the task to be migrated",
1171
+ "stop_on_error",
1172
+ ["--stop-on-error", "--soe"],
1173
+ "Stop migration on first error: 0: False, 1: True (default: 1)",
508
1174
  True
509
1175
  ),
510
1176
  ]
511
1177
 
512
1178
 
513
-
514
1179
  migrate_commands = [
515
1180
  Command(
516
1181
  "help",
@@ -524,47 +1189,10 @@ migrate_commands = [
524
1189
  Command(
525
1190
  "clone_project",
526
1191
  ["clone-project"],
527
- "Clone project from instance",
1192
+ "Clone project components between instances",
528
1193
  clone_project,
529
1194
  ArgumentsEnum.REQUIRED,
530
1195
  [],
531
1196
  clone_project_options
532
1197
  ),
533
- Command(
534
- "clone_agent",
535
- ["clone-agent"],
536
- "Clone agentt from instance",
537
- clone_agent,
538
- ArgumentsEnum.REQUIRED,
539
- [],
540
- clone_agent_options
541
- ),
542
- Command(
543
- "clone_tool",
544
- ["clone-tool"],
545
- "Clone tool from instance",
546
- clone_tool,
547
- ArgumentsEnum.REQUIRED,
548
- [],
549
- clone_tool_options
550
- ),
551
- Command(
552
- "clone_process",
553
- ["clone-process"],
554
- "Clone process from instance",
555
- clone_process,
556
- ArgumentsEnum.REQUIRED,
557
- [],
558
- clone_process_options
559
- ),
560
- # TODO -> Remove clone-task: clone-process includes cloning tasks
561
- # Command(
562
- # "clone_task",
563
- # ["clone-task"],
564
- # "Clone task from instance",
565
- # clone_task,
566
- # ArgumentsEnum.REQUIRED,
567
- # [],
568
- # clone_task_options
569
- #),
570
1198
  ]