From 924dcf8701249cf4ad3db1c70220eea1be40bd8f Mon Sep 17 00:00:00 2001 From: rootflo-hardik Date: Mon, 5 Jan 2026 19:00:29 +0530 Subject: [PATCH 1/3] add existing sub-workflow inside workflow --- .../services/workflow_crud_service.py | 174 +++++++++++++++++- .../services/workflow_inference_service.py | 120 +++++++++++- 2 files changed, 292 insertions(+), 2 deletions(-) diff --git a/wavefront/server/modules/agents_module/agents_module/services/workflow_crud_service.py b/wavefront/server/modules/agents_module/agents_module/services/workflow_crud_service.py index e09087c0..b84330c2 100644 --- a/wavefront/server/modules/agents_module/agents_module/services/workflow_crud_service.py +++ b/wavefront/server/modules/agents_module/agents_module/services/workflow_crud_service.py @@ -85,6 +85,42 @@ def _extract_agent_references(self, yaml_content: str) -> List[str]: logger.error(f'Error extracting agent references from YAML: {str(e)}') return [] + def _extract_subworkflow_references(self, yaml_content: str) -> List[str]: + """ + Extract subworkflow references (namespace/workflow_name) from workflow YAML + + Args: + yaml_content: YAML configuration content + + Returns: + List of subworkflow references in format 'namespace/workflow_name' + """ + try: + yaml_data = yaml.safe_load(yaml_content) + arium_config = yaml_data.get('arium', {}) + ariums_config = arium_config.get('ariums', []) + + subworkflow_references = [] + for arium_def in ariums_config: + arium_name = arium_def.get('name', '') + # If arium name contains '/', it's a reference to cloud storage + if '/' in arium_name: + # Check if this is a reference (not an inline definition) + # If it has agents, workflow, etc., it's inline, not a reference + if ( + arium_def.get('agents') is None + and arium_def.get('workflow') is None + and arium_def.get('function_nodes') is None + and arium_def.get('yaml_file') is None + ): + subworkflow_references.append(arium_name) + logger.info(f'Found subworkflow reference: {arium_name}') + + return subworkflow_references + except Exception as e: + logger.error(f'Error extracting subworkflow references from YAML: {str(e)}') + return [] + async def _build_referenced_agents( self, agent_references: List[str], @@ -158,6 +194,132 @@ async def _build_referenced_agents( return agents_dict + async def get_workflow_yaml_from_bucket( + self, workflow_name: str, namespace: str + ) -> str: + """ + Get workflow YAML content by name and namespace (for workflow references) + + This method is used to fetch subworkflow YAML when they + have namespace/workflow_name references + + Args: + workflow_name: The workflow name + namespace: The namespace name + + Returns: + str: The YAML content as string + + Raises: + ValueError: If workflow not found + """ + # Try YAML cache first + yaml_cache_key = get_workflow_yaml_cache_key(namespace, workflow_name) + cached_yaml = self.cache_manager.get_str(yaml_cache_key) + + if cached_yaml: + logger.info( + f'Cache hit for workflow YAML - namespace: {namespace}, name: {workflow_name}' + ) + return cached_yaml + + # Fetch YAML from cloud storage + yaml_key = get_workflow_yaml_key(namespace, workflow_name) + logger.info(f'Fetching workflow YAML from storage - key: {yaml_key}') + + try: + yaml_bytes = self.cloud_storage_manager.read_file( + self.bucket_name, yaml_key + ) + yaml_content = yaml_bytes.decode('utf-8') + + # Cache YAML + self.cache_manager.add(yaml_cache_key, yaml_content, expiry=self.cache_ttl) + except CloudStorageFileNotFoundError: + logger.error( + f'YAML not found in cloud storage for workflow: {namespace}/{workflow_name}' + ) + raise ValueError( + f'Workflow YAML not found for workflow: {namespace}/{workflow_name}' + ) + + logger.info( + f'Successfully retrieved workflow YAML - namespace: {namespace}, name: {workflow_name}' + ) + return yaml_content + + async def _inline_subworkflow_references( + self, + yaml_content: str, + subworkflow_references: List[str], + ) -> str: + """ + Fetch subworkflow YAML and inline them into the parent workflow YAML + + Args: + yaml_content: Original YAML configuration content + subworkflow_references: List of subworkflow references to inline + + Returns: + Modified YAML content with inlined subworkflow definitions + """ + if not subworkflow_references: + return yaml_content + + yaml_data = yaml.safe_load(yaml_content) + arium_config = yaml_data.get('arium', {}) + ariums_config = arium_config.get('ariums', []) + + # Build a dict to quickly look up subworkflow configs + subworkflow_configs = {} + for ref in subworkflow_references: + parts = ref.split('/', 1) + namespace = parts[0] + workflow_name = parts[1] + + logger.info(f'Fetching subworkflow YAML for inlining: {ref}') + + # Fetch the subworkflow YAML + subworkflow_yaml_content = await self.get_workflow_yaml_from_bucket( + workflow_name, namespace + ) + + # Parse and extract the arium config + subworkflow_data = yaml.safe_load(subworkflow_yaml_content) + subworkflow_arium = subworkflow_data.get('arium', {}) + + subworkflow_configs[ref] = subworkflow_arium + + # Replace references with inline definitions + updated_ariums = [] + for arium_def in ariums_config: + arium_name = arium_def.get('name', '') + + if arium_name in subworkflow_configs: + # This is a reference - inline it + logger.info(f'Inlining subworkflow: {arium_name}') + + # Get the fetched subworkflow config + inline_config = subworkflow_configs[arium_name].copy() + + # Preserve inherit_variables from the reference + if 'inherit_variables' in arium_def: + inline_config['inherit_variables'] = arium_def['inherit_variables'] + + # Update the name to be local (remove namespace prefix) + inline_config['name'] = arium_name.split('/')[-1] + + updated_ariums.append(inline_config) + else: + # Not a reference - keep as is + updated_ariums.append(arium_def) + + # Update the YAML data + yaml_data['arium']['ariums'] = updated_ariums + + # Convert back to YAML string + return yaml.dump(yaml_data, default_flow_style=False, sort_keys=False) + async def _validate_yaml_content( self, yaml_content: str, @@ -178,6 +340,16 @@ async def _validate_yaml_content( ValueError: If YAML is invalid or workflow cannot be built """ try: + # Extract and inline subworkflow references + subworkflow_references = self._extract_subworkflow_references(yaml_content) + if subworkflow_references: + logger.info( + f'Inlining {len(subworkflow_references)} referenced subworkflows for validation' + ) + yaml_content = await self._inline_subworkflow_references( + yaml_content, subworkflow_references + ) + # Extract and build referenced agents agent_references = self._extract_agent_references(yaml_content) agents_dict = {} @@ -190,7 +362,7 @@ async def _validate_yaml_content( agent_references, access_token, app_key ) - # Validate workflow with pre-built agents + # Validate workflow with pre-built agents and inlined subworkflows arium_instance = AriumBuilder.from_yaml( yaml_str=yaml_content, agents=agents_dict, diff --git a/wavefront/server/modules/agents_module/agents_module/services/workflow_inference_service.py b/wavefront/server/modules/agents_module/agents_module/services/workflow_inference_service.py index 8762e415..3a694661 100644 --- a/wavefront/server/modules/agents_module/agents_module/services/workflow_inference_service.py +++ b/wavefront/server/modules/agents_module/agents_module/services/workflow_inference_service.py @@ -106,6 +106,42 @@ def _extract_agent_references(self, yaml_content: str) -> List[str]: logger.error(f'Error extracting agent references from YAML: {str(e)}') return [] + def _extract_subworkflow_references(self, yaml_content: str) -> List[str]: + """ + Extract subworkflow references (namespace/workflow_name) from workflow YAML + + Args: + yaml_content: YAML configuration content + + Returns: + List of subworkflow references in format 'namespace/workflow_name' + """ + try: + yaml_data = yaml.safe_load(yaml_content) + arium_config = yaml_data.get('arium', {}) + ariums_config = arium_config.get('ariums', []) + + subworkflow_references = [] + for arium_def in ariums_config: + arium_name = arium_def.get('name', '') + # If arium name contains '/', it's a reference to cloud storage + if '/' in arium_name: + # Check if this is a reference (not an inline definition) + # If it has agents, workflow, etc., it's inline, not a reference + if ( + arium_def.get('agents') is None + and arium_def.get('workflow') is None + and arium_def.get('function_nodes') is None + and arium_def.get('yaml_file') is None + ): + subworkflow_references.append(arium_name) + logger.info(f'Found subworkflow reference: {arium_name}') + + return subworkflow_references + except Exception as e: + logger.error(f'Error extracting subworkflow references from YAML: {str(e)}') + return [] + async def _build_referenced_agents( self, agent_references: List[str], @@ -181,6 +217,78 @@ async def _build_referenced_agents( return agents_dict + async def _inline_subworkflow_references( + self, + yaml_content: str, + subworkflow_references: List[str], + ) -> str: + """ + Fetch subworkflow YAML and inline them into the parent workflow YAML + + Args: + yaml_content: Original YAML configuration content + subworkflow_references: List of subworkflow references to inline + + Returns: + Modified YAML content with inlined subworkflow definitions + """ + if not subworkflow_references: + return yaml_content + + yaml_data = yaml.safe_load(yaml_content) + arium_config = yaml_data.get('arium', {}) + ariums_config = arium_config.get('ariums', []) + + # Build a dict to quickly look up subworkflow configs + subworkflow_configs = {} + for ref in subworkflow_references: + parts = ref.split('/', 1) + namespace = parts[0] + workflow_name = parts[1] + + logger.info(f'Fetching subworkflow YAML for inlining: {ref}') + + # Fetch the subworkflow YAML using existing fetch method + subworkflow_yaml_content = await self.fetch_workflow_yaml( + workflow_name, namespace + ) + + # Parse and extract the arium config + subworkflow_data = yaml.safe_load(subworkflow_yaml_content) + subworkflow_arium = subworkflow_data.get('arium', {}) + + subworkflow_configs[ref] = subworkflow_arium + + # Replace references with inline definitions + updated_ariums = [] + for arium_def in ariums_config: + arium_name = arium_def.get('name', '') + + if arium_name in subworkflow_configs: + # This is a reference - inline it + logger.info(f'Inlining subworkflow: {arium_name}') + + # Get the fetched subworkflow config + inline_config = subworkflow_configs[arium_name].copy() + + # Preserve inherit_variables from the reference + if 'inherit_variables' in arium_def: + inline_config['inherit_variables'] = arium_def['inherit_variables'] + + # Update the name to be local (remove namespace prefix) + inline_config['name'] = arium_name.split('/')[-1] + + updated_ariums.append(inline_config) + else: + # Not a reference - keep as is + updated_ariums.append(arium_def) + + # Update the YAML data + yaml_data['arium']['ariums'] = updated_ariums + + # Convert back to YAML string + return yaml.dump(yaml_data, default_flow_style=False, sort_keys=False) + async def create_workflow_from_yaml( self, yaml_content: str, @@ -200,6 +308,16 @@ async def create_workflow_from_yaml( """ logger.info(f'Creating workflow from YAML for workflow: {workflow_name}') + # Extract and inline subworkflow references + subworkflow_references = self._extract_subworkflow_references(yaml_content) + if subworkflow_references: + logger.info( + f'Inlining {len(subworkflow_references)} referenced subworkflows for workflow {workflow_name}' + ) + yaml_content = await self._inline_subworkflow_references( + yaml_content, subworkflow_references + ) + # Extract and build referenced agents agent_references = self._extract_agent_references(yaml_content) agents_dict = {} @@ -212,7 +330,7 @@ async def create_workflow_from_yaml( agent_references, access_token, app_key ) - # Build workflow with pre-built agents + # Build workflow with pre-built agents and inlined subworkflows workflow_builder = AriumBuilder.from_yaml( agents=agents_dict, yaml_str=yaml_content, From 4e127ac1978fba2fcd47ff62511da43855cffad2 Mon Sep 17 00:00:00 2001 From: rootflo-hardik Date: Wed, 27 May 2026 17:05:43 +0530 Subject: [PATCH 2/3] resolved review comment --- .../services/workflow_inference_service.py | 76 +++++++++---------- 1 file changed, 34 insertions(+), 42 deletions(-) diff --git a/wavefront/server/modules/agents_module/agents_module/services/workflow_inference_service.py b/wavefront/server/modules/agents_module/agents_module/services/workflow_inference_service.py index 3a694661..a7d7f4f9 100644 --- a/wavefront/server/modules/agents_module/agents_module/services/workflow_inference_service.py +++ b/wavefront/server/modules/agents_module/agents_module/services/workflow_inference_service.py @@ -88,23 +88,19 @@ def _extract_agent_references(self, yaml_content: str) -> List[str]: Returns: List of agent references in format 'namespace/agent_name' """ - try: - yaml_data = yaml.safe_load(yaml_content) - arium_config = yaml_data.get('arium', {}) - agents_config = arium_config.get('agents', []) - - agent_references = [] - for agent_def in agents_config: - agent_name = agent_def.get('name', '') - # If agent name contains '/', it's a reference to cloud storage - if '/' in agent_name: - agent_references.append(agent_name) - logger.info(f'Found agent reference: {agent_name}') - - return agent_references - except Exception as e: - logger.error(f'Error extracting agent references from YAML: {str(e)}') - return [] + yaml_data = yaml.safe_load(yaml_content) + arium_config = yaml_data.get('arium', {}) + agents_config = arium_config.get('agents', []) + + agent_references = [] + for agent_def in agents_config: + agent_name = agent_def.get('name', '') + # If agent name contains '/', it's a reference to cloud storage + if '/' in agent_name: + agent_references.append(agent_name) + logger.info(f'Found agent reference: {agent_name}') + + return agent_references def _extract_subworkflow_references(self, yaml_content: str) -> List[str]: """ @@ -116,31 +112,27 @@ def _extract_subworkflow_references(self, yaml_content: str) -> List[str]: Returns: List of subworkflow references in format 'namespace/workflow_name' """ - try: - yaml_data = yaml.safe_load(yaml_content) - arium_config = yaml_data.get('arium', {}) - ariums_config = arium_config.get('ariums', []) - - subworkflow_references = [] - for arium_def in ariums_config: - arium_name = arium_def.get('name', '') - # If arium name contains '/', it's a reference to cloud storage - if '/' in arium_name: - # Check if this is a reference (not an inline definition) - # If it has agents, workflow, etc., it's inline, not a reference - if ( - arium_def.get('agents') is None - and arium_def.get('workflow') is None - and arium_def.get('function_nodes') is None - and arium_def.get('yaml_file') is None - ): - subworkflow_references.append(arium_name) - logger.info(f'Found subworkflow reference: {arium_name}') - - return subworkflow_references - except Exception as e: - logger.error(f'Error extracting subworkflow references from YAML: {str(e)}') - return [] + yaml_data = yaml.safe_load(yaml_content) + arium_config = yaml_data.get('arium', {}) + ariums_config = arium_config.get('ariums', []) + + subworkflow_references = [] + for arium_def in ariums_config: + arium_name = arium_def.get('name', '') + # If arium name contains '/', it's a reference to cloud storage + if '/' in arium_name: + # Check if this is a reference (not an inline definition) + # If it has agents, workflow, etc., it's inline, not a reference + if ( + arium_def.get('agents') is None + and arium_def.get('workflow') is None + and arium_def.get('function_nodes') is None + and arium_def.get('yaml_file') is None + ): + subworkflow_references.append(arium_name) + logger.info(f'Found subworkflow reference: {arium_name}') + + return subworkflow_references async def _build_referenced_agents( self, From 666aec8ce01b0835bf926c62b76ef6424dc1df6f Mon Sep 17 00:00:00 2001 From: rootflo-hardik Date: Thu, 28 May 2026 17:41:34 +0530 Subject: [PATCH 3/3] use model_id from agent yaml directly llm_config --- .../server/apps/floware/floware/server.py | 21 +++--- .../agents_module/agents_container.py | 3 + .../controllers/agent_controller.py | 25 ++----- .../services/agent_inference_service.py | 70 ++++++++++++++++++- 4 files changed, 86 insertions(+), 33 deletions(-) diff --git a/wavefront/server/apps/floware/floware/server.py b/wavefront/server/apps/floware/floware/server.py index 9f99134f..6ebec535 100644 --- a/wavefront/server/apps/floware/floware/server.py +++ b/wavefront/server/apps/floware/floware/server.py @@ -170,6 +170,16 @@ message_processor_bucket_name=bucket_name, ) +inference_container = InferenceContainer( + db_client=db_repo_container.db_client, + cache_manager=db_repo_container.cache_manager, +) + +llm_inference_config_container = LlmInferenceConfigContainer( + db_client=db_repo_container.db_client, + cache_manager=db_repo_container.cache_manager, +) + agents_container = AgentsContainer( db_client=db_repo_container.db_client, cloud_storage_manager=common_container.cloud_storage_manager, @@ -185,16 +195,7 @@ api_services_manager=api_services_container.api_service_manager, async_agentic_execution_repository=db_repo_container.async_agentic_execution_repository, executions_bucket=config['agents']['executions_bucket'], -) - -inference_container = InferenceContainer( - db_client=db_repo_container.db_client, - cache_manager=db_repo_container.cache_manager, -) - -llm_inference_config_container = LlmInferenceConfigContainer( - db_client=db_repo_container.db_client, - cache_manager=db_repo_container.cache_manager, + llm_inference_config_service=llm_inference_config_container.llm_inference_config_service, ) voice_agents_container = VoiceAgentsContainer( diff --git a/wavefront/server/modules/agents_module/agents_module/agents_container.py b/wavefront/server/modules/agents_module/agents_module/agents_container.py index e00fb31c..71c12167 100644 --- a/wavefront/server/modules/agents_module/agents_module/agents_container.py +++ b/wavefront/server/modules/agents_module/agents_module/agents_container.py @@ -41,6 +41,8 @@ class AgentsContainer(containers.DeclarativeContainer): executions_bucket = providers.Dependency() + llm_inference_config_service = providers.Dependency(default=None) + namespace_service = providers.Singleton( NamespaceService, namespace_repository=namespace_repository, @@ -69,6 +71,7 @@ class AgentsContainer(containers.DeclarativeContainer): cloud_storage_manager=cloud_storage_manager, message_processor_bucket_name=message_processor_bucket_name, api_services_manager=api_services_manager, + llm_inference_config_service=llm_inference_config_service, ) workflow_crud_service = providers.Singleton( diff --git a/wavefront/server/modules/agents_module/agents_module/controllers/agent_controller.py b/wavefront/server/modules/agents_module/agents_module/controllers/agent_controller.py index 1b1e1c62..c482d356 100644 --- a/wavefront/server/modules/agents_module/agents_module/controllers/agent_controller.py +++ b/wavefront/server/modules/agents_module/agents_module/controllers/agent_controller.py @@ -137,9 +137,6 @@ async def agent_inference_v2( response_formatter: ResponseFormatter = Depends( Provide[CommonContainer.response_formatter] ), - llm_inference_config_service: LlmInferenceConfigService = Depends( - Provide[LlmInferenceConfigContainer.llm_inference_config_service] - ), ): """ Run inference using a flo_ai agent (v2 - UUID-based) @@ -151,6 +148,11 @@ async def agent_inference_v2( 4. Runs inference with the provided variables 5. Returns the result along with execution metadata + The LLM is resolved from the agent YAML. When `agent.model.provider` is + `rootflo`, the `model_id` is treated as a LlmInferenceConfig UUID and the + LLM is built from that config. Any `llm_inference_config_id` on the payload + is ignored in v2. + Args: agent_id: The UUID of the agent request: Request containing variables for the agent @@ -163,22 +165,6 @@ async def agent_inference_v2( # Extract authentication credentials access_token, app_key = extract_auth_credentials(request) - # Fetch LLM config if provided - llm_config = None - if agent_inference_payload.llm_inference_config_id: - llm_config_dict = await llm_inference_config_service.get_config( - agent_inference_payload.llm_inference_config_id - ) - if not llm_config_dict: - return JSONResponse( - status_code=status.HTTP_404_NOT_FOUND, - content=response_formatter.buildErrorResponse( - f'LLM inference configuration not found: {agent_inference_payload.llm_inference_config_id}' - ), - ) - else: - llm_config = LlmInferenceConfig(**llm_config_dict) - # Process inputs using common utility function resolved_inputs = process_inference_inputs(agent_inference_payload.inputs) @@ -194,7 +180,6 @@ async def agent_inference_v2( inputs=resolved_inputs if isinstance(resolved_inputs, list) else [resolved_inputs], - llm_config=llm_config, output_json_enabled=agent_inference_payload.output_json_enabled, access_token=access_token, app_key=app_key, diff --git a/wavefront/server/modules/agents_module/agents_module/services/agent_inference_service.py b/wavefront/server/modules/agents_module/agents_module/services/agent_inference_service.py index 3d49a2ec..44fa192e 100644 --- a/wavefront/server/modules/agents_module/agents_module/services/agent_inference_service.py +++ b/wavefront/server/modules/agents_module/agents_module/services/agent_inference_service.py @@ -12,6 +12,9 @@ from flo_ai.tool.base_tool import Tool from flo_cloud.cloud_storage import CloudStorageManager from common_module.log.logger import logger +from llm_inference_config_module.services.llm_inference_config_service import ( + LlmInferenceConfigService, +) from tools_module.registry.tool_loader import ToolLoader from tools_module.utils.message_processor_fn import execute_message_processor_fn from tools_module.utils.api_service_tool_loader import load_api_service_tool @@ -31,6 +34,7 @@ def __init__( cloud_storage_manager: CloudStorageManager, message_processor_bucket_name: str, api_services_manager: Optional[ApiServicesManager] = None, + llm_inference_config_service: Optional[LlmInferenceConfigService] = None, ): """ Initialize the agent inference service @@ -43,6 +47,8 @@ def __init__( cloud_storage_manager: Cloud storage manager instance message_processor_bucket_name: Name of the bucket containing message processor YAML files api_services_manager: API services manager instance (optional) + llm_inference_config_service: LLM inference config service for resolving + rootflo model_id references in agent YAMLs (required for v2 inference) """ self.cache_manager = cache_manager self.tool_loader = tool_loader @@ -51,6 +57,7 @@ def __init__( self.message_processor_repository = message_processor_repository self.cloud_storage_manager = cloud_storage_manager self.message_processor_bucket_name = message_processor_bucket_name + self.llm_inference_config_service = llm_inference_config_service async def create_agent_from_yaml( self, @@ -309,12 +316,60 @@ async def perform_inference( return result, execution_time + async def _resolve_rootflo_llm_config( + self, yaml_content: str + ) -> Optional[LlmInferenceConfig]: + """ + Resolve an LlmInferenceConfig from a YAML's `agent.model` block when the + provider is `rootflo` and the `model_id` is a UUID pointing to a + LlmInferenceConfig row. + + Returns None for any other case (no model block, or provider != rootflo) + so that the caller can fall through to AgentBuilder.from_yaml's default + behavior (which builds the LLM directly from the YAML model block). + + Raises: + ValueError: when provider is rootflo but model_id is missing, + not a valid UUID, or does not resolve to a LlmInferenceConfig row. + """ + yaml_data = yaml.safe_load(yaml_content) + model_config = yaml_data.get('agent', {}).get('model') + if not model_config: + return None + + if model_config.get('provider') != 'rootflo': + return None + + model_id = model_config.get('model_id') + if not model_id: + raise ValueError( + 'rootflo provider requires "model_id" in agent.model block' + ) + + try: + config_uuid = UUID(str(model_id)) + except (ValueError, TypeError): + raise ValueError(f'rootflo model_id must be a valid UUID, got: {model_id}') + + if not self.llm_inference_config_service: + raise ValueError( + 'llm_inference_config_service not initialized. ' + 'Required to resolve rootflo model_id references.' + ) + + llm_config_dict = await self.llm_inference_config_service.get_config( + config_uuid + ) + if not llm_config_dict: + raise ValueError(f'LLM inference configuration not found: {config_uuid}') + + return LlmInferenceConfig(**llm_config_dict) + async def perform_inference_v2( self, agent_id: UUID, variables: Dict[str, Any], inputs: List[BaseMessage] | str, - llm_config: Optional[LlmInferenceConfig] = None, output_json_enabled: bool = True, access_token: Optional[str] = None, app_key: Optional[str] = None, @@ -322,18 +377,24 @@ async def perform_inference_v2( """ Complete inference workflow (v2): fetch agent from DB + cloud storage, run inference + The LLM is resolved from the agent YAML itself: when the YAML's + `agent.model.provider` is `rootflo`, the `model_id` is treated as a + LlmInferenceConfig UUID and the corresponding LLM is built and applied + via with_llm(). For any other provider, AgentBuilder.from_yaml builds + the LLM directly from the YAML. + Args: agent_id: The UUID of the agent variables: Variables to pass to the agent inputs: Inputs to use for inference - llm_config: Optional LLM configuration to override agent's default LLM output_json_enabled: Whether to extract JSON from the response Returns: tuple: (result, execution_time, namespace) Raises: - ValueError: If agent_crud_service is not initialized or agent not found + ValueError: If agent_crud_service is not initialized, agent not found, + or the YAML's rootflo model_id cannot be resolved. """ if not self.agent_crud_service: raise ValueError( @@ -354,6 +415,9 @@ async def perform_inference_v2( f'Retrieved agent - namespace: {namespace}, name: {name}, agent_id: {agent_id}' ) + # Resolve rootflo model_id references from the YAML, if any + llm_config = await self._resolve_rootflo_llm_config(yaml_content) + # Create agent from YAML with optional LLM override and tools agent = await self.create_agent_from_yaml( yaml_content, name, llm_config, access_token, app_key