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
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
[project]
name = "uipath-langchain"
version = "0.4.11"
version = "0.4.12"
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.5.10,<2.6.0",
"uipath>=2.5.15,<2.6.0",
"uipath-runtime>=0.5.1,<0.6.0",
"langgraph>=1.0.0, <2.0.0",
"langchain-core>=1.2.5, <2.0.0",
Expand Down
2 changes: 1 addition & 1 deletion src/uipath_langchain/agent/exceptions/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Exceptions for the basic agent loop."""
"""Exceptions for the agent graph."""

from uipath.runtime.errors import UiPathRuntimeError

Expand Down
9 changes: 5 additions & 4 deletions src/uipath_langchain/agent/react/agent.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import os
from typing import Callable, Sequence, Type, TypeVar

from langchain_core.language_models import BaseChatModel
Expand Down Expand Up @@ -66,8 +65,6 @@ def create_agent(
if config is None:
config = AgentGraphConfig()

os.environ["LANGCHAIN_RECURSION_LIMIT"] = str(config.recursion_limit)

agent_tools = list(tools)
flow_control_tools: list[BaseTool] = (
[] if config.is_conversational else create_flow_control_tools(output_schema)
Expand Down Expand Up @@ -109,7 +106,11 @@ def create_agent(
builder.add_edge(START, AgentGraphNode.INIT)

llm_node = create_llm_node(
model, llm_tools, config.thinking_messages_limit, config.is_conversational
model,
llm_tools,
config.is_conversational,
config.llm_messages_limit,
config.thinking_messages_limit,
)
llm_with_guardrails_subgraph = create_llm_guardrails_subgraph(
(AgentGraphNode.LLM, llm_node), guardrails, input_schema=input_schema
Expand Down
3 changes: 2 additions & 1 deletion src/uipath_langchain/agent/react/constants.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
MAX_CONSECUTIVE_THINKING_MESSAGES = 0
DEFAULT_MAX_CONSECUTIVE_THINKING_MESSAGES = 0
DEFAULT_MAX_LLM_MESSAGES = 25
21 changes: 19 additions & 2 deletions src/uipath_langchain/agent/react/llm_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@
from langchain_core.language_models import BaseChatModel
from langchain_core.messages import AIMessage, AnyMessage, ToolCall
from langchain_core.tools import BaseTool
from uipath.runtime.errors import UiPathErrorCategory, UiPathErrorCode

from .constants import MAX_CONSECUTIVE_THINKING_MESSAGES
from ..exceptions import AgentTerminationException
from .constants import (
DEFAULT_MAX_CONSECUTIVE_THINKING_MESSAGES,
DEFAULT_MAX_LLM_MESSAGES,
)
from .types import FLOW_CONTROL_TOOLS, AgentGraphState
from .utils import count_consecutive_thinking_messages

Expand Down Expand Up @@ -46,8 +51,9 @@ def _filter_control_flow_tool_calls(
def create_llm_node(
model: BaseChatModel,
tools: Sequence[BaseTool] | None = None,
thinking_messages_limit: int = MAX_CONSECUTIVE_THINKING_MESSAGES,
is_conversational: bool = False,
llm_messages_limit: int = DEFAULT_MAX_LLM_MESSAGES,
thinking_messages_limit: int = DEFAULT_MAX_CONSECUTIVE_THINKING_MESSAGES,
):
"""Create LLM node with dynamic tool_choice enforcement.

Expand All @@ -57,6 +63,8 @@ def create_llm_node(
Args:
model: The chat model to use
tools: Available tools to bind
is_conversational: Whether this is a conversational agent
llm_messages_limit: Maximum number of LLM calls allowed per execution
thinking_messages_limit: Max consecutive LLM responses without tool calls
before enforcing tool usage. 0 = force tools every time.
"""
Expand All @@ -67,6 +75,15 @@ def create_llm_node(
async def llm_node(state: AgentGraphState):
messages: list[AnyMessage] = state.messages

agent_ai_messages = sum(1 for msg in messages if isinstance(msg, AIMessage))
if agent_ai_messages >= llm_messages_limit:
raise AgentTerminationException(
code=UiPathErrorCode.EXECUTION_ERROR,
title=f"Maximum iterations of '{llm_messages_limit}' reached.",
detail="Verify the agent's trajectory or consider increasing the max iterations in the agent's settings.",
category=UiPathErrorCategory.USER,
)

consecutive_thinking_messages = count_consecutive_thinking_messages(messages)

if (
Expand Down
8 changes: 5 additions & 3 deletions src/uipath_langchain/agent/react/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,15 @@ class AgentGraphNode(StrEnum):


class AgentGraphConfig(BaseModel):
recursion_limit: int = Field(
default=50, ge=1, description="Maximum recursion limit for the agent graph"
llm_messages_limit: int = Field(
default=25,
ge=1,
description="Maximum number of LLM calls allowed per agent execution",
)
thinking_messages_limit: int = Field(
default=0,
ge=0,
description="Max consecutive thinking messages before enforcing tool usage. 0 = force tools every time.",
description="Max consecutive thinking messages before enforcing tool calling. 0 = force tool calling every time.",
)
is_conversational: bool = Field(
default=False, description="If set, creates a graph for conversational agents"
Expand Down
10 changes: 5 additions & 5 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading