Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# CLAUDE.md — UiPath LLM Client

## Project Overview

This is a Python monorepo providing a unified client for UiPath's LLM services. It has two published packages:

- **`uipath-llm-client`** (core) — located at `src/uipath/llm_client/`. Core HTTP client with auth, retry, and provider-specific clients (OpenAI, Google, Anthropic).
- **`uipath-langchain-client`** (langchain) — located at `packages/uipath_langchain_client/`. LangChain-compatible chat models and embeddings, depends on the core package.

Supported backends: **AgentHub** (default), **LLMGateway**, and **Orchestrator**.

---

## Development Setup

Uses [uv](https://docs.astral.sh/uv/) with workspace support.

```bash
uv sync --all-extras
```

Run tests:
```bash
pytest tests
```

Lint and format:
```bash
ruff check
ruff format --check
pyright
```

Tests use VCR cassettes (SQLite, `tests/cassettes.db`) via `pytest-recording`. HTTP is recorded and replayed; real network calls are not made in CI.

---

## Repository Structure

```
src/uipath/llm_client/ # Core package source
clients/ # Provider clients (openai, google, anthropic)
settings/ # Backend settings (platform, llmgateway)
utils/ # Shared utilities
__version__.py # Core version string
packages/uipath_langchain_client/
src/uipath_langchain_client/
clients/ # LangChain model wrappers per provider
__version__.py # LangChain version string
pyproject.toml # LangChain package config (declares core dep)
pyproject.toml # Root workspace config
CHANGELOG.md # Core changelog
packages/uipath_langchain_client/CHANGELOG.md # LangChain changelog
tests/ # All tests (core/, langchain/, llamaindex/)
.github/workflows/ # CI/CD pipelines
```

---

## Versioning Rules

Both packages follow semantic versioning. The CD pipelines trigger on version changes in the respective `__version__.py` files.

### When core client changes

Update **all** of the following:

1. `src/uipath/llm_client/__version__.py` — bump core version
2. `packages/uipath_langchain_client/src/uipath_langchain_client/__version__.py` — bump to same version
3. `packages/uipath_langchain_client/pyproject.toml` — update the `uipath-llm-client >= X.Y.Z` dependency to match the new core version
4. `CHANGELOG.md` — add entry under new version
5. `packages/uipath_langchain_client/CHANGELOG.md` — add entry under new version

### When only langchain changes

Update **only**:

1. `packages/uipath_langchain_client/src/uipath_langchain_client/__version__.py` — bump langchain version
2. `packages/uipath_langchain_client/CHANGELOG.md` — add entry under new version

Do **not** touch the core `__version__.py` or root `CHANGELOG.md` for langchain-only changes.

---

## CHANGELOG Format

Follow the existing format — newest version first, grouped by date:

```markdown
## [X.Y.Z] - YYYY-MM-DD

### Added / Fixed / Changed
- Description of the change
```

---

## PR Guidelines

### Before Opening a PR

- Run `ruff check`, `ruff format --check`, and `pyright` — all must pass.
- Run `pytest tests` — all tests must pass.
- Apply versioning rules above: the CI workflow (`ci_change_version.yml`) enforces that any changed source files must have a corresponding version bump and changelog entry.

### PR Scope

- Keep PRs focused. One logical change per PR.
- If a change touches both core and langchain, that is a single PR — both packages version together.
- Do not mix version bumps with unrelated refactors.

### What to Include in the PR Description

- What changed and why.
- Which package(s) are affected (core, langchain, or both).
- Reference any related issues.

### Dev Builds

Add the `build:dev` label to a PR to trigger a dev package publish to TestPyPI. The PR description will be updated automatically with install instructions.

---

## Code Style

- **Formatter/linter:** ruff (config in root `pyproject.toml`)
- **Type checker:** pyright (strict)
- **Python version:** 3.11+ required, 3.13 used in CI
- StrEnum string comparisons are acceptable — do not replace with enum constants.
- Provider clients live under `clients/<provider>/` in both the core and langchain packages.
- Settings use pydantic-settings with `BaseSettings`; do not add ad-hoc config.

---

## CI/CD Pipelines

| Workflow | Trigger | Purpose |
|---|---|---|
| `ci.yml` | PR to main | Lint, type check, run tests |
| `ci_change_version.yml` | PR to main | Validate version bump + changelog |
| `cd.yml` | Push to main (core version change) | Build + publish core to PyPI |
| `cd-langchain.yml` | Push to main (langchain version change) | Build + publish langchain to PyPI |
| `publish-dev.yml` | PR with `build:dev` label | Publish dev build to TestPyPI |

The langchain CD waits for the core package to appear on PyPI before building, since it depends on it.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

All notable changes to `uipath_llm_client` (core package) will be documented in this file.

## [1.5.8] - 2026-03-26

### Fix
- Pass `base_url` to `OpenAI` and `AsyncOpenAI` constructors in `UiPathOpenAI` and `UiPathAsyncOpenAI` to ensure the correct endpoint is forwarded to the underlying SDK clients

## [1.5.7] - 2026-03-23

### Fix
Expand Down
5 changes: 5 additions & 0 deletions packages/uipath_langchain_client/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

All notable changes to `uipath_langchain_client` will be documented in this file.

## [1.5.8] - 2026-03-26

### Fix
- Pass `base_url` to `OpenAI` and `AsyncOpenAI` constructors in `UiPathChatOpenAI` to ensure the correct endpoint is used by the underlying SDK clients

## [1.5.7] - 2026-03-23

### Fix
Expand Down
2 changes: 1 addition & 1 deletion packages/uipath_langchain_client/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"langchain>=1.2.13",
"uipath-llm-client>=1.5.7",
"uipath-llm-client>=1.5.8",
]

[project.optional-dependencies]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
__title__ = "UiPath LangChain Client"
__description__ = "A Python client for interacting with UiPath's LLM services via LangChain."
__version__ = "1.5.7"
__version__ = "1.5.8"
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,13 @@ async def fix_url_and_api_flavor_header_async(request: Request):
api_key="PLACEHOLDER",
max_retries=0, # handled by the UiPath client
http_client=self.uipath_sync_client,
base_url=base_url,
)
self.root_async_client = AsyncOpenAI(
api_key="PLACEHOLDER",
max_retries=0, # handled by the UiPath client
http_client=self.uipath_async_client,
base_url=base_url,
)
self.client = self.root_client.chat.completions
self.async_client = self.root_async_client.chat.completions
Expand Down Expand Up @@ -111,6 +113,7 @@ async def fix_url_and_api_flavor_header_async(request: Request):

self.uipath_sync_client.event_hooks["request"].append(fix_url_and_api_flavor_header)
self.uipath_async_client.event_hooks["request"].append(fix_url_and_api_flavor_header_async)

self.root_client = AzureOpenAI(
azure_endpoint="PLACEHOLDER",
api_version="PLACEHOLDER",
Expand Down
2 changes: 1 addition & 1 deletion src/uipath/llm_client/__version__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
__title__ = "UiPath LLM Client"
__description__ = "A Python client for interacting with UiPath's LLM services."
__version__ = "1.5.7"
__version__ = "1.5.8"
2 changes: 2 additions & 0 deletions src/uipath/llm_client/clients/openai/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def __init__(
api_key="PLACEHOLDER",
max_retries=0,
http_client=httpx_client,
base_url=str(httpx_client.base_url).rstrip("/"),
)


Expand Down Expand Up @@ -91,6 +92,7 @@ def __init__(
api_key="PLACEHOLDER",
max_retries=0,
http_client=httpx_client,
base_url=str(httpx_client.base_url).rstrip("/"),
)


Expand Down