diff --git a/packages/uipath-agent-framework/pyproject.toml b/packages/uipath-agent-framework/pyproject.toml index c09cb109..a3ef75ab 100644 --- a/packages/uipath-agent-framework/pyproject.toml +++ b/packages/uipath-agent-framework/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath-agent-framework" -version = "0.1.0" +version = "0.1.1" 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" @@ -9,8 +9,10 @@ dependencies = [ "agent-framework-orchestrations>=1.0.0b260219", "aiosqlite>=0.20.0", "openinference-instrumentation-agent-framework>=0.1.0", - "uipath>=2.13.0, <2.14.0", - "uipath-runtime>=0.12.1, <0.13.0", + # TODO(release): restore ">=2.14.15, <2.15.0" and drop the testpypi + # source once uipath 2.14.15 (UiPath/uipath-python#1886) is on PyPI. + "uipath==2.14.15.dev1018867616", + "uipath-runtime>=0.13.1, <0.14.0", ] classifiers = [ "Intended Audience :: Developers", @@ -129,6 +131,10 @@ url = "https://test.pypi.org/simple/" publish-url = "https://test.pypi.org/legacy/" explicit = true +# TODO(release): drop together with the uipath dev pin above. +[tool.uv.sources] +uipath = { index = "testpypi" } + [tool.coverage.run] source = ["src/uipath_agent_framework"] relative_files = true diff --git a/packages/uipath-agent-framework/src/uipath_agent_framework/_cli/cli_new.py b/packages/uipath-agent-framework/src/uipath_agent_framework/_cli/cli_new.py index a2c86246..41a67b76 100644 --- a/packages/uipath-agent-framework/src/uipath_agent_framework/_cli/cli_new.py +++ b/packages/uipath-agent-framework/src/uipath_agent_framework/_cli/cli_new.py @@ -4,6 +4,8 @@ import click from uipath._cli._utils._console import ConsoleLogger from uipath._cli.middlewares import MiddlewareResult +from uipath._cli.models.agent_frameworks import AgentFramework +from uipath._cli.models.project_types import ProjectType console = ConsoleLogger() @@ -47,14 +49,21 @@ def generate_pyproject(target_directory, project_name): f.write(toml_content) -def agent_framework_new_middleware(name: str) -> MiddlewareResult: +def agent_framework_new_middleware( + name: str, + project_type: ProjectType = ProjectType.AUTO, + agent_framework: AgentFramework | None = None, +) -> MiddlewareResult: """Middleware to create demo Agent Framework agent""" + if not AgentFramework.MICROSOFT_AGENT_FRAMEWORK.claims_scaffold( + project_type, agent_framework + ): + return MiddlewareResult(should_continue=True) directory = os.getcwd() try: with console.spinner(f"Creating new agent {name} in current directory ..."): - generate_pyproject(directory, name) generate_script(directory) console.success("Created 'main.py' file.") console.success("Created 'agent_framework.json' file.") diff --git a/packages/uipath-agent-framework/tests/test_cli_new.py b/packages/uipath-agent-framework/tests/test_cli_new.py index 7ed0133a..c3a3d436 100644 --- a/packages/uipath-agent-framework/tests/test_cli_new.py +++ b/packages/uipath-agent-framework/tests/test_cli_new.py @@ -1,5 +1,8 @@ """Tests for the `uipath new` Agent Framework scaffolding middleware.""" +from uipath._cli.models.agent_frameworks import AgentFramework +from uipath._cli.models.project_types import ProjectType + from uipath_agent_framework._cli import cli_new from uipath_agent_framework._cli.cli_new import ( agent_framework_new_middleware, @@ -24,7 +27,11 @@ def test_middleware_scaffolds_project_files(tmp_path, monkeypatch): assert (tmp_path / "pyproject.toml").exists() -def test_middleware_reports_error_with_stacktrace_on_failure(monkeypatch): +def test_middleware_reports_error_with_stacktrace_on_failure(tmp_path, monkeypatch): + # The middleware scaffolds into the current directory, so keep the files + # this writes before it fails out of the source tree. + monkeypatch.chdir(tmp_path) + def boom(*args, **kwargs): raise OSError("disk full") @@ -35,3 +42,70 @@ def boom(*args, **kwargs): assert result.should_continue is False assert result.should_include_stacktrace is True + + +class TestProjectTypeGate: + """The middleware only claims scaffolds meant for microsoft-agent-framework.""" + + def test_function_type_passes_through(self, tmp_path, monkeypatch): + """project_type='function' must defer to the base `uipath new` scaffold. + + Regression guard for uipath-python#1543: installing an integration used + to hijack `uipath new` unconditionally, making the base function + scaffold unreachable. + """ + monkeypatch.chdir(tmp_path) + result = agent_framework_new_middleware( + "demo", project_type=ProjectType.FUNCTION + ) + assert result.should_continue is True + assert not (tmp_path / "main.py").exists() + assert not (tmp_path / "agent_framework.json").exists() + assert not (tmp_path / "pyproject.toml").exists() + + def test_auto_type_scaffolds_agent(self, tmp_path, monkeypatch): + """--type auto (the default): an installed framework claims the scaffold.""" + monkeypatch.chdir(tmp_path) + result = agent_framework_new_middleware("demo", project_type=ProjectType.AUTO) + assert result.should_continue is False + assert (tmp_path / "agent_framework.json").exists() + + def test_agent_type_scaffolds_agent(self, tmp_path, monkeypatch): + monkeypatch.chdir(tmp_path) + result = agent_framework_new_middleware( + "demo", + project_type=ProjectType.AGENT, + agent_framework=AgentFramework.MICROSOFT_AGENT_FRAMEWORK, + ) + assert result.should_continue is False + assert (tmp_path / "agent_framework.json").exists() + + def test_other_agent_framework_passes_through(self, tmp_path, monkeypatch): + """Another framework's scaffold must be left to its own integration.""" + monkeypatch.chdir(tmp_path) + result = agent_framework_new_middleware( + "demo", + project_type=ProjectType.AGENT, + agent_framework=AgentFramework.PYDANTIC_AI, + ) + assert result.should_continue is True + assert not (tmp_path / "agent_framework.json").exists() + + def test_plain_string_arguments_still_gate_correctly(self, tmp_path, monkeypatch): + """Callers passing raw strings (older CLIs) must hit the same gate.""" + monkeypatch.chdir(tmp_path) + # Deliberately off-type: the point is that plain strings still gate. + result = agent_framework_new_middleware( + "demo", + project_type="agent", # type: ignore[arg-type] + agent_framework="pydantic-ai", # type: ignore[arg-type] + ) + assert result.should_continue is True + assert not (tmp_path / "agent_framework.json").exists() + + def test_default_type_stays_agent_for_old_base_cli(self, tmp_path, monkeypatch): + """An older `uipath new` passes only the name; that still scaffolds.""" + monkeypatch.chdir(tmp_path) + result = agent_framework_new_middleware("demo") + assert result.should_continue is False + assert (tmp_path / "agent_framework.json").exists() diff --git a/packages/uipath-agent-framework/uv.lock b/packages/uipath-agent-framework/uv.lock index 77f56754..59ed0229 100644 --- a/packages/uipath-agent-framework/uv.lock +++ b/packages/uipath-agent-framework/uv.lock @@ -2485,8 +2485,8 @@ wheels = [ [[package]] name = "uipath" -version = "2.13.0" -source = { registry = "https://pypi.org/simple" } +version = "2.14.15.dev1018867616" +source = { registry = "https://test.pypi.org/simple/" } dependencies = [ { name = "applicationinsights" }, { name = "click" }, @@ -2509,14 +2509,14 @@ dependencies = [ { name = "uipath-platform" }, { name = "uipath-runtime" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/53/15/c01594e4458b0963379f3f13fe5472b7740480d2497deba3cbda2f5ef04f/uipath-2.13.0.tar.gz", hash = "sha256:7e8ae855d25e1ab1716aa1e2a1e8ba98118c3b3d671772ebb5869d03cae32139", size = 4493516, upload-time = "2026-07-06T13:44:42.007Z" } +sdist = { url = "https://test-files.pythonhosted.org/packages/c8/08/12aac5a8b9cd53beb8a86399adcb8b346e1ecb63e1bdb3db8b9435ece231/uipath-2.14.15.dev1018867616.tar.gz", hash = "sha256:6c2842944ddd07b7bc63215e467e0c19481f3ef77eb3c67caac532e6eaa3a111", size = 4558272, upload-time = "2026-09-11T12:25:16.287Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/97/54bfcddb08aa1203be74fed1d02d8dd4aace433716979ecc31a152cecd4f/uipath-2.13.0-py3-none-any.whl", hash = "sha256:313e1a3049b22326ab5dfa04a5892c79651a275b9ddeec1f896c28607fe771ce", size = 417567, upload-time = "2026-07-06T13:44:40.12Z" }, + { url = "https://test-files.pythonhosted.org/packages/e7/67/d8fef0e326feb5c4ac9b41f4160ec5116935b863f008e9d89d0235244bf3/uipath-2.14.15.dev1018867616-py3-none-any.whl", hash = "sha256:96d3e0a62950de2a1cfdf2f14f6685e9a5c7a2411bbec7f2ba9b983dd08027b3", size = 443220, upload-time = "2026-09-11T12:25:12.993Z" }, ] [[package]] name = "uipath-agent-framework" -version = "0.1.0" +version = "0.1.1" source = { editable = "." } dependencies = [ { name = "agent-framework-core" }, @@ -2552,8 +2552,8 @@ requires-dist = [ { name = "aiosqlite", specifier = ">=0.20.0" }, { name = "anthropic", marker = "extra == 'anthropic'", specifier = ">=0.43.0" }, { name = "openinference-instrumentation-agent-framework", specifier = ">=0.1.0" }, - { name = "uipath", specifier = ">=2.13.0,<2.14.0" }, - { name = "uipath-runtime", specifier = ">=0.12.1,<0.13.0" }, + { name = "uipath", specifier = "==2.14.15.dev1018867616", index = "https://test.pypi.org/simple/" }, + { name = "uipath-runtime", specifier = ">=0.13.1,<0.14.0" }, ] provides-extras = ["anthropic"] @@ -2570,23 +2570,24 @@ dev = [ [[package]] name = "uipath-core" -version = "0.5.28" +version = "0.5.32" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-instrumentation" }, { name = "opentelemetry-sdk" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4c/f9/8d2f1d98cbebbcf059cf4561f38f34ad4cd58423e4f15cad22bd297a2563/uipath_core-0.5.28.tar.gz", hash = "sha256:942987f6b612c64f93d612ad7b242276ed75f129fdd8f25bc71c24ec8887e388", size = 130578, upload-time = "2026-06-30T14:04:48.841Z" } +sdist = { url = "https://files.pythonhosted.org/packages/99/0b/deb01dc671b075d88841f017a7c394e1b6f6869a9b3c027333cce587d59f/uipath_core-0.5.32.tar.gz", hash = "sha256:f709cc0b6a24d779b1b2200943512bedfc0842bac983ad0a9c668f35a991f4e7", size = 131009, upload-time = "2026-08-19T13:10:13.789Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/1e/385bb166232a57ebe938cc57ad2717f350bc922bb5d2ce31af84306b7569/uipath_core-0.5.28-py3-none-any.whl", hash = "sha256:b952a46a21710073cbc16d6d5684e9aa645c107f57a636b778cfb94aa81a1e48", size = 54980, upload-time = "2026-06-30T14:04:47.374Z" }, + { url = "https://files.pythonhosted.org/packages/2a/af/8c8641ea4b1596158ce47d6917d320a093bef9d7d93f781aeb03ee1ecf50/uipath_core-0.5.32-py3-none-any.whl", hash = "sha256:0a08644b6805db42be11257446947e419d14bb75e9c3ad82c4eee2509a5181da", size = 55123, upload-time = "2026-08-19T13:10:12.238Z" }, ] [[package]] name = "uipath-platform" -version = "0.2.0" +version = "0.2.28" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "anyio" }, { name = "httpx" }, { name = "pydantic-function-models" }, { name = "sqlparse" }, @@ -2594,23 +2595,23 @@ dependencies = [ { name = "truststore" }, { name = "uipath-core" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/22/2e/86bee6d9aa5d00ef95e729f3d37c78b3dbe8ebf83519f88357be5240ae76/uipath_platform-0.2.0.tar.gz", hash = "sha256:86bb1dbb4267afe43719276809bc7ebe7e9f9885ca8238da13a9776848636576", size = 412145, upload-time = "2026-07-06T13:42:39.658Z" } +sdist = { url = "https://files.pythonhosted.org/packages/62/3b/7c95f00997d23b16e9fcc738b54d8847291308e3e116ba8fa2b60093c461/uipath_platform-0.2.28.tar.gz", hash = "sha256:628c5c12ea5ae665c472711d9915216a939043bd4254240a8f645e1d1630cc54", size = 451707, upload-time = "2026-09-09T15:12:38.671Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/17/94/70e858975f022c44fa3d539632eb22a1a038ae39b4b187376e678371941a/uipath_platform-0.2.0-py3-none-any.whl", hash = "sha256:378986cb161521301560e32c2ea93950e1f14e8905bc7e3f6e034f548f2c6ac0", size = 271644, upload-time = "2026-07-06T13:42:38.246Z" }, + { url = "https://files.pythonhosted.org/packages/52/53/f5d1a559239aea5787b51ce6c31de84e45ea35c0ae1fca4ba507057f841f/uipath_platform-0.2.28-py3-none-any.whl", hash = "sha256:557c9cdabcdbe45d668ff8c896a680131aad1cb22720cf6ecbe1d95d8c1c1bcd", size = 293932, upload-time = "2026-09-09T15:12:36.838Z" }, ] [[package]] name = "uipath-runtime" -version = "0.12.1" +version = "0.13.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "chardet" }, { name = "uipath-core" }, { name = "vadersentiment" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/38/0b/6d6bfd912c2ad6978f58646becf3647430ccb087008a8c78dbc33b0b101e/uipath_runtime-0.12.1.tar.gz", hash = "sha256:a63ce739ea53da8479bb0314477f89d42724045ecbd7db3f2a3f0a09ab17a755", size = 235372, upload-time = "2026-07-06T11:14:51.947Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c8/8f/14ff9ccf152b6d97c0881f6b40d3e01037045ee52c467ed00dbb2319a0cb/uipath_runtime-0.13.5.tar.gz", hash = "sha256:923e4514f3d868f3711211fee6e2dd39f76c6aaf7354fec9e80cdc5e6fad5d80", size = 250415, upload-time = "2026-09-10T09:32:53.126Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/4c/433de72c4c5869ee26a63f68361b5c62d04bb03e9c7db9a69c0d3db7b458/uipath_runtime-0.12.1-py3-none-any.whl", hash = "sha256:88b014e0c5a50a5b41b2e532bd628eebc91d70ad42b6d3389fdc61b7ae78e0a6", size = 92004, upload-time = "2026-07-06T11:14:50.363Z" }, + { url = "https://files.pythonhosted.org/packages/95/88/b851e75b3c70bbedde1e24d5b5f38035863ed3d50e8facb75ac7d2db40b8/uipath_runtime-0.13.5-py3-none-any.whl", hash = "sha256:d5eb7d578689f67d5dd631d28a092954c9a8797e130e16fe46e2895e4b0656ac", size = 98513, upload-time = "2026-09-10T09:32:51.404Z" }, ] [[package]] diff --git a/packages/uipath-claude-sdk/pyproject.toml b/packages/uipath-claude-sdk/pyproject.toml index 8078f8fe..40e8a150 100644 --- a/packages/uipath-claude-sdk/pyproject.toml +++ b/packages/uipath-claude-sdk/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath-claude-sdk" -version = "0.1.0" +version = "0.1.1" description = "Python SDK that enables developers to build and deploy Claude Agent SDK agents to the UiPath Cloud Platform" readme = "README.md" requires-python = ">=3.11" @@ -11,8 +11,10 @@ dependencies = [ "httpx>=0.27.0", "botocore>=1.34.0", "aiosqlite>=0.19.0", - "uipath>=2.14.0, <2.15.0", - "uipath-runtime>=0.13.0, <0.14.0", + # TODO(release): restore ">=2.14.15, <2.15.0" and drop the testpypi + # source once uipath 2.14.15 (UiPath/uipath-python#1886) is on PyPI. + "uipath==2.14.15.dev1018867616", + "uipath-runtime>=0.13.1, <0.14.0", "uipath-llm-client>=1.17.1, <1.18.0", ] classifiers = [ @@ -118,6 +120,10 @@ url = "https://test.pypi.org/simple/" publish-url = "https://test.pypi.org/legacy/" explicit = true +# TODO(release): drop together with the uipath dev pin above. +[tool.uv.sources] +uipath = { index = "testpypi" } + [tool.coverage.run] source = ["src/uipath_claude_sdk"] relative_files = true diff --git a/packages/uipath-claude-sdk/src/uipath_claude_sdk/_cli/cli_new.py b/packages/uipath-claude-sdk/src/uipath_claude_sdk/_cli/cli_new.py index 09583c2e..1627c014 100644 --- a/packages/uipath-claude-sdk/src/uipath_claude_sdk/_cli/cli_new.py +++ b/packages/uipath-claude-sdk/src/uipath_claude_sdk/_cli/cli_new.py @@ -4,6 +4,8 @@ import click from uipath._cli._utils._console import ConsoleLogger from uipath._cli.middlewares import MiddlewareResult +from uipath._cli.models.agent_frameworks import AgentFramework +from uipath._cli.models.project_types import ProjectType console = ConsoleLogger() @@ -45,8 +47,14 @@ def generate_pyproject(target_directory, project_name): f.write(toml_content) -def claude_new_middleware(name: str) -> MiddlewareResult: +def claude_new_middleware( + name: str, + project_type: ProjectType = ProjectType.AUTO, + agent_framework: AgentFramework | None = None, +) -> MiddlewareResult: """Middleware to create a demo Claude Agent SDK agent""" + if not AgentFramework.CLAUDE_SDK.claims_scaffold(project_type, agent_framework): + return MiddlewareResult(should_continue=True) directory = os.getcwd() diff --git a/packages/uipath-claude-sdk/tests/test_cli_new.py b/packages/uipath-claude-sdk/tests/test_cli_new.py index cb23a060..951f17ec 100644 --- a/packages/uipath-claude-sdk/tests/test_cli_new.py +++ b/packages/uipath-claude-sdk/tests/test_cli_new.py @@ -16,6 +16,8 @@ import click import pytest +from uipath._cli.models.agent_frameworks import AgentFramework +from uipath._cli.models.project_types import ProjectType from uipath_claude_sdk._cli import cli_new from uipath_claude_sdk._cli.cli_new import claude_new_middleware @@ -80,3 +82,68 @@ def test_new_is_registered_as_the_new_middleware(): register_middleware() assert claude_new_middleware in Middlewares._middlewares["new"] + + +class TestProjectTypeGate: + """The middleware only claims scaffolds meant for claude-sdk.""" + + def test_function_type_passes_through(self, tmp_path, monkeypatch): + """project_type='function' must defer to the base `uipath new` scaffold. + + Regression guard for uipath-python#1543: installing an integration used + to hijack `uipath new` unconditionally, making the base function + scaffold unreachable. + """ + monkeypatch.chdir(tmp_path) + result = claude_new_middleware("demo", project_type=ProjectType.FUNCTION) + assert result.should_continue is True + assert not (tmp_path / "main.py").exists() + assert not (tmp_path / "claude.json").exists() + assert not (tmp_path / "pyproject.toml").exists() + + def test_auto_type_scaffolds_agent(self, tmp_path, monkeypatch): + """--type auto (the default): an installed framework claims the scaffold.""" + monkeypatch.chdir(tmp_path) + result = claude_new_middleware("demo", project_type=ProjectType.AUTO) + assert result.should_continue is False + assert (tmp_path / "claude.json").exists() + + def test_agent_type_scaffolds_agent(self, tmp_path, monkeypatch): + monkeypatch.chdir(tmp_path) + result = claude_new_middleware( + "demo", + project_type=ProjectType.AGENT, + agent_framework=AgentFramework.CLAUDE_SDK, + ) + assert result.should_continue is False + assert (tmp_path / "claude.json").exists() + + def test_other_agent_framework_passes_through(self, tmp_path, monkeypatch): + """Another framework's scaffold must be left to its own integration.""" + monkeypatch.chdir(tmp_path) + result = claude_new_middleware( + "demo", + project_type=ProjectType.AGENT, + agent_framework=AgentFramework.PYDANTIC_AI, + ) + assert result.should_continue is True + assert not (tmp_path / "claude.json").exists() + + def test_plain_string_arguments_still_gate_correctly(self, tmp_path, monkeypatch): + """Callers passing raw strings (older CLIs) must hit the same gate.""" + monkeypatch.chdir(tmp_path) + # Deliberately off-type: the point is that plain strings still gate. + result = claude_new_middleware( + "demo", + project_type="agent", # type: ignore[arg-type] + agent_framework="pydantic-ai", # type: ignore[arg-type] + ) + assert result.should_continue is True + assert not (tmp_path / "claude.json").exists() + + def test_default_type_stays_agent_for_old_base_cli(self, tmp_path, monkeypatch): + """An older `uipath new` passes only the name; that still scaffolds.""" + monkeypatch.chdir(tmp_path) + result = claude_new_middleware("demo") + assert result.should_continue is False + assert (tmp_path / "claude.json").exists() diff --git a/packages/uipath-claude-sdk/uv.lock b/packages/uipath-claude-sdk/uv.lock index 08e7e6a5..3fa21b3e 100644 --- a/packages/uipath-claude-sdk/uv.lock +++ b/packages/uipath-claude-sdk/uv.lock @@ -2377,8 +2377,8 @@ wheels = [ [[package]] name = "uipath" -version = "2.14.1" -source = { registry = "https://pypi.org/simple" } +version = "2.14.15.dev1018867616" +source = { registry = "https://test.pypi.org/simple/" } dependencies = [ { name = "applicationinsights" }, { name = "click" }, @@ -2401,14 +2401,14 @@ dependencies = [ { name = "uipath-platform" }, { name = "uipath-runtime" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/dc/0c/ca54825ca0e51d866d9cbc227f679331a6c98882316a7bd88632608d767e/uipath-2.14.1.tar.gz", hash = "sha256:ba8e13ca54dd9ad470550a186acb4bb27214a83bec8aa03eabc99fc591ad16db", size = 4541290, upload-time = "2026-08-06T13:02:34.988Z" } +sdist = { url = "https://test-files.pythonhosted.org/packages/c8/08/12aac5a8b9cd53beb8a86399adcb8b346e1ecb63e1bdb3db8b9435ece231/uipath-2.14.15.dev1018867616.tar.gz", hash = "sha256:6c2842944ddd07b7bc63215e467e0c19481f3ef77eb3c67caac532e6eaa3a111", size = 4558272, upload-time = "2026-09-11T12:25:16.287Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/50/9b05909e4d22c95855dc509111c774803ab8b3b1ca56853bb69a2ea90319/uipath-2.14.1-py3-none-any.whl", hash = "sha256:04100a72bad2f052a903ed15999887dcc4f7687b8bbb0e5754d3284637f36249", size = 435434, upload-time = "2026-08-06T13:02:33.02Z" }, + { url = "https://test-files.pythonhosted.org/packages/e7/67/d8fef0e326feb5c4ac9b41f4160ec5116935b863f008e9d89d0235244bf3/uipath-2.14.15.dev1018867616-py3-none-any.whl", hash = "sha256:96d3e0a62950de2a1cfdf2f14f6685e9a5c7a2411bbec7f2ba9b983dd08027b3", size = 443220, upload-time = "2026-09-11T12:25:12.993Z" }, ] [[package]] name = "uipath-claude-sdk" -version = "0.1.0" +version = "0.1.1" source = { editable = "." } dependencies = [ { name = "aiohttp" }, @@ -2441,9 +2441,9 @@ requires-dist = [ { name = "claude-agent-sdk", specifier = ">=0.2.132,<0.3.0" }, { name = "httpx", specifier = ">=0.27.0" }, { name = "openinference-instrumentation-claude-agent-sdk", specifier = ">=0.1.9,<0.2.0" }, - { name = "uipath", specifier = ">=2.14.0,<2.15.0" }, + { name = "uipath", specifier = "==2.14.15.dev1018867616", index = "https://test.pypi.org/simple/" }, { name = "uipath-llm-client", specifier = ">=1.17.1,<1.18.0" }, - { name = "uipath-runtime", specifier = ">=0.13.0,<0.14.0" }, + { name = "uipath-runtime", specifier = ">=0.13.1,<0.14.0" }, ] [package.metadata.requires-dev] @@ -2459,16 +2459,16 @@ dev = [ [[package]] name = "uipath-core" -version = "0.5.31" +version = "0.5.32" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-instrumentation" }, { name = "opentelemetry-sdk" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/518fcb7d466a39c675194c492e8accbffdc7fd6b280d89b21ed11700a7bd/uipath_core-0.5.31.tar.gz", hash = "sha256:6abb443582d9a8ab6a504791296d49f524fb430f66d8dfaf6d6d1849958700bb", size = 130979, upload-time = "2026-07-15T06:56:03.867Z" } +sdist = { url = "https://files.pythonhosted.org/packages/99/0b/deb01dc671b075d88841f017a7c394e1b6f6869a9b3c027333cce587d59f/uipath_core-0.5.32.tar.gz", hash = "sha256:f709cc0b6a24d779b1b2200943512bedfc0842bac983ad0a9c668f35a991f4e7", size = 131009, upload-time = "2026-08-19T13:10:13.789Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/37/47a4e12bc7bdcfab4bd4e8e1f2a5c083bd59fca42fd431026a3f14c642a3/uipath_core-0.5.31-py3-none-any.whl", hash = "sha256:3dff5ecb236bf46af683c34d79bb2cf458a942ec041e1cbf7b4825ae246ff00c", size = 55040, upload-time = "2026-07-15T06:56:02.446Z" }, + { url = "https://files.pythonhosted.org/packages/2a/af/8c8641ea4b1596158ce47d6917d320a093bef9d7d93f781aeb03ee1ecf50/uipath_core-0.5.32-py3-none-any.whl", hash = "sha256:0a08644b6805db42be11257446947e419d14bb75e9c3ad82c4eee2509a5181da", size = 55123, upload-time = "2026-08-19T13:10:12.238Z" }, ] [[package]] @@ -2489,7 +2489,7 @@ wheels = [ [[package]] name = "uipath-platform" -version = "0.2.17" +version = "0.2.28" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -2500,23 +2500,23 @@ dependencies = [ { name = "truststore" }, { name = "uipath-core" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/ff/df30f2b271cf751ee0bd8229cac1f51013957b0ea1c7e51a733a0a3dd77d/uipath_platform-0.2.17.tar.gz", hash = "sha256:b9f54531a516428dae056ff171cb2a6b231f04a484e832bed80442d69eb4c305", size = 435023, upload-time = "2026-08-03T19:01:50.564Z" } +sdist = { url = "https://files.pythonhosted.org/packages/62/3b/7c95f00997d23b16e9fcc738b54d8847291308e3e116ba8fa2b60093c461/uipath_platform-0.2.28.tar.gz", hash = "sha256:628c5c12ea5ae665c472711d9915216a939043bd4254240a8f645e1d1630cc54", size = 451707, upload-time = "2026-09-09T15:12:38.671Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/70/b3/12f4c11b1e1ce2c51c6eb85a1c3cb92bbbbfe3d5979a212d586c048e623e/uipath_platform-0.2.17-py3-none-any.whl", hash = "sha256:d190652d2504712ff823322b53f6de0179778acbbc81bd0fe9ef1b6757e36afb", size = 285508, upload-time = "2026-08-03T19:01:48.601Z" }, + { url = "https://files.pythonhosted.org/packages/52/53/f5d1a559239aea5787b51ce6c31de84e45ea35c0ae1fca4ba507057f841f/uipath_platform-0.2.28-py3-none-any.whl", hash = "sha256:557c9cdabcdbe45d668ff8c896a680131aad1cb22720cf6ecbe1d95d8c1c1bcd", size = 293932, upload-time = "2026-09-09T15:12:36.838Z" }, ] [[package]] name = "uipath-runtime" -version = "0.13.0" +version = "0.13.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "chardet" }, { name = "uipath-core" }, { name = "vadersentiment" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/65/c0/d55cf48ee43758c2c1c65f52fd0596fd75f5bd5067a5016e6553b4d2c5fe/uipath_runtime-0.13.0.tar.gz", hash = "sha256:8ae3150df5fb0043210faacf39148789c1fb252c6a8d6bc27174c0d9ce7304f5", size = 243594, upload-time = "2026-08-04T11:19:04.886Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c8/8f/14ff9ccf152b6d97c0881f6b40d3e01037045ee52c467ed00dbb2319a0cb/uipath_runtime-0.13.5.tar.gz", hash = "sha256:923e4514f3d868f3711211fee6e2dd39f76c6aaf7354fec9e80cdc5e6fad5d80", size = 250415, upload-time = "2026-09-10T09:32:53.126Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/88/9518dcbeedaa8117e5fd3d0424c4a7354bae1f3ede1b2e3f48524e4b7efc/uipath_runtime-0.13.0-py3-none-any.whl", hash = "sha256:2a7c128cf14fdbef899b272ee5995da63baeb882acc904f39ef8f8f52bcb019f", size = 96349, upload-time = "2026-08-04T11:19:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/95/88/b851e75b3c70bbedde1e24d5b5f38035863ed3d50e8facb75ac7d2db40b8/uipath_runtime-0.13.5-py3-none-any.whl", hash = "sha256:d5eb7d578689f67d5dd631d28a092954c9a8797e130e16fe46e2895e4b0656ac", size = 98513, upload-time = "2026-09-10T09:32:51.404Z" }, ] [[package]] diff --git a/packages/uipath-google-adk/pyproject.toml b/packages/uipath-google-adk/pyproject.toml index c91b4cf8..370764bf 100644 --- a/packages/uipath-google-adk/pyproject.toml +++ b/packages/uipath-google-adk/pyproject.toml @@ -1,14 +1,16 @@ [project] name = "uipath-google-adk" -version = "0.1.0" +version = "0.1.1" description = "Python SDK that enables developers to build and deploy Google ADK agents to the UiPath Cloud Platform" readme = "README.md" requires-python = ">=3.11" dependencies = [ "google-adk>=1.25.1", "openinference-instrumentation-google-adk>=0.1.9", - "uipath>=2.13.0, <2.14.0", - "uipath-runtime>=0.12.1, <0.13.0", + # TODO(release): restore ">=2.14.15, <2.15.0" and drop the testpypi + # source once uipath 2.14.15 (UiPath/uipath-python#1886) is on PyPI. + "uipath==2.14.15.dev1018867616", + "uipath-runtime>=0.13.1, <0.14.0", ] classifiers = [ "Intended Audience :: Developers", @@ -130,6 +132,10 @@ url = "https://test.pypi.org/simple/" publish-url = "https://test.pypi.org/legacy/" explicit = true +# TODO(release): drop together with the uipath dev pin above. +[tool.uv.sources] +uipath = { index = "testpypi" } + [tool.coverage.run] source = ["src/uipath_google_adk"] relative_files = true diff --git a/packages/uipath-google-adk/src/uipath_google_adk/_cli/cli_new.py b/packages/uipath-google-adk/src/uipath_google_adk/_cli/cli_new.py index 96ba638c..2a8392a4 100644 --- a/packages/uipath-google-adk/src/uipath_google_adk/_cli/cli_new.py +++ b/packages/uipath-google-adk/src/uipath_google_adk/_cli/cli_new.py @@ -4,6 +4,8 @@ import click from uipath._cli._utils._console import ConsoleLogger from uipath._cli.middlewares import MiddlewareResult +from uipath._cli.models.agent_frameworks import AgentFramework +from uipath._cli.models.project_types import ProjectType console = ConsoleLogger() @@ -48,14 +50,19 @@ def generate_pyproject(target_directory, project_name): f.write(toml_content) -def google_adk_new_middleware(name: str) -> MiddlewareResult: +def google_adk_new_middleware( + name: str, + project_type: ProjectType = ProjectType.AUTO, + agent_framework: AgentFramework | None = None, +) -> MiddlewareResult: """Middleware to create demo Google ADK agent""" + if not AgentFramework.GOOGLE_ADK.claims_scaffold(project_type, agent_framework): + return MiddlewareResult(should_continue=True) directory = os.getcwd() try: with console.spinner(f"Creating new agent {name} in current directory ..."): - generate_pyproject(directory, name) generate_script(directory) console.success("Created 'main.py' file.") console.success("Created 'google_adk.json' file.") diff --git a/packages/uipath-google-adk/tests/test_cli_new.py b/packages/uipath-google-adk/tests/test_cli_new.py index 3a70de4b..e8a305fc 100644 --- a/packages/uipath-google-adk/tests/test_cli_new.py +++ b/packages/uipath-google-adk/tests/test_cli_new.py @@ -1,5 +1,8 @@ """Tests for _cli/cli_new.py — project scaffolding middleware.""" +from uipath._cli.models.agent_frameworks import AgentFramework +from uipath._cli.models.project_types import ProjectType + from uipath_google_adk._cli.cli_new import ( generate_pyproject, generate_script, @@ -30,3 +33,68 @@ def test_creates_project_and_stops_pipeline(self, tmp_path, monkeypatch): assert (tmp_path / "main.py").exists() assert (tmp_path / "google_adk.json").exists() assert (tmp_path / "pyproject.toml").exists() + + +class TestProjectTypeGate: + """The middleware only claims scaffolds meant for google-adk.""" + + def test_function_type_passes_through(self, tmp_path, monkeypatch): + """project_type='function' must defer to the base `uipath new` scaffold. + + Regression guard for uipath-python#1543: installing an integration used + to hijack `uipath new` unconditionally, making the base function + scaffold unreachable. + """ + monkeypatch.chdir(tmp_path) + result = google_adk_new_middleware("demo", project_type=ProjectType.FUNCTION) + assert result.should_continue is True + assert not (tmp_path / "main.py").exists() + assert not (tmp_path / "google_adk.json").exists() + assert not (tmp_path / "pyproject.toml").exists() + + def test_auto_type_scaffolds_agent(self, tmp_path, monkeypatch): + """--type auto (the default): an installed framework claims the scaffold.""" + monkeypatch.chdir(tmp_path) + result = google_adk_new_middleware("demo", project_type=ProjectType.AUTO) + assert result.should_continue is False + assert (tmp_path / "google_adk.json").exists() + + def test_agent_type_scaffolds_agent(self, tmp_path, monkeypatch): + monkeypatch.chdir(tmp_path) + result = google_adk_new_middleware( + "demo", + project_type=ProjectType.AGENT, + agent_framework=AgentFramework.GOOGLE_ADK, + ) + assert result.should_continue is False + assert (tmp_path / "google_adk.json").exists() + + def test_other_agent_framework_passes_through(self, tmp_path, monkeypatch): + """Another framework's scaffold must be left to its own integration.""" + monkeypatch.chdir(tmp_path) + result = google_adk_new_middleware( + "demo", + project_type=ProjectType.AGENT, + agent_framework=AgentFramework.PYDANTIC_AI, + ) + assert result.should_continue is True + assert not (tmp_path / "google_adk.json").exists() + + def test_plain_string_arguments_still_gate_correctly(self, tmp_path, monkeypatch): + """Callers passing raw strings (older CLIs) must hit the same gate.""" + monkeypatch.chdir(tmp_path) + # Deliberately off-type: the point is that plain strings still gate. + result = google_adk_new_middleware( + "demo", + project_type="agent", # type: ignore[arg-type] + agent_framework="pydantic-ai", # type: ignore[arg-type] + ) + assert result.should_continue is True + assert not (tmp_path / "google_adk.json").exists() + + def test_default_type_stays_agent_for_old_base_cli(self, tmp_path, monkeypatch): + """An older `uipath new` passes only the name; that still scaffolds.""" + monkeypatch.chdir(tmp_path) + result = google_adk_new_middleware("demo") + assert result.should_continue is False + assert (tmp_path / "google_adk.json").exists() diff --git a/packages/uipath-google-adk/uv.lock b/packages/uipath-google-adk/uv.lock index 780440c2..ef1042ca 100644 --- a/packages/uipath-google-adk/uv.lock +++ b/packages/uipath-google-adk/uv.lock @@ -3598,8 +3598,8 @@ wheels = [ [[package]] name = "uipath" -version = "2.13.1" -source = { registry = "https://pypi.org/simple" } +version = "2.14.15.dev1018867616" +source = { registry = "https://test.pypi.org/simple/" } dependencies = [ { name = "applicationinsights" }, { name = "click" }, @@ -3622,28 +3622,28 @@ dependencies = [ { name = "uipath-platform" }, { name = "uipath-runtime" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8b/0e/f044ea4510a4fe4f4ac03fe147f51847b71c05ed41ec6d49b566f8dec298/uipath-2.13.1.tar.gz", hash = "sha256:b1ad14b11c506d73a08189c245941220cb654953499110bedafcc38e8da5aab1", size = 4494015, upload-time = "2026-07-06T13:57:13.271Z" } +sdist = { url = "https://test-files.pythonhosted.org/packages/c8/08/12aac5a8b9cd53beb8a86399adcb8b346e1ecb63e1bdb3db8b9435ece231/uipath-2.14.15.dev1018867616.tar.gz", hash = "sha256:6c2842944ddd07b7bc63215e467e0c19481f3ef77eb3c67caac532e6eaa3a111", size = 4558272, upload-time = "2026-09-11T12:25:16.287Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/ca/08df99489017e92bf728375a4d5781a884318d06c8251e944674745a1bcd/uipath-2.13.1-py3-none-any.whl", hash = "sha256:e38baece455992f692dade4cf22b3bc6c4a7183d4c8dd2a5e8a09e27ab798aeb", size = 417593, upload-time = "2026-07-06T13:57:11.731Z" }, + { url = "https://test-files.pythonhosted.org/packages/e7/67/d8fef0e326feb5c4ac9b41f4160ec5116935b863f008e9d89d0235244bf3/uipath-2.14.15.dev1018867616-py3-none-any.whl", hash = "sha256:96d3e0a62950de2a1cfdf2f14f6685e9a5c7a2411bbec7f2ba9b983dd08027b3", size = 443220, upload-time = "2026-09-11T12:25:12.993Z" }, ] [[package]] name = "uipath-core" -version = "0.5.28" +version = "0.5.32" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-instrumentation" }, { name = "opentelemetry-sdk" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4c/f9/8d2f1d98cbebbcf059cf4561f38f34ad4cd58423e4f15cad22bd297a2563/uipath_core-0.5.28.tar.gz", hash = "sha256:942987f6b612c64f93d612ad7b242276ed75f129fdd8f25bc71c24ec8887e388", size = 130578, upload-time = "2026-06-30T14:04:48.841Z" } +sdist = { url = "https://files.pythonhosted.org/packages/99/0b/deb01dc671b075d88841f017a7c394e1b6f6869a9b3c027333cce587d59f/uipath_core-0.5.32.tar.gz", hash = "sha256:f709cc0b6a24d779b1b2200943512bedfc0842bac983ad0a9c668f35a991f4e7", size = 131009, upload-time = "2026-08-19T13:10:13.789Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/1e/385bb166232a57ebe938cc57ad2717f350bc922bb5d2ce31af84306b7569/uipath_core-0.5.28-py3-none-any.whl", hash = "sha256:b952a46a21710073cbc16d6d5684e9aa645c107f57a636b778cfb94aa81a1e48", size = 54980, upload-time = "2026-06-30T14:04:47.374Z" }, + { url = "https://files.pythonhosted.org/packages/2a/af/8c8641ea4b1596158ce47d6917d320a093bef9d7d93f781aeb03ee1ecf50/uipath_core-0.5.32-py3-none-any.whl", hash = "sha256:0a08644b6805db42be11257446947e419d14bb75e9c3ad82c4eee2509a5181da", size = 55123, upload-time = "2026-08-19T13:10:12.238Z" }, ] [[package]] name = "uipath-google-adk" -version = "0.1.0" +version = "0.1.1" source = { editable = "." } dependencies = [ { name = "google-adk" }, @@ -3673,8 +3673,8 @@ requires-dist = [ { name = "anthropic", marker = "extra == 'anthropic'", specifier = ">=0.43.0" }, { name = "google-adk", specifier = ">=1.25.1" }, { name = "openinference-instrumentation-google-adk", specifier = ">=0.1.9" }, - { name = "uipath", specifier = ">=2.13.0,<2.14.0" }, - { name = "uipath-runtime", specifier = ">=0.12.1,<0.13.0" }, + { name = "uipath", specifier = "==2.14.15.dev1018867616", index = "https://test.pypi.org/simple/" }, + { name = "uipath-runtime", specifier = ">=0.13.1,<0.14.0" }, ] provides-extras = ["anthropic"] @@ -3691,9 +3691,10 @@ dev = [ [[package]] name = "uipath-platform" -version = "0.2.0" +version = "0.2.28" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "anyio" }, { name = "httpx" }, { name = "pydantic-function-models" }, { name = "sqlparse" }, @@ -3701,23 +3702,23 @@ dependencies = [ { name = "truststore" }, { name = "uipath-core" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/22/2e/86bee6d9aa5d00ef95e729f3d37c78b3dbe8ebf83519f88357be5240ae76/uipath_platform-0.2.0.tar.gz", hash = "sha256:86bb1dbb4267afe43719276809bc7ebe7e9f9885ca8238da13a9776848636576", size = 412145, upload-time = "2026-07-06T13:42:39.658Z" } +sdist = { url = "https://files.pythonhosted.org/packages/62/3b/7c95f00997d23b16e9fcc738b54d8847291308e3e116ba8fa2b60093c461/uipath_platform-0.2.28.tar.gz", hash = "sha256:628c5c12ea5ae665c472711d9915216a939043bd4254240a8f645e1d1630cc54", size = 451707, upload-time = "2026-09-09T15:12:38.671Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/17/94/70e858975f022c44fa3d539632eb22a1a038ae39b4b187376e678371941a/uipath_platform-0.2.0-py3-none-any.whl", hash = "sha256:378986cb161521301560e32c2ea93950e1f14e8905bc7e3f6e034f548f2c6ac0", size = 271644, upload-time = "2026-07-06T13:42:38.246Z" }, + { url = "https://files.pythonhosted.org/packages/52/53/f5d1a559239aea5787b51ce6c31de84e45ea35c0ae1fca4ba507057f841f/uipath_platform-0.2.28-py3-none-any.whl", hash = "sha256:557c9cdabcdbe45d668ff8c896a680131aad1cb22720cf6ecbe1d95d8c1c1bcd", size = 293932, upload-time = "2026-09-09T15:12:36.838Z" }, ] [[package]] name = "uipath-runtime" -version = "0.12.1" +version = "0.13.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "chardet" }, { name = "uipath-core" }, { name = "vadersentiment" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/38/0b/6d6bfd912c2ad6978f58646becf3647430ccb087008a8c78dbc33b0b101e/uipath_runtime-0.12.1.tar.gz", hash = "sha256:a63ce739ea53da8479bb0314477f89d42724045ecbd7db3f2a3f0a09ab17a755", size = 235372, upload-time = "2026-07-06T11:14:51.947Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c8/8f/14ff9ccf152b6d97c0881f6b40d3e01037045ee52c467ed00dbb2319a0cb/uipath_runtime-0.13.5.tar.gz", hash = "sha256:923e4514f3d868f3711211fee6e2dd39f76c6aaf7354fec9e80cdc5e6fad5d80", size = 250415, upload-time = "2026-09-10T09:32:53.126Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/4c/433de72c4c5869ee26a63f68361b5c62d04bb03e9c7db9a69c0d3db7b458/uipath_runtime-0.12.1-py3-none-any.whl", hash = "sha256:88b014e0c5a50a5b41b2e532bd628eebc91d70ad42b6d3389fdc61b7ae78e0a6", size = 92004, upload-time = "2026-07-06T11:14:50.363Z" }, + { url = "https://files.pythonhosted.org/packages/95/88/b851e75b3c70bbedde1e24d5b5f38035863ed3d50e8facb75ac7d2db40b8/uipath_runtime-0.13.5-py3-none-any.whl", hash = "sha256:d5eb7d578689f67d5dd631d28a092954c9a8797e130e16fe46e2895e4b0656ac", size = 98513, upload-time = "2026-09-10T09:32:51.404Z" }, ] [[package]] diff --git a/packages/uipath-llamaindex/pyproject.toml b/packages/uipath-llamaindex/pyproject.toml index 26933773..6b6b25d3 100644 --- a/packages/uipath-llamaindex/pyproject.toml +++ b/packages/uipath-llamaindex/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath-llamaindex" -version = "0.6.1" +version = "0.6.2" description = "Python SDK that enables developers to build and deploy LlamaIndex agents to the UiPath Cloud Platform" readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.11" @@ -11,9 +11,11 @@ dependencies = [ "llama-index-embeddings-azure-openai>=0.4.1", "llama-index-llms-azure-openai>=0.4.2", "openinference-instrumentation-llama-index>=4.3.9", - "uipath>=2.13.0, <2.14.0", - "uipath-platform>=0.2.19, <0.3.0", - "uipath-runtime>=0.12.1, <0.13.0", + # TODO(release): restore ">=2.14.15, <2.15.0" and drop the testpypi + # source once uipath 2.14.15 (UiPath/uipath-python#1886) is on PyPI. + "uipath==2.14.15.dev1018867616", + "uipath-platform>=0.2.27, <0.3.0", + "uipath-runtime>=0.13.1, <0.14.0", ] classifiers = [ "Intended Audience :: Developers", @@ -130,6 +132,10 @@ url = "https://test.pypi.org/simple/" publish-url = "https://test.pypi.org/legacy/" explicit = true +# TODO(release): drop together with the uipath dev pin above. +[tool.uv.sources] +uipath = { index = "testpypi" } + [tool.coverage.run] source = ["src/uipath_llamaindex"] relative_files = true diff --git a/packages/uipath-llamaindex/src/uipath_llamaindex/_cli/cli_new.py b/packages/uipath-llamaindex/src/uipath_llamaindex/_cli/cli_new.py index 81c0daf1..f0af2342 100644 --- a/packages/uipath-llamaindex/src/uipath_llamaindex/_cli/cli_new.py +++ b/packages/uipath-llamaindex/src/uipath_llamaindex/_cli/cli_new.py @@ -4,6 +4,8 @@ import click from uipath._cli._utils._console import ConsoleLogger from uipath._cli.middlewares import MiddlewareResult +from uipath._cli.models.agent_frameworks import AgentFramework +from uipath._cli.models.project_types import ProjectType console = ConsoleLogger() @@ -40,14 +42,19 @@ def generate_pyproject(target_directory, project_name): f.write(toml_content) -def llamaindex_new_middleware(name: str) -> MiddlewareResult: +def llamaindex_new_middleware( + name: str, + project_type: ProjectType = ProjectType.AUTO, + agent_framework: AgentFramework | None = None, +) -> MiddlewareResult: """Middleware to create demo llamaindex agent""" + if not AgentFramework.LLAMAINDEX.claims_scaffold(project_type, agent_framework): + return MiddlewareResult(should_continue=True) directory = os.getcwd() try: with console.spinner(f"Creating new agent {name} in current directory ..."): - generate_pyproject(directory, name) generate_script(directory) console.success("Created 'main.py' file.") console.success("Created 'llama_index.json' file.") diff --git a/packages/uipath-llamaindex/tests/cli/test_new.py b/packages/uipath-llamaindex/tests/cli/test_new.py new file mode 100644 index 00000000..6bb9e1cc --- /dev/null +++ b/packages/uipath-llamaindex/tests/cli/test_new.py @@ -0,0 +1,102 @@ +"""Tests for the `uipath new` LlamaIndex scaffolding middleware.""" + +import os + +import pytest +from uipath._cli.models.agent_frameworks import AgentFramework +from uipath._cli.models.project_types import ProjectType + +from uipath_llamaindex._cli.cli_new import llamaindex_new_middleware + + +def test_middleware_scaffolds_project_files( + tmp_path, monkeypatch: pytest.MonkeyPatch +) -> None: + """The middleware writes main.py, llama_index.json and pyproject into cwd.""" + monkeypatch.chdir(tmp_path) + + result = llamaindex_new_middleware("my-agent") + + assert result.should_continue is False + for name in ("main.py", "llama_index.json", "pyproject.toml"): + assert os.path.exists(tmp_path / name) + pyproject = (tmp_path / "pyproject.toml").read_text() + assert 'name = "my-agent"' in pyproject + assert "uipath-llamaindex" in pyproject + + +class TestProjectTypeGate: + """The middleware only claims scaffolds meant for llamaindex.""" + + def test_function_type_passes_through( + self, tmp_path, monkeypatch: pytest.MonkeyPatch + ) -> None: + """project_type='function' must defer to the base `uipath new` scaffold. + + Regression guard for uipath-python#1543: installing an integration used + to hijack `uipath new` unconditionally, making the base function + scaffold unreachable. + """ + monkeypatch.chdir(tmp_path) + result = llamaindex_new_middleware("demo", project_type=ProjectType.FUNCTION) + assert result.should_continue is True + assert not os.path.exists(tmp_path / "main.py") + assert not os.path.exists(tmp_path / "llama_index.json") + assert not os.path.exists(tmp_path / "pyproject.toml") + + def test_auto_type_scaffolds_agent( + self, tmp_path, monkeypatch: pytest.MonkeyPatch + ) -> None: + """--type auto (the default): an installed framework claims the scaffold.""" + monkeypatch.chdir(tmp_path) + result = llamaindex_new_middleware("demo", project_type=ProjectType.AUTO) + assert result.should_continue is False + assert os.path.exists(tmp_path / "llama_index.json") + + def test_agent_type_scaffolds_agent( + self, tmp_path, monkeypatch: pytest.MonkeyPatch + ) -> None: + monkeypatch.chdir(tmp_path) + result = llamaindex_new_middleware( + "demo", + project_type=ProjectType.AGENT, + agent_framework=AgentFramework.LLAMAINDEX, + ) + assert result.should_continue is False + assert os.path.exists(tmp_path / "llama_index.json") + + def test_other_agent_framework_passes_through( + self, tmp_path, monkeypatch: pytest.MonkeyPatch + ) -> None: + """Another framework's scaffold must be left to its own integration.""" + monkeypatch.chdir(tmp_path) + result = llamaindex_new_middleware( + "demo", + project_type=ProjectType.AGENT, + agent_framework=AgentFramework.PYDANTIC_AI, + ) + assert result.should_continue is True + assert not os.path.exists(tmp_path / "llama_index.json") + + def test_plain_string_arguments_still_gate_correctly( + self, tmp_path, monkeypatch: pytest.MonkeyPatch + ) -> None: + """Callers passing raw strings (older CLIs) must hit the same gate.""" + monkeypatch.chdir(tmp_path) + # Deliberately off-type: the point is that plain strings still gate. + result = llamaindex_new_middleware( + "demo", + project_type="agent", # type: ignore[arg-type] + agent_framework="pydantic-ai", # type: ignore[arg-type] + ) + assert result.should_continue is True + assert not os.path.exists(tmp_path / "llama_index.json") + + def test_default_type_stays_agent_for_old_base_cli( + self, tmp_path, monkeypatch: pytest.MonkeyPatch + ) -> None: + """An older `uipath new` passes only the name; that still scaffolds.""" + monkeypatch.chdir(tmp_path) + result = llamaindex_new_middleware("demo") + assert result.should_continue is False + assert os.path.exists(tmp_path / "llama_index.json") diff --git a/packages/uipath-llamaindex/uv.lock b/packages/uipath-llamaindex/uv.lock index 96939dc9..365079bb 100644 --- a/packages/uipath-llamaindex/uv.lock +++ b/packages/uipath-llamaindex/uv.lock @@ -3499,8 +3499,8 @@ wheels = [ [[package]] name = "uipath" -version = "2.13.1" -source = { registry = "https://pypi.org/simple" } +version = "2.14.15.dev1018867616" +source = { registry = "https://test.pypi.org/simple/" } dependencies = [ { name = "applicationinsights" }, { name = "click" }, @@ -3523,28 +3523,28 @@ dependencies = [ { name = "uipath-platform" }, { name = "uipath-runtime" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8b/0e/f044ea4510a4fe4f4ac03fe147f51847b71c05ed41ec6d49b566f8dec298/uipath-2.13.1.tar.gz", hash = "sha256:b1ad14b11c506d73a08189c245941220cb654953499110bedafcc38e8da5aab1", size = 4494015, upload-time = "2026-07-06T13:57:13.271Z" } +sdist = { url = "https://test-files.pythonhosted.org/packages/c8/08/12aac5a8b9cd53beb8a86399adcb8b346e1ecb63e1bdb3db8b9435ece231/uipath-2.14.15.dev1018867616.tar.gz", hash = "sha256:6c2842944ddd07b7bc63215e467e0c19481f3ef77eb3c67caac532e6eaa3a111", size = 4558272, upload-time = "2026-09-11T12:25:16.287Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/ca/08df99489017e92bf728375a4d5781a884318d06c8251e944674745a1bcd/uipath-2.13.1-py3-none-any.whl", hash = "sha256:e38baece455992f692dade4cf22b3bc6c4a7183d4c8dd2a5e8a09e27ab798aeb", size = 417593, upload-time = "2026-07-06T13:57:11.731Z" }, + { url = "https://test-files.pythonhosted.org/packages/e7/67/d8fef0e326feb5c4ac9b41f4160ec5116935b863f008e9d89d0235244bf3/uipath-2.14.15.dev1018867616-py3-none-any.whl", hash = "sha256:96d3e0a62950de2a1cfdf2f14f6685e9a5c7a2411bbec7f2ba9b983dd08027b3", size = 443220, upload-time = "2026-09-11T12:25:12.993Z" }, ] [[package]] name = "uipath-core" -version = "0.5.28" +version = "0.5.32" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-instrumentation" }, { name = "opentelemetry-sdk" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4c/f9/8d2f1d98cbebbcf059cf4561f38f34ad4cd58423e4f15cad22bd297a2563/uipath_core-0.5.28.tar.gz", hash = "sha256:942987f6b612c64f93d612ad7b242276ed75f129fdd8f25bc71c24ec8887e388", size = 130578, upload-time = "2026-06-30T14:04:48.841Z" } +sdist = { url = "https://files.pythonhosted.org/packages/99/0b/deb01dc671b075d88841f017a7c394e1b6f6869a9b3c027333cce587d59f/uipath_core-0.5.32.tar.gz", hash = "sha256:f709cc0b6a24d779b1b2200943512bedfc0842bac983ad0a9c668f35a991f4e7", size = 131009, upload-time = "2026-08-19T13:10:13.789Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/1e/385bb166232a57ebe938cc57ad2717f350bc922bb5d2ce31af84306b7569/uipath_core-0.5.28-py3-none-any.whl", hash = "sha256:b952a46a21710073cbc16d6d5684e9aa645c107f57a636b778cfb94aa81a1e48", size = 54980, upload-time = "2026-06-30T14:04:47.374Z" }, + { url = "https://files.pythonhosted.org/packages/2a/af/8c8641ea4b1596158ce47d6917d320a093bef9d7d93f781aeb03ee1ecf50/uipath_core-0.5.32-py3-none-any.whl", hash = "sha256:0a08644b6805db42be11257446947e419d14bb75e9c3ad82c4eee2509a5181da", size = 55123, upload-time = "2026-08-19T13:10:12.238Z" }, ] [[package]] name = "uipath-llamaindex" -version = "0.6.0" +version = "0.6.2" source = { editable = "." } dependencies = [ { name = "aiosqlite" }, @@ -3554,6 +3554,7 @@ dependencies = [ { name = "llama-index-workflows" }, { name = "openinference-instrumentation-llama-index" }, { name = "uipath" }, + { name = "uipath-platform" }, { name = "uipath-runtime" }, ] @@ -3603,8 +3604,9 @@ requires-dist = [ { name = "llama-index-llms-google-genai", marker = "extra == 'vertex'", specifier = ">=0.8.0" }, { name = "llama-index-workflows", specifier = ">=2.18.0,<3.0.0" }, { name = "openinference-instrumentation-llama-index", specifier = ">=4.3.9" }, - { name = "uipath", specifier = ">=2.13.0,<2.14.0" }, - { name = "uipath-runtime", specifier = ">=0.12.1,<0.13.0" }, + { name = "uipath", specifier = "==2.14.15.dev1018867616", index = "https://test.pypi.org/simple/" }, + { name = "uipath-platform", specifier = ">=0.2.27,<0.3.0" }, + { name = "uipath-runtime", specifier = ">=0.13.1,<0.14.0" }, ] provides-extras = ["bedrock", "vertex"] @@ -3630,9 +3632,10 @@ dev = [ [[package]] name = "uipath-platform" -version = "0.2.0" +version = "0.2.28" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "anyio" }, { name = "httpx" }, { name = "pydantic-function-models" }, { name = "sqlparse" }, @@ -3640,23 +3643,23 @@ dependencies = [ { name = "truststore" }, { name = "uipath-core" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/22/2e/86bee6d9aa5d00ef95e729f3d37c78b3dbe8ebf83519f88357be5240ae76/uipath_platform-0.2.0.tar.gz", hash = "sha256:86bb1dbb4267afe43719276809bc7ebe7e9f9885ca8238da13a9776848636576", size = 412145, upload-time = "2026-07-06T13:42:39.658Z" } +sdist = { url = "https://files.pythonhosted.org/packages/62/3b/7c95f00997d23b16e9fcc738b54d8847291308e3e116ba8fa2b60093c461/uipath_platform-0.2.28.tar.gz", hash = "sha256:628c5c12ea5ae665c472711d9915216a939043bd4254240a8f645e1d1630cc54", size = 451707, upload-time = "2026-09-09T15:12:38.671Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/17/94/70e858975f022c44fa3d539632eb22a1a038ae39b4b187376e678371941a/uipath_platform-0.2.0-py3-none-any.whl", hash = "sha256:378986cb161521301560e32c2ea93950e1f14e8905bc7e3f6e034f548f2c6ac0", size = 271644, upload-time = "2026-07-06T13:42:38.246Z" }, + { url = "https://files.pythonhosted.org/packages/52/53/f5d1a559239aea5787b51ce6c31de84e45ea35c0ae1fca4ba507057f841f/uipath_platform-0.2.28-py3-none-any.whl", hash = "sha256:557c9cdabcdbe45d668ff8c896a680131aad1cb22720cf6ecbe1d95d8c1c1bcd", size = 293932, upload-time = "2026-09-09T15:12:36.838Z" }, ] [[package]] name = "uipath-runtime" -version = "0.12.1" +version = "0.13.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "chardet" }, { name = "uipath-core" }, { name = "vadersentiment" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/38/0b/6d6bfd912c2ad6978f58646becf3647430ccb087008a8c78dbc33b0b101e/uipath_runtime-0.12.1.tar.gz", hash = "sha256:a63ce739ea53da8479bb0314477f89d42724045ecbd7db3f2a3f0a09ab17a755", size = 235372, upload-time = "2026-07-06T11:14:51.947Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c8/8f/14ff9ccf152b6d97c0881f6b40d3e01037045ee52c467ed00dbb2319a0cb/uipath_runtime-0.13.5.tar.gz", hash = "sha256:923e4514f3d868f3711211fee6e2dd39f76c6aaf7354fec9e80cdc5e6fad5d80", size = 250415, upload-time = "2026-09-10T09:32:53.126Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/4c/433de72c4c5869ee26a63f68361b5c62d04bb03e9c7db9a69c0d3db7b458/uipath_runtime-0.12.1-py3-none-any.whl", hash = "sha256:88b014e0c5a50a5b41b2e532bd628eebc91d70ad42b6d3389fdc61b7ae78e0a6", size = 92004, upload-time = "2026-07-06T11:14:50.363Z" }, + { url = "https://files.pythonhosted.org/packages/95/88/b851e75b3c70bbedde1e24d5b5f38035863ed3d50e8facb75ac7d2db40b8/uipath_runtime-0.13.5-py3-none-any.whl", hash = "sha256:d5eb7d578689f67d5dd631d28a092954c9a8797e130e16fe46e2895e4b0656ac", size = 98513, upload-time = "2026-09-10T09:32:51.404Z" }, ] [[package]] diff --git a/packages/uipath-openai-agents/pyproject.toml b/packages/uipath-openai-agents/pyproject.toml index feec8f0b..f950e25a 100644 --- a/packages/uipath-openai-agents/pyproject.toml +++ b/packages/uipath-openai-agents/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath-openai-agents" -version = "0.1.1" +version = "0.1.2" description = "Python SDK that enables developers to build and deploy OpenAI agents to the UiPath Cloud Platform" readme = "README.md" requires-python = ">=3.11" @@ -9,9 +9,11 @@ dependencies = [ "openai>=1.0.0", "openai-agents>=0.6.5", "openinference-instrumentation-openai-agents>=1.4.0", - "uipath>=2.13.0, <2.14.0", - "uipath-platform>=0.2.19, <0.3.0", - "uipath-runtime>=0.12.1, <0.13.0", + # TODO(release): restore ">=2.14.15, <2.15.0" and drop the testpypi + # source once uipath 2.14.15 (UiPath/uipath-python#1886) is on PyPI. + "uipath==2.14.15.dev1018867616", + "uipath-platform>=0.2.27, <0.3.0", + "uipath-runtime>=0.13.1, <0.14.0", ] classifiers = [ "Intended Audience :: Developers", @@ -109,6 +111,10 @@ url = "https://test.pypi.org/simple/" publish-url = "https://test.pypi.org/legacy/" explicit = true +# TODO(release): drop together with the uipath dev pin above. +[tool.uv.sources] +uipath = { index = "testpypi" } + [tool.coverage.run] source = ["src/uipath_openai_agents"] relative_files = true 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 639ab62d..8e89eebf 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 @@ -4,6 +4,8 @@ import click from uipath._cli._utils._console import ConsoleLogger from uipath._cli.middlewares import MiddlewareResult +from uipath._cli.models.agent_frameworks import AgentFramework +from uipath._cli.models.project_types import ProjectType console = ConsoleLogger() @@ -48,14 +50,19 @@ def generate_pyproject(target_directory, project_name): f.write(toml_content) -def openai_agents_new_middleware(name: str) -> MiddlewareResult: +def openai_agents_new_middleware( + name: str, + project_type: ProjectType = ProjectType.AUTO, + agent_framework: AgentFramework | None = None, +) -> MiddlewareResult: """Middleware to create demo OpenAI agent""" + if not AgentFramework.OPENAI_AGENTS.claims_scaffold(project_type, agent_framework): + return MiddlewareResult(should_continue=True) directory = os.getcwd() try: with console.spinner(f"Creating new agent {name} in current directory ..."): - generate_pyproject(directory, name) generate_script(directory) console.success("Created 'main.py' file.") console.success("Created 'openai_agents.json' file.") diff --git a/packages/uipath-openai-agents/tests/test_cli_new.py b/packages/uipath-openai-agents/tests/test_cli_new.py index 5918ede6..77f06c85 100644 --- a/packages/uipath-openai-agents/tests/test_cli_new.py +++ b/packages/uipath-openai-agents/tests/test_cli_new.py @@ -3,6 +3,8 @@ import os import pytest +from uipath._cli.models.agent_frameworks import AgentFramework +from uipath._cli.models.project_types import ProjectType from uipath_openai_agents._cli import cli_new from uipath_openai_agents._cli.cli_new import openai_agents_new_middleware @@ -41,3 +43,68 @@ def boom(*args, **kwargs): assert errors and "disk full" in errors[0] assert result.should_continue is False assert result.should_include_stacktrace is True + + +class TestProjectTypeGate: + """The middleware only claims scaffolds meant for openai-agents.""" + + def test_function_type_passes_through(self, tmp_path, monkeypatch): + """project_type='function' must defer to the base `uipath new` scaffold. + + Regression guard for uipath-python#1543: installing an integration used + to hijack `uipath new` unconditionally, making the base function + scaffold unreachable. + """ + monkeypatch.chdir(tmp_path) + result = openai_agents_new_middleware("demo", project_type=ProjectType.FUNCTION) + assert result.should_continue is True + assert not (tmp_path / "main.py").exists() + assert not (tmp_path / "openai_agents.json").exists() + assert not (tmp_path / "pyproject.toml").exists() + + def test_auto_type_scaffolds_agent(self, tmp_path, monkeypatch): + """--type auto (the default): an installed framework claims the scaffold.""" + monkeypatch.chdir(tmp_path) + result = openai_agents_new_middleware("demo", project_type=ProjectType.AUTO) + assert result.should_continue is False + assert (tmp_path / "openai_agents.json").exists() + + def test_agent_type_scaffolds_agent(self, tmp_path, monkeypatch): + monkeypatch.chdir(tmp_path) + result = openai_agents_new_middleware( + "demo", + project_type=ProjectType.AGENT, + agent_framework=AgentFramework.OPENAI_AGENTS, + ) + assert result.should_continue is False + assert (tmp_path / "openai_agents.json").exists() + + def test_other_agent_framework_passes_through(self, tmp_path, monkeypatch): + """Another framework's scaffold must be left to its own integration.""" + monkeypatch.chdir(tmp_path) + result = openai_agents_new_middleware( + "demo", + project_type=ProjectType.AGENT, + agent_framework=AgentFramework.PYDANTIC_AI, + ) + assert result.should_continue is True + assert not (tmp_path / "openai_agents.json").exists() + + def test_plain_string_arguments_still_gate_correctly(self, tmp_path, monkeypatch): + """Callers passing raw strings (older CLIs) must hit the same gate.""" + monkeypatch.chdir(tmp_path) + # Deliberately off-type: the point is that plain strings still gate. + result = openai_agents_new_middleware( + "demo", + project_type="agent", # type: ignore[arg-type] + agent_framework="pydantic-ai", # type: ignore[arg-type] + ) + assert result.should_continue is True + assert not (tmp_path / "openai_agents.json").exists() + + def test_default_type_stays_agent_for_old_base_cli(self, tmp_path, monkeypatch): + """An older `uipath new` passes only the name; that still scaffolds.""" + monkeypatch.chdir(tmp_path) + result = openai_agents_new_middleware("demo") + assert result.should_continue is False + assert (tmp_path / "openai_agents.json").exists() diff --git a/packages/uipath-openai-agents/uv.lock b/packages/uipath-openai-agents/uv.lock index f583e2e8..50ee5113 100644 --- a/packages/uipath-openai-agents/uv.lock +++ b/packages/uipath-openai-agents/uv.lock @@ -2341,8 +2341,8 @@ wheels = [ [[package]] name = "uipath" -version = "2.13.1" -source = { registry = "https://pypi.org/simple" } +version = "2.14.15.dev1018867616" +source = { registry = "https://test.pypi.org/simple/" } dependencies = [ { name = "applicationinsights" }, { name = "click" }, @@ -2365,28 +2365,28 @@ dependencies = [ { name = "uipath-platform" }, { name = "uipath-runtime" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8b/0e/f044ea4510a4fe4f4ac03fe147f51847b71c05ed41ec6d49b566f8dec298/uipath-2.13.1.tar.gz", hash = "sha256:b1ad14b11c506d73a08189c245941220cb654953499110bedafcc38e8da5aab1", size = 4494015, upload-time = "2026-07-06T13:57:13.271Z" } +sdist = { url = "https://test-files.pythonhosted.org/packages/c8/08/12aac5a8b9cd53beb8a86399adcb8b346e1ecb63e1bdb3db8b9435ece231/uipath-2.14.15.dev1018867616.tar.gz", hash = "sha256:6c2842944ddd07b7bc63215e467e0c19481f3ef77eb3c67caac532e6eaa3a111", size = 4558272, upload-time = "2026-09-11T12:25:16.287Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/ca/08df99489017e92bf728375a4d5781a884318d06c8251e944674745a1bcd/uipath-2.13.1-py3-none-any.whl", hash = "sha256:e38baece455992f692dade4cf22b3bc6c4a7183d4c8dd2a5e8a09e27ab798aeb", size = 417593, upload-time = "2026-07-06T13:57:11.731Z" }, + { url = "https://test-files.pythonhosted.org/packages/e7/67/d8fef0e326feb5c4ac9b41f4160ec5116935b863f008e9d89d0235244bf3/uipath-2.14.15.dev1018867616-py3-none-any.whl", hash = "sha256:96d3e0a62950de2a1cfdf2f14f6685e9a5c7a2411bbec7f2ba9b983dd08027b3", size = 443220, upload-time = "2026-09-11T12:25:12.993Z" }, ] [[package]] name = "uipath-core" -version = "0.5.28" +version = "0.5.32" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-instrumentation" }, { name = "opentelemetry-sdk" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4c/f9/8d2f1d98cbebbcf059cf4561f38f34ad4cd58423e4f15cad22bd297a2563/uipath_core-0.5.28.tar.gz", hash = "sha256:942987f6b612c64f93d612ad7b242276ed75f129fdd8f25bc71c24ec8887e388", size = 130578, upload-time = "2026-06-30T14:04:48.841Z" } +sdist = { url = "https://files.pythonhosted.org/packages/99/0b/deb01dc671b075d88841f017a7c394e1b6f6869a9b3c027333cce587d59f/uipath_core-0.5.32.tar.gz", hash = "sha256:f709cc0b6a24d779b1b2200943512bedfc0842bac983ad0a9c668f35a991f4e7", size = 131009, upload-time = "2026-08-19T13:10:13.789Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/1e/385bb166232a57ebe938cc57ad2717f350bc922bb5d2ce31af84306b7569/uipath_core-0.5.28-py3-none-any.whl", hash = "sha256:b952a46a21710073cbc16d6d5684e9aa645c107f57a636b778cfb94aa81a1e48", size = 54980, upload-time = "2026-06-30T14:04:47.374Z" }, + { url = "https://files.pythonhosted.org/packages/2a/af/8c8641ea4b1596158ce47d6917d320a093bef9d7d93f781aeb03ee1ecf50/uipath_core-0.5.32-py3-none-any.whl", hash = "sha256:0a08644b6805db42be11257446947e419d14bb75e9c3ad82c4eee2509a5181da", size = 55123, upload-time = "2026-08-19T13:10:12.238Z" }, ] [[package]] name = "uipath-openai-agents" -version = "0.1.0" +version = "0.1.2" source = { editable = "." } dependencies = [ { name = "aiosqlite" }, @@ -2394,6 +2394,7 @@ dependencies = [ { name = "openai-agents" }, { name = "openinference-instrumentation-openai-agents" }, { name = "uipath" }, + { name = "uipath-platform" }, { name = "uipath-runtime" }, ] @@ -2414,8 +2415,9 @@ requires-dist = [ { name = "openai", specifier = ">=1.0.0" }, { name = "openai-agents", specifier = ">=0.6.5" }, { name = "openinference-instrumentation-openai-agents", specifier = ">=1.4.0" }, - { name = "uipath", specifier = ">=2.13.0,<2.14.0" }, - { name = "uipath-runtime", specifier = ">=0.12.1,<0.13.0" }, + { name = "uipath", specifier = "==2.14.15.dev1018867616", index = "https://test.pypi.org/simple/" }, + { name = "uipath-platform", specifier = ">=0.2.27,<0.3.0" }, + { name = "uipath-runtime", specifier = ">=0.13.1,<0.14.0" }, ] [package.metadata.requires-dev] @@ -2431,9 +2433,10 @@ dev = [ [[package]] name = "uipath-platform" -version = "0.2.0" +version = "0.2.28" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "anyio" }, { name = "httpx" }, { name = "pydantic-function-models" }, { name = "sqlparse" }, @@ -2441,23 +2444,23 @@ dependencies = [ { name = "truststore" }, { name = "uipath-core" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/22/2e/86bee6d9aa5d00ef95e729f3d37c78b3dbe8ebf83519f88357be5240ae76/uipath_platform-0.2.0.tar.gz", hash = "sha256:86bb1dbb4267afe43719276809bc7ebe7e9f9885ca8238da13a9776848636576", size = 412145, upload-time = "2026-07-06T13:42:39.658Z" } +sdist = { url = "https://files.pythonhosted.org/packages/62/3b/7c95f00997d23b16e9fcc738b54d8847291308e3e116ba8fa2b60093c461/uipath_platform-0.2.28.tar.gz", hash = "sha256:628c5c12ea5ae665c472711d9915216a939043bd4254240a8f645e1d1630cc54", size = 451707, upload-time = "2026-09-09T15:12:38.671Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/17/94/70e858975f022c44fa3d539632eb22a1a038ae39b4b187376e678371941a/uipath_platform-0.2.0-py3-none-any.whl", hash = "sha256:378986cb161521301560e32c2ea93950e1f14e8905bc7e3f6e034f548f2c6ac0", size = 271644, upload-time = "2026-07-06T13:42:38.246Z" }, + { url = "https://files.pythonhosted.org/packages/52/53/f5d1a559239aea5787b51ce6c31de84e45ea35c0ae1fca4ba507057f841f/uipath_platform-0.2.28-py3-none-any.whl", hash = "sha256:557c9cdabcdbe45d668ff8c896a680131aad1cb22720cf6ecbe1d95d8c1c1bcd", size = 293932, upload-time = "2026-09-09T15:12:36.838Z" }, ] [[package]] name = "uipath-runtime" -version = "0.12.1" +version = "0.13.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "chardet" }, { name = "uipath-core" }, { name = "vadersentiment" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/38/0b/6d6bfd912c2ad6978f58646becf3647430ccb087008a8c78dbc33b0b101e/uipath_runtime-0.12.1.tar.gz", hash = "sha256:a63ce739ea53da8479bb0314477f89d42724045ecbd7db3f2a3f0a09ab17a755", size = 235372, upload-time = "2026-07-06T11:14:51.947Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c8/8f/14ff9ccf152b6d97c0881f6b40d3e01037045ee52c467ed00dbb2319a0cb/uipath_runtime-0.13.5.tar.gz", hash = "sha256:923e4514f3d868f3711211fee6e2dd39f76c6aaf7354fec9e80cdc5e6fad5d80", size = 250415, upload-time = "2026-09-10T09:32:53.126Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/4c/433de72c4c5869ee26a63f68361b5c62d04bb03e9c7db9a69c0d3db7b458/uipath_runtime-0.12.1-py3-none-any.whl", hash = "sha256:88b014e0c5a50a5b41b2e532bd628eebc91d70ad42b6d3389fdc61b7ae78e0a6", size = 92004, upload-time = "2026-07-06T11:14:50.363Z" }, + { url = "https://files.pythonhosted.org/packages/95/88/b851e75b3c70bbedde1e24d5b5f38035863ed3d50e8facb75ac7d2db40b8/uipath_runtime-0.13.5-py3-none-any.whl", hash = "sha256:d5eb7d578689f67d5dd631d28a092954c9a8797e130e16fe46e2895e4b0656ac", size = 98513, upload-time = "2026-09-10T09:32:51.404Z" }, ] [[package]] diff --git a/packages/uipath-pydantic-ai/pyproject.toml b/packages/uipath-pydantic-ai/pyproject.toml index 36fd76e9..5128d0cc 100644 --- a/packages/uipath-pydantic-ai/pyproject.toml +++ b/packages/uipath-pydantic-ai/pyproject.toml @@ -1,15 +1,17 @@ [project] name = "uipath-pydantic-ai" -version = "0.1.1" +version = "0.1.2" description = "Python SDK that enables developers to build and deploy PydanticAI agents to the UiPath Cloud Platform" readme = "README.md" requires-python = ">=3.11" dependencies = [ "pydantic-ai>=1.63.0, <2.0.0", "openinference-instrumentation-pydantic-ai>=0.1.12", - "uipath>=2.13.0, <2.14.0", - "uipath-platform>=0.2.19, <0.3.0", - "uipath-runtime>=0.12.1, <0.13.0", + # TODO(release): restore ">=2.14.15, <2.15.0" and drop the testpypi + # source once uipath 2.14.15 (UiPath/uipath-python#1886) is on PyPI. + "uipath==2.14.15.dev1018867616", + "uipath-platform>=0.2.27, <0.3.0", + "uipath-runtime>=0.13.1, <0.14.0", ] classifiers = [ "Intended Audience :: Developers", @@ -106,6 +108,10 @@ url = "https://test.pypi.org/simple/" publish-url = "https://test.pypi.org/legacy/" explicit = true +# TODO(release): drop together with the uipath dev pin above. +[tool.uv.sources] +uipath = { index = "testpypi" } + [tool.coverage.run] source = ["src/uipath_pydantic_ai"] relative_files = true diff --git a/packages/uipath-pydantic-ai/src/uipath_pydantic_ai/_cli/cli_new.py b/packages/uipath-pydantic-ai/src/uipath_pydantic_ai/_cli/cli_new.py index feef3bee..582b4395 100644 --- a/packages/uipath-pydantic-ai/src/uipath_pydantic_ai/_cli/cli_new.py +++ b/packages/uipath-pydantic-ai/src/uipath_pydantic_ai/_cli/cli_new.py @@ -4,6 +4,8 @@ import click from uipath._cli._utils._console import ConsoleLogger from uipath._cli.middlewares import MiddlewareResult +from uipath._cli.models.agent_frameworks import AgentFramework +from uipath._cli.models.project_types import ProjectType console = ConsoleLogger() @@ -40,8 +42,14 @@ def generate_pyproject(target_directory, project_name): f.write(toml_content) -def pydantic_ai_new_middleware(name: str) -> MiddlewareResult: +def pydantic_ai_new_middleware( + name: str, + project_type: ProjectType = ProjectType.AUTO, + agent_framework: AgentFramework | None = None, +) -> MiddlewareResult: """Middleware to create demo PydanticAI agent""" + if not AgentFramework.PYDANTIC_AI.claims_scaffold(project_type, agent_framework): + return MiddlewareResult(should_continue=True) directory = os.getcwd() diff --git a/packages/uipath-pydantic-ai/tests/test_cli_new.py b/packages/uipath-pydantic-ai/tests/test_cli_new.py index ac31b2ea..139dbca3 100644 --- a/packages/uipath-pydantic-ai/tests/test_cli_new.py +++ b/packages/uipath-pydantic-ai/tests/test_cli_new.py @@ -5,6 +5,8 @@ import click import pytest +from uipath._cli.models.agent_frameworks import AgentFramework +from uipath._cli.models.project_types import ProjectType from uipath_pydantic_ai._cli.cli_new import pydantic_ai_new_middleware @@ -37,3 +39,68 @@ def _boom(*a, **k): with click.Context(click.Command("new")): with pytest.raises((SystemExit, click.exceptions.Exit)): pydantic_ai_new_middleware("broken") + + +class TestProjectTypeGate: + """The middleware only claims scaffolds meant for pydantic-ai.""" + + def test_function_type_passes_through(self, tmp_path, monkeypatch): + """project_type='function' must defer to the base `uipath new` scaffold. + + Regression guard for uipath-python#1543: installing an integration used + to hijack `uipath new` unconditionally, making the base function + scaffold unreachable. + """ + monkeypatch.chdir(tmp_path) + result = pydantic_ai_new_middleware("demo", project_type=ProjectType.FUNCTION) + assert result.should_continue is True + assert not (tmp_path / "main.py").exists() + assert not (tmp_path / "pydantic_ai.json").exists() + assert not (tmp_path / "pyproject.toml").exists() + + def test_auto_type_scaffolds_agent(self, tmp_path, monkeypatch): + """--type auto (the default): an installed framework claims the scaffold.""" + monkeypatch.chdir(tmp_path) + result = pydantic_ai_new_middleware("demo", project_type=ProjectType.AUTO) + assert result.should_continue is False + assert (tmp_path / "pydantic_ai.json").exists() + + def test_agent_type_scaffolds_agent(self, tmp_path, monkeypatch): + monkeypatch.chdir(tmp_path) + result = pydantic_ai_new_middleware( + "demo", + project_type=ProjectType.AGENT, + agent_framework=AgentFramework.PYDANTIC_AI, + ) + assert result.should_continue is False + assert (tmp_path / "pydantic_ai.json").exists() + + def test_other_agent_framework_passes_through(self, tmp_path, monkeypatch): + """Another framework's scaffold must be left to its own integration.""" + monkeypatch.chdir(tmp_path) + result = pydantic_ai_new_middleware( + "demo", + project_type=ProjectType.AGENT, + agent_framework=AgentFramework.LANGCHAIN, + ) + assert result.should_continue is True + assert not (tmp_path / "pydantic_ai.json").exists() + + def test_plain_string_arguments_still_gate_correctly(self, tmp_path, monkeypatch): + """Callers passing raw strings (older CLIs) must hit the same gate.""" + monkeypatch.chdir(tmp_path) + # Deliberately off-type: the point is that plain strings still gate. + result = pydantic_ai_new_middleware( + "demo", + project_type="agent", # type: ignore[arg-type] + agent_framework="langchain", # type: ignore[arg-type] + ) + assert result.should_continue is True + assert not (tmp_path / "pydantic_ai.json").exists() + + def test_default_type_stays_agent_for_old_base_cli(self, tmp_path, monkeypatch): + """An older `uipath new` passes only the name; that still scaffolds.""" + monkeypatch.chdir(tmp_path) + result = pydantic_ai_new_middleware("demo") + assert result.should_continue is False + assert (tmp_path / "pydantic_ai.json").exists() diff --git a/packages/uipath-pydantic-ai/uv.lock b/packages/uipath-pydantic-ai/uv.lock index 8af2317b..db65843b 100644 --- a/packages/uipath-pydantic-ai/uv.lock +++ b/packages/uipath-pydantic-ai/uv.lock @@ -3661,8 +3661,8 @@ wheels = [ [[package]] name = "uipath" -version = "2.13.1" -source = { registry = "https://pypi.org/simple" } +version = "2.14.15.dev1018867616" +source = { registry = "https://test.pypi.org/simple/" } dependencies = [ { name = "applicationinsights" }, { name = "click" }, @@ -3685,30 +3685,31 @@ dependencies = [ { name = "uipath-platform" }, { name = "uipath-runtime" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8b/0e/f044ea4510a4fe4f4ac03fe147f51847b71c05ed41ec6d49b566f8dec298/uipath-2.13.1.tar.gz", hash = "sha256:b1ad14b11c506d73a08189c245941220cb654953499110bedafcc38e8da5aab1", size = 4494015, upload-time = "2026-07-06T13:57:13.271Z" } +sdist = { url = "https://test-files.pythonhosted.org/packages/c8/08/12aac5a8b9cd53beb8a86399adcb8b346e1ecb63e1bdb3db8b9435ece231/uipath-2.14.15.dev1018867616.tar.gz", hash = "sha256:6c2842944ddd07b7bc63215e467e0c19481f3ef77eb3c67caac532e6eaa3a111", size = 4558272, upload-time = "2026-09-11T12:25:16.287Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/ca/08df99489017e92bf728375a4d5781a884318d06c8251e944674745a1bcd/uipath-2.13.1-py3-none-any.whl", hash = "sha256:e38baece455992f692dade4cf22b3bc6c4a7183d4c8dd2a5e8a09e27ab798aeb", size = 417593, upload-time = "2026-07-06T13:57:11.731Z" }, + { url = "https://test-files.pythonhosted.org/packages/e7/67/d8fef0e326feb5c4ac9b41f4160ec5116935b863f008e9d89d0235244bf3/uipath-2.14.15.dev1018867616-py3-none-any.whl", hash = "sha256:96d3e0a62950de2a1cfdf2f14f6685e9a5c7a2411bbec7f2ba9b983dd08027b3", size = 443220, upload-time = "2026-09-11T12:25:12.993Z" }, ] [[package]] name = "uipath-core" -version = "0.5.28" +version = "0.5.32" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-instrumentation" }, { name = "opentelemetry-sdk" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4c/f9/8d2f1d98cbebbcf059cf4561f38f34ad4cd58423e4f15cad22bd297a2563/uipath_core-0.5.28.tar.gz", hash = "sha256:942987f6b612c64f93d612ad7b242276ed75f129fdd8f25bc71c24ec8887e388", size = 130578, upload-time = "2026-06-30T14:04:48.841Z" } +sdist = { url = "https://files.pythonhosted.org/packages/99/0b/deb01dc671b075d88841f017a7c394e1b6f6869a9b3c027333cce587d59f/uipath_core-0.5.32.tar.gz", hash = "sha256:f709cc0b6a24d779b1b2200943512bedfc0842bac983ad0a9c668f35a991f4e7", size = 131009, upload-time = "2026-08-19T13:10:13.789Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/1e/385bb166232a57ebe938cc57ad2717f350bc922bb5d2ce31af84306b7569/uipath_core-0.5.28-py3-none-any.whl", hash = "sha256:b952a46a21710073cbc16d6d5684e9aa645c107f57a636b778cfb94aa81a1e48", size = 54980, upload-time = "2026-06-30T14:04:47.374Z" }, + { url = "https://files.pythonhosted.org/packages/2a/af/8c8641ea4b1596158ce47d6917d320a093bef9d7d93f781aeb03ee1ecf50/uipath_core-0.5.32-py3-none-any.whl", hash = "sha256:0a08644b6805db42be11257446947e419d14bb75e9c3ad82c4eee2509a5181da", size = 55123, upload-time = "2026-08-19T13:10:12.238Z" }, ] [[package]] name = "uipath-platform" -version = "0.2.0" +version = "0.2.28" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "anyio" }, { name = "httpx" }, { name = "pydantic-function-models" }, { name = "sqlparse" }, @@ -3716,19 +3717,20 @@ dependencies = [ { name = "truststore" }, { name = "uipath-core" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/22/2e/86bee6d9aa5d00ef95e729f3d37c78b3dbe8ebf83519f88357be5240ae76/uipath_platform-0.2.0.tar.gz", hash = "sha256:86bb1dbb4267afe43719276809bc7ebe7e9f9885ca8238da13a9776848636576", size = 412145, upload-time = "2026-07-06T13:42:39.658Z" } +sdist = { url = "https://files.pythonhosted.org/packages/62/3b/7c95f00997d23b16e9fcc738b54d8847291308e3e116ba8fa2b60093c461/uipath_platform-0.2.28.tar.gz", hash = "sha256:628c5c12ea5ae665c472711d9915216a939043bd4254240a8f645e1d1630cc54", size = 451707, upload-time = "2026-09-09T15:12:38.671Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/17/94/70e858975f022c44fa3d539632eb22a1a038ae39b4b187376e678371941a/uipath_platform-0.2.0-py3-none-any.whl", hash = "sha256:378986cb161521301560e32c2ea93950e1f14e8905bc7e3f6e034f548f2c6ac0", size = 271644, upload-time = "2026-07-06T13:42:38.246Z" }, + { url = "https://files.pythonhosted.org/packages/52/53/f5d1a559239aea5787b51ce6c31de84e45ea35c0ae1fca4ba507057f841f/uipath_platform-0.2.28-py3-none-any.whl", hash = "sha256:557c9cdabcdbe45d668ff8c896a680131aad1cb22720cf6ecbe1d95d8c1c1bcd", size = 293932, upload-time = "2026-09-09T15:12:36.838Z" }, ] [[package]] name = "uipath-pydantic-ai" -version = "0.1.0" +version = "0.1.2" source = { editable = "." } dependencies = [ { name = "openinference-instrumentation-pydantic-ai" }, { name = "pydantic-ai" }, { name = "uipath" }, + { name = "uipath-platform" }, { name = "uipath-runtime" }, ] @@ -3747,8 +3749,9 @@ dev = [ requires-dist = [ { name = "openinference-instrumentation-pydantic-ai", specifier = ">=0.1.12" }, { name = "pydantic-ai", specifier = ">=1.63.0,<2.0.0" }, - { name = "uipath", specifier = ">=2.13.0,<2.14.0" }, - { name = "uipath-runtime", specifier = ">=0.12.1,<0.13.0" }, + { name = "uipath", specifier = "==2.14.15.dev1018867616", index = "https://test.pypi.org/simple/" }, + { name = "uipath-platform", specifier = ">=0.2.27,<0.3.0" }, + { name = "uipath-runtime", specifier = ">=0.13.1,<0.14.0" }, ] [package.metadata.requires-dev] @@ -3764,16 +3767,16 @@ dev = [ [[package]] name = "uipath-runtime" -version = "0.12.1" +version = "0.13.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "chardet" }, { name = "uipath-core" }, { name = "vadersentiment" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/38/0b/6d6bfd912c2ad6978f58646becf3647430ccb087008a8c78dbc33b0b101e/uipath_runtime-0.12.1.tar.gz", hash = "sha256:a63ce739ea53da8479bb0314477f89d42724045ecbd7db3f2a3f0a09ab17a755", size = 235372, upload-time = "2026-07-06T11:14:51.947Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c8/8f/14ff9ccf152b6d97c0881f6b40d3e01037045ee52c467ed00dbb2319a0cb/uipath_runtime-0.13.5.tar.gz", hash = "sha256:923e4514f3d868f3711211fee6e2dd39f76c6aaf7354fec9e80cdc5e6fad5d80", size = 250415, upload-time = "2026-09-10T09:32:53.126Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/4c/433de72c4c5869ee26a63f68361b5c62d04bb03e9c7db9a69c0d3db7b458/uipath_runtime-0.12.1-py3-none-any.whl", hash = "sha256:88b014e0c5a50a5b41b2e532bd628eebc91d70ad42b6d3389fdc61b7ae78e0a6", size = 92004, upload-time = "2026-07-06T11:14:50.363Z" }, + { url = "https://files.pythonhosted.org/packages/95/88/b851e75b3c70bbedde1e24d5b5f38035863ed3d50e8facb75ac7d2db40b8/uipath_runtime-0.13.5-py3-none-any.whl", hash = "sha256:d5eb7d578689f67d5dd631d28a092954c9a8797e130e16fe46e2895e4b0656ac", size = 98513, upload-time = "2026-09-10T09:32:51.404Z" }, ] [[package]]