Skip to content

PC-4992: move uipath-agent-framework to agent-framework-openai split - #381

Draft
al3xanndru wants to merge 1 commit into
mainfrom
wabbit-dri/PC-4992_1
Draft

al3xanndru wants to merge 1 commit into
mainfrom
wabbit-dri/PC-4992_1

Conversation

@al3xanndru

@al3xanndru al3xanndru commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Problem

PC-4992: uipath-agent-framework can't be installed into a working environment from a clean checkout. The package and its samples declare agent-framework-core>=1.0.0rc1 with no upper bound, so a fresh uv sync resolves today's core. Every agent then fails at import:

  1. The package agent-framework-openai is required to use OpenAIChatClient (the package doesn't depend on it).
  2. After installing it by hand: TypeError: OpenAIChatClient.__init__() got an unexpected keyword argument 'model_id' (chat/openai.py).

Our CI never saw this. The package's [tool.uv] exclude-newer = "2 days" only affects lock resolution, and uv.lock was still pinned at core 1.0.0rc1.

Root cause

At agent-framework-core 1.0.0rc6 (2026-03-30) Microsoft made three changes:

  • moved the OpenAI clients into the separate agent-framework-openai package (agent_framework.openai is now a lazy re-export stub);
  • renamed the client constructor argument model_id to model (the attribute is now .model). agent-framework-anthropic followed at 1.0.0b260402;
  • made OpenAIChatClient the Responses API client and moved Chat Completions to OpenAIChatCompletionClient.

The third change matters beyond the rename. UiPathOpenAIChatClient calls the LLM Gateway with ApiFlavor OpenAiChatCompletions, and its transport rewrites only /chat/completions URLs. Renaming the kwarg alone would send /responses requests straight to api.openai.com with a UiPath token.

Change

  • chat/openai.py: UiPathOpenAIChatClient now subclasses OpenAIChatCompletionClient and passes model=. A type: ignore[arg-type] covers openai>=3: it is typed for httpx2 but still accepts the httpx client the gateway transport builds.
  • chat/anthropic.py: passes model= to AnthropicClient.
  • runtime/schema.py: _get_model_name reads client.model (graph node model names).
  • _cli/_templates/main.py.template (uipath new): OpenAIChatCompletionClient(model=...).
  • pyproject.toml:
    • agent-framework-core>=1.0.0, plus a new agent-framework-openai>=1.0.0 dependency;
    • agent-framework-orchestrations>=1.0.0b260402, <1.0.0b260409 (see below);
    • [anthropic] extra: anthropic>=0.43.0, <1.0.0, agent-framework-anthropic>=1.0.0b260402;
    • mypy override for agent_framework_openai.*;
    • version 0.1.2.
  • samples/*/pyproject.toml: uipath-agent-framework>=0.1.2, agent-framework-core>=1.0.0, agent-framework-orchestrations>=1.0.0b260402.
  • uv.lock re-locked: core 1.23.0, openai 1.17.0, orchestrations 1.0.0b260402, anthropic 0.116.0, agent-framework-anthropic 1.0.0b261002, openai SDK 3.40.0.

Why two upper bounds

Resolving everything to the latest release fixes the rename but breaks other parts of this package. I bisected each break with the test suite:

  • agent-framework-orchestrations 1.0.0b260409+ requires require_per_service_call_history_persistence=True on every handoff participant. With that flag set, test_hitl_e2e.py::test_multi_turn_after_hitl_resume fails: the session we persist after a HITL resume keeps a handoff_to_billing_agent function_call with no result, and OpenAI rejects the next turn on that.
  • agent-framework-orchestrations 1.0.0b260429 also changed what orchestrations yield: AgentResponse/AgentResponseUpdate in place of list[Message]. The sequential breakpoint e2e test then loses the last agent's message events.
  • anthropic 1.0.0+ (2026-08-20) is built on httpx2 and raises TypeError on our httpx.AsyncClient. Without the cap, the extra resolves agent-framework-anthropic 1.0.0b261022 (which needs anthropic>=1.11) and UiPathAnthropicClient() fails.

Adapting the runtime to these is a separate change. The caps keep core and openai on their latest releases and hold back only the packages the runtime isn't ready for.

Tests

Added:

  • test_chat_clients.py::test_openai_client_uses_chat_completions_client_with_model: the client is an OpenAIChatCompletionClient, .model is set, and the graph schema reports it.
  • test_chat_clients.py::test_openai_client_sends_chat_completion_to_gateway: a real get_response goes through the openai SDK and our transport (only the socket send is faked). It asserts the gateway URL, the OpenAiChatCompletions flavor header and the model field. With the base class switched back to OpenAIChatClient, this test and the one above fail.
  • test_chat_clients.py::test_anthropic_client_passes_model.
  • test_cli_new.py::test_scaffolded_main_builds_its_agent: runs the scaffolded main.py against the installed agent-framework, so drift in the template's constructor fails a build.

Updated:

  • The e2e tests build OpenAIChatCompletionClient(model=...).
  • test_graph.py mocks .model.
  • conftest.py's streaming factories return a MockAsyncStream, which supports both async for and async with like openai.AsyncStream. The new client opens streams with async with.

Run, in a venv synced from the new uv.lock (Python 3.11):

  • pytest (CI's command, with coverage): 169 passed, coverage 86.00%.
  • ruff check . passes, ruff format --check . passes, and mypy --config-file pyproject.toml . reports no issues in 35 files.
  • The full suite also passes on the floor set: core 1.0.0, openai 1.0.0, orchestrations 1.0.0b260402, agent-framework-anthropic 1.0.0b260402, anthropic 0.80.0.

Risks / open questions

  • The orchestrations cap amounts to a pin at 1.0.0b260402 (April 2026). It doesn't restrict users' core or openai, but agent-framework-orchestrations stays old until the runtime handles handoff history persistence and the new output shape. That needs a follow-up ticket.
  • Coverage gap: no live call through the LLM Gateway was made. The request shape is covered by the new transport-level test only.
  • uipath-agent-framework>=0.1.2 in the samples resolves only once 0.1.2 is published, which happens when this merges.
  • Already-deployed agents are unaffected. They keep their own locks. The fix reaches new projects and re-locks once 0.1.2 is out.
  • Not in this PR: the ticket's second defect, uipath init writing requires-python inside [tool.uv]. It isn't in this package: cli_new.generate_pyproject writes it under [project]. It belongs to the uipath CLI's init.
  • The PR title follows the DRI-loop convention (PC-4992: …). This repo's commitlint expects fix(agent-framework): … [PC-4992], so rename the PR before merge.

Drafted by the wabbit-dri draft-fixes loop for PC-4992 (draft #1), for a human to review: not ready to merge as is.

🤖 Generated with Claude Code

agent-framework-core 1.0.0rc6 moved the OpenAI clients into
agent-framework-openai and renamed model_id to model; OpenAIChatClient
became the Responses API client and Chat Completions moved to
OpenAIChatCompletionClient. The package still passed model_id and an
unbounded core floor, so a fresh install failed at import.

- UiPathOpenAIChatClient subclasses OpenAIChatCompletionClient (the
  gateway flavor it calls) and passes model=; the Anthropic client and
  the scaffold template pass model= too; the graph schema reads .model.
- Depend on agent-framework-openai>=1.0.0 and core>=1.0.0.
- Cap agent-framework-orchestrations below 1.0.0b260409, which made
  handoff require per-service-call history persistence and (b260429)
  changed what orchestrations yield; the runtime is not adapted to
  either yet.
- Cap anthropic below 1.0.0: anthropic 1.x requires httpx2 clients and
  rejects the gateway's httpx transport.
- Test stream mocks support async with, like openai.AsyncStream.
- Bump to 0.1.2 and floor the samples at it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant