diff --git a/examples/fitness_helper/README.md b/examples/fitness_helper/README.md new file mode 100644 index 0000000..bc5895e --- /dev/null +++ b/examples/fitness_helper/README.md @@ -0,0 +1,112 @@ +# Fitness Helper Example + +This example demonstrates how to use the framework for fitness advice tasks with switch_case functionality. The example code can be found in the `examples/fitness_helper` directory. + +```bash + cd examples/fitness_helper +``` + +## Overview + +This example implements an fitness advice workflow that uses switch-case functionality to conditionally include fitness information in the advice process. The workflow consists of the following key components: + +1. **Input Interface** + - Handles user input containing type of fitness exercises and image data + - Processes and caches any uploaded images + - Extracts the type of fitness exercises + +2. **Fitness Decision Logic** + - FitDecider: Analyzes the user's request to determine if fitness information is needed + - Makes a binary decision (0 or 1) based on context in the user's request + - Controls whether fitness data should be fetched + +3. **Conditional Fitness Search** + - FitSearcher: Only executes if FitDecider returns 0 (fitness info needed) + - Uses web search functionality to fetch how to do right fitness exercise + - Integrates fitness data into the advice context + +4. **Improvement Advice** + - Generates final fitness suggestions based on: + - User's original request + - fitness information (if available) + - Any provided image context + - Provides complete fitness advice + +The workflow follows this sequence: + + +## Prerequisites + +- Python 3.10+ +- Required packages installed (see requirements.txt) +- Access to OpenAI API or compatible endpoint (see configs/llms/gpt.yml) +- Access to Bing API key for web search functionality to search how to do right fitness exercise (see configs/tools/websearch.yml) +- Redis server running locally or remotely +- Conductor server running locally or remotely + +## Configuration + +The container.yaml file is a configuration file that manages dependencies and settings for different components of the system, including Conductor connections, Redis connections, and other service configurations. To set up your configuration: + +1. Generate the container.yaml file: + ```bash + python compile_container.py + ``` + This will create a container.yaml file with default settings under `examples/fitness_helper`. + + + +2. Configure your LLM settings in `configs/llms/gpt.yml` and `configs/llms/text_res.yml`: + + - Set your OpenAI API key or compatible endpoint through environment variable or by directly modifying the yml file + ```bash + export custom_openai_key="your_openai_api_key" + export custom_openai_endpoint="your_openai_endpoint" + ``` + - Configure other model settings like temperature as needed through environment variable or by directly modifying the yml file + +3. Configure your Bing Search API key in `configs/tools/websearch.yml`: + - Set your Bing API key through environment variable or by directly modifying the yml file + ```bash + export bing_api_key="your_bing_api_key" + ``` + +4. Update settings in the generated `container.yaml`: + - Modify Redis connection settings: + - Set the host, port and credentials for your Redis instance + - Configure both `redis_stream_client` and `redis_stm_client` sections + - Update the Conductor server URL under conductor_config section + - Adjust any other component settings as needed + +## Running the Example + +3. Run the fitness helper example: + + For terminal/CLI usage: + ```bash + python run_cli.py + ``` + + For app/GUI usage: + ```bash + python run_app.py + ``` + + + +## Troubleshooting + +If you encounter issues: + +- Verify Conductor and Redis are running and accessible +- Check your OpenAI API key and Bing API key are valid +- Check Redis Stream client and Redis STM client configuration + +- Ensure all dependencies are installed correctly +- Review logs for any error messages + + +## Building the Example + +Coming soon! This section will provide detailed instructions for building the fitness_helper example step by step. + diff --git a/examples/fitness_helper/agent/fit_advices/fit_advices.py b/examples/fitness_helper/agent/fit_advices/fit_advices.py new file mode 100644 index 0000000..0574af8 --- /dev/null +++ b/examples/fitness_helper/agent/fit_advices/fit_advices.py @@ -0,0 +1,73 @@ +from pathlib import Path +from typing import List + +from omagent_core.models.llms.base import BaseLLMBackend +from omagent_core.engine.worker.base import BaseWorker +from omagent_core.utils.registry import registry +from omagent_core.models.llms.prompt.prompt import PromptTemplate +from omagent_core.models.llms.openai_gpt import OpenaiGPTLLM + +from pydantic import Field + + +CURRENT_PATH = Path(__file__).parents[0] + + +@registry.register_worker() +class FitAdvices(BaseWorker, BaseLLMBackend): + """Outfit recommendation processor that generates personalized clothing suggestions. + + This processor: + 1. Retrieves user instruction and weather information from workflow context + 2. Loads system and user prompts from template files + 3. Uses LLM to generate contextual outfit recommendations based on weather and preferences + 4. Returns recommendations and sends them via callback + + Attributes: + llm (OpenaiGPTLLM): LLM model for generating recommendations + prompts (List[PromptTemplate]): System and user prompts loaded from files + """ + llm: OpenaiGPTLLM + + prompts: List[PromptTemplate] = Field( + default=[ + PromptTemplate.from_file( + CURRENT_PATH.joinpath("sys_prompt.prompt"), role="system" + ), + PromptTemplate.from_file( + CURRENT_PATH.joinpath("user_prompt.prompt"), role="user" + ), + ] + ) + + def _run(self, *args, **kwargs): + """Process user input and generate outfit recommendations. + + Retrieves user instruction and weather information from workflow context, + generates outfit recommendations using the LLM model, and returns the + recommendations while also sending them via callback. + + Args: + *args: Variable length argument list + **kwargs: Arbitrary keyword arguments + + Returns: + str: Generated outfit recommendations + """ + # Retrieve user instruction and optional weather info from workflow context + user_instruct = self.stm(self.workflow_instance_id).get("user_instruction") + search_info = self.stm(self.workflow_instance_id)["search_info"] if "search_info" in self.stm(self.workflow_instance_id) else None + + # Generate outfit recommendations using LLM with weather and user input + image_cache = self.stm(self.workflow_instance_id)['image_cache'] + chat_complete_res = self.simple_infer(weather=str(search_info), instruction=user_instruct, image=image_cache.get('')) + + # Extract recommendations from LLM response + outfit_recommendation = chat_complete_res["choices"][0]["message"]["content"] + + # Send recommendations via callback and return + self.callback.send_answer(agent_id=self.workflow_instance_id, msg=outfit_recommendation) + + self.stm(self.workflow_instance_id).clear() + return outfit_recommendation + diff --git a/examples/fitness_helper/agent/fit_advices/sys_prompt.prompt b/examples/fitness_helper/agent/fit_advices/sys_prompt.prompt new file mode 100644 index 0000000..f28c5ce --- /dev/null +++ b/examples/fitness_helper/agent/fit_advices/sys_prompt.prompt @@ -0,0 +1,5 @@ +You are a professional fitness coach whose task is to provide users with detailed guidance on fitness movements. Please compare the movements provided with the user's own fitness pictures to determine if they meet the standards and generate guidance. + +Please ensure that your answers are detailed and easy to understand, helping users to exercise safely and effectively. If you have any reference pictures, please explain them in conjunction with the demonstration actions shown in the pictures. + +Please provide detailed guidance for the following fitness pictures based on the above requirements: \ No newline at end of file diff --git a/examples/fitness_helper/agent/fit_advices/user_prompt.prompt b/examples/fitness_helper/agent/fit_advices/user_prompt.prompt new file mode 100644 index 0000000..91ba127 --- /dev/null +++ b/examples/fitness_helper/agent/fit_advices/user_prompt.prompt @@ -0,0 +1,7 @@ +Now, it's your turn to complete the task. +Give anwer using the language according to the user's answer. + +Input Information: +- Type of fitness exercises: {{type}} +- User's specific requirements or preferences: {{instruction}} +- Fitness Exercises in the image: {{image}} \ No newline at end of file diff --git a/examples/fitness_helper/agent/fit_decider/__init__.py b/examples/fitness_helper/agent/fit_decider/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/fitness_helper/agent/fit_decider/fit_decider.py b/examples/fitness_helper/agent/fit_decider/fit_decider.py new file mode 100644 index 0000000..47fab1b --- /dev/null +++ b/examples/fitness_helper/agent/fit_decider/fit_decider.py @@ -0,0 +1,100 @@ +import re +import json_repair +from pathlib import Path +from typing import List +from pydantic import Field +from omagent_core.models.llms.base import BaseLLMBackend +from omagent_core.engine.worker.base import BaseWorker +from omagent_core.utils.registry import registry +from omagent_core.models.llms.prompt.prompt import PromptTemplate +from omagent_core.models.llms.openai_gpt import OpenaiGPTLLM +from omagent_core.utils.logger import logging + + +CURRENT_PATH = root_path = Path(__file__).parents[0] + + +@registry.register_worker() +class FitDecider(BaseLLMBackend, BaseWorker): + """Weather decider node that determines if weather information is provided in user input. + + This node: + 1. Takes user instruction from workflow context + 2. Uses LLM to analyze if weather information is present in the instruction + 3. Returns a switch case value (0 or 1) to control workflow branching + 4. Logs and sends notifications about weather info status + + Attributes: + llm (OpenaiGPTLLM): LLM model for analyzing weather info + prompts (List[PromptTemplate]): System and user prompts loaded from files + """ + llm: OpenaiGPTLLM + + prompts: List[PromptTemplate] = Field( + default=[ + PromptTemplate.from_file( + CURRENT_PATH.joinpath("sys_prompt.prompt"), role="system" + ), + PromptTemplate.from_file( + CURRENT_PATH.joinpath("user_prompt.prompt"), role="user" + ), + ] + ) + + def _run(self, user_instruction:str, *args, **kwargs): + """Process user instruction to determine if it contains weather information. + + Args: + user_instruction (str): The user's input text to analyze + *args: Variable length argument list + **kwargs: Arbitrary keyword arguments + + Returns: + dict: Contains 'switch_case_value' key with value: + 1 if weather info found in instruction + 0 if weather info not found and needs to be searched + + Raises: + ValueError: If LLM output cannot be parsed as valid JSON + """ + self.stm(self.workflow_instance_id)['user_instruction'] = user_instruction + + chat_complete_res = self.simple_infer(instruction=user_instruction) + + content = chat_complete_res["choices"][0]["message"]["content"] + content = self._extract_from_result(content) + logging.info(content) + + if content.get("fitInfo_provided") is not None: + fitInfo_provided = content['fitInfo_provided'] + if fitInfo_provided: + self.callback.info(agent_id=self.workflow_instance_id, progress='Fit Decider', message='Fitness information provided') + return {'switch_case_value': 1} + else: + self.callback.info(agent_id=self.workflow_instance_id, progress='Fit Decider', message='Need web search for fitness information') + return {'switch_case_value': 0} + else: + raise ValueError("LLM generation is not valid.") + + def _extract_from_result(self, result: str) -> dict: + """Extract JSON content from LLM response. + + Args: + result (str): Raw LLM response text + + Returns: + dict: Parsed JSON content + + Raises: + ValueError: If response cannot be parsed as valid JSON + """ + try: + pattern = r"```json\s*(\{(?:.|\s)*?\})\s*```" + result = result.replace("\n", "") + match = re.search(pattern, result, re.DOTALL) + if match: + return json_repair.loads(match.group(1)) + else: + return json_repair.loads(result) + except Exception as error: + raise ValueError("LLM generation is not valid.") \ No newline at end of file diff --git a/examples/fitness_helper/agent/fit_decider/sys_prompt.prompt b/examples/fitness_helper/agent/fit_decider/sys_prompt.prompt new file mode 100644 index 0000000..41bd5a5 --- /dev/null +++ b/examples/fitness_helper/agent/fit_decider/sys_prompt.prompt @@ -0,0 +1,10 @@ +You are a cutting edge super capable autonomous agent specialized in learning from environmental feedback and following rules to do correct and efficient actions. As a Super Agent build with super powerful tools, you are capable of handling any given task, thus your capabilities are far above regular simple AI or LLM. +Your task is to determine whether the given instruction provides enough fitness-related information to advise improvement of fitness. + +--- Output --- +The output should in json format: +{ + "fitInfo_provided": Boolean. Answer "True" in this field only if the given instruction provides enough fitness-related information to advise improvement of fitness. +} + +Only ouput this json, don't output anything else. \ No newline at end of file diff --git a/examples/fitness_helper/agent/fit_decider/user_prompt.prompt b/examples/fitness_helper/agent/fit_decider/user_prompt.prompt new file mode 100644 index 0000000..608503f --- /dev/null +++ b/examples/fitness_helper/agent/fit_decider/user_prompt.prompt @@ -0,0 +1,3 @@ +Now, it's your turn to complete the task. + +User Instruction : {{instruction}} \ No newline at end of file diff --git a/examples/fitness_helper/agent/fit_searcher/__init__.py b/examples/fitness_helper/agent/fit_searcher/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/fitness_helper/agent/fit_searcher/fit_searcher.py b/examples/fitness_helper/agent/fit_searcher/fit_searcher.py new file mode 100644 index 0000000..cf12073 --- /dev/null +++ b/examples/fitness_helper/agent/fit_searcher/fit_searcher.py @@ -0,0 +1,56 @@ +from pathlib import Path +from omagent_core.engine.worker.base import BaseWorker +from omagent_core.utils.registry import registry +from omagent_core.tool_system.manager import ToolManager +from omagent_core.utils.logger import logging + +CURRENT_PATH = root_path = Path(__file__).parents[0] + + +@registry.register_worker() +class FitSearcher(BaseWorker): + """Weather searcher processor that searches for weather information based on user input. + + This processor: + 1. Takes user instruction from workflow context + 2. Constructs a search query to find weather and temperature data + 3. Executes the search using tool manager and logs results + 4. Stores search results in workflow context for downstream use + + Attributes: + tool_manager: Tool manager instance for executing web searches and retrieving weather data + """ + + tool_manager : ToolManager + + def _run(self, user_instruction:str, *args, **kwargs): + """Process user instruction to search for weather information. + + Args: + user_instruction (str): The user's input text containing weather-related query + *args: Variable length argument list + **kwargs: Arbitrary keyword arguments + + Returns: + None: Results are stored in workflow context + + Raises: + ValueError: If the web search tool execution fails to retrieve weather data + """ + # Construct search query with instructions for datetime and location extraction + search_query = "Please consider the user instruction and generate a search query for the web search tool to search for the fitness exercises according to user requirements. You MUST choose the web search tool in the tool_call to excute. When generating the search query, consider to include the kind of fitness exercises from provided information. User Instruction: {}".format(user_instruction) + + # Execute weather search via tool manager and notify user + execution_status, execution_results = self.tool_manager.execute_task( + task=search_query + ) + self.callback.send_block(agent_id=self.workflow_instance_id, msg='Using web search tool to search for fitness exercises information') + logging.info(execution_results) + + # Store successful results in workflow context or raise error + if execution_status == "success": + self.stm(self.workflow_instance_id)["search_info"] = execution_results + else: + raise ValueError("Web search tool execution failed.") + + return diff --git a/examples/fitness_helper/compile_container.py b/examples/fitness_helper/compile_container.py new file mode 100644 index 0000000..d35c8ba --- /dev/null +++ b/examples/fitness_helper/compile_container.py @@ -0,0 +1,20 @@ +from omagent_core.utils.container import container +from pathlib import Path +from omagent_core.utils.registry import registry + + +# Load all registered workflow components +registry.import_module() + +# Configure import path for agent modules +from pathlib import Path +CURRENT_PATH = Path(__file__).parents[0] + +# Register core workflow components for state management, callbacks and input handling +container.register_stm(stm='RedisSTM') +container.register_callback(callback='AppCallback') +container.register_input(input='AppInput') + + +# Compile container config +container.compile_config(CURRENT_PATH) \ No newline at end of file diff --git a/examples/fitness_helper/configs/llms/gpt.yml b/examples/fitness_helper/configs/llms/gpt.yml new file mode 100644 index 0000000..bc312b7 --- /dev/null +++ b/examples/fitness_helper/configs/llms/gpt.yml @@ -0,0 +1,6 @@ +name: OpenaiGPTLLM +model_id: gpt-4o +api_key: ${env| custom_openai_key, sk-4zr6uGzVbNfIiq7U513aCc94Af614792938cE9AdB7D0E295} +endpoint: ${env| custom_openai_endpoint, http://192.168.0.114:6006/v1} +temperature: 0 +vision: true \ No newline at end of file diff --git a/examples/fitness_helper/configs/llms/text_res.yml b/examples/fitness_helper/configs/llms/text_res.yml new file mode 100644 index 0000000..8af0ddd --- /dev/null +++ b/examples/fitness_helper/configs/llms/text_res.yml @@ -0,0 +1,6 @@ +name: OpenaiGPTLLM +model_id: gpt-4o +api_key: ${env| custom_openai_key, sk-4zr6uGzVbNfIiq7U513aCc94Af614792938cE9AdB7D0E295} +endpoint: ${env| custom_openai_endpoint, http://192.168.0.114:6006/v1} +temperature: 0 +vision: false \ No newline at end of file diff --git a/examples/fitness_helper/configs/tools/websearch.yml b/examples/fitness_helper/configs/tools/websearch.yml new file mode 100644 index 0000000..3b7244c --- /dev/null +++ b/examples/fitness_helper/configs/tools/websearch.yml @@ -0,0 +1,5 @@ +llm: ${sub| text_res} +tools: + - name: WebSearch + bing_api_key: ${env| bing_api_key, 573bfabb7359487b90b8f8d26a4f6fc5} + llm: ${sub|text_res} \ No newline at end of file diff --git a/examples/fitness_helper/configs/workers/fit_advices.yml b/examples/fitness_helper/configs/workers/fit_advices.yml new file mode 100644 index 0000000..1042634 --- /dev/null +++ b/examples/fitness_helper/configs/workers/fit_advices.yml @@ -0,0 +1,2 @@ +name: FitAdvices +llm: ${sub| gpt} \ No newline at end of file diff --git a/examples/fitness_helper/configs/workers/fit_decider.yml b/examples/fitness_helper/configs/workers/fit_decider.yml new file mode 100644 index 0000000..3f7737d --- /dev/null +++ b/examples/fitness_helper/configs/workers/fit_decider.yml @@ -0,0 +1,2 @@ +name: FitDecider +llm: ${sub| gpt} \ No newline at end of file diff --git a/examples/fitness_helper/configs/workers/fit_searcher.yml b/examples/fitness_helper/configs/workers/fit_searcher.yml new file mode 100644 index 0000000..4a8ed2a --- /dev/null +++ b/examples/fitness_helper/configs/workers/fit_searcher.yml @@ -0,0 +1,3 @@ +name: FitSearcher +llm: ${sub| text_res} +tool_manager: ${sub|websearch} \ No newline at end of file diff --git a/examples/fitness_helper/container.yaml b/examples/fitness_helper/container.yaml new file mode 100644 index 0000000..8a88f6a --- /dev/null +++ b/examples/fitness_helper/container.yaml @@ -0,0 +1,84 @@ +conductor_config: + name: Configuration + base_url: + value: http://192.168.0.251:8080 + description: The Conductor Server API endpoint + env_var: CONDUCTOR_SERVER_URL + auth_key: + value: null + description: The authorization key + env_var: AUTH_KEY + auth_secret: + value: null + description: The authorization secret + env_var: CONDUCTOR_AUTH_SECRET + auth_token_ttl_min: + value: 45 + description: The authorization token refresh interval in minutes. + env_var: AUTH_TOKEN_TTL_MIN + debug: + value: false + description: Debug mode + env_var: DEBUG +connectors: + redis_stream_client: + name: RedisConnector + host: + value: 192.168.0.251 + env_var: HOST + port: + value: 6379 + env_var: PORT + password: + value: null + env_var: PASSWORD + username: + value: null + env_var: USERNAME + db: + value: 0 + env_var: DB + redis_stm_client: + name: RedisConnector + host: + value: 192.168.0.251 + env_var: HOST + port: + value: 6379 + env_var: PORT + password: + value: null + env_var: PASSWORD + username: + value: null + env_var: USERNAME + db: + value: 0 + env_var: DB +components: + AppCallback: + name: AppCallback + bot_id: + value: '' + env_var: BOT_ID + start_time: + value: 2024-12-05_16:02:18 + env_var: START_TIME + folder_name: + value: ./running_logs/2024-12-05_16:02:18 + env_var: FOLDER_NAME + AppInput: + name: AppInput + DefaultCallback: + name: DefaultCallback + bot_id: + value: '' + env_var: BOT_ID + start_time: + value: 2024-12-05_16:02:18 + env_var: START_TIME + folder_name: + value: ./running_logs/2024-12-05_16:02:18 + env_var: FOLDER_NAME + RedisSTM: + name: RedisSTM diff --git a/examples/fitness_helper/run_app.py b/examples/fitness_helper/run_app.py new file mode 100644 index 0000000..a88ff52 --- /dev/null +++ b/examples/fitness_helper/run_app.py @@ -0,0 +1,59 @@ +# Import required modules from omagent_core +from omagent_core.utils.container import container # For dependency injection container +from omagent_core.engine.workflow.conductor_workflow import ConductorWorkflow # For workflow management +from omagent_core.engine.workflow.task.simple_task import simple_task # For defining workflow tasks +from omagent_core.utils.registry import registry # For registering components +from omagent_core.clients.devices.app.client import AppClient # For app client interface +from omagent_core.utils.logger import logging # For logging functionality +logging.init_logger("omagent", "omagent", level="INFO") # Initialize logger + +# Set up path configuration +from pathlib import Path +CURRENT_PATH = Path(__file__).parents[0] # Get current directory path +# Import agent modules +registry.import_module(project_path=CURRENT_PATH.joinpath('agent')) + +# Add parent directory to Python path for imports +import sys +import os +sys.path.append(os.path.abspath(CURRENT_PATH.joinpath('../../'))) + +# Import input interface from previous example +from examples.step1_simpleVQA.agent.input_interface.input_interface import InputInterface + + +# Load container configuration from YAML file +# This configures dependencies like Redis connections and API endpoints +container.register_stm("RedisSTM") +container.from_config(CURRENT_PATH.joinpath('container.yaml')) + + +# Initialize outfit recommendation workflow with unique name +# This workflow will handle the outfit recommendation process +workflow = ConductorWorkflow(name='step2_outfit_with_switch') + +# Configure workflow tasks: +# 1. Input interface task to get user's clothing request +task1 = simple_task(task_def_name='InputInterface', task_reference_name='input_task') +# 2. Weather decision task to determine if weather info is needed +task2 = simple_task(task_def_name='WeatherDecider', task_reference_name='weather_decider', inputs={'user_instruction': task1.output('user_instruction')}) +# 3. Weather search task to fetch current weather conditions if needed +task3 = simple_task(task_def_name='WeatherSearcher', task_reference_name='weather_searcher', inputs={'user_instruction': task1.output('user_instruction')}) +# 4. Outfit recommendation task to generate final clothing suggestions +task4 = simple_task(task_def_name='OutfitRecommendation', task_reference_name='outfit_recommendation') + +# Configure workflow execution flow: +# The workflow follows this sequence: +# 1. Get user input +# 2. Analyze if weather info is needed +# 3. Conditionally fetch weather (only if decision task returns 0) +# 4. Generate outfit recommendations based on all gathered info +workflow >> task1 >> task2 >> {0 : task3} >> task4 + +# Register workflow +workflow.register(True) + +# Initialize and start app client with workflow configuration +config_path = CURRENT_PATH.joinpath('configs') +agent_client = AppClient(interactor=workflow, config_path=config_path, workers=[InputInterface()]) +agent_client.start_interactor() diff --git a/examples/fitness_helper/run_cli.py b/examples/fitness_helper/run_cli.py new file mode 100644 index 0000000..eb4dbe0 --- /dev/null +++ b/examples/fitness_helper/run_cli.py @@ -0,0 +1,55 @@ +# Import required modules from omagent_core +from omagent_core.utils.container import container # For dependency injection container +from omagent_core.engine.workflow.conductor_workflow import ConductorWorkflow # For workflow management +from omagent_core.engine.workflow.task.simple_task import simple_task # For defining workflow tasks +from omagent_core.utils.registry import registry # For registering components +from omagent_core.clients.devices.cli.client import DefaultClient # For CLI client interface +from omagent_core.utils.logger import logging # For logging functionality +logging.init_logger("omagent", "omagent", level="INFO") # Initialize logger + +# Set up path configuration +from pathlib import Path +CURRENT_PATH = Path(__file__).parents[0] # Get current directory path +# Import agent modules +registry.import_module(project_path=CURRENT_PATH.joinpath('agent')) + +# Add parent directory to Python path for imports +import sys +import os +sys.path.append(os.path.abspath(CURRENT_PATH.joinpath('../../'))) +# Import input interface from previous example +from examples.step1_simpleVQA.agent.input_interface.input_interface import InputInterface + + +# Load container configuration from YAML file +# This configures dependencies like Redis connections and API endpoints +container.register_stm("RedisSTM") +container.from_config(CURRENT_PATH.joinpath('container.yaml')) + + +# Initialize outfit recommendation workflow with unique name +# This workflow will handle the outfit recommendation process +workflow = ConductorWorkflow(name='fitness_helper') + +# Configure workflow tasks: +# 1. Input interface for user interaction +task1 = simple_task(task_def_name='InputInterface', task_reference_name='input_task') +# 2. Weather decision logic based on user input +task2 = simple_task(task_def_name='FitDecider', task_reference_name='fit_decider', inputs={'user_instruction': task1.output('user_instruction')}) +# 3. Weather information retrieval +task3 = simple_task(task_def_name='FitSearcher', task_reference_name='fit_searcher', inputs={'user_instruction': task1.output('user_instruction')}) +# 4. Final outfit recommendation generation +task4 = simple_task(task_def_name='FitAdvices', task_reference_name='fit_advices') + +# Configure workflow execution flow: +# Input -> Weather Decision -> Optional Weather Search -> Outfit Recommendation +# Weather search is only executed if weather information is needed (condition = 0) +workflow >> task1 >> task2 >> {0 : task3} >> task4 + +# Register workflow +workflow.register(True) + +# Initialize and start CLI client with workflow configuration +config_path = CURRENT_PATH.joinpath('configs') +cli_client = DefaultClient(interactor=workflow, config_path=config_path, workers=[InputInterface()]) +cli_client.start_interactor() diff --git a/examples/fitness_helper/run_webpage.py b/examples/fitness_helper/run_webpage.py new file mode 100644 index 0000000..87dd5cc --- /dev/null +++ b/examples/fitness_helper/run_webpage.py @@ -0,0 +1,59 @@ +# Import required modules from omagent_core +from omagent_core.utils.container import container # For dependency injection container +from omagent_core.engine.workflow.conductor_workflow import ConductorWorkflow # For workflow management +from omagent_core.engine.workflow.task.simple_task import simple_task # For defining workflow tasks +from omagent_core.utils.registry import registry # For registering components +from omagent_core.clients.devices.webpage.client import WebpageClient # For webpage client interface +from omagent_core.utils.logger import logging # For logging functionality +logging.init_logger("omagent", "omagent", level="INFO") # Initialize logger + +# Set up path configuration +from pathlib import Path +CURRENT_PATH = Path(__file__).parents[0] # Get current directory path +# Import agent modules +registry.import_module(project_path=CURRENT_PATH.joinpath('agent')) + +# Add parent directory to Python path for imports +import sys +import os +sys.path.append(os.path.abspath(CURRENT_PATH.joinpath('../../'))) + +# Import input interface from previous example +from examples.step1_simpleVQA.agent.input_interface.input_interface import InputInterface + + +# Load container configuration from YAML file +# This configures dependencies like Redis connections and API endpoints +container.register_stm("RedisSTM") +container.from_config(CURRENT_PATH.joinpath('container.yaml')) + + +# Initialize outfit recommendation workflow with unique name +# This workflow will handle the outfit recommendation process +workflow = ConductorWorkflow(name='step2_outfit_with_switch') + +# Configure workflow tasks: +# 1. Input interface task to get user's clothing request +task1 = simple_task(task_def_name='InputInterface', task_reference_name='input_task') +# 2. Weather decision task to determine if weather info is needed +task2 = simple_task(task_def_name='WeatherDecider', task_reference_name='weather_decider', inputs={'user_instruction': task1.output('user_instruction')}) +# 3. Weather search task to fetch current weather conditions if needed +task3 = simple_task(task_def_name='WeatherSearcher', task_reference_name='weather_searcher', inputs={'user_instruction': task1.output('user_instruction')}) +# 4. Outfit recommendation task to generate final clothing suggestions +task4 = simple_task(task_def_name='OutfitRecommendation', task_reference_name='outfit_recommendation') + +# Configure workflow execution flow: +# The workflow follows this sequence: +# 1. Get user input +# 2. Analyze if weather info is needed +# 3. Conditionally fetch weather (only if decision task returns 0) +# 4. Generate outfit recommendations based on all gathered info +workflow >> task1 >> task2 >> {0 : task3} >> task4 + +# Register workflow +workflow.register(True) + +# Initialize and start app client with workflow configuration +config_path = CURRENT_PATH.joinpath('configs') +agent_client = WebpageClient(interactor=workflow, config_path=config_path, workers=[InputInterface()]) +agent_client.start_interactor()