diff --git a/.gitignore b/.gitignore index e4c6a771a..6865f9e0b 100644 --- a/.gitignore +++ b/.gitignore @@ -203,3 +203,6 @@ testcases/**/CLAUDE.md testcases/**/.agent/ testcases/**/agent.mermaid testcases/**/project.uiproj + +# Generated at build time by hatch_build.py +/src/uipath_langchain/_version.py diff --git a/hatch_build.py b/hatch_build.py new file mode 100644 index 000000000..07699b728 --- /dev/null +++ b/hatch_build.py @@ -0,0 +1,90 @@ +"""Hatch build hook that stamps the source git commit into the built package. + +Writes the file named by ``version-file`` in ``[tool.hatch.build.hooks.custom]`` +using the setuptools-scm / hatch-vcs convention (``__version__`` and +``__commit_id__``), so an installed package can report the exact commit it was +built from:: + + from ._version import __commit_id__ + +The file is generated at build time and git-ignored. The commit comes from +``git rev-parse HEAD`` when building from a checkout of this repo. When there is +no checkout (e.g. building a wheel from an sdist), the commit already recorded +in the file is kept. If neither is available, ``__commit_id__`` is ``None``. +""" + +import ast +import subprocess +from pathlib import Path +from typing import Any + +from hatchling.builders.config import BuilderConfig +from hatchling.builders.hooks.plugin.interface import BuildHookInterface + +TEMPLATE = '''\ +"""Version and source commit of this build. Generated by hatch_build.py.""" + +__all__ = ["__version__", "__commit_id__", "version", "commit_id"] + +__version__ = version = {version!r} +__commit_id__ = commit_id = {commit_id!r} +''' + + +class CommitIdBuildHook(BuildHookInterface[BuilderConfig]): + """Generate the version file before each build.""" + + PLUGIN_NAME = "custom" + + def initialize(self, version: str, build_data: dict[str, Any]) -> None: + """Write the version file and ship it despite it being git-ignored.""" + relative_path = self.config["version-file"] + version_file = Path(self.root) / relative_path + commit_id = _git_commit(Path(self.root)) or _recorded_commit(version_file) + version_file.write_text( + TEMPLATE.format(version=self.metadata.version, commit_id=commit_id), + encoding="utf-8", + ) + build_data["artifacts"].append(f"/{relative_path}") + + +def _git_commit(root: Path) -> str | None: + """Return HEAD's full SHA if ``root`` is tracked in a git checkout.""" + try: + # Confirms root belongs to this repo, not to an unrelated parent checkout. + subprocess.run( + ["git", "ls-files", "--error-unmatch", "pyproject.toml"], + cwd=root, + check=True, + capture_output=True, + ) + result = subprocess.run( + ["git", "rev-parse", "HEAD"], + cwd=root, + check=True, + capture_output=True, + text=True, + ) + except (OSError, subprocess.CalledProcessError): + return None + return result.stdout.strip() or None + + +def _recorded_commit(version_file: Path) -> str | None: + """Return the commit already in ``version_file`` (e.g. one shipped in an sdist).""" + try: + tree = ast.parse(version_file.read_text(encoding="utf-8")) + except (OSError, SyntaxError): + return None + for node in tree.body: + if ( + isinstance(node, ast.Assign) + and any( + isinstance(t, ast.Name) and t.id == "__commit_id__" + for t in node.targets + ) + and isinstance(node.value, ast.Constant) + and isinstance(node.value.value, str) + ): + return node.value.value + return None diff --git a/pyproject.toml b/pyproject.toml index 4632160bf..d344a4943 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath-langchain" -version = "0.18.30" +version = "0.18.31" 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" @@ -102,8 +102,12 @@ dev = [ # directly, so they are declared here rather than relied on by accident. "starlette>=1.3.1, <2.0.0", "uvicorn>=0.30.0", + "hatchling", ] +[tool.hatch.build.hooks.custom] +version-file = "src/uipath_langchain/_version.py" + [tool.hatch.build.targets.wheel] packages = ["src/uipath_langchain"] diff --git a/uv.lock b/uv.lock index 8748475f8..5a1a522a9 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 3 +revision = 5 requires-python = ">=3.11" resolution-markers = [ "python_full_version >= '3.15'", @@ -14,13 +14,13 @@ exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for exclude-newer-span = "P2D" [options.exclude-newer-package] +jsonschema-pydantic-converter = false uipath = false +uipath-core = false +uipath-langchain-client = false +uipath-llm-client = false uipath-platform = false uipath-runtime = false -uipath-llm-client = false -jsonschema-pydantic-converter = false -uipath-langchain-client = false -uipath-core = false [[package]] name = "a2a-sdk" @@ -173,9 +173,9 @@ name = "aiologic" version = "0.17.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "sniffio", marker = "python_full_version < '3.13'" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, - { name = "wrapt", marker = "python_full_version < '3.13'" }, + { name = "sniffio" }, + { name = "typing-extensions" }, + { name = "wrapt" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f1/7a/d51f2fde1e8ae8a83431f8e97b7a71e9358cdb1d4d2ce6be387fa44d68de/aiologic-0.17.1.tar.gz", hash = "sha256:2e1b93b9e88ced318c2a63ad7b382688f40cbfe40e3d42258d49dc9c5aea179d", size = 252354, upload-time = "2026-06-27T20:41:33.25Z" } wheels = [ @@ -456,12 +456,12 @@ name = "black" version = "26.5.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "click", marker = "python_full_version != '3.12.*' or sys_platform != 'emscripten'" }, - { name = "mypy-extensions", marker = "python_full_version != '3.12.*' or sys_platform != 'emscripten'" }, - { name = "packaging", marker = "python_full_version != '3.12.*' or sys_platform != 'emscripten'" }, - { name = "pathspec", marker = "python_full_version != '3.12.*' or sys_platform != 'emscripten'" }, - { name = "platformdirs", marker = "python_full_version != '3.12.*' or sys_platform != 'emscripten'" }, - { name = "pytokens", marker = "python_full_version != '3.12.*' or sys_platform != 'emscripten'" }, + { name = "click" }, + { name = "mypy-extensions" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "platformdirs" }, + { name = "pytokens" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c0/37/5628dd55bf2b34257fc7603f0fe97c40e3aaf24265f416a9c85c95ca1436/black-26.5.1.tar.gz", hash = "sha256:dd321f668053961824bcc1be1cc1df748b2d7e4fa28086b08331e577b0100a73", size = 679439, upload-time = "2026-05-18T16:53:36.107Z" } wheels = [ @@ -1042,8 +1042,8 @@ name = "culsans" version = "0.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "aiologic", marker = "python_full_version < '3.13'" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "aiologic" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d9/e3/49afa1bc180e0d28008ec6bcdf82a4072d1c7a41032b5b759b60814ca4b0/culsans-0.11.0.tar.gz", hash = "sha256:0b43d0d05dce6106293d114c86e3fb4bfc63088cfe8ff08ed3fe36891447fe33", size = 107546, upload-time = "2025-12-31T23:15:38.196Z" } wheels = [ @@ -1648,6 +1648,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, ] +[[package]] +name = "hatchling" +version = "1.32.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "pathspec" }, + { name = "pluggy" }, + { name = "tomlkit" }, + { name = "trove-classifiers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f6/97/b5312f01a8c6daf729a9d272dd442e0c546dbcc630495788786c4b567ed0/hatchling-1.32.4.tar.gz", hash = "sha256:c4468f73144c054d2aab4ef0f0378c43b9878bf07f8ffd6b79690e970d375f07", size = 58065, upload-time = "2026-09-20T22:48:45.398Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/80/91f51f439c05d4ec4623c22928ce16a938d6d793bf709477830823497859/hatchling-1.32.4-py3-none-any.whl", hash = "sha256:08ecf7548fb48205e7f213d70c71e67b8271b7242093dc3f1da578b42c734a2c", size = 78748, upload-time = "2026-09-20T22:48:44.19Z" }, +] + [[package]] name = "hf-xet" version = "1.5.1" @@ -1698,8 +1714,8 @@ name = "httpcore2" version = "2.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "h11", marker = "python_full_version != '3.12.*' or sys_platform != 'emscripten'" }, - { name = "truststore", marker = "python_full_version != '3.12.*' or sys_platform != 'emscripten'" }, + { name = "h11" }, + { name = "truststore" }, ] sdist = { url = "https://files.pythonhosted.org/packages/be/ad/f4f0e57345f1870f3e8cb624e058d7eca6e5a27d33bcc3311d9b618734cd/httpcore2-2.12.0.tar.gz", hash = "sha256:9293522bba0aa7c4c8e9e3f040c16575bd8868e155a77fa30c7a9085a5eae648", size = 67548, upload-time = "2026-08-18T13:22:08.211Z" } wheels = [ @@ -4663,6 +4679,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, ] +[[package]] +name = "tomlkit" +version = "0.15.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/96/e07752635b98536177fa1f37671c8f3cdde2e724c6bcf6034b2cfb571565/tomlkit-0.15.1.tar.gz", hash = "sha256:e25bbf38843005246210a12982776f27f99cb9be67160e14434d0c0d21ee1e97", size = 180129, upload-time = "2026-07-17T01:48:04.562Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/bc/8c13eb66537dce1d2bd3a57132902f38d0e7f5bb46fa9f4daed9fe9d76ee/tomlkit-0.15.1-py3-none-any.whl", hash = "sha256:177a05aece5a8ca5266fd3c448abb47b8d352f09d477d3ca8332db4d89b24304", size = 49449, upload-time = "2026-07-17T01:48:05.728Z" }, +] + [[package]] name = "tqdm" version = "4.68.3" @@ -4675,6 +4700,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d8/8e/bb97bb0c71802080bfc8952937d174e49cfc50de5c951dd47b2496f0dcdb/tqdm-4.68.3-py3-none-any.whl", hash = "sha256:39832cc2def2789a6f29df83f172db7416cea70052c0907a57801c5f2fdccb03", size = 78337, upload-time = "2026-06-17T07:36:50.132Z" }, ] +[[package]] +name = "trove-classifiers" +version = "2026.9.21.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bf/93/af436dfaa845cab5d96f0adbc1e4f3730532d37fa249e4eb796fb1d7fc82/trove_classifiers-2026.9.21.13.tar.gz", hash = "sha256:0a9ebc8d4e2f3e8a22848c5258033035bec17a3012ac3fea16dbaa764489eb71", size = 17085, upload-time = "2026-09-21T13:29:07.301Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/81/0da8afb52a71d0a4f2bd3152357b1a441e393b286374802b9d3addab4ab5/trove_classifiers-2026.9.21.13-py3-none-any.whl", hash = "sha256:8b1ff4f9c191b1040b71c37f1e445ab99732911e3cd91de52838453a854d7a17", size = 14232, upload-time = "2026-09-21T13:29:06.123Z" }, +] + [[package]] name = "truststore" version = "0.10.4" @@ -4828,7 +4862,7 @@ wheels = [ [[package]] name = "uipath-langchain" -version = "0.18.30" +version = "0.18.31" source = { editable = "." } dependencies = [ { name = "a2a-sdk" }, @@ -4882,6 +4916,7 @@ vertex = [ [package.dev-dependencies] dev = [ { name = "filelock" }, + { name = "hatchling" }, { name = "mypy" }, { name = "numpy" }, { name = "packaging" }, @@ -4940,6 +4975,7 @@ provides-extras = ["anthropic", "vertex", "bedrock", "fireworks", "code-interpre [package.metadata.requires-dev] dev = [ { name = "filelock", specifier = ">=3.20.3" }, + { name = "hatchling" }, { name = "mypy", specifier = ">=1.14.1" }, { name = "numpy", specifier = ">=1.24.0" }, { name = "packaging", specifier = ">=24.0" },