diff --git a/README.md b/README.md index e15f616..de2b995 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ All packages are extensions to the [UiPath Python SDK](https://github.com/UiPath Build agents using the [LlamaIndex SDK](https://www.llamaindex.ai/): +- [README](packages/uipath-llamaindex/README.md) - [Docs](https://uipath.github.io/uipath-python/llamaindex/quick_start/) - [Samples](packages/uipath-llamaindex/samples/) @@ -23,6 +24,7 @@ Build agents using the [LlamaIndex SDK](https://www.llamaindex.ai/): Build agents using the [OpenAI Agents SDK](https://github.com/openai/openai-agents-python): +- [README](packages/uipath-openai-agents/README.md) - [Docs](https://uipath.github.io/uipath-python/openai-agents/quick_start/) - [Samples](packages/uipath-openai-agents/samples/) diff --git a/packages/uipath-openai-agents/README.md b/packages/uipath-openai-agents/README.md index 293900b..e6ae954 100644 --- a/packages/uipath-openai-agents/README.md +++ b/packages/uipath-openai-agents/README.md @@ -1,6 +1,19 @@ -# UiPath OpenAI Agents SDK +# UiPath OpenAI Agents Python SDK -Build intelligent AI agents with OpenAI's Agents framework and UiPath. +[![PyPI - Version](https://img.shields.io/pypi/v/uipath-openai-agents)](https://pypi.org/project/uipath-openai-agents/) +[![PyPI downloads](https://img.shields.io/pypi/dm/uipath-openai-agents.svg)](https://pypi.org/project/uipath-openai-agents/) +[![Python versions](https://img.shields.io/pypi/pyversions/uipath-openai-agents.svg)](https://pypi.org/project/uipath-openai-agents/) + +A Python SDK that enables developers to build and deploy OpenAI Agents to the UiPath Cloud Platform. It provides programmatic interaction with UiPath Cloud Platform services. + +This package is an extension to the [UiPath Python SDK](https://github.com/UiPath/uipath-python) and implements the [UiPath Runtime Protocol](https://github.com/UiPath/uipath-runtime-python). + +Check out these [sample projects](https://github.com/UiPath/uipath-integrations-python/tree/main/packages/uipath-openai-agents/samples) to see the SDK in action. + +## Requirements + +- Python 3.11 or higher +- UiPath Automation Cloud account ## Installation @@ -8,25 +21,105 @@ Build intelligent AI agents with OpenAI's Agents framework and UiPath. pip install uipath-openai-agents ``` -## Quick Start +using `uv`: + +```bash +uv add uipath-openai-agents +``` + +## Configuration + +### Environment Variables + +Create a `.env` file in your project root with the following variables: + +``` +UIPATH_URL=https://cloud.uipath.com/ACCOUNT_NAME/TENANT_NAME +UIPATH_ACCESS_TOKEN=YOUR_TOKEN_HERE +``` + +## Command Line Interface (CLI) + +The SDK provides a command-line interface for creating, packaging, and deploying OpenAI Agents: + +### Authentication + +```bash +uipath auth +``` + +This command opens a browser for authentication and creates/updates your `.env` file with the proper credentials. + +### Initialize a Project + +```bash +uipath init +``` + +Running `uipath init` will process the agent definitions in the `openai_agents.json` file and create the corresponding `entry-points.json` file needed for deployment. + +For more details on the configuration format, see the [UiPath configuration specifications](https://github.com/UiPath/uipath-python/blob/main/specs/README.md). + +### Debug a Project + +```bash +uipath run AGENT [INPUT] +``` + +Executes the agent with the provided JSON input arguments. + +### Package a Project + +```bash +uipath pack +``` + +Packages your project into a `.nupkg` file that can be deployed to UiPath. + +**Note:** Your `pyproject.toml` must include: + +- A description field (avoid characters: &, <, >, ", ', ;) +- Author information + +Example: + +```toml +description = "Your package description" +authors = [{name = "Your Name", email = "your.email@example.com"}] +``` + +### Publish a Package + +```bash +uipath publish +``` + +Publishes the most recently created package to your UiPath Orchestrator. + +## Project Structure + +To properly use the CLI for packaging and publishing, your project should include: -See the [main repository documentation](../../docs/) for getting started guides and examples. +- A `pyproject.toml` file with project metadata +- A `openai_agents.json` file with your agent definitions (e.g., `"agents": {"agent": "main.py:agent"}`) +- A `entry-points.json` file (generated by `uipath init`) +- A `bindings.json` file (generated by `uipath init`) to configure resource overrides +- Any Python files needed for your automation -## Features +## Development -- **OpenAI Agents Integration**: Build agents using OpenAI's native Agents framework -- **Agent Orchestration**: Multi-agent coordination and communication -- **State Management**: Persistent agent state with SQLite sessions -- **UiPath Integration**: Seamless integration with UiPath runtime and tooling +### Developer Tools -## Status +Check out [uipath-dev](https://github.com/uipath/uipath-dev-python) - an interactive terminal application for building, testing, and debugging UiPath Python runtimes, agents, and automation scripts. -⚠️ **Early Development**: This package is in early development (v0.1.0). APIs may change as the OpenAI Agents framework evolves. +### Setting Up a Development Environment -## Documentation +Please read our [contribution guidelines](https://github.com/UiPath/uipath-integrations-python/packages/uipath-openai-agents/blob/main/CONTRIBUTING.md) before submitting a pull request. -Full documentation is available in the [main repository](https://github.com/UiPath/uipath-llamaindex-python). +### Special Thanks -## License +A huge thank-you to the open-source community and the maintainers of the libraries that make this project possible: -See [LICENSE](../../LICENSE) in the repository root. +- [OpenAI](https://github.com/openai/openai-python) for providing a powerful framework for building AI agents. +- [OpenInference](https://github.com/Arize-ai/openinference) for observability and instrumentation support. +- [Pydantic](https://github.com/pydantic/pydantic) for reliable, typed configuration and validation. diff --git a/packages/uipath-openai-agents/docs/quick_start.md b/packages/uipath-openai-agents/docs/quick_start.md index dc26b8c..13410db 100644 --- a/packages/uipath-openai-agents/docs/quick_start.md +++ b/packages/uipath-openai-agents/docs/quick_start.md @@ -106,7 +106,7 @@ Generate your first UiPath OpenAI agent: ✓ Created 'pyproject.toml' file. 🔧 Please ensure to define OPENAI_API_KEY in your .env file. 💡 Initialize project: uipath init -💡 Run agent: uipath run agent '{"message": "Hello"}' +💡 Run agent: uipath run agent '{"messages": "Hello"}' ``` This command creates the following files: @@ -173,7 +173,7 @@ Execute the agent with a sample input: ```shell -> uipath run agent '{"message": "Hello"}' +> uipath run agent '{"messages": "Hello"}' {'response': 'Hello! How can I help you today?', 'agent_used': 'main'} ✓ Successful execution. ``` @@ -185,19 +185,19 @@ Depending on the shell you are using, it may be necessary to escape the input js /// tab | Bash/ZSH/PowerShell ```console -uipath run agent '{"message": "Hello"}' +uipath run agent '{"messages": "Hello"}' ``` /// /// tab | Windows CMD ```console -uipath run agent "{""message"": ""Hello""}" +uipath run agent "{""messages"": ""Hello""}" ``` /// /// tab | Windows PowerShell ```console -uipath run agent '{\"message\":\"Hello\"}' +uipath run agent '{\"messages\":\"Hello\"}' ``` /// @@ -215,7 +215,7 @@ The `run` command can also take a .json file as an input. You can create a file ```json { - "message": "Hello" + "messages": "Hello" } ``` @@ -275,7 +275,7 @@ Set the environment variables using the provided link. ```shell -> uipath invoke agent '{"message": "Hello"}' +> uipath invoke agent '{"messages": "Hello"}' ⠴ Loading configuration ... ⠴ Starting job ... ✨ Job started successfully! diff --git a/packages/uipath-openai-agents/samples/agent-as-tools/input.json b/packages/uipath-openai-agents/samples/agent-as-tools/input.json index 3cbe087..e276566 100644 --- a/packages/uipath-openai-agents/samples/agent-as-tools/input.json +++ b/packages/uipath-openai-agents/samples/agent-as-tools/input.json @@ -1,3 +1,3 @@ { - "message": "Tell me a joke" + "messages": "Tell me a joke" } \ No newline at end of file diff --git a/packages/uipath-openai-agents/samples/rag-assistant/entry-points.json b/packages/uipath-openai-agents/samples/rag-assistant/entry-points.json index a3d7a31..09e4f82 100644 --- a/packages/uipath-openai-agents/samples/rag-assistant/entry-points.json +++ b/packages/uipath-openai-agents/samples/rag-assistant/entry-points.json @@ -9,7 +9,7 @@ "input": { "type": "object", "properties": { - "message": { + "messages": { "anyOf": [ { "type": "string" @@ -21,12 +21,12 @@ } } ], - "title": "Message", - "description": "User message(s) to send to the agent" + "title": "Messages", + "description": "User messages to send to the agent" } }, "required": [ - "message" + "messages" ] }, "output": { diff --git a/packages/uipath-openai-agents/samples/rag-assistant/input.json b/packages/uipath-openai-agents/samples/rag-assistant/input.json index 4bb1e69..113dace 100644 --- a/packages/uipath-openai-agents/samples/rag-assistant/input.json +++ b/packages/uipath-openai-agents/samples/rag-assistant/input.json @@ -1,3 +1,3 @@ { - "message": "Who is the best ai llm?" + "messages": "Who is the best ai llm?" } \ No newline at end of file diff --git a/packages/uipath-openai-agents/samples/triage-agent/README.md b/packages/uipath-openai-agents/samples/triage-agent/README.md index f639484..fdfb302 100644 --- a/packages/uipath-openai-agents/samples/triage-agent/README.md +++ b/packages/uipath-openai-agents/samples/triage-agent/README.md @@ -29,13 +29,13 @@ In this pattern: ```bash # Spanish message -uipath run main '{"message": "Hola, ¿cómo estás?"}' +uipath run main '{"messages": "Hola, ¿cómo estás?"}' # French message -uipath run main '{"message": "Bonjour, comment allez-vous?"}' +uipath run main '{"messages": "Bonjour, comment allez-vous?"}' # English message -uipath run main '{"message": "Hello, how are you?"}' +uipath run main '{"messages": "Hello, how are you?"}' ``` ### Configure in openai_agents.json @@ -53,7 +53,7 @@ uipath run main '{"message": "Hello, how are you?"}' ### Input ```python class Input(BaseModel): - message: str # User message in any language + messages: str # User message in any language ``` ### Output @@ -66,7 +66,7 @@ class Output(BaseModel): ## Example Execution ```bash -$ uipath run main '{"message": "Hola, ¿cómo estás?"}' +$ uipath run main '{"messages": "Hola, ¿cómo estás?"}' Processing message: Hola, ¿cómo estás? ¡Hola! Estoy muy bien, gracias. ¿Y tú, cómo estás? diff --git a/packages/uipath-openai-agents/samples/triage-agent/entry-points.json b/packages/uipath-openai-agents/samples/triage-agent/entry-points.json index bb0969f..0c34f69 100644 --- a/packages/uipath-openai-agents/samples/triage-agent/entry-points.json +++ b/packages/uipath-openai-agents/samples/triage-agent/entry-points.json @@ -9,7 +9,7 @@ "input": { "type": "object", "properties": { - "message": { + "messages": { "anyOf": [ { "type": "string" @@ -21,12 +21,12 @@ } } ], - "title": "Message", - "description": "User message(s) to send to the agent" + "title": "Messages", + "description": "User messages to send to the agent" } }, "required": [ - "message" + "messages" ] }, "output": { diff --git a/packages/uipath-openai-agents/samples/triage-agent/input.json b/packages/uipath-openai-agents/samples/triage-agent/input.json index b29e37c..d6f78c3 100644 --- a/packages/uipath-openai-agents/samples/triage-agent/input.json +++ b/packages/uipath-openai-agents/samples/triage-agent/input.json @@ -1,3 +1,3 @@ { - "message": "une blague." + "messages": "une blague." } diff --git a/packages/uipath-openai-agents/src/uipath_openai_agents/__init__.py b/packages/uipath-openai-agents/src/uipath_openai_agents/__init__.py index 123fb62..8d27080 100644 --- a/packages/uipath-openai-agents/src/uipath_openai_agents/__init__.py +++ b/packages/uipath-openai-agents/src/uipath_openai_agents/__init__.py @@ -1,7 +1,28 @@ -"""UiPath OpenAI Agents SDK.""" +""" +UiPath OpenAI Agents SDK. + +NOTE: This module uses lazy imports via __getattr__ to avoid loading heavy +dependencies (openai SDK) at import time. This significantly improves CLI +startup performance. + +Do NOT add eager imports like: + from .chat import UiPathChatOpenAI # BAD - loads openai SDK immediately + +Instead, all exports are loaded on-demand when first accessed. +""" + + +def __getattr__(name): + if name == "UiPathChatOpenAI": + from .chat import UiPathChatOpenAI + + return UiPathChatOpenAI + if name == "register_middleware": + from .middlewares import register_middleware + + return register_middleware + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") -from .chat import UiPathChatOpenAI -from .middlewares import register_middleware __version__ = "0.1.0" __all__ = ["register_middleware", "UiPathChatOpenAI"] diff --git a/packages/uipath-openai-agents/src/uipath_openai_agents/_cli/_templates/AGENTS.md.template b/packages/uipath-openai-agents/src/uipath_openai_agents/_cli/_templates/AGENTS.md.template index 5ffabe2..abde426 100644 --- a/packages/uipath-openai-agents/src/uipath_openai_agents/_cli/_templates/AGENTS.md.template +++ b/packages/uipath-openai-agents/src/uipath_openai_agents/_cli/_templates/AGENTS.md.template @@ -18,4 +18,4 @@ This documentation is split into multiple files for efficient context loading. L 3. **@.agent/CLI_REFERENCE.md** - CLI commands documentation - **When to load:** Working with `uipath init`, `uipath run agent`, or deployment commands - - **Contains:** Command syntax for OpenAI agents, options, input formats (`{"message": "..."}` or `{"messages": [...]}`), debug mode, usage examples + - **Contains:** Command syntax for OpenAI agents, options, input formats (`{"messages": "..."}` or `{"messages": [...]}`), debug mode, usage examples diff --git a/packages/uipath-openai-agents/src/uipath_openai_agents/_cli/_templates/main.py.template b/packages/uipath-openai-agents/src/uipath_openai_agents/_cli/_templates/main.py.template index a50c8ce..e949291 100644 --- a/packages/uipath-openai-agents/src/uipath_openai_agents/_cli/_templates/main.py.template +++ b/packages/uipath-openai-agents/src/uipath_openai_agents/_cli/_templates/main.py.template @@ -1,7 +1,11 @@ -from agents import Agent -from openai import OpenAI +from agents import Agent, function_tool +from agents.models import _openai_shared +from uipath_openai_agents.chat import UiPathChatOpenAI +from uipath_openai_agents.chat.supported_models import OpenAIModels + +@function_tool def get_weather(location: str) -> str: """Get the current weather for a location. @@ -12,17 +16,19 @@ def get_weather(location: str) -> str: Weather information for the location """ # This is a mock implementation - return f"The weather in {location} is sunny and 72F" + return f"The weather in {location} is sunny and 32 degrees Celsius." + +MODEL = OpenAIModels.gpt_5_2_2025_12_11 -# Initialize the OpenAI client -client = OpenAI() +# Initialize the UiPath OpenAI client +uipath_openai_client = UiPathChatOpenAI(model_name=MODEL) +_openai_shared.set_default_openai_client(uipath_openai_client.async_client) # Create an agent with tools agent = Agent( name="weather_agent", instructions="You are a helpful weather assistant. Use the get_weather tool to provide weather information.", - model="gpt-4o-mini", + model=MODEL, tools=[get_weather], - client=client, ) diff --git a/packages/uipath-openai-agents/src/uipath_openai_agents/_cli/cli_new.py b/packages/uipath-openai-agents/src/uipath_openai_agents/_cli/cli_new.py index f9ac2c7..639ab62 100644 --- a/packages/uipath-openai-agents/src/uipath_openai_agents/_cli/cli_new.py +++ b/packages/uipath-openai-agents/src/uipath_openai_agents/_cli/cli_new.py @@ -39,8 +39,7 @@ def generate_pyproject(target_directory, project_name): description = "{project_name}" authors = [{{ name = "John Doe", email = "john.doe@myemail.com" }}] dependencies = [ - "uipath-openai-agents>=0.1.0", - "openai>=1.0.0" + "uipath-openai-agents>=0.0.1, <0.1.0", ] requires-python = ">=3.11" """ @@ -63,11 +62,8 @@ def openai_agents_new_middleware(name: str) -> MiddlewareResult: console.success("Created 'AGENTS.md' file.") generate_pyproject(directory, name) console.success("Created 'pyproject.toml' file.") - console.config( - f""" Please ensure to define {click.style("OPENAI_API_KEY", fg="bright_yellow")} in your .env file. """ - ) init_command = """uipath init""" - run_command = """uipath run agent '{"message": "What is the weather in San Francisco?"}'""" + run_command = """uipath run agent '{"messages": "What is the weather in San Francisco?"}'""" console.hint( f""" Initialize project: {click.style(init_command, fg="cyan")}""" ) diff --git a/packages/uipath-openai-agents/src/uipath_openai_agents/chat/__init__.py b/packages/uipath-openai-agents/src/uipath_openai_agents/chat/__init__.py index 6553005..3e5abab 100644 --- a/packages/uipath-openai-agents/src/uipath_openai_agents/chat/__init__.py +++ b/packages/uipath-openai-agents/src/uipath_openai_agents/chat/__init__.py @@ -1,5 +1,32 @@ -"""UiPath OpenAI Chat models.""" +""" +UiPath OpenAI Chat module. -from .openai import UiPathChatOpenAI +NOTE: This module uses lazy imports via __getattr__ to avoid loading heavy +dependencies (openai SDK, httpx) at import time. This significantly +improves CLI startup performance. -__all__ = ["UiPathChatOpenAI"] +Do NOT add eager imports like: + from .openai import UiPathChatOpenAI # BAD - loads openai SDK immediately + +Instead, all exports are loaded on-demand when first accessed. +""" + + +def __getattr__(name): + if name == "UiPathChatOpenAI": + from .openai import UiPathChatOpenAI + + return UiPathChatOpenAI + if name in ("OpenAIModels", "GeminiModels", "BedrockModels"): + from . import supported_models + + return getattr(supported_models, name) + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + +__all__ = [ + "UiPathChatOpenAI", + "OpenAIModels", + "GeminiModels", + "BedrockModels", +] diff --git a/packages/uipath-openai-agents/src/uipath_openai_agents/chat/supported_models.py b/packages/uipath-openai-agents/src/uipath_openai_agents/chat/supported_models.py index 6ad9039..226be09 100644 --- a/packages/uipath-openai-agents/src/uipath_openai_agents/chat/supported_models.py +++ b/packages/uipath-openai-agents/src/uipath_openai_agents/chat/supported_models.py @@ -1,78 +1,60 @@ -"""Supported OpenAI model definitions for UiPath LLM Gateway.""" +"""Supported model definitions for UiPath LLM Gateway. +OpenAI Agents SDK supports other LLM providers (Bedrock, Gemini, etc.) +through LiteLLM integration, so we provide model definitions for all supported providers. +""" -class OpenAIModels: - """OpenAI model names supported by UiPath LLM Gateway. +from enum import StrEnum - These are specific model versions required by UiPath. - Generic names like "gpt-4o" are not supported - use specific versions. - """ - # GPT-4o Models (recommended) - gpt_4o_2024_11_20 = "gpt-4o-2024-11-20" - gpt_4o_2024_08_06 = "gpt-4o-2024-08-06" +class OpenAIModels(StrEnum): + """Supported OpenAI model identifiers.""" + + # GPT-4o models gpt_4o_2024_05_13 = "gpt-4o-2024-05-13" + gpt_4o_2024_08_06 = "gpt-4o-2024-08-06" + gpt_4o_2024_11_20 = "gpt-4o-2024-11-20" gpt_4o_mini_2024_07_18 = "gpt-4o-mini-2024-07-18" - # GPT-4.1 Models + # GPT-4.1 models gpt_4_1_2025_04_14 = "gpt-4.1-2025-04-14" gpt_4_1_mini_2025_04_14 = "gpt-4.1-mini-2025-04-14" gpt_4_1_nano_2025_04_14 = "gpt-4.1-nano-2025-04-14" - # GPT-4 Models - gpt_4 = "gpt-4" - gpt_4_32k = "gpt-4-32k" - gpt_4_turbo_2024_04_09 = "gpt-4-turbo-2024-04-09" - gpt_4_1106_preview = "gpt-4-1106-Preview" - gpt_4_vision_preview = "gpt-4-vision-preview" - - # GPT-3.5 Models - gpt_35_turbo = "gpt-35-turbo" - gpt_35_turbo_0125 = "gpt-35-turbo-0125" - gpt_35_turbo_1106 = "gpt-35-turbo-1106" - gpt_35_turbo_16k = "gpt-35-turbo-16k" - - # GPT-5 Models + # GPT-5 models gpt_5_2025_08_07 = "gpt-5-2025-08-07" gpt_5_chat_2025_08_07 = "gpt-5-chat-2025-08-07" gpt_5_mini_2025_08_07 = "gpt-5-mini-2025-08-07" gpt_5_nano_2025_08_07 = "gpt-5-nano-2025-08-07" + + # GPT-5.1 models gpt_5_1_2025_11_13 = "gpt-5.1-2025-11-13" + + # GPT-5.2 models gpt_5_2_2025_12_11 = "gpt-5.2-2025-12-11" - # o3 Models - o3_mini_2025_01_31 = "o3-mini-2025-01-31" - - # Other Models - computer_use_preview_2025_03_11 = "computer-use-preview-2025-03-11" - text_davinci_003 = "text-davinci-003" - - # Embedding Models - text_embedding_3_large = "text-embedding-3-large" - text_embedding_3_large_community_ecs = "text-embedding-3-large-community-ecs" - text_embedding_ada_002 = "text-embedding-ada-002" - - # Model aliases - maps generic names to specific versions - MODEL_ALIASES = { - # Map gpt-4.1 variants to gpt-4o (most capable available model) - "gpt-4.1": gpt_4o_2024_11_20, - "gpt-4.1-mini": gpt_4o_mini_2024_07_18, - "gpt-4.1-nano": gpt_4o_mini_2024_07_18, - "gpt-4.1-2025-04-14": gpt_4o_2024_11_20, # Map invalid model to valid one - "gpt-4.1-mini-2025-04-14": gpt_4o_mini_2024_07_18, - "gpt-4.1-nano-2025-04-14": gpt_4o_mini_2024_07_18, - # Generic model mappings - "gpt-4o": gpt_4o_2024_11_20, - "gpt-4o-mini": gpt_4o_mini_2024_07_18, - "gpt-5": gpt_5_2025_08_07, - "gpt-5-mini": gpt_5_mini_2025_08_07, - "gpt-5-nano": gpt_5_nano_2025_08_07, - "gpt-5.1": gpt_5_1_2025_11_13, - "gpt-5.2": gpt_5_2_2025_12_11, - "o3-mini": o3_mini_2025_01_31, - } - - @classmethod - def normalize_model_name(cls, model_name: str) -> str: - """Normalize a model name to UiPath-specific version.""" - return cls.MODEL_ALIASES.get(model_name, model_name) + +class GeminiModels(StrEnum): + """Supported Google Gemini model identifiers.""" + + # Gemini 2 models + gemini_2_5_pro = "gemini-2.5-pro" + gemini_2_5_flash = "gemini-2.5-flash" + gemini_2_0_flash_001 = "gemini-2.0-flash-001" + + # Gemini 3 models + gemini_3_pro_preview = "gemini-3-pro-preview" + + +class BedrockModels(StrEnum): + """Supported AWS Bedrock model identifiers.""" + + # Claude 3.7 models + anthropic_claude_3_7_sonnet = "anthropic.claude-3-7-sonnet-20250219-v1:0" + + # Claude 4 models + anthropic_claude_sonnet_4 = "anthropic.claude-sonnet-4-20250514-v1:0" + + # Claude 4.5 models + anthropic_claude_sonnet_4_5 = "anthropic.claude-sonnet-4-5-20250929-v1:0" + anthropic_claude_haiku_4_5 = "anthropic.claude-haiku-4-5-20251001-v1:0" diff --git a/packages/uipath-openai-agents/src/uipath_openai_agents/runtime/__init__.py b/packages/uipath-openai-agents/src/uipath_openai_agents/runtime/__init__.py index c1acb4e..9aab7d5 100644 --- a/packages/uipath-openai-agents/src/uipath_openai_agents/runtime/__init__.py +++ b/packages/uipath-openai-agents/src/uipath_openai_agents/runtime/__init__.py @@ -1,4 +1,10 @@ -"""UiPath OpenAI Agents Runtime.""" +""" +UiPath OpenAI Agents Runtime. + +NOTE: This module uses lazy imports for most exports to avoid loading heavy +dependencies (openai SDK) at import time. However, the runtime factory +registration must happen eagerly for CLI discovery. +""" from uipath.runtime import ( UiPathRuntimeContext, @@ -6,13 +12,6 @@ UiPathRuntimeFactoryRegistry, ) -from uipath_openai_agents.runtime.factory import UiPathOpenAIAgentRuntimeFactory -from uipath_openai_agents.runtime.runtime import UiPathOpenAIAgentRuntime -from uipath_openai_agents.runtime.schema import ( - get_agent_schema, - get_entrypoints_schema, -) - def register_runtime_factory() -> None: """Register the OpenAI Agents factory. Called automatically via entry point.""" @@ -20,6 +19,11 @@ def register_runtime_factory() -> None: def create_factory( context: UiPathRuntimeContext | None = None, ) -> UiPathRuntimeFactoryProtocol: + # Import lazily when factory is actually created + from uipath_openai_agents.runtime.factory import ( + UiPathOpenAIAgentRuntimeFactory, + ) + return UiPathOpenAIAgentRuntimeFactory( context=context if context else UiPathRuntimeContext(), ) @@ -29,8 +33,30 @@ def create_factory( ) +# Register factory eagerly (required for CLI discovery) register_runtime_factory() + +def __getattr__(name): + if name == "get_entrypoints_schema": + from .schema import get_entrypoints_schema + + return get_entrypoints_schema + if name == "get_agent_schema": + from .schema import get_agent_schema + + return get_agent_schema + if name == "UiPathOpenAIAgentRuntimeFactory": + from .factory import UiPathOpenAIAgentRuntimeFactory + + return UiPathOpenAIAgentRuntimeFactory + if name == "UiPathOpenAIAgentRuntime": + from .runtime import UiPathOpenAIAgentRuntime + + return UiPathOpenAIAgentRuntime + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + __all__ = [ "register_runtime_factory", "get_entrypoints_schema", diff --git a/packages/uipath-openai-agents/src/uipath_openai_agents/runtime/runtime.py b/packages/uipath-openai-agents/src/uipath_openai_agents/runtime/runtime.py index 0cde361..7a67556 100644 --- a/packages/uipath-openai-agents/src/uipath_openai_agents/runtime/runtime.py +++ b/packages/uipath-openai-agents/src/uipath_openai_agents/runtime/runtime.py @@ -219,10 +219,20 @@ def _convert_stream_event_to_runtime_event( return None def _prepare_agent_input(self, input: dict[str, Any] | None) -> str | list[Any]: - """Prepare agent input from UiPath input dictionary.""" - if input and "messages" in input and isinstance(input["messages"], list): - return input.get("messages", []) - return input.get("message", "") if input else "" + """ + Prepare agent input from UiPath input dictionary. + + """ + if not input: + return "" + + messages = input.get("messages", "") + + if isinstance(messages, (str, list)): + return messages + + # Fallback to empty string for unexpected types + return "" def _serialize_message(self, message: Any) -> dict[str, Any]: """ diff --git a/packages/uipath-openai-agents/src/uipath_openai_agents/runtime/schema.py b/packages/uipath-openai-agents/src/uipath_openai_agents/runtime/schema.py index 03483f6..28c91e2 100644 --- a/packages/uipath-openai-agents/src/uipath_openai_agents/runtime/schema.py +++ b/packages/uipath-openai-agents/src/uipath_openai_agents/runtime/schema.py @@ -63,7 +63,7 @@ def get_entrypoints_schema(agent: Agent) -> dict[str, Any]: schema["input"] = { "type": "object", "properties": { - "message": { + "messages": { "anyOf": [ {"type": "string"}, { @@ -71,11 +71,11 @@ def get_entrypoints_schema(agent: Agent) -> dict[str, Any]: "items": {"type": "object"}, }, ], - "title": "Message", - "description": "User message(s) to send to the agent", + "title": "Messages", + "description": "User messages to send to the agent", } }, - "required": ["message"], + "required": ["messages"], } # Extract output schema - Agent's output_type (native OpenAI Agents pattern) diff --git a/packages/uipath-openai-agents/testcases/triage-agent/input.json b/packages/uipath-openai-agents/testcases/triage-agent/input.json index 5d8e6c8..aceda16 100644 --- a/packages/uipath-openai-agents/testcases/triage-agent/input.json +++ b/packages/uipath-openai-agents/testcases/triage-agent/input.json @@ -1,3 +1,3 @@ { - "message": "Hola, ¿cómo estás?" + "messages": "Hola, ¿cómo estás?" } diff --git a/packages/uipath-openai-agents/tests/cli/test_run.py b/packages/uipath-openai-agents/tests/cli/test_run.py index 5e612f3..5f240c3 100644 --- a/packages/uipath-openai-agents/tests/cli/test_run.py +++ b/packages/uipath-openai-agents/tests/cli/test_run.py @@ -22,7 +22,7 @@ def test_run_basic_agent_invocation( ) -> None: """Test basic agent invocation structure (without actual OpenAI API calls).""" input_file_name = "input.json" - input_json_content = '{"message": "Hello, agent!"}' + input_json_content = '{"messages": "Hello, agent!"}' with runner.isolated_filesystem(temp_dir=temp_dir): # create input file @@ -54,7 +54,7 @@ def test_run_with_invalid_agent_name( ) -> None: """Test run command with non-existent agent name.""" input_file_name = "input.json" - input_json_content = '{"message": "test"}' + input_json_content = '{"messages": "test"}' with runner.isolated_filesystem(temp_dir=temp_dir): input_file_path = os.path.join(temp_dir, input_file_name) @@ -83,7 +83,7 @@ def test_run_without_config_file( ) -> None: """Test run command without openai_agents.json file.""" input_file_name = "input.json" - input_json_content = '{"message": "test"}' + input_json_content = '{"messages": "test"}' with runner.isolated_filesystem(temp_dir=temp_dir): input_file_path = os.path.join(temp_dir, input_file_name) @@ -109,7 +109,7 @@ def test_run_with_malformed_input_json( ) -> None: """Test run command with malformed JSON input.""" input_file_name = "input.json" - input_json_content = '{"message": invalid json}' # Malformed + input_json_content = '{"messages": invalid json}' # Malformed with runner.isolated_filesystem(temp_dir=temp_dir): input_file_path = os.path.join(temp_dir, input_file_name) diff --git a/packages/uipath-openai-agents/tests/test_integration.py b/packages/uipath-openai-agents/tests/test_integration.py index 4bfecc6..76c3eed 100644 --- a/packages/uipath-openai-agents/tests/test_integration.py +++ b/packages/uipath-openai-agents/tests/test_integration.py @@ -56,7 +56,7 @@ def test_schema_extraction_with_new_serialization(): # Verify input schema (messages format) assert "input" in schema - assert "message" in schema["input"]["properties"] + assert "messages" in schema["input"]["properties"] # Verify output schema (from agent's output_type) assert "output" in schema