diff --git a/pyproject.toml b/pyproject.toml index d90ebd408..13926e46d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,11 +1,13 @@ [project] name = "uipath-langchain" -version = "0.17.4" +version = "0.17.5" description = "Python SDK that enables developers to build and deploy LangGraph agents to the UiPath Cloud Platform" readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.11" dependencies = [ - "uipath>=2.14.6, <2.15.0", + # TODO(release): restore ">=2.14.13, <2.15.0" and drop the testpypi + # source once uipath 2.14.13 (UiPath/uipath-python#1886) is on PyPI. + "uipath==2.14.13.dev1018867574", "uipath-core>=0.5.29, <0.6.0", "uipath-platform>=0.2.24, <0.3.0", "uipath-runtime>=0.13.0, <0.14.0", @@ -179,3 +181,6 @@ name = "testpypi" url = "https://test.pypi.org/simple/" publish-url = "https://test.pypi.org/legacy/" explicit = true + +[tool.uv.sources] +uipath = { index = "testpypi" } diff --git a/src/uipath_langchain/_cli/cli_new.py b/src/uipath_langchain/_cli/cli_new.py index bd6e6c642..91b8b2142 100644 --- a/src/uipath_langchain/_cli/cli_new.py +++ b/src/uipath_langchain/_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,8 +49,21 @@ def generate_pyproject(target_directory, project_name): f.write(toml_content) -def langgraph_new_middleware(name: str) -> MiddlewareResult: - """Middleware to create demo langchain agent""" +def langgraph_new_middleware( + name: str, + project_type: ProjectType = ProjectType.AGENT, + agent_framework: AgentFramework = AgentFramework.LANGCHAIN, +) -> MiddlewareResult: + """Middleware to create demo langchain agent. + + Only handles `--type agent` scaffolds for the langchain framework; anything + else falls through to the base `uipath new` scaffold (default: function + project) or to another framework's integration. + """ + # `!=` instead of `is not`: callers may pass plain strings, which + # StrEnum equality handles. + if project_type != ProjectType.AGENT or agent_framework != AgentFramework.LANGCHAIN: + return MiddlewareResult(should_continue=True) directory = os.getcwd() diff --git a/tests/cli/test_new.py b/tests/cli/test_new.py index eaf8258d8..9b987a464 100644 --- a/tests/cli/test_new.py +++ b/tests/cli/test_new.py @@ -5,6 +5,8 @@ import pytest from packaging.specifiers import SpecifierSet from packaging.version import Version +from uipath._cli.models.agent_frameworks import AgentFramework +from uipath._cli.models.project_types import ProjectType from uipath_langchain._cli.cli_new import ( UIPATH_LANGCHAIN_SCAFFOLD_MINOR, @@ -14,6 +16,74 @@ PIN_RE = re.compile(r'"uipath-langchain\[bedrock,vertex\]([^"]*)"') +class TestProjectTypeGate: + """The middleware only claims agent scaffolds; everything else passes through.""" + + 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 uipath-langchain + used to hijack `uipath new` unconditionally, making the base function + scaffold unreachable. + """ + monkeypatch.chdir(tmp_path) + result = langgraph_new_middleware("demo", project_type=ProjectType.FUNCTION) + assert result.should_continue is True + assert not os.path.exists("main.py") + assert not os.path.exists("langgraph.json") + assert not os.path.exists("pyproject.toml") + + def test_agent_type_scaffolds_agent( + self, tmp_path, monkeypatch: pytest.MonkeyPatch + ) -> None: + monkeypatch.chdir(tmp_path) + result = langgraph_new_middleware( + "demo", + project_type=ProjectType.AGENT, + agent_framework=AgentFramework.LANGCHAIN, + ) + assert result.should_continue is False + assert os.path.exists("main.py") + assert os.path.exists("langgraph.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 = langgraph_new_middleware( + "demo", + project_type=ProjectType.AGENT, + agent_framework=AgentFramework.PYDANTIC_AI, + ) + assert result.should_continue is True + assert not os.path.exists("main.py") + assert not os.path.exists("langgraph.json") + assert not os.path.exists("pyproject.toml") + + 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) + result = langgraph_new_middleware( + "demo", project_type="agent", agent_framework="pydantic-ai" + ) + assert result.should_continue is True + assert not os.path.exists("langgraph.json") + + def test_default_type_stays_agent_for_old_base_cli( + self, tmp_path, monkeypatch: pytest.MonkeyPatch + ) -> None: + """Older `uipath` versions call without project_type; keep scaffolding an agent.""" + monkeypatch.chdir(tmp_path) + result = langgraph_new_middleware("demo") + assert result.should_continue is False + assert os.path.exists("langgraph.json") + + class TestUipathLangchainScaffoldPin: """The scaffolded pin must admit the uipath-langchain release it ships with.""" diff --git a/uv.lock b/uv.lock index 242fcee73..c3c23b732 100644 --- a/uv.lock +++ b/uv.lock @@ -4529,8 +4529,8 @@ wheels = [ [[package]] name = "uipath" -version = "2.14.6" -source = { registry = "https://pypi.org/simple" } +version = "2.14.13.dev1018867574" +source = { registry = "https://test.pypi.org/simple/" } dependencies = [ { name = "applicationinsights" }, { name = "click" }, @@ -4553,9 +4553,9 @@ dependencies = [ { name = "uipath-platform" }, { name = "uipath-runtime" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/43/f0/ab87d6776423d63320dc359cbb88cc1e57d5792fdd63c0f322ff55c110a7/uipath-2.14.6.tar.gz", hash = "sha256:c44fda80db5fae79b7e92aa2c0f34ba374eeea20d9efac7857568643ebffd456", size = 4543177, upload-time = "2026-08-19T13:13:10.581Z" } +sdist = { url = "https://test-files.pythonhosted.org/packages/9c/5f/fdfac5e8cb86c18b062930f4dccd043ecf62a3470f29ef6bfc967bd59727/uipath-2.14.13.dev1018867574.tar.gz", hash = "sha256:56674e7ea941cb212373f1dfe7797ff9fcb9c872b218a5c59d843a5339fa8900", size = 4555227, upload-time = "2026-09-08T12:56:42.708Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ad/21/fceec66117831747a5c547d00b02030a8b97bf497e61619a2d8c3ea089c3/uipath-2.14.6-py3-none-any.whl", hash = "sha256:8ae40208aa4c8d987eba49bb5cff9fdaf4ee24f4db984e99b66af6948708bbcc", size = 435763, upload-time = "2026-08-19T13:13:08.627Z" }, + { url = "https://test-files.pythonhosted.org/packages/1b/12/f9cd7c51ddd869a4982eb6c2234da548febaa30a5c618e5ecb992bf18415/uipath-2.14.13.dev1018867574-py3-none-any.whl", hash = "sha256:b7d9b1d2342ab1d024afae7f635384f9784f6634f363ca066f5df449a7c26cc9", size = 442097, upload-time = "2026-09-08T12:56:41.061Z" }, ] [[package]] @@ -4574,7 +4574,7 @@ wheels = [ [[package]] name = "uipath-langchain" -version = "0.17.4" +version = "0.17.5" source = { editable = "." } dependencies = [ { name = "a2a-sdk" }, @@ -4658,7 +4658,7 @@ requires-dist = [ { name = "pydantic-settings", specifier = ">=2.6.0" }, { name = "python-dotenv", specifier = ">=1.0.1" }, { name = "rdflib", specifier = ">=7.0.0,<8.0.0" }, - { name = "uipath", specifier = ">=2.14.6,<2.15.0" }, + { name = "uipath", specifier = "==2.14.13.dev1018867574", index = "https://test.pypi.org/simple/" }, { name = "uipath-core", specifier = ">=0.5.29,<0.6.0" }, { name = "uipath-langchain-client", extras = ["all"], marker = "extra == 'all'", specifier = ">=1.18.3,<1.19.0" }, { name = "uipath-langchain-client", extras = ["anthropic"], marker = "extra == 'anthropic'", specifier = ">=1.18.3,<1.19.0" }, @@ -4758,7 +4758,7 @@ wheels = [ [[package]] name = "uipath-platform" -version = "0.2.24" +version = "0.2.26" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -4769,9 +4769,9 @@ dependencies = [ { name = "truststore" }, { name = "uipath-core" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4d/d2/d2d79bd778e029ea32590be2aeab0b32f5f62ffab38d1a773bc13fb326f8/uipath_platform-0.2.24.tar.gz", hash = "sha256:6f1b8673d96c048142e6421a13b67002ca756ed80083e2c767ded4e0eccefd52", size = 447671, upload-time = "2026-09-03T12:21:32.472Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/24/48d06e76fb9ff4f67ffa44032238c040c4be84fd1b960c62e1fe1734f699/uipath_platform-0.2.26.tar.gz", hash = "sha256:d1e44fdcee0e97e51a94c642a9e69507d9cf552f6af7f5da5574d3b32eb60a0c", size = 448465, upload-time = "2026-09-04T07:43:29.301Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/40/f007b2819a5613047eff60f1f3b6ff8b108276abe2b7428cc0884d8c52d7/uipath_platform-0.2.24-py3-none-any.whl", hash = "sha256:1144874dba7e812ee2092580f94abba27c18f9552ca3a331992a571bfecdd561", size = 291730, upload-time = "2026-09-03T12:21:30.9Z" }, + { url = "https://files.pythonhosted.org/packages/b0/42/4cf10a375e1138fcda28bd2d4ef1caca3b496ca0f7ac3616d34291864354/uipath_platform-0.2.26-py3-none-any.whl", hash = "sha256:cf16450e8af14aa5ed4a84afa12a0c52986aead9425d2b0de50c6483a9c1dc10", size = 292090, upload-time = "2026-09-04T07:43:27.731Z" }, ] [[package]]