Skip to content
Draft
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
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, typed input/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
43 changes: 43 additions & 0 deletions samples/coded-deepagent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Coded DeepAgent

This sample demonstrates a task-mode coded agent built with the UiPath
DeepAgents contract.

The graph uses `create_uipath_deep_agent_graph`, which tags the graph for the
UiPath LangGraph runtime. At runtime, UiPath creates a workspace, injects its
path into LangGraph config, and hydrates that workspace through job
attachments. Task-mode DeepAgents persist workspace changes when the run
successfully completes or suspends. The sample does not configure a custom
bucket backend.

## What It Shows

- Typed coded-agent input and output with Pydantic models.
- System and user prompts rendered from typed input.
- A standard LangChain tool used by the main DeepAgent.
- A DeepAgents subagent used for risk review.
- Runtime-provided workspace persistence through the UiPath contract.

## Files

- `graph.py`: task-mode coded DeepAgent graph.
- `input.json`: sample input payload.
- `langgraph.json`: LangGraph entrypoint.
- `uipath.json`: UiPath task-mode runtime configuration.
- `agent.mermaid`: high-level graph diagram.

## Requirements

- UiPath runtime credentials for `UiPathChat`.
- Access to the configured model, `gpt-4o-2024-08-06`.

## Run

```bash
cd samples/coded-deepagent
uv sync
uipath run agent "$(cat input.json)"
```

The agent writes `/launch/brief.md` and `/launch/risks.md` in the DeepAgents
workspace and returns those paths in `workspace_files`.
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__)
transform_input(transform_input)
advanced_agent(advanced_agent)
risk_reviewer([risk_reviewer subagent])
workspace[(runtime workspace)]
transform_output(transform_output)
__end__(__end__)

__start__ --> transform_input
transform_input --> advanced_agent
advanced_agent -. delegates review .-> risk_reviewer
advanced_agent -. writes files .-> workspace
advanced_agent --> transform_output
transform_output --> __end__
98 changes: 98 additions & 0 deletions samples/coded-deepagent/graph.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
"""Task-mode coded DeepAgent using the UiPath DeepAgents contract."""

from langchain_core.tools import tool
from pydantic import BaseModel, Field

from uipath_langchain.chat import UiPathChat
from uipath_langchain.deepagents import create_uipath_deep_agent_graph


class BriefInput(BaseModel):
"""Input for the product launch brief."""

product_name: str = Field(description="Name of the product or feature.")
audience: str = Field(description="Primary customer or user audience.")
objective: str = Field(description="Main launch objective.")
constraints: list[str] = Field(
default_factory=list,
description="Important limits, requirements, or risks to account for.",
)


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

executive_summary: str
launch_plan: list[str]
risk_review: list[str]
workspace_files: 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-4o-2024-08-06", 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,
}


def build_system_prompt(args: dict) -> str:
return f"""You are a product launch planning agent for {args["product_name"]}.

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.

Tailor all planning to this audience: {args["audience"]}.

The UiPath runtime provides your DeepAgents filesystem through the tagged graph
contract. Write these workspace 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. Include the workspace file paths
you wrote in workspace_files."""


def build_user_prompt(args: dict) -> str:
constraints = args.get("constraints") or []
constraint_lines = "\n".join(f"- {item}" for item in constraints) or "- None"
return f"""Create a launch brief.

Product: {args["product_name"]}
Audience: {args["audience"]}
Objective: {args["objective"]}
Constraints:
{constraint_lines}
"""


graph = create_uipath_deep_agent_graph(
model=MODEL,
input_schema=BriefInput,
output_schema=BriefOutput,
system_prompt=build_system_prompt,
user_prompt=build_user_prompt,
tools=[score_launch_readiness],
subagents=[RISK_REVIEWER],
)
10 changes: 10 additions & 0 deletions samples/coded-deepagent/input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"product_name": "Invoice Exception Copilot",
"audience": "accounts payable operations managers",
"objective": "prepare a pilot launch plan for three enterprise customers",
"constraints": [
"security review must complete before external rollout",
"deadline is six weeks away",
"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.3, <0.15.0",
"uipath>=2.13.6, <2.14.0",
]

[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
}
}
41 changes: 30 additions & 11 deletions src/uipath_langchain/agent/advanced/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
from deepagents import CompiledSubAgent, SubAgent
from deepagents import create_deep_agent as _create_deep_agent
from deepagents.backends import BackendProtocol
from deepagents.backends.filesystem import FilesystemBackend
from deepagents.backends.protocol import BackendFactory
from langchain.agents.structured_output import ResponseFormat
from langchain_core.language_models import BaseChatModel
from langchain_core.messages import HumanMessage
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_core.runnables.config import RunnableConfig
from langchain_core.tools import BaseTool
from langgraph.graph import END, START
from langgraph.graph.state import CompiledStateGraph, StateGraph
Expand All @@ -23,6 +23,7 @@
from .utils import (
MEMORY_INDEX_VIRTUAL_PATH,
create_state_with_input,
is_workspace_filesystem_backend,
resolve_input_attachments,
)

Expand Down Expand Up @@ -62,22 +63,25 @@ def create_advanced_agent_graph(
input_schema: type[BaseModel] | None,
output_schema: type[BaseModel],
build_user_message: Callable[[dict[str, Any]], str],
build_system_message: Callable[[dict[str, Any]], str] | None = None,
subagents: Sequence[SubAgent | CompiledSubAgent] = (),
) -> StateGraph[Any, Any, Any, Any]:
"""Wrap the advanced agent in a parent graph that maps typed I/O to/from messages.

With a ``FilesystemBackend``, attachment-shaped inputs are downloaded into the
workspace and given a ``FilePath`` before the user message is built. A
``FilesystemBackend`` also enables workspace memory: deepagents'
With a filesystem workspace backend, attachment-shaped inputs are downloaded
into the workspace and given a ``FilePath`` before the user message is built.
Filesystem workspaces also enable workspace memory: deepagents'
``MemoryMiddleware`` reads ``/memory/MEMORY.md`` from the backend each turn.
Memory stays disabled for non-filesystem backends, which carry no workspace.
Memory stays disabled for backends which carry no workspace.
"""
memory_sources = (
[MEMORY_INDEX_VIRTUAL_PATH] if isinstance(backend, FilesystemBackend) else []
[MEMORY_INDEX_VIRTUAL_PATH] if is_workspace_filesystem_backend(backend) else []
)

inner_graph = create_advanced_agent(
model=model,
tools=tools,
subagents=subagents,
system_prompt=system_prompt,
backend=backend,
response_format=response_format,
Expand All @@ -90,7 +94,10 @@ def create_advanced_agent_graph(
get_job_attachment_paths(input_schema) if input_schema is not None else []
)

async def transform_input_async(state: BaseModel) -> dict[str, Any]:
async def transform_input_async(
state: BaseModel,
config: RunnableConfig,
) -> dict[str, Any]:
state_data = state.model_dump()
input_data = {k: v for k, v in state_data.items() if k not in internal_fields}
input_args = (
Expand All @@ -100,10 +107,20 @@ async def transform_input_async(state: BaseModel) -> dict[str, Any]:
)
if attachment_paths:
input_args = await resolve_input_attachments(
backend, attachment_paths, input_args
backend,
attachment_paths,
input_args,
state=state,
config=config,
)
messages = []
if build_system_message is not None:
system_text = build_system_message(input_args)
if system_text:
messages.append(SystemMessage(content=system_text, id="system-input"))
user_text = build_user_message(input_args)
return {"messages": [HumanMessage(content=user_text, id="user-input")]}
messages.append(HumanMessage(content=user_text, id="user-input"))
return {"messages": messages}

def transform_output(state: BaseModel) -> dict[str, Any]:
structured = getattr(state, "structured_response", {})
Expand All @@ -128,6 +145,7 @@ def create_conversational_advanced_agent_graph(
tools: Sequence[BaseTool],
system_prompt: str,
backend: BackendProtocol | BackendFactory | None,
subagents: Sequence[SubAgent | CompiledSubAgent] = (),
) -> StateGraph[Any, Any, Any, Any]:
"""Wrap the advanced agent in a parent graph that speaks the conversational contract.

Expand All @@ -141,12 +159,13 @@ def create_conversational_advanced_agent_graph(
from uipath_langchain.runtime.messages import UiPathChatMessagesMapper

memory_sources = (
[MEMORY_INDEX_VIRTUAL_PATH] if isinstance(backend, FilesystemBackend) else []
[MEMORY_INDEX_VIRTUAL_PATH] if is_workspace_filesystem_backend(backend) else []
)

inner_graph = create_advanced_agent(
model=model,
tools=tools,
subagents=subagents,
system_prompt=system_prompt,
backend=backend,
memory=memory_sources,
Expand Down
Loading
Loading