amd-gaia 0.14.3__py3-none-any.whl → 0.15.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. {amd_gaia-0.14.3.dist-info → amd_gaia-0.15.1.dist-info}/METADATA +223 -223
  2. amd_gaia-0.15.1.dist-info/RECORD +178 -0
  3. {amd_gaia-0.14.3.dist-info → amd_gaia-0.15.1.dist-info}/entry_points.txt +1 -0
  4. {amd_gaia-0.14.3.dist-info → amd_gaia-0.15.1.dist-info}/licenses/LICENSE.md +20 -20
  5. gaia/__init__.py +29 -29
  6. gaia/agents/__init__.py +19 -19
  7. gaia/agents/base/__init__.py +9 -9
  8. gaia/agents/base/agent.py +2177 -2177
  9. gaia/agents/base/api_agent.py +120 -120
  10. gaia/agents/base/console.py +1841 -1841
  11. gaia/agents/base/errors.py +237 -237
  12. gaia/agents/base/mcp_agent.py +86 -86
  13. gaia/agents/base/tools.py +83 -83
  14. gaia/agents/blender/agent.py +556 -556
  15. gaia/agents/blender/agent_simple.py +133 -135
  16. gaia/agents/blender/app.py +211 -211
  17. gaia/agents/blender/app_simple.py +41 -41
  18. gaia/agents/blender/core/__init__.py +16 -16
  19. gaia/agents/blender/core/materials.py +506 -506
  20. gaia/agents/blender/core/objects.py +316 -316
  21. gaia/agents/blender/core/rendering.py +225 -225
  22. gaia/agents/blender/core/scene.py +220 -220
  23. gaia/agents/blender/core/view.py +146 -146
  24. gaia/agents/chat/__init__.py +9 -9
  25. gaia/agents/chat/agent.py +835 -835
  26. gaia/agents/chat/app.py +1058 -1058
  27. gaia/agents/chat/session.py +508 -508
  28. gaia/agents/chat/tools/__init__.py +15 -15
  29. gaia/agents/chat/tools/file_tools.py +96 -96
  30. gaia/agents/chat/tools/rag_tools.py +1729 -1729
  31. gaia/agents/chat/tools/shell_tools.py +436 -436
  32. gaia/agents/code/__init__.py +7 -7
  33. gaia/agents/code/agent.py +549 -549
  34. gaia/agents/code/cli.py +377 -0
  35. gaia/agents/code/models.py +135 -135
  36. gaia/agents/code/orchestration/__init__.py +24 -24
  37. gaia/agents/code/orchestration/checklist_executor.py +1763 -1763
  38. gaia/agents/code/orchestration/checklist_generator.py +713 -713
  39. gaia/agents/code/orchestration/factories/__init__.py +9 -9
  40. gaia/agents/code/orchestration/factories/base.py +63 -63
  41. gaia/agents/code/orchestration/factories/nextjs_factory.py +118 -118
  42. gaia/agents/code/orchestration/factories/python_factory.py +106 -106
  43. gaia/agents/code/orchestration/orchestrator.py +841 -841
  44. gaia/agents/code/orchestration/project_analyzer.py +391 -391
  45. gaia/agents/code/orchestration/steps/__init__.py +67 -67
  46. gaia/agents/code/orchestration/steps/base.py +188 -188
  47. gaia/agents/code/orchestration/steps/error_handler.py +314 -314
  48. gaia/agents/code/orchestration/steps/nextjs.py +828 -828
  49. gaia/agents/code/orchestration/steps/python.py +307 -307
  50. gaia/agents/code/orchestration/template_catalog.py +469 -469
  51. gaia/agents/code/orchestration/workflows/__init__.py +14 -14
  52. gaia/agents/code/orchestration/workflows/base.py +80 -80
  53. gaia/agents/code/orchestration/workflows/nextjs.py +186 -186
  54. gaia/agents/code/orchestration/workflows/python.py +94 -94
  55. gaia/agents/code/prompts/__init__.py +11 -11
  56. gaia/agents/code/prompts/base_prompt.py +77 -77
  57. gaia/agents/code/prompts/code_patterns.py +2036 -2036
  58. gaia/agents/code/prompts/nextjs_prompt.py +40 -40
  59. gaia/agents/code/prompts/python_prompt.py +109 -109
  60. gaia/agents/code/schema_inference.py +365 -365
  61. gaia/agents/code/system_prompt.py +41 -41
  62. gaia/agents/code/tools/__init__.py +42 -42
  63. gaia/agents/code/tools/cli_tools.py +1138 -1138
  64. gaia/agents/code/tools/code_formatting.py +319 -319
  65. gaia/agents/code/tools/code_tools.py +769 -769
  66. gaia/agents/code/tools/error_fixing.py +1347 -1347
  67. gaia/agents/code/tools/external_tools.py +180 -180
  68. gaia/agents/code/tools/file_io.py +845 -845
  69. gaia/agents/code/tools/prisma_tools.py +190 -190
  70. gaia/agents/code/tools/project_management.py +1016 -1016
  71. gaia/agents/code/tools/testing.py +321 -321
  72. gaia/agents/code/tools/typescript_tools.py +122 -122
  73. gaia/agents/code/tools/validation_parsing.py +461 -461
  74. gaia/agents/code/tools/validation_tools.py +806 -806
  75. gaia/agents/code/tools/web_dev_tools.py +1758 -1758
  76. gaia/agents/code/validators/__init__.py +16 -16
  77. gaia/agents/code/validators/antipattern_checker.py +241 -241
  78. gaia/agents/code/validators/ast_analyzer.py +197 -197
  79. gaia/agents/code/validators/requirements_validator.py +145 -145
  80. gaia/agents/code/validators/syntax_validator.py +171 -171
  81. gaia/agents/docker/__init__.py +7 -7
  82. gaia/agents/docker/agent.py +642 -642
  83. gaia/agents/emr/__init__.py +8 -8
  84. gaia/agents/emr/agent.py +1506 -1506
  85. gaia/agents/emr/cli.py +1322 -1322
  86. gaia/agents/emr/constants.py +475 -475
  87. gaia/agents/emr/dashboard/__init__.py +4 -4
  88. gaia/agents/emr/dashboard/server.py +1974 -1974
  89. gaia/agents/jira/__init__.py +11 -11
  90. gaia/agents/jira/agent.py +894 -894
  91. gaia/agents/jira/jql_templates.py +299 -299
  92. gaia/agents/routing/__init__.py +7 -7
  93. gaia/agents/routing/agent.py +567 -570
  94. gaia/agents/routing/system_prompt.py +75 -75
  95. gaia/agents/summarize/__init__.py +11 -0
  96. gaia/agents/summarize/agent.py +885 -0
  97. gaia/agents/summarize/prompts.py +129 -0
  98. gaia/api/__init__.py +23 -23
  99. gaia/api/agent_registry.py +238 -238
  100. gaia/api/app.py +305 -305
  101. gaia/api/openai_server.py +575 -575
  102. gaia/api/schemas.py +186 -186
  103. gaia/api/sse_handler.py +373 -373
  104. gaia/apps/__init__.py +4 -4
  105. gaia/apps/llm/__init__.py +6 -6
  106. gaia/apps/llm/app.py +173 -169
  107. gaia/apps/summarize/app.py +116 -633
  108. gaia/apps/summarize/html_viewer.py +133 -133
  109. gaia/apps/summarize/pdf_formatter.py +284 -284
  110. gaia/audio/__init__.py +2 -2
  111. gaia/audio/audio_client.py +439 -439
  112. gaia/audio/audio_recorder.py +269 -269
  113. gaia/audio/kokoro_tts.py +599 -599
  114. gaia/audio/whisper_asr.py +432 -432
  115. gaia/chat/__init__.py +16 -16
  116. gaia/chat/app.py +430 -430
  117. gaia/chat/prompts.py +522 -522
  118. gaia/chat/sdk.py +1228 -1225
  119. gaia/cli.py +5481 -5621
  120. gaia/database/__init__.py +10 -10
  121. gaia/database/agent.py +176 -176
  122. gaia/database/mixin.py +290 -290
  123. gaia/database/testing.py +64 -64
  124. gaia/eval/batch_experiment.py +2332 -2332
  125. gaia/eval/claude.py +542 -542
  126. gaia/eval/config.py +37 -37
  127. gaia/eval/email_generator.py +512 -512
  128. gaia/eval/eval.py +3179 -3179
  129. gaia/eval/groundtruth.py +1130 -1130
  130. gaia/eval/transcript_generator.py +582 -582
  131. gaia/eval/webapp/README.md +167 -167
  132. gaia/eval/webapp/package-lock.json +875 -875
  133. gaia/eval/webapp/package.json +20 -20
  134. gaia/eval/webapp/public/app.js +3402 -3402
  135. gaia/eval/webapp/public/index.html +87 -87
  136. gaia/eval/webapp/public/styles.css +3661 -3661
  137. gaia/eval/webapp/server.js +415 -415
  138. gaia/eval/webapp/test-setup.js +72 -72
  139. gaia/llm/__init__.py +9 -2
  140. gaia/llm/base_client.py +60 -0
  141. gaia/llm/exceptions.py +12 -0
  142. gaia/llm/factory.py +70 -0
  143. gaia/llm/lemonade_client.py +3236 -3221
  144. gaia/llm/lemonade_manager.py +294 -294
  145. gaia/llm/providers/__init__.py +9 -0
  146. gaia/llm/providers/claude.py +108 -0
  147. gaia/llm/providers/lemonade.py +120 -0
  148. gaia/llm/providers/openai_provider.py +79 -0
  149. gaia/llm/vlm_client.py +382 -382
  150. gaia/logger.py +189 -189
  151. gaia/mcp/agent_mcp_server.py +245 -245
  152. gaia/mcp/blender_mcp_client.py +138 -138
  153. gaia/mcp/blender_mcp_server.py +648 -648
  154. gaia/mcp/context7_cache.py +332 -332
  155. gaia/mcp/external_services.py +518 -518
  156. gaia/mcp/mcp_bridge.py +811 -550
  157. gaia/mcp/servers/__init__.py +6 -6
  158. gaia/mcp/servers/docker_mcp.py +83 -83
  159. gaia/perf_analysis.py +361 -0
  160. gaia/rag/__init__.py +10 -10
  161. gaia/rag/app.py +293 -293
  162. gaia/rag/demo.py +304 -304
  163. gaia/rag/pdf_utils.py +235 -235
  164. gaia/rag/sdk.py +2194 -2194
  165. gaia/security.py +163 -163
  166. gaia/talk/app.py +289 -289
  167. gaia/talk/sdk.py +538 -538
  168. gaia/testing/__init__.py +87 -87
  169. gaia/testing/assertions.py +330 -330
  170. gaia/testing/fixtures.py +333 -333
  171. gaia/testing/mocks.py +493 -493
  172. gaia/util.py +46 -46
  173. gaia/utils/__init__.py +33 -33
  174. gaia/utils/file_watcher.py +675 -675
  175. gaia/utils/parsing.py +223 -223
  176. gaia/version.py +100 -100
  177. amd_gaia-0.14.3.dist-info/RECORD +0 -168
  178. gaia/agents/code/app.py +0 -266
  179. gaia/llm/llm_client.py +0 -729
  180. {amd_gaia-0.14.3.dist-info → amd_gaia-0.15.1.dist-info}/WHEEL +0 -0
  181. {amd_gaia-0.14.3.dist-info → amd_gaia-0.15.1.dist-info}/top_level.txt +0 -0
@@ -1,556 +1,556 @@
1
- # Copyright(C) 2025-2026 Advanced Micro Devices, Inc. All rights reserved.
2
- # SPDX-License-Identifier: MIT
3
- """
4
- Blender-specific agent for creating and modifying 3D scenes.
5
- """
6
-
7
- import logging
8
- from typing import Any, Dict, Optional
9
-
10
- from gaia.agents.base.agent import Agent
11
- from gaia.agents.base.console import AgentConsole
12
- from gaia.agents.base.tools import tool
13
- from gaia.agents.blender.core.scene import generate_scene_diagnosis_code
14
- from gaia.mcp.blender_mcp_client import MCPClient
15
-
16
- # Set up logging
17
- logging.basicConfig(level=logging.INFO)
18
- logger = logging.getLogger(__name__)
19
-
20
-
21
- class BlenderAgent(Agent):
22
- """
23
- Blender-specific agent focused on 3D scene creation and modification.
24
- Inherits core functionality from the base Agent class.
25
- """
26
-
27
- # Define Blender-specific tools that can execute directly without requiring a plan
28
- SIMPLE_TOOLS = ["clear_scene", "get_scene_info"]
29
-
30
- def __init__(
31
- self,
32
- mcp: Optional[MCPClient] = None,
33
- model_id: str = None,
34
- base_url: str = "http://localhost:8000/api/v1",
35
- max_steps: int = 5,
36
- debug_prompts: bool = False,
37
- output_dir: str = None,
38
- streaming: bool = False,
39
- show_stats: bool = True,
40
- ):
41
- """
42
- Initialize the BlenderAgent with MCP client and LLM client.
43
-
44
- Args:
45
- mcp: An optional pre-configured MCP client, otherwise a new one will be created
46
- model_id: The ID of the model to use with LLM server
47
- base_url: Base URL for the local LLM server API
48
- max_steps: Maximum number of steps the agent can take before terminating
49
- debug_prompts: If True, includes prompts in the conversation history
50
- output_dir: Directory for storing JSON output files (default: current directory)
51
- streaming: If True, enables real-time streaming of LLM responses (default: False)
52
- show_stats: If True, displays LLM performance stats after each response (default: True)
53
- """
54
- # Initialize the MCP client for Blender communication
55
- self.mcp = mcp if mcp else MCPClient()
56
-
57
- # Call the parent class constructor
58
- super().__init__(
59
- model_id=model_id,
60
- base_url=base_url,
61
- max_steps=max_steps,
62
- debug_prompts=debug_prompts,
63
- output_dir=output_dir,
64
- streaming=streaming,
65
- show_stats=show_stats,
66
- )
67
-
68
- # Register Blender-specific tools
69
- self._register_tools()
70
-
71
- def _create_console(self) -> AgentConsole:
72
- """
73
- Create and return a Agent-specific console output handler.
74
-
75
- Returns:
76
- A AgentConsole instance
77
- """
78
- return AgentConsole()
79
-
80
- def _get_system_prompt(self) -> str:
81
- """Generate the system prompt for the Blender agent."""
82
- # Get formatted tools from registry
83
- return f"""
84
- You are a specialized Blender 3D assistant that can create and modify 3D scenes.
85
- You will use a set of tools to accomplish tasks based on the user's request.
86
-
87
- ==== JSON RESPONSE FORMAT ====
88
- ALWAYS respond with a single valid JSON object. NO text outside this structure.
89
- - Use double quotes for keys and string values
90
- - Ensure all braces and brackets are properly closed
91
- - No trailing commas in arrays or objects
92
- - All required fields must be included
93
- - Never wrap your JSON in code blocks or backticks
94
-
95
- Your JSON response must follow this format:
96
- {{
97
- "thought": "your reasoning about what to do",
98
- "goal": "clear statement of what you're achieving",
99
- "plan": [
100
- {{"tool": "tool1", "tool_args": {{"arg1": "val1"}}}},
101
- {{"tool": "tool2", "tool_args": {{"arg1": "val1"}}}}
102
- ],
103
- "tool": "first_tool_to_execute",
104
- "tool_args": {{"arg1": "val1", "arg2": "val2"}}
105
- }}
106
-
107
- For final answers:
108
- {{
109
- "thought": "your reasoning",
110
- "goal": "what was achieved",
111
- "answer": "your final answer"
112
- }}
113
-
114
- ==== CRITICAL RULES ====
115
- 1. Create a plan for multi-step tasks, but simple single operations (like clear_scene) can execute directly
116
- 2. Each plan step must be atomic (one tool call per step)
117
- 3. For colored objects, ALWAYS include both create_object AND set_material_color steps
118
- 4. When clearing a scene, ONLY use clear_scene without creating new objects unless requested
119
- 5. Always use the actual returned object names for subsequent operations
120
- 6. Never repeat the same tool call with identical arguments
121
-
122
- ==== COLORED OBJECT DETECTION ====
123
- 🔍 SCAN the user request for color words:
124
- - "red", "green", "blue", "yellow", "purple", "cyan", "white", "black"
125
- - "colored", "paint", "material"
126
-
127
- ⚠️ IF you find ANY color words, you MUST:
128
- 1. Create the object with create_object
129
- 2. Set its color with set_material_color
130
- 3. Then do any other modifications
131
-
132
- ❌ NEVER skip the color step if a color is mentioned!
133
-
134
- Examples of colored requests:
135
- - "blue cylinder" → needs create_object + set_material_color
136
- - "red sphere" → needs create_object + set_material_color
137
- - "green cube and yellow cone" → needs 4 steps total
138
-
139
- ==== TOOL PARAMETER RULES ====
140
- ⚠️ CRITICAL: create_object does NOT accept a 'color' parameter!
141
- ✅ CORRECT workflow for colored objects:
142
- Step 1: create_object (type, name, location, rotation, scale ONLY)
143
- Step 2: set_material_color (object_name, color)
144
-
145
- ⚠️ CRITICAL: Colors must be RGBA format with 4 values [r, g, b, a]
146
- ❌ WRONG: [0, 0, 1] (only 3 values)
147
- ✅ CORRECT: [0, 0, 1, 1] (4 values including alpha)
148
-
149
- ⚠️ CRITICAL: EVERY colored object must have BOTH steps!
150
- If user asks for "green cube and red sphere", you need 4 steps:
151
- 1. create_object (cube)
152
- 2. set_material_color (cube, green)
153
- 3. create_object (sphere)
154
- 4. set_material_color (sphere, red)
155
-
156
- ==== COMMON WORKFLOWS ====
157
- 1. Clearing a scene: Use clear_scene() with no arguments
158
- 2. Creating a single colored object:
159
- - Step 1: create_object(type="CYLINDER", name="my_obj", location=[0,0,0])
160
- - Step 2: set_material_color(object_name="my_obj", color=[0,0,1,1])
161
- 3. Creating multiple colored objects:
162
- - Step 1: create_object(type="CUBE", name="cube1", location=[0,0,0])
163
- - Step 2: set_material_color(object_name="cube1", color=[0,1,0,1])
164
- - Step 3: create_object(type="SPHERE", name="sphere1", location=[3,0,0])
165
- - Step 4: set_material_color(object_name="sphere1", color=[1,0,0,1])
166
- 4. Modifying objects: Use modify_object with the parameters you want to change
167
- """
168
-
169
- def _register_tools(self):
170
- """Register all Blender-related tools for the agent."""
171
-
172
- @tool
173
- def clear_scene() -> Dict[str, Any]:
174
- """
175
- Remove all objects from the current Blender scene.
176
-
177
- Returns:
178
- Dictionary containing the operation result
179
-
180
- Example JSON response:
181
- ```json
182
- {
183
- "thought": "I will clear the scene to start fresh",
184
- "goal": "Clear the scene to start fresh",
185
- "tool": "clear_scene",
186
- "tool_args": {}
187
- }
188
- ```
189
- """
190
- try:
191
- from gaia.agents.blender.core.scene import SceneManager
192
-
193
- scene_manager = SceneManager(self.mcp)
194
- return scene_manager.clear_scene()
195
- except Exception as e:
196
- self.error_history.append(str(e))
197
- return {"status": "error", "error": str(e)}
198
-
199
- @tool
200
- def create_object(
201
- type: str = "CUBE",
202
- name: str = None,
203
- location: tuple = (0, 0, 0),
204
- rotation: tuple = (0, 0, 0),
205
- scale: tuple = (1, 1, 1),
206
- ) -> Dict[str, Any]:
207
- """
208
- Create a 3D object in Blender.
209
-
210
- Args:
211
- type: Object type (CUBE, SPHERE, CYLINDER, CONE, TORUS)
212
- name: Optional name for the object (default: generated from type)
213
- location: (x, y, z) coordinates for object position (default: (0,0,0))
214
- rotation: (rx, ry, rz) rotation in radians (default: (0,0,0))
215
- scale: (sx, sy, sz) scaling factors for the object (default: (1,1,1))
216
-
217
- Returns:
218
- Dictionary containing the creation result
219
-
220
- Example JSON response:
221
- ```json
222
- {
223
- "thought": "I will create a cube at the center of the scene",
224
- "goal": "Create a red cube at the center of the scene",
225
- "tool": "create_object",
226
- "tool_args": {
227
- "type": "CUBE",
228
- "name": "my_cube",
229
- "location": [0, 0, 0],
230
- "rotation": [0, 0, 0],
231
- "scale": [1, 1, 1]
232
- }
233
- }
234
- ```
235
- """
236
- try:
237
- result = self.mcp.create_object(
238
- type=type.upper(),
239
- name=name or f"generated_{type.lower()}",
240
- location=location,
241
- rotation=rotation,
242
- scale=scale,
243
- )
244
- return result
245
- except Exception as e:
246
- self.error_history.append(str(e))
247
- return {"status": "error", "error": str(e)}
248
-
249
- @tool
250
- def set_material_color(
251
- object_name: str, color: tuple = (1, 0, 0, 1)
252
- ) -> Dict[str, Any]:
253
- """
254
- Set the material color for an object. Creates a new material if one doesn't exist.
255
-
256
- Args:
257
- object_name: Name of the object to modify
258
- color: RGBA color values as tuple (red, green, blue, alpha), values from 0-1
259
-
260
- Returns:
261
- Dictionary with the operation result
262
-
263
- Example JSON response:
264
- ```json
265
- {
266
- "thought": "I will set the cube's material to red",
267
- "goal": "Create a red cube at the center of the scene",
268
- "tool": "set_material_color",
269
- "tool_args": {
270
- "object_name": "my_cube",
271
- "color": [1, 0, 0, 1]
272
- }
273
- }
274
- ```
275
- """
276
- try:
277
- from gaia.agents.blender.core.materials import MaterialManager
278
-
279
- material_manager = MaterialManager(self.mcp)
280
- return material_manager.set_material_color(object_name, color)
281
- except Exception as e:
282
- self.error_history.append(str(e))
283
- return {"status": "error", "error": str(e)}
284
-
285
- # @tool
286
- def get_object_info(name: str) -> Dict[str, Any]:
287
- """
288
- Get information about an object in the scene.
289
-
290
- Args:
291
- name: Name of the object
292
-
293
- Returns:
294
- Dictionary containing object information
295
-
296
- Example JSON response:
297
- ```json
298
- {
299
- "thought": "I will get information about the cube",
300
- "goal": "Create a red cube at the center of the scene",
301
- "tool": "get_object_info",
302
- "tool_args": {
303
- "name": "my_cube"
304
- }
305
- }
306
- ```
307
- """
308
- try:
309
- return self.mcp.get_object_info(name)
310
- except Exception as e:
311
- self.error_history.append(str(e))
312
- return {"status": "error", "error": str(e)}
313
-
314
- @tool
315
- def modify_object(
316
- name: str,
317
- location: tuple = None,
318
- scale: tuple = None,
319
- rotation: tuple = None,
320
- ) -> Dict[str, Any]:
321
- """
322
- Modify an existing object in Blender.
323
-
324
- Args:
325
- name: Name of the object to modify
326
- location: New (x, y, z) location or None to keep current
327
- scale: New (sx, sy, sz) scale or None to keep current
328
- rotation: New (rx, ry, rz) rotation or None to keep current
329
-
330
- Returns:
331
- Dictionary with the modification result
332
-
333
- Example JSON response:
334
- ```json
335
- {
336
- "thought": "I will move the cube up by 2 units",
337
- "goal": "Create a red cube at the center of the scene",
338
- "tool": "modify_object",
339
- "tool_args": {
340
- "name": "my_cube",
341
- "location": [0, 0, 2],
342
- "scale": null,
343
- "rotation": null
344
- }
345
- }
346
- ```
347
- """
348
- try:
349
- return self.mcp.modify_object(
350
- name=name, location=location, scale=scale, rotation=rotation
351
- )
352
- except Exception as e:
353
- self.error_history.append(str(e))
354
- return {"status": "error", "error": str(e)}
355
-
356
- # @tool
357
- def delete_object(name: str) -> Dict[str, Any]:
358
- """
359
- Delete an object from the scene.
360
-
361
- Args:
362
- name: Name of the object to delete
363
-
364
- Returns:
365
- Dictionary with the deletion result
366
-
367
- Example JSON response:
368
- ```json
369
- {
370
- "thought": "I will delete the cube",
371
- "goal": "Clear the scene to start fresh",
372
- "tool": "delete_object",
373
- "tool_args": {
374
- "name": "my_cube"
375
- }
376
- }
377
- ```
378
- """
379
- try:
380
- return self.mcp.delete_object(name)
381
- except Exception as e:
382
- self.error_history.append(str(e))
383
- return {"status": "error", "error": str(e)}
384
-
385
- @tool
386
- def get_scene_info() -> Dict[str, Any]:
387
- """
388
- Get information about the current scene.
389
-
390
- Returns:
391
- Dictionary containing scene information
392
-
393
- Example JSON response:
394
- ```json
395
- {
396
- "thought": "I will get information about the current scene",
397
- "goal": "Clear the scene to start fresh",
398
- "tool": "get_scene_info",
399
- "tool_args": {}
400
- }
401
- ```
402
- """
403
- try:
404
- return self.mcp.get_scene_info()
405
- except Exception as e:
406
- self.error_history.append(str(e))
407
- return {"status": "error", "error": str(e)}
408
-
409
- # @tool
410
- def execute_blender_code(code: str) -> Dict[str, Any]:
411
- """
412
- Execute arbitrary Python code in Blender with error handling.
413
-
414
- Args:
415
- code: Python code to execute in Blender
416
-
417
- Returns:
418
- Dictionary with execution results or error information
419
-
420
- Example JSON response:
421
- ```json
422
- {
423
- "thought": "I will execute custom code to create a complex shape",
424
- "goal": "Create a red cube at the center of the scene",
425
- "tool": "execute_blender_code",
426
- "tool_args": {
427
- "code": "import bpy\\nbpy.ops.mesh.primitive_cube_add()"
428
- }
429
- }
430
- ```
431
- """
432
- try:
433
- return self.mcp.execute_code(code)
434
- except Exception as e:
435
- self.error_history.append(str(e))
436
- return {"status": "error", "error": str(e)}
437
-
438
- # @tool
439
- def diagnose_scene() -> Dict[str, Any]:
440
- """
441
- Diagnose the current Blender scene for common issues.
442
- Returns information about objects, materials, and potential problems.
443
-
444
- Returns:
445
- Dictionary with diagnostic information
446
-
447
- Example JSON response:
448
- ```json
449
- {
450
- "thought": "I will diagnose the scene for any issues",
451
- "goal": "Clear the scene to start fresh",
452
- "tool": "diagnose_scene",
453
- "tool_args": {}
454
- }
455
- ```
456
- """
457
- try:
458
- # Use the core library's scene diagnosis code generator
459
- diagnostic_code = generate_scene_diagnosis_code()
460
- return self.mcp.execute_code(diagnostic_code)
461
- except Exception as e:
462
- self.error_history.append(str(e))
463
- return {"status": "error", "error": str(e)}
464
-
465
- def _post_process_tool_result(
466
- self, tool_name: str, tool_args: Dict[str, Any], tool_result: Dict[str, Any]
467
- ) -> None:
468
- """
469
- Post-process the tool result for Blender-specific handling.
470
-
471
- Args:
472
- tool_name: Name of the tool that was executed
473
- tool_args: Arguments that were passed to the tool
474
- tool_result: Result returned by the tool
475
- """
476
- # Track object name if created
477
- if tool_name == "create_object":
478
- actual_name = self._track_object_name(tool_result)
479
- if actual_name:
480
- logger.debug(f"Actual object name created: {actual_name}")
481
- self.console.print_info(
482
- f"Note: Blender assigned name '{actual_name}' to the created object"
483
- )
484
-
485
- # Update subsequent steps in the plan that might use this object
486
- if self.current_plan and self.current_step < len(self.current_plan) - 1:
487
- for i in range(self.current_step + 1, len(self.current_plan)):
488
- future_step = self.current_plan[i]
489
- if isinstance(future_step, dict) and "tool_args" in future_step:
490
- args = future_step["tool_args"]
491
- # Look for object_name or name parameters
492
- if "object_name" in args and args[
493
- "object_name"
494
- ] == tool_args.get("name"):
495
- logger.debug(
496
- f"Updating object_name in future step {i+1} from {args['object_name']} to {actual_name}"
497
- )
498
- self.current_plan[i]["tool_args"][
499
- "object_name"
500
- ] = actual_name
501
- if "name" in args and args["name"] == tool_args.get("name"):
502
- logger.debug(
503
- f"Updating name in future step {i+1} from {args['name']} to {actual_name}"
504
- )
505
- self.current_plan[i]["tool_args"]["name"] = actual_name
506
-
507
- def _track_object_name(self, result):
508
- """
509
- Extract and track the actual object name returned by Blender.
510
-
511
- Args:
512
- result: The result dictionary from a tool execution
513
-
514
- Returns:
515
- The actual object name if found, None otherwise
516
- """
517
- try:
518
- if isinstance(result, dict):
519
- if result.get("status") == "success":
520
- if "result" in result and isinstance(result["result"], dict):
521
- # Extract name from create_object result
522
- if "name" in result["result"]:
523
- actual_name = result["result"]["name"]
524
- logger.debug(f"Extracted object name: {actual_name}")
525
- return actual_name
526
- return None
527
- except Exception as e:
528
- logger.error(f"Error extracting object name: {str(e)}")
529
- return None
530
-
531
- def create_interactive_scene(
532
- self,
533
- scene_description: str,
534
- max_steps: int = None,
535
- trace: bool = True,
536
- filename: str = None,
537
- ) -> Dict[str, Any]:
538
- """
539
- Create a more complex scene with multiple objects and relationships.
540
-
541
- Args:
542
- scene_description: Description of the scene to create
543
- max_steps: Maximum number of steps to take in the conversation (overrides class default if provided)
544
- trace: If True, write detailed trace to file
545
- filename: Optional filename for trace output, if None a timestamped name will be generated
546
-
547
- Returns:
548
- Dict containing the scene creation result
549
- """
550
- # Same process as process_query but with more steps allowed if specified
551
- return self.process_query(
552
- f"Create a complete 3D scene with the following description: {scene_description}",
553
- max_steps=max_steps if max_steps is not None else self.max_steps * 2,
554
- trace=trace,
555
- filename=filename,
556
- )
1
+ # Copyright(C) 2025-2026 Advanced Micro Devices, Inc. All rights reserved.
2
+ # SPDX-License-Identifier: MIT
3
+ """
4
+ Blender-specific agent for creating and modifying 3D scenes.
5
+ """
6
+
7
+ import logging
8
+ from typing import Any, Dict, Optional
9
+
10
+ from gaia.agents.base.agent import Agent
11
+ from gaia.agents.base.console import AgentConsole
12
+ from gaia.agents.base.tools import tool
13
+ from gaia.agents.blender.core.scene import generate_scene_diagnosis_code
14
+ from gaia.mcp.blender_mcp_client import MCPClient
15
+
16
+ # Set up logging
17
+ logging.basicConfig(level=logging.INFO)
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class BlenderAgent(Agent):
22
+ """
23
+ Blender-specific agent focused on 3D scene creation and modification.
24
+ Inherits core functionality from the base Agent class.
25
+ """
26
+
27
+ # Define Blender-specific tools that can execute directly without requiring a plan
28
+ SIMPLE_TOOLS = ["clear_scene", "get_scene_info"]
29
+
30
+ def __init__(
31
+ self,
32
+ mcp: Optional[MCPClient] = None,
33
+ model_id: str = None,
34
+ base_url: str = "http://localhost:8000/api/v1",
35
+ max_steps: int = 5,
36
+ debug_prompts: bool = False,
37
+ output_dir: str = None,
38
+ streaming: bool = False,
39
+ show_stats: bool = True,
40
+ ):
41
+ """
42
+ Initialize the BlenderAgent with MCP client and LLM client.
43
+
44
+ Args:
45
+ mcp: An optional pre-configured MCP client, otherwise a new one will be created
46
+ model_id: The ID of the model to use with LLM server
47
+ base_url: Base URL for the local LLM server API
48
+ max_steps: Maximum number of steps the agent can take before terminating
49
+ debug_prompts: If True, includes prompts in the conversation history
50
+ output_dir: Directory for storing JSON output files (default: current directory)
51
+ streaming: If True, enables real-time streaming of LLM responses (default: False)
52
+ show_stats: If True, displays LLM performance stats after each response (default: True)
53
+ """
54
+ # Initialize the MCP client for Blender communication
55
+ self.mcp = mcp if mcp else MCPClient()
56
+
57
+ # Call the parent class constructor
58
+ super().__init__(
59
+ model_id=model_id,
60
+ base_url=base_url,
61
+ max_steps=max_steps,
62
+ debug_prompts=debug_prompts,
63
+ output_dir=output_dir,
64
+ streaming=streaming,
65
+ show_stats=show_stats,
66
+ )
67
+
68
+ # Register Blender-specific tools
69
+ self._register_tools()
70
+
71
+ def _create_console(self) -> AgentConsole:
72
+ """
73
+ Create and return a Agent-specific console output handler.
74
+
75
+ Returns:
76
+ A AgentConsole instance
77
+ """
78
+ return AgentConsole()
79
+
80
+ def _get_system_prompt(self) -> str:
81
+ """Generate the system prompt for the Blender agent."""
82
+ # Get formatted tools from registry
83
+ return f"""
84
+ You are a specialized Blender 3D assistant that can create and modify 3D scenes.
85
+ You will use a set of tools to accomplish tasks based on the user's request.
86
+
87
+ ==== JSON RESPONSE FORMAT ====
88
+ ALWAYS respond with a single valid JSON object. NO text outside this structure.
89
+ - Use double quotes for keys and string values
90
+ - Ensure all braces and brackets are properly closed
91
+ - No trailing commas in arrays or objects
92
+ - All required fields must be included
93
+ - Never wrap your JSON in code blocks or backticks
94
+
95
+ Your JSON response must follow this format:
96
+ {{
97
+ "thought": "your reasoning about what to do",
98
+ "goal": "clear statement of what you're achieving",
99
+ "plan": [
100
+ {{"tool": "tool1", "tool_args": {{"arg1": "val1"}}}},
101
+ {{"tool": "tool2", "tool_args": {{"arg1": "val1"}}}}
102
+ ],
103
+ "tool": "first_tool_to_execute",
104
+ "tool_args": {{"arg1": "val1", "arg2": "val2"}}
105
+ }}
106
+
107
+ For final answers:
108
+ {{
109
+ "thought": "your reasoning",
110
+ "goal": "what was achieved",
111
+ "answer": "your final answer"
112
+ }}
113
+
114
+ ==== CRITICAL RULES ====
115
+ 1. Create a plan for multi-step tasks, but simple single operations (like clear_scene) can execute directly
116
+ 2. Each plan step must be atomic (one tool call per step)
117
+ 3. For colored objects, ALWAYS include both create_object AND set_material_color steps
118
+ 4. When clearing a scene, ONLY use clear_scene without creating new objects unless requested
119
+ 5. Always use the actual returned object names for subsequent operations
120
+ 6. Never repeat the same tool call with identical arguments
121
+
122
+ ==== COLORED OBJECT DETECTION ====
123
+ 🔍 SCAN the user request for color words:
124
+ - "red", "green", "blue", "yellow", "purple", "cyan", "white", "black"
125
+ - "colored", "paint", "material"
126
+
127
+ ⚠️ IF you find ANY color words, you MUST:
128
+ 1. Create the object with create_object
129
+ 2. Set its color with set_material_color
130
+ 3. Then do any other modifications
131
+
132
+ ❌ NEVER skip the color step if a color is mentioned!
133
+
134
+ Examples of colored requests:
135
+ - "blue cylinder" → needs create_object + set_material_color
136
+ - "red sphere" → needs create_object + set_material_color
137
+ - "green cube and yellow cone" → needs 4 steps total
138
+
139
+ ==== TOOL PARAMETER RULES ====
140
+ ⚠️ CRITICAL: create_object does NOT accept a 'color' parameter!
141
+ ✅ CORRECT workflow for colored objects:
142
+ Step 1: create_object (type, name, location, rotation, scale ONLY)
143
+ Step 2: set_material_color (object_name, color)
144
+
145
+ ⚠️ CRITICAL: Colors must be RGBA format with 4 values [r, g, b, a]
146
+ ❌ WRONG: [0, 0, 1] (only 3 values)
147
+ ✅ CORRECT: [0, 0, 1, 1] (4 values including alpha)
148
+
149
+ ⚠️ CRITICAL: EVERY colored object must have BOTH steps!
150
+ If user asks for "green cube and red sphere", you need 4 steps:
151
+ 1. create_object (cube)
152
+ 2. set_material_color (cube, green)
153
+ 3. create_object (sphere)
154
+ 4. set_material_color (sphere, red)
155
+
156
+ ==== COMMON WORKFLOWS ====
157
+ 1. Clearing a scene: Use clear_scene() with no arguments
158
+ 2. Creating a single colored object:
159
+ - Step 1: create_object(type="CYLINDER", name="my_obj", location=[0,0,0])
160
+ - Step 2: set_material_color(object_name="my_obj", color=[0,0,1,1])
161
+ 3. Creating multiple colored objects:
162
+ - Step 1: create_object(type="CUBE", name="cube1", location=[0,0,0])
163
+ - Step 2: set_material_color(object_name="cube1", color=[0,1,0,1])
164
+ - Step 3: create_object(type="SPHERE", name="sphere1", location=[3,0,0])
165
+ - Step 4: set_material_color(object_name="sphere1", color=[1,0,0,1])
166
+ 4. Modifying objects: Use modify_object with the parameters you want to change
167
+ """
168
+
169
+ def _register_tools(self):
170
+ """Register all Blender-related tools for the agent."""
171
+
172
+ @tool
173
+ def clear_scene() -> Dict[str, Any]:
174
+ """
175
+ Remove all objects from the current Blender scene.
176
+
177
+ Returns:
178
+ Dictionary containing the operation result
179
+
180
+ Example JSON response:
181
+ ```json
182
+ {
183
+ "thought": "I will clear the scene to start fresh",
184
+ "goal": "Clear the scene to start fresh",
185
+ "tool": "clear_scene",
186
+ "tool_args": {}
187
+ }
188
+ ```
189
+ """
190
+ try:
191
+ from gaia.agents.blender.core.scene import SceneManager
192
+
193
+ scene_manager = SceneManager(self.mcp)
194
+ return scene_manager.clear_scene()
195
+ except Exception as e:
196
+ self.error_history.append(str(e))
197
+ return {"status": "error", "error": str(e)}
198
+
199
+ @tool
200
+ def create_object(
201
+ type: str = "CUBE",
202
+ name: str = None,
203
+ location: tuple = (0, 0, 0),
204
+ rotation: tuple = (0, 0, 0),
205
+ scale: tuple = (1, 1, 1),
206
+ ) -> Dict[str, Any]:
207
+ """
208
+ Create a 3D object in Blender.
209
+
210
+ Args:
211
+ type: Object type (CUBE, SPHERE, CYLINDER, CONE, TORUS)
212
+ name: Optional name for the object (default: generated from type)
213
+ location: (x, y, z) coordinates for object position (default: (0,0,0))
214
+ rotation: (rx, ry, rz) rotation in radians (default: (0,0,0))
215
+ scale: (sx, sy, sz) scaling factors for the object (default: (1,1,1))
216
+
217
+ Returns:
218
+ Dictionary containing the creation result
219
+
220
+ Example JSON response:
221
+ ```json
222
+ {
223
+ "thought": "I will create a cube at the center of the scene",
224
+ "goal": "Create a red cube at the center of the scene",
225
+ "tool": "create_object",
226
+ "tool_args": {
227
+ "type": "CUBE",
228
+ "name": "my_cube",
229
+ "location": [0, 0, 0],
230
+ "rotation": [0, 0, 0],
231
+ "scale": [1, 1, 1]
232
+ }
233
+ }
234
+ ```
235
+ """
236
+ try:
237
+ result = self.mcp.create_object(
238
+ type=type.upper(),
239
+ name=name or f"generated_{type.lower()}",
240
+ location=location,
241
+ rotation=rotation,
242
+ scale=scale,
243
+ )
244
+ return result
245
+ except Exception as e:
246
+ self.error_history.append(str(e))
247
+ return {"status": "error", "error": str(e)}
248
+
249
+ @tool
250
+ def set_material_color(
251
+ object_name: str, color: tuple = (1, 0, 0, 1)
252
+ ) -> Dict[str, Any]:
253
+ """
254
+ Set the material color for an object. Creates a new material if one doesn't exist.
255
+
256
+ Args:
257
+ object_name: Name of the object to modify
258
+ color: RGBA color values as tuple (red, green, blue, alpha), values from 0-1
259
+
260
+ Returns:
261
+ Dictionary with the operation result
262
+
263
+ Example JSON response:
264
+ ```json
265
+ {
266
+ "thought": "I will set the cube's material to red",
267
+ "goal": "Create a red cube at the center of the scene",
268
+ "tool": "set_material_color",
269
+ "tool_args": {
270
+ "object_name": "my_cube",
271
+ "color": [1, 0, 0, 1]
272
+ }
273
+ }
274
+ ```
275
+ """
276
+ try:
277
+ from gaia.agents.blender.core.materials import MaterialManager
278
+
279
+ material_manager = MaterialManager(self.mcp)
280
+ return material_manager.set_material_color(object_name, color)
281
+ except Exception as e:
282
+ self.error_history.append(str(e))
283
+ return {"status": "error", "error": str(e)}
284
+
285
+ # @tool
286
+ def get_object_info(name: str) -> Dict[str, Any]:
287
+ """
288
+ Get information about an object in the scene.
289
+
290
+ Args:
291
+ name: Name of the object
292
+
293
+ Returns:
294
+ Dictionary containing object information
295
+
296
+ Example JSON response:
297
+ ```json
298
+ {
299
+ "thought": "I will get information about the cube",
300
+ "goal": "Create a red cube at the center of the scene",
301
+ "tool": "get_object_info",
302
+ "tool_args": {
303
+ "name": "my_cube"
304
+ }
305
+ }
306
+ ```
307
+ """
308
+ try:
309
+ return self.mcp.get_object_info(name)
310
+ except Exception as e:
311
+ self.error_history.append(str(e))
312
+ return {"status": "error", "error": str(e)}
313
+
314
+ @tool
315
+ def modify_object(
316
+ name: str,
317
+ location: tuple = None,
318
+ scale: tuple = None,
319
+ rotation: tuple = None,
320
+ ) -> Dict[str, Any]:
321
+ """
322
+ Modify an existing object in Blender.
323
+
324
+ Args:
325
+ name: Name of the object to modify
326
+ location: New (x, y, z) location or None to keep current
327
+ scale: New (sx, sy, sz) scale or None to keep current
328
+ rotation: New (rx, ry, rz) rotation or None to keep current
329
+
330
+ Returns:
331
+ Dictionary with the modification result
332
+
333
+ Example JSON response:
334
+ ```json
335
+ {
336
+ "thought": "I will move the cube up by 2 units",
337
+ "goal": "Create a red cube at the center of the scene",
338
+ "tool": "modify_object",
339
+ "tool_args": {
340
+ "name": "my_cube",
341
+ "location": [0, 0, 2],
342
+ "scale": null,
343
+ "rotation": null
344
+ }
345
+ }
346
+ ```
347
+ """
348
+ try:
349
+ return self.mcp.modify_object(
350
+ name=name, location=location, scale=scale, rotation=rotation
351
+ )
352
+ except Exception as e:
353
+ self.error_history.append(str(e))
354
+ return {"status": "error", "error": str(e)}
355
+
356
+ # @tool
357
+ def delete_object(name: str) -> Dict[str, Any]:
358
+ """
359
+ Delete an object from the scene.
360
+
361
+ Args:
362
+ name: Name of the object to delete
363
+
364
+ Returns:
365
+ Dictionary with the deletion result
366
+
367
+ Example JSON response:
368
+ ```json
369
+ {
370
+ "thought": "I will delete the cube",
371
+ "goal": "Clear the scene to start fresh",
372
+ "tool": "delete_object",
373
+ "tool_args": {
374
+ "name": "my_cube"
375
+ }
376
+ }
377
+ ```
378
+ """
379
+ try:
380
+ return self.mcp.delete_object(name)
381
+ except Exception as e:
382
+ self.error_history.append(str(e))
383
+ return {"status": "error", "error": str(e)}
384
+
385
+ @tool
386
+ def get_scene_info() -> Dict[str, Any]:
387
+ """
388
+ Get information about the current scene.
389
+
390
+ Returns:
391
+ Dictionary containing scene information
392
+
393
+ Example JSON response:
394
+ ```json
395
+ {
396
+ "thought": "I will get information about the current scene",
397
+ "goal": "Clear the scene to start fresh",
398
+ "tool": "get_scene_info",
399
+ "tool_args": {}
400
+ }
401
+ ```
402
+ """
403
+ try:
404
+ return self.mcp.get_scene_info()
405
+ except Exception as e:
406
+ self.error_history.append(str(e))
407
+ return {"status": "error", "error": str(e)}
408
+
409
+ # @tool
410
+ def execute_blender_code(code: str) -> Dict[str, Any]:
411
+ """
412
+ Execute arbitrary Python code in Blender with error handling.
413
+
414
+ Args:
415
+ code: Python code to execute in Blender
416
+
417
+ Returns:
418
+ Dictionary with execution results or error information
419
+
420
+ Example JSON response:
421
+ ```json
422
+ {
423
+ "thought": "I will execute custom code to create a complex shape",
424
+ "goal": "Create a red cube at the center of the scene",
425
+ "tool": "execute_blender_code",
426
+ "tool_args": {
427
+ "code": "import bpy\\nbpy.ops.mesh.primitive_cube_add()"
428
+ }
429
+ }
430
+ ```
431
+ """
432
+ try:
433
+ return self.mcp.execute_code(code)
434
+ except Exception as e:
435
+ self.error_history.append(str(e))
436
+ return {"status": "error", "error": str(e)}
437
+
438
+ # @tool
439
+ def diagnose_scene() -> Dict[str, Any]:
440
+ """
441
+ Diagnose the current Blender scene for common issues.
442
+ Returns information about objects, materials, and potential problems.
443
+
444
+ Returns:
445
+ Dictionary with diagnostic information
446
+
447
+ Example JSON response:
448
+ ```json
449
+ {
450
+ "thought": "I will diagnose the scene for any issues",
451
+ "goal": "Clear the scene to start fresh",
452
+ "tool": "diagnose_scene",
453
+ "tool_args": {}
454
+ }
455
+ ```
456
+ """
457
+ try:
458
+ # Use the core library's scene diagnosis code generator
459
+ diagnostic_code = generate_scene_diagnosis_code()
460
+ return self.mcp.execute_code(diagnostic_code)
461
+ except Exception as e:
462
+ self.error_history.append(str(e))
463
+ return {"status": "error", "error": str(e)}
464
+
465
+ def _post_process_tool_result(
466
+ self, tool_name: str, tool_args: Dict[str, Any], tool_result: Dict[str, Any]
467
+ ) -> None:
468
+ """
469
+ Post-process the tool result for Blender-specific handling.
470
+
471
+ Args:
472
+ tool_name: Name of the tool that was executed
473
+ tool_args: Arguments that were passed to the tool
474
+ tool_result: Result returned by the tool
475
+ """
476
+ # Track object name if created
477
+ if tool_name == "create_object":
478
+ actual_name = self._track_object_name(tool_result)
479
+ if actual_name:
480
+ logger.debug(f"Actual object name created: {actual_name}")
481
+ self.console.print_info(
482
+ f"Note: Blender assigned name '{actual_name}' to the created object"
483
+ )
484
+
485
+ # Update subsequent steps in the plan that might use this object
486
+ if self.current_plan and self.current_step < len(self.current_plan) - 1:
487
+ for i in range(self.current_step + 1, len(self.current_plan)):
488
+ future_step = self.current_plan[i]
489
+ if isinstance(future_step, dict) and "tool_args" in future_step:
490
+ args = future_step["tool_args"]
491
+ # Look for object_name or name parameters
492
+ if "object_name" in args and args[
493
+ "object_name"
494
+ ] == tool_args.get("name"):
495
+ logger.debug(
496
+ f"Updating object_name in future step {i+1} from {args['object_name']} to {actual_name}"
497
+ )
498
+ self.current_plan[i]["tool_args"][
499
+ "object_name"
500
+ ] = actual_name
501
+ if "name" in args and args["name"] == tool_args.get("name"):
502
+ logger.debug(
503
+ f"Updating name in future step {i+1} from {args['name']} to {actual_name}"
504
+ )
505
+ self.current_plan[i]["tool_args"]["name"] = actual_name
506
+
507
+ def _track_object_name(self, result):
508
+ """
509
+ Extract and track the actual object name returned by Blender.
510
+
511
+ Args:
512
+ result: The result dictionary from a tool execution
513
+
514
+ Returns:
515
+ The actual object name if found, None otherwise
516
+ """
517
+ try:
518
+ if isinstance(result, dict):
519
+ if result.get("status") == "success":
520
+ if "result" in result and isinstance(result["result"], dict):
521
+ # Extract name from create_object result
522
+ if "name" in result["result"]:
523
+ actual_name = result["result"]["name"]
524
+ logger.debug(f"Extracted object name: {actual_name}")
525
+ return actual_name
526
+ return None
527
+ except Exception as e:
528
+ logger.error(f"Error extracting object name: {str(e)}")
529
+ return None
530
+
531
+ def create_interactive_scene(
532
+ self,
533
+ scene_description: str,
534
+ max_steps: int = None,
535
+ trace: bool = True,
536
+ filename: str = None,
537
+ ) -> Dict[str, Any]:
538
+ """
539
+ Create a more complex scene with multiple objects and relationships.
540
+
541
+ Args:
542
+ scene_description: Description of the scene to create
543
+ max_steps: Maximum number of steps to take in the conversation (overrides class default if provided)
544
+ trace: If True, write detailed trace to file
545
+ filename: Optional filename for trace output, if None a timestamped name will be generated
546
+
547
+ Returns:
548
+ Dict containing the scene creation result
549
+ """
550
+ # Same process as process_query but with more steps allowed if specified
551
+ return self.process_query(
552
+ f"Create a complete 3D scene with the following description: {scene_description}",
553
+ max_steps=max_steps if max_steps is not None else self.max_steps * 2,
554
+ trace=trace,
555
+ filename=filename,
556
+ )