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.
- {amd_gaia-0.15.1/src/amd_gaia.egg-info → amd_gaia-0.15.2}/PKG-INFO +1 -2
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/README.md +0 -1
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/setup.py +1 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2/src/amd_gaia.egg-info}/PKG-INFO +1 -2
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/amd_gaia.egg-info/SOURCES.txt +6 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/base/agent.py +45 -90
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/base/api_agent.py +0 -1
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/base/console.py +126 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/base/tools.py +7 -2
- amd_gaia-0.15.2/src/gaia/agents/blender/__init__.py +7 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/blender/agent.py +7 -10
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/blender/core/view.py +2 -2
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/chat/agent.py +22 -48
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/chat/app.py +7 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/chat/tools/rag_tools.py +23 -8
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/chat/tools/shell_tools.py +1 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/prompts/code_patterns.py +2 -4
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/docker/agent.py +1 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/emr/agent.py +3 -5
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/emr/cli.py +1 -1
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/emr/dashboard/server.py +2 -4
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/apps/llm/app.py +14 -3
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/chat/app.py +2 -4
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/cli.py +511 -333
- amd_gaia-0.15.2/src/gaia/installer/__init__.py +23 -0
- amd_gaia-0.15.2/src/gaia/installer/init_command.py +1275 -0
- amd_gaia-0.15.2/src/gaia/installer/lemonade_installer.py +619 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/llm/__init__.py +2 -1
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/llm/lemonade_client.py +284 -99
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/llm/providers/lemonade.py +12 -14
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/rag/sdk.py +1 -1
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/security.py +24 -4
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/talk/app.py +2 -4
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/version.py +2 -2
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_chat_agent.py +350 -68
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_checklist_orchestration.py +2 -4
- amd_gaia-0.15.2/tests/test_hardware_advisor_agent.py +189 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_lemonade_client.py +180 -11
- amd_gaia-0.15.2/tests/test_lemonade_health.py +62 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_typescript_tools.py +2 -4
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/LICENSE.md +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/MANIFEST.in +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/pyproject.toml +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/setup.cfg +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/amd_gaia.egg-info/dependency_links.txt +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/amd_gaia.egg-info/entry_points.txt +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/amd_gaia.egg-info/requires.txt +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/amd_gaia.egg-info/top_level.txt +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/base/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/base/errors.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/base/mcp_agent.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/blender/agent_simple.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/blender/app.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/blender/app_simple.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/blender/core/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/blender/core/materials.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/blender/core/objects.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/blender/core/rendering.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/blender/core/scene.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/chat/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/chat/session.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/chat/tools/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/chat/tools/file_tools.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/agent.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/cli.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/models.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/checklist_executor.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/checklist_generator.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/factories/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/factories/base.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/factories/nextjs_factory.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/factories/python_factory.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/orchestrator.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/project_analyzer.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/steps/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/steps/base.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/steps/error_handler.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/steps/nextjs.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/steps/python.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/template_catalog.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/workflows/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/workflows/base.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/workflows/nextjs.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/orchestration/workflows/python.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/prompts/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/prompts/base_prompt.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/prompts/nextjs_prompt.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/prompts/python_prompt.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/schema_inference.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/system_prompt.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/cli_tools.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/code_formatting.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/code_tools.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/error_fixing.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/external_tools.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/file_io.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/prisma_tools.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/project_management.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/testing.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/typescript_tools.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/validation_parsing.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/validation_tools.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/tools/web_dev_tools.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/validators/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/validators/antipattern_checker.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/validators/ast_analyzer.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/validators/requirements_validator.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/code/validators/syntax_validator.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/docker/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/emr/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/emr/constants.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/emr/dashboard/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/jira/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/jira/agent.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/jira/jql_templates.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/routing/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/routing/agent.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/routing/system_prompt.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/summarize/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/summarize/agent.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/agents/summarize/prompts.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/api/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/api/agent_registry.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/api/app.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/api/openai_server.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/api/schemas.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/api/sse_handler.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/apps/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/apps/llm/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/apps/summarize/app.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/apps/summarize/html_viewer.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/apps/summarize/pdf_formatter.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/audio/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/audio/audio_client.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/audio/audio_recorder.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/audio/kokoro_tts.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/audio/whisper_asr.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/chat/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/chat/prompts.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/chat/sdk.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/database/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/database/agent.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/database/mixin.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/database/testing.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/batch_experiment.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/claude.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/config.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/email_generator.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/eval.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/groundtruth.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/transcript_generator.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/webapp/README.md +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/webapp/package-lock.json +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/webapp/package.json +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/webapp/public/app.js +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/webapp/public/index.html +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/webapp/public/styles.css +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/webapp/server.js +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/eval/webapp/test-setup.js +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/llm/base_client.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/llm/exceptions.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/llm/factory.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/llm/lemonade_manager.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/llm/providers/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/llm/providers/claude.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/llm/providers/openai_provider.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/llm/vlm_client.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/logger.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/mcp/agent_mcp_server.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/mcp/blender_mcp_client.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/mcp/blender_mcp_server.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/mcp/context7_cache.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/mcp/external_services.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/mcp/mcp_bridge.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/mcp/servers/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/mcp/servers/docker_mcp.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/perf_analysis.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/rag/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/rag/app.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/rag/demo.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/rag/pdf_utils.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/talk/sdk.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/testing/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/testing/assertions.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/testing/fixtures.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/testing/mocks.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/util.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/utils/__init__.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/utils/file_watcher.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/src/gaia/utils/parsing.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_api.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_chat_sdk.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_code_agent.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_code_agent_mixins.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_eval.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_external_tools.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_jira.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_lemonade_embeddings.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_rag.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_rag_integration.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_sdk.py +0 -0
- {amd_gaia-0.15.1 → amd_gaia-0.15.2}/tests/test_summarizer.py +0 -0
- {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.
|
|
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
|
-
[](https://github.com/amd/gaia/tree/main/tests "Check out our build")
|
|
104
103
|
[](https://github.com/amd/gaia/tree/main/tests "Check out our cli tests")
|
|
105
104
|
[](https://github.com/amd/gaia/releases/latest "Download the latest release")
|
|
106
105
|
[](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
|
-
[](https://github.com/amd/gaia/tree/main/tests "Check out our build")
|
|
4
3
|
[](https://github.com/amd/gaia/tree/main/tests "Check out our cli tests")
|
|
5
4
|
[](https://github.com/amd/gaia/releases/latest "Download the latest release")
|
|
6
5
|
[](https://pypi.org/project/amd-gaia/)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: amd-gaia
|
|
3
|
-
Version: 0.15.
|
|
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
|
-
[](https://github.com/amd/gaia/tree/main/tests "Check out our build")
|
|
104
103
|
[](https://github.com/amd/gaia/tree/main/tests "Check out our cli tests")
|
|
105
104
|
[](https://github.com/amd/gaia/releases/latest "Download the latest release")
|
|
106
105
|
[](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
|
|
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
|
-
#
|
|
2019
|
-
|
|
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
|
|
@@ -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,
|
|
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
|
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
616
|
+
self.update_system_prompt()
|
|
651
617
|
|
|
652
|
-
def
|
|
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: {
|
|
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(
|
|
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 {
|
|
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 {
|
|
718
|
+
logger.error(f"Failed to reindex {real_file_path}: {error}")
|
|
745
719
|
except Exception as e:
|
|
746
|
-
logger.error(f"Failed to reindex {
|
|
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."""
|