Skip to content
Open
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
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
[project]
name = "uipath-langchain"
version = "0.14.9"
version = "0.14.10"
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.13.7, <2.14.0",
"uipath-core>=0.5.29, <0.6.0",
"uipath-platform>=0.2.10, <0.3.0",
"uipath-runtime>=0.12.1, <0.13.0",
"uipath-runtime>=0.12.4, <0.13.0",
"uipath-llm-client>=1.16.3, <1.17.0",
"langgraph>=1.1.8, <2.0.0",
"langchain-core>=1.2.27, <2.0.0",
Expand Down
3 changes: 3 additions & 0 deletions samples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ This sample demonstrates a simple LangGraph agent that performs basic arithmetic
## [Chat agent](chat-agent)
This sample shows how to build an AI assistant using LangGraph and Tavily search for movie research and recommendations.

## [Coded DeepAgent](coded-deepagent)
This sample demonstrates a task-mode coded DeepAgent using the UiPath runtime workspace contract, structured output, a tool, and a review subagent.

## [Company research agent](company-research-agent)
This sample demonstrates how to create an agent that researches companies and develops outreach strategies using web search capabilities.

Expand Down
48 changes: 48 additions & 0 deletions samples/coded-deepagent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Coded DeepAgent

This sample demonstrates a task-mode coded agent built with
`uipath_langchain.deepagents.create_uipath_deep_agent`.

The API follows the authoring surface of `deepagents.create_deep_agent`. UiPath
owns compile-time configuration, including the filesystem backend and
checkpointer. It marks the graph for the UiPath runtime, which creates and
hydrates a disk-backed workspace through job attachments.

## What It Shows

- Standard DeepAgents message input and structured output.
- A standard LangChain tool used by the main DeepAgent.
- A DeepAgents subagent used for risk review.
- Runtime-provided, attachment-hydrated filesystem workspace.

## Files

- `graph.py`: task-mode coded DeepAgent graph.
- `input.json`: sample input payload.
- `langgraph.json`: LangGraph entrypoint.
- `uipath.json`: UiPath task-mode runtime configuration.
- `pyproject.toml`: sample dependencies.
- `agent.mermaid`: conceptual view of the agent workflow.

## Requirements

- UiPath runtime credentials for `UiPathChat`.
- Access to the configured model, `gpt-5.4`.

## Installation

```bash
cd samples/coded-deepagent
uv sync
uv run uipath auth
```

## Usage

```bash
uv run uipath init
uv run uipath run agent "$(cat input.json)"
```

The agent uses the filesystem tools supplied by the DeepAgents harness for its
working files and returns a structured launch brief.
15 changes: 15 additions & 0 deletions samples/coded-deepagent/agent.mermaid
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
flowchart TB
__start__(__start__)
deep_agent(deep_agent)
tools([agent tools])
risk_reviewer([risk_reviewer subagent])
workspace[(runtime workspace)]
structured_output(structured output)
__end__(__end__)

__start__ --> deep_agent
deep_agent <--> tools
deep_agent -. delegates review .-> risk_reviewer
deep_agent -. writes files .-> workspace
deep_agent --> structured_output
structured_output --> __end__
66 changes: 66 additions & 0 deletions samples/coded-deepagent/graph.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""Task-mode coded DeepAgent using the UiPath DeepAgents API."""

from langchain_core.tools import tool
from pydantic import BaseModel

from uipath_langchain.chat import UiPathChat
from uipath_langchain.deepagents import create_uipath_deep_agent


class BriefOutput(BaseModel):
"""Structured launch brief returned by the agent."""

executive_summary: str
launch_plan: list[str]
risk_review: list[str]


@tool
def score_launch_readiness(audience: str, constraints: list[str]) -> str:
"""Score launch readiness from simple deterministic planning signals."""
score = 80
if len(constraints) >= 3:
score -= 10
if any("compliance" in item.lower() for item in constraints):
score -= 10
if any("deadline" in item.lower() for item in constraints):
score -= 5
return f"Launch readiness for {audience}: {max(score, 35)}/100"


MODEL = UiPathChat(model="gpt-5.4", temperature=0)

RISK_REVIEWER_PROMPT = """You are a launch risk reviewer.
Review the proposed launch plan for practical delivery risks, compliance gaps,
unclear ownership, and missing follow-up work. Return concise findings that the
main agent can incorporate into the final brief."""

RISK_REVIEWER = {
"name": "risk_reviewer",
"description": "Reviews launch plans for execution risks and missing safeguards.",
"system_prompt": RISK_REVIEWER_PROMPT,
"model": MODEL,
}


SYSTEM_PROMPT = """You are a product launch planning agent.

Use the planning tools provided by DeepAgents. Use score_launch_readiness to get
a deterministic readiness signal. Delegate a plan review to risk_reviewer before
finalizing the answer.

Use the DeepAgents filesystem to write these working files before producing the
final answer:
- /launch/brief.md with the final launch brief
- /launch/risks.md with the risk review

Return structured output matching the schema."""


graph = create_uipath_deep_agent(
model=MODEL,
system_prompt=SYSTEM_PROMPT,
response_format=BriefOutput,
tools=[score_launch_readiness],
subagents=[RISK_REVIEWER],
)
8 changes: 8 additions & 0 deletions samples/coded-deepagent/input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"messages": [
{
"type": "human",
"content": "Create a pilot launch plan for Invoice Exception Copilot for accounts payable operations managers at three enterprise customers. The security review must complete before external rollout, the deadline is six weeks away, and the support team needs enablement material."
}
]
}
7 changes: 7 additions & 0 deletions samples/coded-deepagent/langgraph.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"dependencies": ["."],
"graphs": {
"agent": "./graph.py:graph"
},
"env": ".env"
}
15 changes: 15 additions & 0 deletions samples/coded-deepagent/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[project]
name = "coded-deepagent"
version = "0.0.1"
description = "Task-mode coded DeepAgent using the UiPath runtime workspace contract"
authors = [{ name = "UiPath", email = "support@uipath.com" }]
requires-python = ">=3.11"
dependencies = [
"uipath-langchain>=0.14.10, <0.15.0",
"uipath>=2.13.7, <2.14.0",
]
Comment thread
andreitava-uip marked this conversation as resolved.

[dependency-groups]
dev = [
"uipath-dev",
]
5 changes: 5 additions & 0 deletions samples/coded-deepagent/uipath.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"runtimeOptions": {
"isConversational": false
}
}
13 changes: 13 additions & 0 deletions src/uipath_langchain/deepagents/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""UiPath DeepAgents integration."""

from typing import Any

__all__ = ["create_uipath_deep_agent"]


def __getattr__(name: str) -> Any:
if name == "create_uipath_deep_agent":
from .agent import create_uipath_deep_agent

return create_uipath_deep_agent
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
66 changes: 66 additions & 0 deletions src/uipath_langchain/deepagents/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""UiPath-aware DeepAgents authoring API."""

from collections.abc import Callable, Sequence
from typing import Any

from deepagents import (
AsyncSubAgent,
CompiledSubAgent,
FilesystemPermission,
SubAgent,
)
from deepagents import create_deep_agent as _create_deep_agent
from langchain.agents import AgentState
from langchain.agents.middleware import AgentMiddleware, InterruptOnConfig
from langchain.agents.middleware.types import ResponseT
from langchain.agents.structured_output import ResponseFormat
from langchain_core.language_models import BaseChatModel
from langchain_core.messages import SystemMessage
from langchain_core.tools import BaseTool
from langgraph.graph.state import CompiledStateGraph
from langgraph.typing import ContextT

from .backend import _UiPathWorkspaceBackendFactory
from .metadata import mark_uipath_deep_agent


# This API intentionally omits DeepAgents' compile-time arguments.
# UiPath-compatible DeepAgents use the runtime-managed filesystem backend and
# checkpointer. Other compile-time settings are currently unsupported.
def create_uipath_deep_agent(
model: str | BaseChatModel | None = None,
tools: Sequence[BaseTool | Callable[..., Any] | dict[str, Any]] | None = None,
*,
system_prompt: str | SystemMessage | None = None,
middleware: Sequence[AgentMiddleware] = (),
subagents: Sequence[SubAgent | CompiledSubAgent | AsyncSubAgent] | None = None,
skills: list[str] | None = None,
memory: list[str] | None = None,
permissions: list[FilesystemPermission] | None = None,
interrupt_on: dict[str, bool | InterruptOnConfig] | None = None,
response_format: ResponseFormat[ResponseT]
| type[ResponseT]
| dict[str, Any]
| None = None,
context_schema: type[ContextT] | None = None,
) -> CompiledStateGraph[AgentState[ResponseT], ContextT, Any, Any]:
"""Create a DeepAgent backed by the UiPath runtime workspace.

Parameters match ``deepagents.create_deep_agent`` and are forwarded unchanged,
except that UiPath owns compile-time configuration.
"""
graph = _create_deep_agent(
model=model,
tools=tools,
system_prompt=system_prompt,
middleware=middleware,
subagents=subagents,
skills=skills,
memory=memory,
permissions=permissions,
backend=_UiPathWorkspaceBackendFactory(),
interrupt_on=interrupt_on,
response_format=response_format,
context_schema=context_schema,
)
return mark_uipath_deep_agent(graph)
24 changes: 24 additions & 0 deletions src/uipath_langchain/deepagents/backend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""Workspace backend helpers for UiPath DeepAgents."""

from deepagents.backends import FilesystemBackend
from langchain.tools import ToolRuntime
from langchain_core.runnables import RunnableConfig
from langgraph.config import get_config

from uipath_langchain.runtime._workspace import get_workspace_path


class _UiPathWorkspaceBackendFactory:
"""DeepAgents backend factory resolved from UiPath runtime config."""

def __call__(self, runtime: ToolRuntime) -> FilesystemBackend:
config: RunnableConfig | None = getattr(runtime, "config", None)
if config is None:
try:
config = get_config()
except RuntimeError:
config = {}
return FilesystemBackend(
root_dir=get_workspace_path(config),
virtual_mode=True,
)
39 changes: 39 additions & 0 deletions src/uipath_langchain/deepagents/metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""Runtime marker for UiPath-created DeepAgents graphs."""

from typing import Any, TypeVar

from langgraph.graph.state import CompiledStateGraph

_UIPATH_INTEGRATION_METADATA_KEY = "uipath_integration"
_UIPATH_DEEPAGENTS_INTEGRATION = "deepagents"

CompiledGraphT = TypeVar(
"CompiledGraphT",
bound=CompiledStateGraph[Any, Any, Any, Any],
)


def mark_uipath_deep_agent(
graph: CompiledGraphT,
) -> CompiledGraphT:
"""Return a graph copy with explicit UiPath DeepAgents runtime metadata."""
return graph.with_config(
{
"metadata": {
_UIPATH_INTEGRATION_METADATA_KEY: _UIPATH_DEEPAGENTS_INTEGRATION,
}
}
)


def is_uipath_deep_agent(
graph: CompiledStateGraph[Any, Any, Any, Any],
) -> bool:
"""Return whether ``graph`` was built by the UiPath DeepAgents API."""
config = getattr(graph, "config", None) or {}
metadata = config.get("metadata", {}) if isinstance(config, dict) else {}
if not isinstance(metadata, dict):
return False
return (
metadata.get(_UIPATH_INTEGRATION_METADATA_KEY) == _UIPATH_DEEPAGENTS_INTEGRATION
)
43 changes: 43 additions & 0 deletions src/uipath_langchain/runtime/_workspace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""Internal workspace integration shared by runtime-backed agents."""

from pathlib import Path
from typing import Any, NotRequired, TypedDict

from langchain_core.runnables import RunnableConfig

WORKSPACE_PATH_CONFIG_KEY = "uipath_workspace_path"


class _UiPathGraphConfigurable(TypedDict):
"""UiPath-owned values injected into LangGraph's configurable payload."""

thread_id: str
uipath_workspace_path: NotRequired[str]


def create_graph_configurable(
*,
thread_id: str,
workspace_path: Path | None,
) -> dict[str, Any]:
"""Create the UiPath-owned portion of LangGraph's configurable payload."""
configurable: _UiPathGraphConfigurable = {"thread_id": thread_id}
if workspace_path is not None:
configurable["uipath_workspace_path"] = str(workspace_path)
return dict(configurable)


def get_workspace_path(config: RunnableConfig) -> Path:
"""Decode and validate the UiPath-managed workspace path from graph config."""
configurable = config.get("configurable")
workspace_path = (
configurable.get(WORKSPACE_PATH_CONFIG_KEY)
if isinstance(configurable, dict)
else None
)
if not isinstance(workspace_path, str) or not workspace_path:
raise RuntimeError(
"UiPath DeepAgents workspace path is unavailable. Run graphs created "
"by create_uipath_deep_agent through the UiPath runtime."
)
return Path(workspace_path)
Loading
Loading