amd-gaia 0.15.1__tar.gz → 0.15.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. {amd_gaia-0.15.1/src/amd_gaia.egg-info → amd_gaia-0.15.2}/PKG-INFO +1 -2
  2. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/README.md +0 -1
  3. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/setup.py +1 -0
  4. {amd_gaia-0.15.1 → amd_gaia-0.15.2/src/amd_gaia.egg-info}/PKG-INFO +1 -2
  5. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/amd_gaia.egg-info/SOURCES.txt +6 -0
  6. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/base/agent.py +45 -90
  7. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/base/api_agent.py +0 -1
  8. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/base/console.py +126 -0
  9. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/base/tools.py +7 -2
  10. amd_gaia-0.15.2/src/gaia/agents/blender/__init__.py +7 -0
  11. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/blender/agent.py +7 -10
  12. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/blender/core/view.py +2 -2
  13. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/chat/agent.py +22 -48
  14. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/chat/app.py +7 -0
  15. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/chat/tools/rag_tools.py +23 -8
  16. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/chat/tools/shell_tools.py +1 -0
  17. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/prompts/code_patterns.py +2 -4
  18. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/docker/agent.py +1 -0
  19. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/emr/agent.py +3 -5
  20. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/emr/cli.py +1 -1
  21. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/emr/dashboard/server.py +2 -4
  22. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/apps/llm/app.py +14 -3
  23. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/chat/app.py +2 -4
  24. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/cli.py +511 -333
  25. amd_gaia-0.15.2/src/gaia/installer/__init__.py +23 -0
  26. amd_gaia-0.15.2/src/gaia/installer/init_command.py +1275 -0
  27. amd_gaia-0.15.2/src/gaia/installer/lemonade_installer.py +619 -0
  28. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/llm/__init__.py +2 -1
  29. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/llm/lemonade_client.py +284 -99
  30. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/llm/providers/lemonade.py +12 -14
  31. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/rag/sdk.py +1 -1
  32. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/security.py +24 -4
  33. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/talk/app.py +2 -4
  34. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/version.py +2 -2
  35. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_chat_agent.py +350 -68
  36. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_checklist_orchestration.py +2 -4
  37. amd_gaia-0.15.2/tests/test_hardware_advisor_agent.py +189 -0
  38. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_lemonade_client.py +180 -11
  39. amd_gaia-0.15.2/tests/test_lemonade_health.py +62 -0
  40. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_typescript_tools.py +2 -4
  41. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/LICENSE.md +0 -0
  42. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/MANIFEST.in +0 -0
  43. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/pyproject.toml +0 -0
  44. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/setup.cfg +0 -0
  45. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/amd_gaia.egg-info/dependency_links.txt +0 -0
  46. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/amd_gaia.egg-info/entry_points.txt +0 -0
  47. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/amd_gaia.egg-info/requires.txt +0 -0
  48. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/amd_gaia.egg-info/top_level.txt +0 -0
  49. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/__init__.py +0 -0
  50. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/__init__.py +0 -0
  51. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/base/__init__.py +0 -0
  52. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/base/errors.py +0 -0
  53. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/base/mcp_agent.py +0 -0
  54. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/blender/agent_simple.py +0 -0
  55. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/blender/app.py +0 -0
  56. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/blender/app_simple.py +0 -0
  57. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/blender/core/__init__.py +0 -0
  58. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/blender/core/materials.py +0 -0
  59. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/blender/core/objects.py +0 -0
  60. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/blender/core/rendering.py +0 -0
  61. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/blender/core/scene.py +0 -0
  62. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/chat/__init__.py +0 -0
  63. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/chat/session.py +0 -0
  64. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/chat/tools/__init__.py +0 -0
  65. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/chat/tools/file_tools.py +0 -0
  66. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/__init__.py +0 -0
  67. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/agent.py +0 -0
  68. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/cli.py +0 -0
  69. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/models.py +0 -0
  70. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/__init__.py +0 -0
  71. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/checklist_executor.py +0 -0
  72. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/checklist_generator.py +0 -0
  73. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/factories/__init__.py +0 -0
  74. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/factories/base.py +0 -0
  75. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/factories/nextjs_factory.py +0 -0
  76. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/factories/python_factory.py +0 -0
  77. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/orchestrator.py +0 -0
  78. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/project_analyzer.py +0 -0
  79. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/steps/__init__.py +0 -0
  80. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/steps/base.py +0 -0
  81. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/steps/error_handler.py +0 -0
  82. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/steps/nextjs.py +0 -0
  83. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/steps/python.py +0 -0
  84. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/template_catalog.py +0 -0
  85. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/workflows/__init__.py +0 -0
  86. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/workflows/base.py +0 -0
  87. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/workflows/nextjs.py +0 -0
  88. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/workflows/python.py +0 -0
  89. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/prompts/__init__.py +0 -0
  90. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/prompts/base_prompt.py +0 -0
  91. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/prompts/nextjs_prompt.py +0 -0
  92. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/prompts/python_prompt.py +0 -0
  93. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/schema_inference.py +0 -0
  94. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/system_prompt.py +0 -0
  95. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/__init__.py +0 -0
  96. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/cli_tools.py +0 -0
  97. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/code_formatting.py +0 -0
  98. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/code_tools.py +0 -0
  99. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/error_fixing.py +0 -0
  100. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/external_tools.py +0 -0
  101. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/file_io.py +0 -0
  102. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/prisma_tools.py +0 -0
  103. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/project_management.py +0 -0
  104. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/testing.py +0 -0
  105. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/typescript_tools.py +0 -0
  106. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/validation_parsing.py +0 -0
  107. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/validation_tools.py +0 -0
  108. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/web_dev_tools.py +0 -0
  109. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/validators/__init__.py +0 -0
  110. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/validators/antipattern_checker.py +0 -0
  111. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/validators/ast_analyzer.py +0 -0
  112. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/validators/requirements_validator.py +0 -0
  113. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/validators/syntax_validator.py +0 -0
  114. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/docker/__init__.py +0 -0
  115. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/emr/__init__.py +0 -0
  116. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/emr/constants.py +0 -0
  117. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/emr/dashboard/__init__.py +0 -0
  118. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/jira/__init__.py +0 -0
  119. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/jira/agent.py +0 -0
  120. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/jira/jql_templates.py +0 -0
  121. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/routing/__init__.py +0 -0
  122. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/routing/agent.py +0 -0
  123. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/routing/system_prompt.py +0 -0
  124. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/summarize/__init__.py +0 -0
  125. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/summarize/agent.py +0 -0
  126. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/summarize/prompts.py +0 -0
  127. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/api/__init__.py +0 -0
  128. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/api/agent_registry.py +0 -0
  129. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/api/app.py +0 -0
  130. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/api/openai_server.py +0 -0
  131. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/api/schemas.py +0 -0
  132. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/api/sse_handler.py +0 -0
  133. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/apps/__init__.py +0 -0
  134. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/apps/llm/__init__.py +0 -0
  135. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/apps/summarize/app.py +0 -0
  136. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/apps/summarize/html_viewer.py +0 -0
  137. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/apps/summarize/pdf_formatter.py +0 -0
  138. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/audio/__init__.py +0 -0
  139. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/audio/audio_client.py +0 -0
  140. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/audio/audio_recorder.py +0 -0
  141. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/audio/kokoro_tts.py +0 -0
  142. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/audio/whisper_asr.py +0 -0
  143. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/chat/__init__.py +0 -0
  144. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/chat/prompts.py +0 -0
  145. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/chat/sdk.py +0 -0
  146. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/database/__init__.py +0 -0
  147. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/database/agent.py +0 -0
  148. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/database/mixin.py +0 -0
  149. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/database/testing.py +0 -0
  150. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/batch_experiment.py +0 -0
  151. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/claude.py +0 -0
  152. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/config.py +0 -0
  153. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/email_generator.py +0 -0
  154. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/eval.py +0 -0
  155. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/groundtruth.py +0 -0
  156. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/transcript_generator.py +0 -0
  157. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/webapp/README.md +0 -0
  158. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/webapp/package-lock.json +0 -0
  159. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/webapp/package.json +0 -0
  160. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/webapp/public/app.js +0 -0
  161. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/webapp/public/index.html +0 -0
  162. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/webapp/public/styles.css +0 -0
  163. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/webapp/server.js +0 -0
  164. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/webapp/test-setup.js +0 -0
  165. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/llm/base_client.py +0 -0
  166. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/llm/exceptions.py +0 -0
  167. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/llm/factory.py +0 -0
  168. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/llm/lemonade_manager.py +0 -0
  169. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/llm/providers/__init__.py +0 -0
  170. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/llm/providers/claude.py +0 -0
  171. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/llm/providers/openai_provider.py +0 -0
  172. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/llm/vlm_client.py +0 -0
  173. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/logger.py +0 -0
  174. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/mcp/agent_mcp_server.py +0 -0
  175. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/mcp/blender_mcp_client.py +0 -0
  176. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/mcp/blender_mcp_server.py +0 -0
  177. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/mcp/context7_cache.py +0 -0
  178. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/mcp/external_services.py +0 -0
  179. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/mcp/mcp_bridge.py +0 -0
  180. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/mcp/servers/__init__.py +0 -0
  181. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/mcp/servers/docker_mcp.py +0 -0
  182. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/perf_analysis.py +0 -0
  183. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/rag/__init__.py +0 -0
  184. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/rag/app.py +0 -0
  185. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/rag/demo.py +0 -0
  186. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/rag/pdf_utils.py +0 -0
  187. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/talk/sdk.py +0 -0
  188. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/testing/__init__.py +0 -0
  189. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/testing/assertions.py +0 -0
  190. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/testing/fixtures.py +0 -0
  191. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/testing/mocks.py +0 -0
  192. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/util.py +0 -0
  193. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/utils/__init__.py +0 -0
  194. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/utils/file_watcher.py +0 -0
  195. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/utils/parsing.py +0 -0
  196. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_api.py +0 -0
  197. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_chat_sdk.py +0 -0
  198. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_code_agent.py +0 -0
  199. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_code_agent_mixins.py +0 -0
  200. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_eval.py +0 -0
  201. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_external_tools.py +0 -0
  202. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_jira.py +0 -0
  203. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_lemonade_embeddings.py +0 -0
  204. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_rag.py +0 -0
  205. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_rag_integration.py +0 -0
  206. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_sdk.py +0 -0
  207. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_summarizer.py +0 -0
  208. {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_vlm_integration.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: amd-gaia
3
- Version: 0.15.1
3
+ Version: 0.15.2
4
4
  Summary: GAIA is a lightweight agent framework designed for the edge and AI PCs.
5
5
  Home-page: https://github.com/amd/gaia
6
6
  Author: AMD
@@ -100,7 +100,6 @@ Dynamic: summary
100
100
 
101
101
  # <img src="https://raw.githubusercontent.com/amd/gaia/main/src/gaia/img/gaia.ico" alt="GAIA Logo" width="64" height="64" style="vertical-align: middle;"> GAIA: AI Agent Framework for AMD Ryzen AI
102
102
 
103
- [![GAIA Build Installer](https://github.com/amd/gaia/actions/workflows/build_installer.yml/badge.svg)](https://github.com/amd/gaia/tree/main/tests "Check out our build")
104
103
  [![GAIA CLI Tests](https://github.com/amd/gaia/actions/workflows/test_gaia_cli.yml/badge.svg)](https://github.com/amd/gaia/tree/main/tests "Check out our cli tests")
105
104
  [![Latest Release](https://img.shields.io/github/v/release/amd/gaia?include_prereleases)](https://github.com/amd/gaia/releases/latest "Download the latest release")
106
105
  [![PyPI](https://img.shields.io/pypi/v/amd-gaia)](https://pypi.org/project/amd-gaia/)
@@ -1,6 +1,5 @@
1
1
  # <img src="https://raw.githubusercontent.com/amd/gaia/main/src/gaia/img/gaia.ico" alt="GAIA Logo" width="64" height="64" style="vertical-align: middle;"> GAIA: AI Agent Framework for AMD Ryzen AI
2
2
 
3
- [![GAIA Build Installer](https://github.com/amd/gaia/actions/workflows/build_installer.yml/badge.svg)](https://github.com/amd/gaia/tree/main/tests "Check out our build")
4
3
  [![GAIA CLI Tests](https://github.com/amd/gaia/actions/workflows/test_gaia_cli.yml/badge.svg)](https://github.com/amd/gaia/tree/main/tests "Check out our cli tests")
5
4
  [![Latest Release](https://img.shields.io/github/v/release/amd/gaia?include_prereleases)](https://github.com/amd/gaia/releases/latest "Download the latest release")
6
5
  [![PyPI](https://img.shields.io/pypi/v/amd-gaia)](https://pypi.org/project/amd-gaia/)
@@ -36,6 +36,7 @@ setup(
36
36
  "gaia.apps.llm",
37
37
  "gaia.apps.summarize",
38
38
  "gaia.eval",
39
+ "gaia.installer",
39
40
  "gaia.rag",
40
41
  "gaia.mcp",
41
42
  "gaia.mcp.servers",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: amd-gaia
3
- Version: 0.15.1
3
+ Version: 0.15.2
4
4
  Summary: GAIA is a lightweight agent framework designed for the edge and AI PCs.
5
5
  Home-page: https://github.com/amd/gaia
6
6
  Author: AMD
@@ -100,7 +100,6 @@ Dynamic: summary
100
100
 
101
101
  # <img src="https://raw.githubusercontent.com/amd/gaia/main/src/gaia/img/gaia.ico" alt="GAIA Logo" width="64" height="64" style="vertical-align: middle;"> GAIA: AI Agent Framework for AMD Ryzen AI
102
102
 
103
- [![GAIA Build Installer](https://github.com/amd/gaia/actions/workflows/build_installer.yml/badge.svg)](https://github.com/amd/gaia/tree/main/tests "Check out our build")
104
103
  [![GAIA CLI Tests](https://github.com/amd/gaia/actions/workflows/test_gaia_cli.yml/badge.svg)](https://github.com/amd/gaia/tree/main/tests "Check out our cli tests")
105
104
  [![Latest Release](https://img.shields.io/github/v/release/amd/gaia?include_prereleases)](https://github.com/amd/gaia/releases/latest "Download the latest release")
106
105
  [![PyPI](https://img.shields.io/pypi/v/amd-gaia)](https://pypi.org/project/amd-gaia/)
@@ -24,6 +24,7 @@ src/gaia/agents/base/console.py
24
24
  src/gaia/agents/base/errors.py
25
25
  src/gaia/agents/base/mcp_agent.py
26
26
  src/gaia/agents/base/tools.py
27
+ src/gaia/agents/blender/__init__.py
27
28
  src/gaia/agents/blender/agent.py
28
29
  src/gaia/agents/blender/agent_simple.py
29
30
  src/gaia/agents/blender/app.py
@@ -148,6 +149,9 @@ src/gaia/eval/webapp/test-setup.js
148
149
  src/gaia/eval/webapp/public/app.js
149
150
  src/gaia/eval/webapp/public/index.html
150
151
  src/gaia/eval/webapp/public/styles.css
152
+ src/gaia/installer/__init__.py
153
+ src/gaia/installer/init_command.py
154
+ src/gaia/installer/lemonade_installer.py
151
155
  src/gaia/llm/__init__.py
152
156
  src/gaia/llm/base_client.py
153
157
  src/gaia/llm/exceptions.py
@@ -189,9 +193,11 @@ tests/test_code_agent.py
189
193
  tests/test_code_agent_mixins.py
190
194
  tests/test_eval.py
191
195
  tests/test_external_tools.py
196
+ tests/test_hardware_advisor_agent.py
192
197
  tests/test_jira.py
193
198
  tests/test_lemonade_client.py
194
199
  tests/test_lemonade_embeddings.py
200
+ tests/test_lemonade_health.py
195
201
  tests/test_rag.py
196
202
  tests/test_rag_integration.py
197
203
  tests/test_sdk.py
@@ -61,10 +61,6 @@ class Agent(abc.ABC):
61
61
  STATE_ERROR_RECOVERY = "ERROR_RECOVERY"
62
62
  STATE_COMPLETION = "COMPLETION"
63
63
 
64
- # Define tools that can execute directly without requiring a plan
65
- # Subclasses can override this to specify domain-specific simple tools
66
- SIMPLE_TOOLS = []
67
-
68
64
  def __init__(
69
65
  self,
70
66
  use_claude: bool = False,
@@ -280,7 +276,7 @@ You must respond ONLY in valid JSON. No text before { or after }.
280
276
  self.console.print_header(f"🛠️ Registered Tools for {self.__class__.__name__}")
281
277
  self.console.print_separator()
282
278
 
283
- for name, tool_info in _TOOL_REGISTRY.items():
279
+ for name, tool_info in self.get_tools_info().items():
284
280
  # Format parameters
285
281
  params = []
286
282
  for param_name, param_info in tool_info["parameters"].items():
@@ -313,6 +309,14 @@ You must respond ONLY in valid JSON. No text before { or after }.
313
309
 
314
310
  return None
315
311
 
312
+ def get_tools_info(self) -> Dict[str, Any]:
313
+ """Get information about all registered tools."""
314
+ return _TOOL_REGISTRY
315
+
316
+ def get_tools(self) -> List[Dict[str, Any]]:
317
+ """Get a list of registered tools for the agent."""
318
+ return list(_TOOL_REGISTRY.values())
319
+
316
320
  def _extract_json_from_response(self, response: str) -> Optional[Dict[str, Any]]:
317
321
  """
318
322
  Apply multiple extraction strategies to find valid JSON in the response.
@@ -1177,43 +1181,6 @@ You must respond ONLY in valid JSON. No text before { or after }.
1177
1181
  steps_taken += 1
1178
1182
  logger.debug(f"Step {steps_taken}/{steps_limit}")
1179
1183
 
1180
- # Check if we're at the limit and ask user if they want to continue
1181
- if steps_taken == steps_limit and final_answer is None:
1182
- # Show what was accomplished
1183
- max_steps_msg = self._generate_max_steps_message(
1184
- conversation, steps_taken, steps_limit
1185
- )
1186
- self.console.print_warning(max_steps_msg)
1187
-
1188
- # Ask user if they want to continue (skip in silent mode OR if stdin is not available)
1189
- # IMPORTANT: Never call input() in API/CI contexts to avoid blocking threads
1190
- import sys
1191
-
1192
- has_stdin = sys.stdin and sys.stdin.isatty()
1193
- if has_stdin and not (
1194
- hasattr(self, "silent_mode") and self.silent_mode
1195
- ):
1196
- try:
1197
- response = (
1198
- input("\nContinue with 50 more steps? (y/n): ")
1199
- .strip()
1200
- .lower()
1201
- )
1202
- if response in ["y", "yes"]:
1203
- steps_limit += 50
1204
- self.console.print_info(
1205
- f"✓ Continuing with {steps_limit} total steps...\n"
1206
- )
1207
- else:
1208
- self.console.print_info("Stopping at user request.")
1209
- break
1210
- except (EOFError, KeyboardInterrupt):
1211
- self.console.print_info("\nStopping at user request.")
1212
- break
1213
- else:
1214
- # Silent mode - just stop
1215
- break
1216
-
1217
1184
  # Display current step
1218
1185
  self.console.print_step_header(steps_taken, steps_limit)
1219
1186
 
@@ -1668,9 +1635,6 @@ You must respond ONLY in valid JSON. No text before { or after }.
1668
1635
  # Add assistant response to messages for chat history
1669
1636
  messages.append({"role": "assistant", "content": response})
1670
1637
 
1671
- # Validate the response has a plan if required
1672
- self._validate_plan_required(parsed, steps_taken)
1673
-
1674
1638
  # If the LLM needs to create a plan first, re-prompt it specifically for that
1675
1639
  if "needs_plan" in parsed and parsed["needs_plan"]:
1676
1640
  # Prepare a special prompt that specifically requests a plan
@@ -2015,8 +1979,42 @@ You must respond ONLY in valid JSON. No text before { or after }.
2015
1979
  self.console.print_final_answer(final_answer, streaming=self.streaming)
2016
1980
  break
2017
1981
 
2018
- # Validate plan required
2019
- self._validate_plan_required(parsed, steps_taken)
1982
+ # Check if we're at the limit and ask user if they want to continue
1983
+ if steps_taken == steps_limit and final_answer is None:
1984
+ # Show what was accomplished
1985
+ max_steps_msg = self._generate_max_steps_message(
1986
+ conversation, steps_taken, steps_limit
1987
+ )
1988
+ self.console.print_warning(max_steps_msg)
1989
+
1990
+ # Ask user if they want to continue (skip in silent mode OR if stdin is not available)
1991
+ # IMPORTANT: Never call input() in API/CI contexts to avoid blocking threads
1992
+ import sys
1993
+
1994
+ has_stdin = sys.stdin and sys.stdin.isatty()
1995
+ if has_stdin and not (
1996
+ hasattr(self, "silent_mode") and self.silent_mode
1997
+ ):
1998
+ try:
1999
+ response = (
2000
+ input("\nContinue with 50 more steps? (y/n): ")
2001
+ .strip()
2002
+ .lower()
2003
+ )
2004
+ if response in ["y", "yes"]:
2005
+ steps_limit += 50
2006
+ self.console.print_info(
2007
+ f"✓ Continuing with {steps_limit} total steps...\n"
2008
+ )
2009
+ else:
2010
+ self.console.print_info("Stopping at user request.")
2011
+ break
2012
+ except (EOFError, KeyboardInterrupt):
2013
+ self.console.print_info("\nStopping at user request.")
2014
+ break
2015
+ else:
2016
+ # Silent mode - just stop
2017
+ break
2020
2018
 
2021
2019
  # Print completion message
2022
2020
  self.console.print_completion(steps_taken, steps_limit)
@@ -2132,46 +2130,3 @@ You must respond ONLY in valid JSON. No text before { or after }.
2132
2130
  List of error messages
2133
2131
  """
2134
2132
  return self.error_history
2135
-
2136
- def _validate_plan_required(self, parsed: Dict[str, Any], step: int) -> None:
2137
- """
2138
- Validate that the response includes a plan when required by the agent.
2139
-
2140
- Args:
2141
- parsed: The parsed response from the LLM
2142
- step: The current step number
2143
- """
2144
- # Skip validation if we're not in planning mode or if we're already executing a plan
2145
- if self.execution_state != self.STATE_PLANNING or self.current_plan is not None:
2146
- return
2147
-
2148
- # Allow simple single-tool operations without requiring a plan
2149
- if "tool" in parsed and step == 1:
2150
- tool_name = parsed.get("tool", "")
2151
- # List of tools that can execute directly without a plan
2152
- simple_tools = self.SIMPLE_TOOLS
2153
- if tool_name in simple_tools:
2154
- logger.debug(f"Allowing direct execution of simple tool: {tool_name}")
2155
- return
2156
-
2157
- # Check if plan is missing on the first step
2158
- # BUT: Allow direct answers without plans (for simple conversational queries)
2159
- if "plan" not in parsed and "answer" not in parsed and step == 1:
2160
- warning_msg = f"No plan found in step {step} response. The agent should create a plan for all tasks."
2161
- logger.warning(warning_msg)
2162
- self.console.print_warning(warning_msg)
2163
-
2164
- # For the first step, we'll add a flag to indicate we need to re-prompt for a plan
2165
- parsed["needs_plan"] = True
2166
-
2167
- # If there's a tool in the response, store it but don't execute it yet
2168
- if "tool" in parsed:
2169
- parsed["deferred_tool"] = parsed["tool"]
2170
- parsed["deferred_tool_args"] = parsed.get("tool_args", {})
2171
- # Remove the tool so it won't be executed
2172
- del parsed["tool"]
2173
- if "tool_args" in parsed:
2174
- del parsed["tool_args"]
2175
-
2176
- # Set state to indicate we need planning
2177
- self.execution_state = self.STATE_PLANNING
@@ -13,7 +13,6 @@ Inheritance patterns:
13
13
  - Future: FooAgent(MCPAgent, ApiAgent, Agent) - Multiple inheritance
14
14
  """
15
15
 
16
-
17
16
  from typing import Any, Dict
18
17
 
19
18
  from .agent import Agent
@@ -1233,6 +1233,132 @@ class AgentConsole(OutputHandler):
1233
1233
  else:
1234
1234
  print(f"✅ Model {status}: {model_name}")
1235
1235
 
1236
+ # === Download Progress Methods ===
1237
+
1238
+ def print_download_start(self, model_name: str) -> None:
1239
+ """
1240
+ Print download starting notification.
1241
+
1242
+ Args:
1243
+ model_name: Name of the model being downloaded
1244
+ """
1245
+ if self.rich_available and self.console:
1246
+ self.console.print()
1247
+ self.console.print(
1248
+ f"[bold blue]📥 Downloading:[/bold blue] [cyan]{model_name}[/cyan]"
1249
+ )
1250
+ else:
1251
+ rprint(f"\n📥 Downloading: {model_name}")
1252
+
1253
+ def print_download_progress(
1254
+ self,
1255
+ percent: int,
1256
+ bytes_downloaded: int,
1257
+ bytes_total: int,
1258
+ speed_mbps: float = 0.0,
1259
+ ) -> None:
1260
+ """
1261
+ Print download progress with a progress bar that updates in place.
1262
+
1263
+ Args:
1264
+ percent: Download percentage (0-100)
1265
+ bytes_downloaded: Bytes downloaded so far
1266
+ bytes_total: Total bytes to download
1267
+ speed_mbps: Download speed in MB/s (optional)
1268
+ """
1269
+ import sys
1270
+
1271
+ # Format sizes
1272
+ if bytes_total > 1024**3: # > 1 GB
1273
+ dl_str = f"{bytes_downloaded / 1024**3:.2f} GB"
1274
+ total_str = f"{bytes_total / 1024**3:.2f} GB"
1275
+ elif bytes_total > 1024**2: # > 1 MB
1276
+ dl_str = f"{bytes_downloaded / 1024**2:.0f} MB"
1277
+ total_str = f"{bytes_total / 1024**2:.0f} MB"
1278
+ else:
1279
+ dl_str = f"{bytes_downloaded / 1024:.0f} KB"
1280
+ total_str = f"{bytes_total / 1024:.0f} KB"
1281
+
1282
+ # Progress bar characters
1283
+ bar_width = 25
1284
+ filled = int(bar_width * percent / 100)
1285
+ bar = "━" * filled + "─" * (bar_width - filled)
1286
+
1287
+ # Build progress line with optional speed
1288
+ progress_line = f" [{bar}] {percent:3d}% {dl_str} / {total_str}"
1289
+ if speed_mbps > 0.1:
1290
+ progress_line += f" @ {speed_mbps:.0f} MB/s"
1291
+
1292
+ # Update in place with carriage return
1293
+ sys.stdout.write(f"\r{progress_line:<80}")
1294
+ sys.stdout.flush()
1295
+
1296
+ def print_download_complete(self, model_name: str = None) -> None:
1297
+ """
1298
+ Print download complete notification.
1299
+
1300
+ Args:
1301
+ model_name: Optional name of the downloaded model
1302
+ """
1303
+ if self.rich_available and self.console:
1304
+ self.console.print() # Newline after progress bar
1305
+ if model_name:
1306
+ self.console.print(
1307
+ f" [green]✅ Downloaded successfully:[/green] [cyan]{model_name}[/cyan]"
1308
+ )
1309
+ else:
1310
+ self.console.print(" [green]✅ Download complete[/green]")
1311
+ else:
1312
+ rprint()
1313
+ msg = (
1314
+ f" ✅ Downloaded: {model_name}"
1315
+ if model_name
1316
+ else " ✅ Download complete"
1317
+ )
1318
+ rprint(msg)
1319
+
1320
+ def print_download_error(self, error_message: str, model_name: str = None) -> None:
1321
+ """
1322
+ Print download error notification.
1323
+
1324
+ Args:
1325
+ error_message: Error description
1326
+ model_name: Optional name of the model that failed
1327
+ """
1328
+ if self.rich_available and self.console:
1329
+ self.console.print() # Newline after progress bar
1330
+ if model_name:
1331
+ self.console.print(
1332
+ f" [red]❌ Download failed for {model_name}:[/red] {error_message}"
1333
+ )
1334
+ else:
1335
+ self.console.print(f" [red]❌ Download failed:[/red] {error_message}")
1336
+ else:
1337
+ rprint()
1338
+ msg = (
1339
+ f" ❌ Download failed for {model_name}: {error_message}"
1340
+ if model_name
1341
+ else f" ❌ Download failed: {error_message}"
1342
+ )
1343
+ rprint(msg)
1344
+
1345
+ def print_download_skipped(
1346
+ self, model_name: str, reason: str = "already downloaded"
1347
+ ) -> None:
1348
+ """
1349
+ Print download skipped notification.
1350
+
1351
+ Args:
1352
+ model_name: Name of the model that was skipped
1353
+ reason: Reason for skipping
1354
+ """
1355
+ if self.rich_available and self.console:
1356
+ self.console.print(
1357
+ f"[green]✅[/green] [cyan]{model_name}[/cyan] [dim]({reason})[/dim]"
1358
+ )
1359
+ else:
1360
+ rprint(f"✅ {model_name} ({reason})")
1361
+
1236
1362
  def print_extraction_start(
1237
1363
  self, image_num: int, page_num: int, mime_type: str
1238
1364
  ) -> None:
@@ -17,7 +17,10 @@ _TOOL_REGISTRY = {}
17
17
 
18
18
 
19
19
  def tool(
20
- func: Callable = None, **kwargs # pylint: disable=unused-argument
20
+ func: Callable = None,
21
+ *,
22
+ atomic: bool = False,
23
+ **kwargs, # pylint: disable=unused-argument
21
24
  ) -> Callable:
22
25
  """
23
26
  Decorator to register a function as a tool.
@@ -28,6 +31,7 @@ def tool(
28
31
 
29
32
  Args:
30
33
  func: Function to register as a tool (when used as @tool)
34
+ atomic: If True, marks this tool as atomic (can execute without multi-step planning)
31
35
  **kwargs: Optional arguments (ignored, for backward compatibility)
32
36
 
33
37
  Returns:
@@ -63,12 +67,13 @@ def tool(
63
67
 
64
68
  params[name] = param_info
65
69
 
66
- # Register the tool
70
+ # Register the tool with atomic metadata
67
71
  _TOOL_REGISTRY[tool_name] = {
68
72
  "name": tool_name,
69
73
  "description": f.__doc__ or "",
70
74
  "parameters": params,
71
75
  "function": f,
76
+ "atomic": atomic,
72
77
  }
73
78
 
74
79
  # Return the function unchanged
@@ -0,0 +1,7 @@
1
+ # Copyright(C) 2025-2026 Advanced Micro Devices, Inc. All rights reserved.
2
+ # SPDX-License-Identifier: MIT
3
+ """Blender Agent for 3D scene automation."""
4
+
5
+ from gaia.agents.blender.agent import BlenderAgent
6
+
7
+ __all__ = ["BlenderAgent"]
@@ -24,9 +24,6 @@ class BlenderAgent(Agent):
24
24
  Inherits core functionality from the base Agent class.
25
25
  """
26
26
 
27
- # Define Blender-specific tools that can execute directly without requiring a plan
28
- SIMPLE_TOOLS = ["clear_scene", "get_scene_info"]
29
-
30
27
  def __init__(
31
28
  self,
32
29
  mcp: Optional[MCPClient] = None,
@@ -80,7 +77,7 @@ class BlenderAgent(Agent):
80
77
  def _get_system_prompt(self) -> str:
81
78
  """Generate the system prompt for the Blender agent."""
82
79
  # Get formatted tools from registry
83
- return f"""
80
+ return """
84
81
  You are a specialized Blender 3D assistant that can create and modify 3D scenes.
85
82
  You will use a set of tools to accomplish tasks based on the user's request.
86
83
 
@@ -169,7 +166,7 @@ Examples of colored requests:
169
166
  def _register_tools(self):
170
167
  """Register all Blender-related tools for the agent."""
171
168
 
172
- @tool
169
+ @tool(atomic=True)
173
170
  def clear_scene() -> Dict[str, Any]:
174
171
  """
175
172
  Remove all objects from the current Blender scene.
@@ -283,7 +280,7 @@ Examples of colored requests:
283
280
  return {"status": "error", "error": str(e)}
284
281
 
285
282
  # @tool
286
- def get_object_info(name: str) -> Dict[str, Any]:
283
+ def _get_object_info(name: str) -> Dict[str, Any]:
287
284
  """
288
285
  Get information about an object in the scene.
289
286
 
@@ -354,7 +351,7 @@ Examples of colored requests:
354
351
  return {"status": "error", "error": str(e)}
355
352
 
356
353
  # @tool
357
- def delete_object(name: str) -> Dict[str, Any]:
354
+ def _delete_object(name: str) -> Dict[str, Any]:
358
355
  """
359
356
  Delete an object from the scene.
360
357
 
@@ -382,7 +379,7 @@ Examples of colored requests:
382
379
  self.error_history.append(str(e))
383
380
  return {"status": "error", "error": str(e)}
384
381
 
385
- @tool
382
+ @tool(atomic=True)
386
383
  def get_scene_info() -> Dict[str, Any]:
387
384
  """
388
385
  Get information about the current scene.
@@ -407,7 +404,7 @@ Examples of colored requests:
407
404
  return {"status": "error", "error": str(e)}
408
405
 
409
406
  # @tool
410
- def execute_blender_code(code: str) -> Dict[str, Any]:
407
+ def _execute_blender_code(code: str) -> Dict[str, Any]:
411
408
  """
412
409
  Execute arbitrary Python code in Blender with error handling.
413
410
 
@@ -436,7 +433,7 @@ Examples of colored requests:
436
433
  return {"status": "error", "error": str(e)}
437
434
 
438
435
  # @tool
439
- def diagnose_scene() -> Dict[str, Any]:
436
+ def _diagnose_scene() -> Dict[str, Any]:
440
437
  """
441
438
  Diagnose the current Blender scene for common issues.
442
439
  Returns information about objects, materials, and potential problems.
@@ -13,13 +13,13 @@ class ViewManager:
13
13
  self.mcp = mcp
14
14
 
15
15
  def adjust_for_large_scale(
16
- self, clip_end: float = 100000, orbit_selection: bool = True
16
+ self, clip_end: float = 100000, _orbit_selection: bool = True
17
17
  ) -> Dict:
18
18
  """Adjust viewport settings to properly view large-scale objects like Earth.
19
19
 
20
20
  Args:
21
21
  clip_end: The maximum view distance to set for the 3D viewport (default: 100000)
22
- orbit_selection: Whether to enable orbit around selection (default: True, but may not work in all Blender versions)
22
+ _orbit_selection: Whether to enable orbit around selection (default: True, but may not work in all Blender versions)
23
23
  """
24
24
 
25
25
  def generate_code():
@@ -78,23 +78,6 @@ class ChatAgent(
78
78
  - MCP server integration
79
79
  """
80
80
 
81
- # Define simple tools that can execute without requiring a multi-step plan
82
- SIMPLE_TOOLS = [
83
- "list_indexed_documents",
84
- "rag_status",
85
- "query_documents",
86
- "query_specific_file",
87
- "search_indexed_chunks", # RAG: Search indexed document chunks
88
- "dump_document", # RAG: Export cached extracted text
89
- "search_file_content", # Shared: Grep-like disk search
90
- "search_file", # Shared: Find files by name
91
- "search_directory", # Shared: Find directories by name
92
- "read_file", # Shared: Read any file
93
- "write_file", # Shared: Write any file
94
- "index_directory", # RAG: Index directory
95
- "run_shell_command", # Shell: Execute commands
96
- ]
97
-
98
81
  def __init__(self, config: Optional[ChatAgentConfig] = None):
99
82
  """
100
83
  Initialize Chat Agent.
@@ -109,6 +92,9 @@ class ChatAgent(
109
92
  # Initialize path validator
110
93
  self.path_validator = PathValidator(config.allowed_paths)
111
94
 
95
+ # Store config for access in other methods
96
+ self.config = config
97
+
112
98
  # Now use config for all initialization
113
99
  # Store RAG configuration from config
114
100
  self.rag_documents = config.rag_documents
@@ -148,6 +134,7 @@ class ChatAgent(
148
134
  use_local_llm=not (config.use_claude or config.use_chatgpt),
149
135
  use_llm_chunking=config.use_llm_chunking, # Enable semantic chunking
150
136
  base_url=config.base_url, # Pass base_url to RAG for VLM client
137
+ allowed_paths=config.allowed_paths, # Pass allowed paths to RAG SDK
151
138
  )
152
139
  self.rag = RAGSDK(rag_config)
153
140
  except ImportError as e:
@@ -267,10 +254,7 @@ No documents are currently indexed.
267
254
  """
268
255
 
269
256
  # Add indexed documents section
270
- prompt = (
271
- base_prompt
272
- + indexed_docs_section
273
- + """
257
+ prompt = base_prompt + indexed_docs_section + """
274
258
  **WHEN TO USE TOOLS VS DIRECT ANSWERS:**
275
259
 
276
260
  Use Format 1 (answer) for:
@@ -376,7 +360,6 @@ When user asks to "index my data folder" or similar:
376
360
  2. Show user the matches and ask which one (if multiple)
377
361
  3. Use index_directory on the chosen path
378
362
  4. Report indexing results"""
379
- )
380
363
 
381
364
  return prompt
382
365
 
@@ -473,7 +456,7 @@ When user asks to "index my data folder" or similar:
473
456
  def _is_path_allowed(self, path: str) -> bool:
474
457
  """
475
458
  Check if a path is within allowed directories.
476
- Uses real path resolution to prevent TOCTOU attacks.
459
+ Uses PathValidator for the actual check.
477
460
 
478
461
  Args:
479
462
  path: Path to validate
@@ -481,24 +464,7 @@ When user asks to "index my data folder" or similar:
481
464
  Returns:
482
465
  True if path is allowed, False otherwise
483
466
  """
484
- try:
485
- # Resolve path using os.path.realpath to follow symlinks
486
- # This prevents TOCTOU attacks by resolving at check time
487
- real_path = Path(os.path.realpath(path)).resolve()
488
-
489
- # Check if real path is within any allowed directory
490
- for allowed_path in self.allowed_paths:
491
- try:
492
- # is_relative_to requires Python 3.9+, use alternative for compatibility
493
- real_path.relative_to(allowed_path)
494
- return True
495
- except ValueError:
496
- continue
497
-
498
- return False
499
- except Exception as e:
500
- logger.error(f"Error validating path {path}: {e}")
501
- return False
467
+ return self.path_validator.is_path_allowed(path, prompt_user=False)
502
468
 
503
469
  def _validate_and_open_file(self, file_path: str, mode: str = "r"):
504
470
  """
@@ -647,9 +613,9 @@ When user asks to "index my data folder" or similar:
647
613
  logger.error(f"Failed to index {doc}: {e}")
648
614
 
649
615
  # Update system prompt after indexing to include the new documents
650
- self._update_system_prompt()
616
+ self.update_system_prompt()
651
617
 
652
- def _update_system_prompt(self) -> None:
618
+ def update_system_prompt(self) -> None:
653
619
  """Update the system prompt with current indexed documents."""
654
620
  # Regenerate the system prompt with updated document list
655
621
  self.system_prompt = self._get_system_prompt()
@@ -732,18 +698,26 @@ When user asks to "index my data folder" or similar:
732
698
  )
733
699
  return
734
700
 
701
+ # Resolve to real path for consistent validation
702
+ real_file_path = os.path.realpath(file_path)
703
+
704
+ # Security check
705
+ if not self._is_path_allowed(real_file_path):
706
+ logger.warning(f"Re-indexing skipped: Path not allowed {real_file_path}")
707
+ return
708
+
735
709
  try:
736
- logger.info(f"Reindexing: {file_path}")
710
+ logger.info(f"Reindexing: {real_file_path}")
737
711
  # Use the new reindex_document method which removes old chunks first
738
- result = self.rag.reindex_document(file_path)
712
+ result = self.rag.reindex_document(real_file_path)
739
713
  if result.get("success"):
740
714
  self.indexed_files.add(file_path)
741
- logger.info(f"Successfully reindexed {file_path}")
715
+ logger.info(f"Successfully reindexed {real_file_path}")
742
716
  else:
743
717
  error = result.get("error", "Unknown error")
744
- logger.error(f"Failed to reindex {file_path}: {error}")
718
+ logger.error(f"Failed to reindex {real_file_path}: {error}")
745
719
  except Exception as e:
746
- logger.error(f"Failed to reindex {file_path}: {e}")
720
+ logger.error(f"Failed to reindex {real_file_path}: {e}")
747
721
 
748
722
  def stop_watching(self) -> None:
749
723
  """Stop all file system observers."""