# OpenAI Agents Integration Guide

A lightweight integration for running OpenAI Agents SDK agents on the UiPath platform. This guide covers project structure, entrypoint detection, LLM models, and common pitfalls.

> **Note:** This is a streamlined integration focused on agent execution and streaming. It does **not** support Human-in-the-Loop (HITL), state persistence/checkpointing, or UiPath process invocation from within agents. For workflows that need those features, use the [LangGraph](langgraph-integration.md) or [LlamaIndex](llamaindex-integration.md) integrations instead.

> **STOP — input shape decides framework fit.** The OpenAI Agents SDK runner accepts **only** a `messages` conversation input ([Input](#input) § CONSTRAINT) — an SDK constraint, not a UiPath one; UiPath merely surfaces it as the required `messages` field in the generated schema. When the requested input is not a conversation, do NOT map it onto `messages` and proceed — STOP before scaffolding, tell the user, and let them choose another framework (e.g. [LangGraph](langgraph-integration.md)).

## Scaffolding a New Project

If there is **no existing agent code**, scaffold an OpenAI Agents project with:

```bash
mkdir my-agent && cd my-agent
uip codedagent new my-agent
```

This generates `main.py` (with an Agent + tool template), `openai_agents.json`, `AGENTS.md`, and `pyproject.toml`. Then modify `main.py` to implement your actual agent logic.

> **Prerequisite:** `uipath-openai-agents` must be installed for the OpenAI Agents template to be used.

## Project Structure

OpenAI Agents use the `Agent` class from the `agents` package and an `openai_agents.json` configuration file.

```
my-agent/
├── main.py               # Agent definition — exports `agent` variable or `main()` function
├── openai_agents.json    # Agent configuration — maps names to agent variables
├── pyproject.toml        # Dependencies (must include uipath-openai-agents)
├── .env                  # Environment variables
└── ...                   # Generated by uip codedagent init
```

**`openai_agents.json`** tells the runtime where to find your agent:

```json
{
  "agents": {
    "agent": "main.py:agent"
  }
}
```

- `"agent"` — the entrypoint name (used with `uip codedagent run agent`)
- `"main.py:agent"` — path to the Python file and the variable name (`file:variable`)

You can also point to a function that returns an Agent:

```json
{
  "agents": {
    "agent": "main.py:main"
  }
}
```

---

## Required Dependencies

Every OpenAI Agents agent needs these in `pyproject.toml`:

```toml
[project]
name = "my-agent"
version = "0.1.0"
description = "My OpenAI Agents agent"
requires-python = ">=3.11"

dependencies = [
    "uipath-openai-agents",
]

[dependency-groups]
dev = [
    "uipath-dev",
]
```

**Key point:** `uipath-openai-agents` is required — it registers the OpenAI Agents runtime factory that enables `uip codedagent run`, `uip codedagent init`, and deployment to detect and execute your agent.

---

## LLM Models

Use UiPath's LLM wrapper to route through UiPath's LLM Gateway for centralized model management and billing.

### UiPathChatOpenAI

Instantiate `UiPathChatOpenAI` inside a factory function, not at module level. See [../lifecycle/build.md](../lifecycle/build.md) § Additional Instructions for the rule and the factory pattern below for the specific shape OpenAI Agents needs.

```python
from uipath_openai_agents.chat import UiPathChatOpenAI
from uipath_openai_agents.chat.supported_models import OpenAIModels
from agents.models import _openai_shared

# INSIDE a factory function, not at module level:
MODEL = OpenAIModels.gpt_4_1_2025_04_14
client = UiPathChatOpenAI(model_name=MODEL)
_openai_shared.set_default_openai_client(client.async_client)
```

- Routes through UiPath's passthrough endpoint
- No API key needed — uses UiPath authentication
- Must call `set_default_openai_client()` to make all agents use UiPath's endpoint

### Supported Models

Available via `OpenAIModels` enum: `gpt_4o_2024_05_13`, `gpt_4o_2024_08_06`, `gpt_4o_2024_11_20`, `gpt_4o_mini_2024_07_18`, `gpt_4_1_2025_04_14`, `gpt_4_1_mini_2025_04_14`, `gpt_4_1_nano_2025_04_14`, `o3_mini_2025_01_31`.

Also supports Google Gemini models (`GeminiModels`) and AWS Bedrock models (`BedrockModels`).

---

## Agent Definition

> **IMPORTANT:** Always define a Pydantic context model for your agent's input using `Agent[ContextType]`. This gives the agent typed, structured input fields beyond just `messages`. Without a context model, the agent only accepts a raw `messages` string — which means `uip codedagent init` cannot generate a proper input schema and the agent has no typed interface.

### Standard Pattern: Agent with Context Model

Every agent should define a Pydantic model for its input context and use `Agent[ContextType]`:

```python
from pydantic import BaseModel, Field
from agents import Agent

class AgentInput(BaseModel):
    query: str = Field(description="The user's question or request")

agent = Agent[AgentInput](
    name="my_agent",
    instructions="You are a helpful assistant. Answer the user's query.",
    model="gpt-4o",
)
```

Input for this agent:
```json
{"messages": "Hello!", "query": "What is Python?"}
```

The runtime always extracts `messages` for the LLM conversation. Context fields (`query`, etc.) are parsed into the Pydantic model and available to tools and instructions.

### Agent with Tools

```python
from pydantic import BaseModel, Field
from agents import Agent, function_tool

class WeatherInput(BaseModel):
    location: str = Field(description="City to check weather for")

@function_tool
def get_weather(location: str) -> str:
    """Get the current weather for a location."""
    return f"Sunny, 72F in {location}"

agent = Agent[WeatherInput](
    name="weather_agent",
    instructions="Use tools to answer weather questions about the requested location.",
    model="gpt-4o",
    tools=[get_weather],
)
```

### Agent with Structured Output

Combine a context model (typed input) with `output_type` (typed output):

```python
from pydantic import BaseModel, Field
from agents import Agent

class AnalysisInput(BaseModel):
    text: str = Field(description="Text to analyze")

class AnalysisResult(BaseModel):
    sentiment: str = Field(description="Overall sentiment")
    confidence: float = Field(description="Confidence score 0-1")
    summary: str = Field(description="Brief summary")

agent = Agent[AnalysisInput](
    name="analyzer",
    instructions="Analyze the given text.",
    model="gpt-4o",
    output_type=AnalysisResult,
)
```

Input:
```json
{"messages": "Analyze this", "text": "I love this product, it works great!"}
```

### Multi-Agent with Handoffs

For multi-agent systems, define the context model on the **top-level agent** (the entry point). Sub-agents inherit the context type:

```python
from pydantic import BaseModel, Field
from agents import Agent

class CustomerInput(BaseModel):
    customer_id: str = Field(description="Customer ID for lookup")
    issue_category: str = Field(default="", description="Optional pre-classification")

specialist_agent = Agent(
    name="specialist",
    instructions="You handle technical questions.",
    model="gpt-4o",
)

general_agent = Agent[CustomerInput](
    name="general",
    instructions="You handle general questions. Hand off technical ones.",
    model="gpt-4o",
    handoffs=[specialist_agent],
)

agent = general_agent
```

### Factory Function Pattern

For agents that need setup logic, use a function that returns `Agent[ContextType]`:

```python
from pydantic import BaseModel, Field
from agents import Agent
from agents.models import _openai_shared
from uipath_openai_agents.chat import UiPathChatOpenAI
from uipath_openai_agents.chat.supported_models import OpenAIModels

class AgentInput(BaseModel):
    query: str = Field(description="The user's question")

def main() -> Agent:
    """Configure UiPath client and return the agent."""
    MODEL = OpenAIModels.gpt_4_1_2025_04_14
    client = UiPathChatOpenAI(model_name=MODEL)
    _openai_shared.set_default_openai_client(client.async_client)

    return Agent[AgentInput](
        name="my_agent",
        instructions="You are a helpful assistant.",
        model=MODEL,
    )
```

Then in `openai_agents.json`:
```json
{
  "agents": {
    "agent": "main.py:main"
  }
}
```

---

## Input/Output Schemas

### Input

> **CONSTRAINT — `messages` is mandatory and cannot be removed.** The OpenAI Agents runner only accepts conversation input: a string (treated as a user message) or a list of message objects in the OpenAI Responses API format ([SDK docs — The agent loop](https://openai.github.io/openai-agents-python/running_agents/)). UiPath surfaces this as the `messages` field, so `uip codedagent init` ALWAYS emits `messages` as a **required** property in the entry-point input schema — every OpenAI Agents agent carries it, with or without a context model. Context fields from `Agent[ContextType]` are added **alongside** `messages`, never instead of it. There is no way to produce an OpenAI Agents input contract without `messages`. If the agent must accept a strict structured input with no `messages` field, OpenAI Agents cannot express it — pick a different framework (see [build.md](../lifecycle/build.md) § framework selection).

OpenAI Agents accept the required `messages` field (the LLM conversation) **plus** optional context fields from the Pydantic model:

```json
{"messages": "What is the weather?", "location": "New York"}
```

`messages` can also be a list of message objects:
```json
{"messages": [{"role": "user", "content": "What is the weather?"}], "location": "New York"}
```

The context fields (`location`, etc.) come from the `Agent[ContextType]` Pydantic model. They are parsed separately from `messages` and available to the agent's tools and instructions.

### Output

When `output_type` is set to a Pydantic model, the agent returns structured data matching that model:
```json
{"temperature": "72F", "conditions": "Sunny", "location": "New York"}
```

When no `output_type` is set, the agent returns:
```json
{"result": "The weather is sunny."}
```

Always prefer setting `output_type` for a typed output schema.

---

## Tracing

OpenAI Agents get **automatic tracing** via `openinference-instrumentation-openai-agents` (included with `uipath-openai-agents`). You do NOT need `@traced()` on agent definitions.

Use `@traced()` only on helper functions **outside** the agent that you want to monitor:

```python
from uipath.tracing import traced

@traced(name="preprocess", span_type="tool")
def preprocess_input(text: str) -> str:
    return text.strip().lower()
```

---

## Running `uip codedagent init`

After creating your agent, generate project configuration:

```bash
uv sync
uip codedagent init
```

### How Entrypoint Detection Works

1. `uip codedagent init` loads all registered middleware via Python entry points
2. The `uipath-openai-agents` package registers an OpenAI Agents middleware
3. The middleware checks for `openai_agents.json` — if found, it handles init
4. The runtime imports your Python file and resolves the agent (variable or function return)
5. Input/Output schemas are extracted from the agent's `output_type` and generic type parameter

### Troubleshooting `uip codedagent init`

**"No function entrypoints found"**
- Ensure `openai_agents.json` exists with the correct `"agents"` mapping
- The file path and variable/function name must match exactly
- Ensure `uipath-openai-agents` is installed (`uv sync`)

**Import errors during init / authentication failures**
- `uip codedagent init` imports your Python file to introspect the agent
- Module-level `UiPathChatOpenAI()` or `set_default_openai_client()` will try to authenticate on import and fail
- **Fix: Always use the factory function pattern** (`def main() -> Agent`) to defer LLM setup:
  ```python
  # BAD — fails during uip codedagent init:
  client = UiPathChatOpenAI(model_name=MODEL)
  _openai_shared.set_default_openai_client(client.async_client)
  agent = Agent[MyInput](name="my_agent", ...)

  # GOOD — deferred inside factory function:
  def main() -> Agent:
      client = UiPathChatOpenAI(model_name=MODEL)
      _openai_shared.set_default_openai_client(client.async_client)
      return Agent[MyInput](name="my_agent", ...)
  ```

**Schema not detected**
- Set `output_type` on your Agent for custom output schemas
- Use `Agent[ContextType]` for custom input schemas beyond `messages`

---

## Running Your Agent

```bash
uip codedagent run agent '{"messages": "Tell me about this", "query": "What is Python?"}'
```

The entrypoint name comes from the key in `openai_agents.json` `"agents"`. The input JSON must include `messages` plus any fields defined in your context model.

---

