From 2912791c80ea529f0137fc8e69a6be172fd047fe Mon Sep 17 00:00:00 2001 From: Vlad Cimpeanu Date: Tue, 15 Sep 2026 16:57:24 +0300 Subject: [PATCH] fix(cli): write pyproject.toml once when scaffolding a new agent The agent framework, Google ADK, LlamaIndex and OpenAI Agents scaffolds called `generate_pyproject` twice per run: once before generating the project files and again after. The second call overwrote the first with identical content, so the bug was invisible, but it left the generation order inconsistent between packages and would silently discard anything written to pyproject.toml in between. Each scaffold now generates the script and its config first, then writes pyproject.toml once, matching the other integrations. A regression test per package counts the calls. The agent framework failure test needed a working directory of its own: with the redundant first call gone, the file generation that precedes the failure now writes into the current directory, which was the source tree. Co-Authored-By: Claude Opus 5 (1M context) --- .../uipath-agent-framework/pyproject.toml | 2 +- .../uipath_agent_framework/_cli/cli_new.py | 1 - .../tests/test_cli_new.py | 23 ++++++++++- packages/uipath-agent-framework/uv.lock | 2 +- packages/uipath-google-adk/pyproject.toml | 2 +- .../src/uipath_google_adk/_cli/cli_new.py | 1 - .../uipath-google-adk/tests/test_cli_new.py | 18 ++++++++ packages/uipath-google-adk/uv.lock | 2 +- packages/uipath-llamaindex/pyproject.toml | 2 +- .../src/uipath_llamaindex/_cli/cli_new.py | 1 - .../uipath-llamaindex/tests/cli/test_new.py | 41 +++++++++++++++++++ packages/uipath-llamaindex/uv.lock | 17 ++++---- packages/uipath-openai-agents/pyproject.toml | 2 +- .../src/uipath_openai_agents/_cli/cli_new.py | 1 - .../tests/test_cli_new.py | 17 ++++++++ packages/uipath-openai-agents/uv.lock | 17 ++++---- 16 files changed, 124 insertions(+), 25 deletions(-) create mode 100644 packages/uipath-llamaindex/tests/cli/test_new.py diff --git a/packages/uipath-agent-framework/pyproject.toml b/packages/uipath-agent-framework/pyproject.toml index c09cb109..f43a2fb7 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" 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..ec689365 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 @@ -54,7 +54,6 @@ def agent_framework_new_middleware(name: str) -> MiddlewareResult: 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..f6262018 100644 --- a/packages/uipath-agent-framework/tests/test_cli_new.py +++ b/packages/uipath-agent-framework/tests/test_cli_new.py @@ -24,7 +24,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 + # it writes before failing out of the source tree. + monkeypatch.chdir(tmp_path) + def boom(*args, **kwargs): raise OSError("disk full") @@ -35,3 +39,20 @@ def boom(*args, **kwargs): assert result.should_continue is False assert result.should_include_stacktrace is True + + +def test_middleware_writes_pyproject_once(tmp_path, monkeypatch): + """The scaffold generated pyproject.toml twice per run; guard against that.""" + monkeypatch.chdir(tmp_path) + calls = [] + original = cli_new.generate_pyproject + + def counting(directory, project_name): + calls.append(directory) + return original(directory, project_name) + + monkeypatch.setattr(cli_new, "generate_pyproject", counting) + result = agent_framework_new_middleware("demo") + + assert result.should_continue is False + assert len(calls) == 1 diff --git a/packages/uipath-agent-framework/uv.lock b/packages/uipath-agent-framework/uv.lock index 77f56754..1756b968 100644 --- a/packages/uipath-agent-framework/uv.lock +++ b/packages/uipath-agent-framework/uv.lock @@ -2516,7 +2516,7 @@ wheels = [ [[package]] name = "uipath-agent-framework" -version = "0.1.0" +version = "0.1.1" source = { editable = "." } dependencies = [ { name = "agent-framework-core" }, diff --git a/packages/uipath-google-adk/pyproject.toml b/packages/uipath-google-adk/pyproject.toml index c91b4cf8..30e1ca8b 100644 --- a/packages/uipath-google-adk/pyproject.toml +++ b/packages/uipath-google-adk/pyproject.toml @@ -1,6 +1,6 @@ [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" 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..54df34d0 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 @@ -55,7 +55,6 @@ def google_adk_new_middleware(name: str) -> MiddlewareResult: 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..3c792a8f 100644 --- a/packages/uipath-google-adk/tests/test_cli_new.py +++ b/packages/uipath-google-adk/tests/test_cli_new.py @@ -1,5 +1,6 @@ """Tests for _cli/cli_new.py — project scaffolding middleware.""" +from uipath_google_adk._cli import cli_new from uipath_google_adk._cli.cli_new import ( generate_pyproject, generate_script, @@ -30,3 +31,20 @@ 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() + + +def test_middleware_writes_pyproject_once(tmp_path, monkeypatch): + """The scaffold generated pyproject.toml twice per run; guard against that.""" + monkeypatch.chdir(tmp_path) + calls = [] + original = cli_new.generate_pyproject + + def counting(directory, project_name): + calls.append(directory) + return original(directory, project_name) + + monkeypatch.setattr(cli_new, "generate_pyproject", counting) + result = google_adk_new_middleware("demo") + + assert result.should_continue is False + assert len(calls) == 1 diff --git a/packages/uipath-google-adk/uv.lock b/packages/uipath-google-adk/uv.lock index 780440c2..a851eb75 100644 --- a/packages/uipath-google-adk/uv.lock +++ b/packages/uipath-google-adk/uv.lock @@ -3643,7 +3643,7 @@ wheels = [ [[package]] name = "uipath-google-adk" -version = "0.1.0" +version = "0.1.1" source = { editable = "." } dependencies = [ { name = "google-adk" }, diff --git a/packages/uipath-llamaindex/pyproject.toml b/packages/uipath-llamaindex/pyproject.toml index 26933773..e64c067f 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" 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..c0a54e2f 100644 --- a/packages/uipath-llamaindex/src/uipath_llamaindex/_cli/cli_new.py +++ b/packages/uipath-llamaindex/src/uipath_llamaindex/_cli/cli_new.py @@ -47,7 +47,6 @@ def llamaindex_new_middleware(name: str) -> MiddlewareResult: 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..6de696ed --- /dev/null +++ b/packages/uipath-llamaindex/tests/cli/test_new.py @@ -0,0 +1,41 @@ +"""Tests for the `uipath new` LlamaIndex scaffolding middleware.""" + +import pytest + +from uipath_llamaindex._cli import cli_new +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 (tmp_path / name).exists() + pyproject = (tmp_path / "pyproject.toml").read_text() + assert 'name = "my-agent"' in pyproject + assert "uipath-llamaindex" in pyproject + + +def test_middleware_writes_pyproject_once( + tmp_path, monkeypatch: pytest.MonkeyPatch +) -> None: + """The scaffold generated pyproject.toml twice per run; guard against that.""" + monkeypatch.chdir(tmp_path) + calls = [] + original = cli_new.generate_pyproject + + def counting(directory, project_name): + calls.append(directory) + return original(directory, project_name) + + monkeypatch.setattr(cli_new, "generate_pyproject", counting) + result = llamaindex_new_middleware("demo") + + assert result.should_continue is False + assert len(calls) == 1 diff --git a/packages/uipath-llamaindex/uv.lock b/packages/uipath-llamaindex/uv.lock index 96939dc9..acf97b2a 100644 --- a/packages/uipath-llamaindex/uv.lock +++ b/packages/uipath-llamaindex/uv.lock @@ -3530,21 +3530,21 @@ wheels = [ [[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" }, ] @@ -3604,6 +3605,7 @@ requires-dist = [ { 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-platform", specifier = ">=0.2.19,<0.3.0" }, { name = "uipath-runtime", specifier = ">=0.12.1,<0.13.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,9 +3643,9 @@ 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]] diff --git a/packages/uipath-openai-agents/pyproject.toml b/packages/uipath-openai-agents/pyproject.toml index feec8f0b..67cc18da 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" 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..7479a4cd 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 @@ -55,7 +55,6 @@ def openai_agents_new_middleware(name: str) -> MiddlewareResult: 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..6c496a01 100644 --- a/packages/uipath-openai-agents/tests/test_cli_new.py +++ b/packages/uipath-openai-agents/tests/test_cli_new.py @@ -41,3 +41,20 @@ 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 + + +def test_middleware_writes_pyproject_once(tmp_path, monkeypatch): + """The scaffold generated pyproject.toml twice per run; guard against that.""" + monkeypatch.chdir(tmp_path) + calls = [] + original = cli_new.generate_pyproject + + def counting(directory, project_name): + calls.append(directory) + return original(directory, project_name) + + monkeypatch.setattr(cli_new, "generate_pyproject", counting) + result = openai_agents_new_middleware("demo") + + assert result.should_continue is False + assert len(calls) == 1 diff --git a/packages/uipath-openai-agents/uv.lock b/packages/uipath-openai-agents/uv.lock index f583e2e8..4381f704 100644 --- a/packages/uipath-openai-agents/uv.lock +++ b/packages/uipath-openai-agents/uv.lock @@ -2372,21 +2372,21 @@ wheels = [ [[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" }, ] @@ -2415,6 +2416,7 @@ requires-dist = [ { 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-platform", specifier = ">=0.2.19,<0.3.0" }, { name = "uipath-runtime", specifier = ">=0.12.1,<0.13.0" }, ] @@ -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,9 +2444,9 @@ 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]]