Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions packages/uipath-agent-framework/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
[project]
name = "uipath-agent-framework"
version = "0.1.1"
version = "0.1.2"
description = "Python SDK that enables developers to build and deploy Microsoft Agent Framework agents to the UiPath Cloud Platform"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"agent-framework-core>=1.0.0rc1",
"agent-framework-orchestrations>=1.0.0b260219",
"agent-framework-core>=1.0.0",
"agent-framework-openai>=1.0.0",
"agent-framework-orchestrations>=1.0.0b260402, <1.0.0b260409",
"aiosqlite>=0.20.0",
"openinference-instrumentation-agent-framework>=0.1.0",
"uipath>=2.14.41, <2.15.0",
Expand All @@ -24,7 +25,7 @@ maintainers = [
]

[project.optional-dependencies]
anthropic = ["anthropic>=0.43.0", "agent-framework-anthropic>=1.0.0b260219"]
anthropic = ["anthropic>=0.43.0, <1.0.0", "agent-framework-anthropic>=1.0.0b260402"]

[project.entry-points."uipath.middlewares"]
register = "uipath_agent_framework.middlewares:register_middleware"
Expand Down Expand Up @@ -101,6 +102,11 @@ ignore_missing_imports = true
module = ["anthropic", "anthropic.*"]
ignore_missing_imports = true

[[tool.mypy.overrides]]
module = "agent_framework_openai.*"
ignore_missing_imports = true
ignore_errors = true

[[tool.mypy.overrides]]
module = "agent_framework_anthropic.*"
ignore_missing_imports = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"uipath>=2.14.41",
"uipath-agent-framework>=0.1.1",
"agent-framework-core>=1.0.0rc1",
"agent-framework-orchestrations>=1.0.0b260219",
"uipath-agent-framework>=0.1.2",
"agent-framework-core>=1.0.0",
"agent-framework-orchestrations>=1.0.0b260402",
]

[dependency-groups]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"uipath>=2.14.41",
"uipath-agent-framework>=0.1.1",
"agent-framework-core>=1.0.0rc1",
"agent-framework-orchestrations>=1.0.0b260219",
"uipath-agent-framework>=0.1.2",
"agent-framework-core>=1.0.0",
"agent-framework-orchestrations>=1.0.0b260402",
]

[dependency-groups]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"uipath>=2.14.41",
"uipath-agent-framework>=0.1.1",
"agent-framework-core>=1.0.0rc1",
"agent-framework-orchestrations>=1.0.0b260219",
"uipath-agent-framework>=0.1.2",
"agent-framework-core>=1.0.0",
"agent-framework-orchestrations>=1.0.0b260402",
]

[dependency-groups]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"uipath>=2.14.41",
"uipath-agent-framework>=0.1.1",
"agent-framework-core>=1.0.0rc1",
"uipath-agent-framework>=0.1.2",
"agent-framework-core>=1.0.0",
]

[dependency-groups]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"uipath>=2.14.41",
"uipath-agent-framework>=0.1.1",
"agent-framework-core>=1.0.0rc1",
"agent-framework-orchestrations>=1.0.0b260219",
"uipath-agent-framework>=0.1.2",
"agent-framework-core>=1.0.0",
"agent-framework-orchestrations>=1.0.0b260402",
]

[dependency-groups]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"uipath>=2.14.41",
"uipath-agent-framework>=0.1.1",
"agent-framework-core>=1.0.0rc1",
"uipath-agent-framework>=0.1.2",
"agent-framework-core>=1.0.0",
]

[dependency-groups]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"uipath>=2.14.41",
"uipath-agent-framework>=0.1.1",
"agent-framework-core>=1.0.0rc1",
"agent-framework-orchestrations>=1.0.0b260219",
"uipath-agent-framework>=0.1.2",
"agent-framework-core>=1.0.0",
"agent-framework-orchestrations>=1.0.0b260402",
]

[dependency-groups]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"uipath>=2.14.41",
"uipath-agent-framework>=0.1.1",
"agent-framework-core>=1.0.0rc1",
"agent-framework-orchestrations>=1.0.0b260219",
"uipath-agent-framework>=0.1.2",
"agent-framework-core>=1.0.0",
"agent-framework-orchestrations>=1.0.0b260402",
]

[dependency-groups]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"uipath>=2.14.41",
"uipath-agent-framework>=0.1.1",
"agent-framework-core>=1.0.0rc1",
"uipath-agent-framework>=0.1.2",
"agent-framework-core>=1.0.0",
]

[dependency-groups]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from agent_framework.openai import OpenAIChatClient
from agent_framework.openai import OpenAIChatCompletionClient


def get_weather(location: str) -> str:
Expand All @@ -15,7 +15,7 @@ def get_weather(location: str) -> str:


# Create an agent with tools
agent = OpenAIChatClient(model_id="gpt-4o-mini").as_agent(
agent = OpenAIChatCompletionClient(model="gpt-4o-mini").as_agent(
name="weather_agent",
instructions="You are a helpful weather assistant. Use the get_weather tool to provide weather information.",
tools=[get_weather],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def __new__(
)

return AnthropicClient(
model_id=model,
model=model,
anthropic_client=anthropic_client,
**kwargs,
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Routes OpenAI requests through UiPath's LLM Gateway using a
URL-rewriting httpx transport, then passes the pre-configured
``AsyncOpenAI`` client to the Agent Framework ``OpenAIChatClient``.
``AsyncOpenAI`` client to the Agent Framework ``OpenAIChatCompletionClient``.
"""

from __future__ import annotations
Expand All @@ -13,7 +13,7 @@
import httpx
from agent_framework import FunctionTool
from agent_framework import tool as tool_decorator
from agent_framework.openai import OpenAIChatClient
from agent_framework.openai import OpenAIChatCompletionClient
from openai import AsyncOpenAI
from uipath._utils._ssl_context import get_httpx_client_kwargs

Expand Down Expand Up @@ -57,10 +57,11 @@ async def handle_async_request(self, request: httpx.Request) -> httpx.Response:
return await super().handle_async_request(request)


class UiPathOpenAIChatClient(OpenAIChatClient):
class UiPathOpenAIChatClient(OpenAIChatCompletionClient):
"""OpenAI Chat Client that routes requests through UiPath's LLM Gateway.

Subclasses Agent Framework's ``OpenAIChatClient`` and configures a
Subclasses Agent Framework's ``OpenAIChatCompletionClient`` (the Chat
Completions API, the flavor the gateway is called with) and configures a
pre-built ``AsyncOpenAI`` client with URL-rewriting transport.

Example::
Expand Down Expand Up @@ -91,13 +92,14 @@ def __init__(self, model: str = "gpt-4.1-mini-2025-04-14", **kwargs: Any):
**client_kwargs,
)

# openai>=3 is typed for httpx2 but still accepts an httpx client.
async_client = AsyncOpenAI(
api_key=token,
http_client=http_client,
http_client=http_client, # type: ignore[arg-type]
)

super().__init__(
model_id=model,
model=model,
async_client=async_client,
**kwargs,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -333,13 +333,13 @@ def _add_executor_tool_nodes(
def _get_model_name(agent: Any) -> str | None:
"""Extract the model name from an agent's chat client.

Chat clients (OpenAIChatClient, AnthropicClient) store the model
identifier as ``model_id`` on the client instance.
Chat clients (OpenAIChatCompletionClient, AnthropicClient) store the
model identifier as ``model`` on the client instance.
"""
try:
model_id = agent.client.model_id
if isinstance(model_id, str):
return model_id
model = agent.client.model
if isinstance(model, str) and model:
return model
except AttributeError:
pass
return None
Expand Down
37 changes: 37 additions & 0 deletions packages/uipath-agent-framework/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from __future__ import annotations

import functools
from collections.abc import AsyncIterator, Callable
from typing import Any

from openai.types import CompletionUsage
Expand All @@ -22,6 +24,38 @@
)


class MockAsyncStream:
"""Async iterable that is also an async context manager, like ``openai.AsyncStream``.

``OpenAIChatCompletionClient`` consumes a streaming completion with
``async with ... as stream: async for chunk in stream``.
"""

def __init__(self, chunks: AsyncIterator[ChatCompletionChunk]):
self._chunks = chunks

def __aiter__(self) -> AsyncIterator[ChatCompletionChunk]:
return self._chunks

async def __aenter__(self) -> MockAsyncStream:
return self

async def __aexit__(self, *exc_info: object) -> None:
await self._chunks.aclose() # type: ignore[attr-defined]


def as_stream(
factory: Callable[..., AsyncIterator[ChatCompletionChunk]],
) -> Callable[..., MockAsyncStream]:
"""Wrap an async generator of chunks so it returns a ``MockAsyncStream``."""

@functools.wraps(factory)
def wrapper(*args: Any, **kwargs: Any) -> MockAsyncStream:
return MockAsyncStream(factory(*args, **kwargs))

return wrapper


def extract_system_text(messages: list[dict[str, Any]]) -> str:
"""Extract system/developer message text from OpenAI-format messages.

Expand Down Expand Up @@ -63,6 +97,7 @@ def make_chat_completion(text: str) -> ChatCompletion:
)


@as_stream
async def make_streaming_response(text: str):
"""Create an async iterable of ChatCompletionChunks for streaming."""
yield ChatCompletionChunk(
Expand Down Expand Up @@ -94,6 +129,7 @@ async def make_streaming_response(text: str):
)


@as_stream
async def make_chunked_streaming_response(text: str, chunk_size: int = 4):
"""Create streaming chunks that simulate token-by-token LLM output."""
for i in range(0, len(text), chunk_size):
Expand Down Expand Up @@ -165,6 +201,7 @@ def make_tool_call_completion(tool_name: str, arguments: str = "{}") -> ChatComp
)


@as_stream
async def make_streaming_tool_call(tool_name: str, arguments: str = "{}"):
"""Create streaming chunks for a tool call response."""
yield ChatCompletionChunk(
Expand Down
Loading
Loading