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
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "uipath-langchain"
version = "0.4.17"
version = "0.4.18"
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"
Expand Down
18 changes: 17 additions & 1 deletion src/uipath_langchain/agent/react/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
from pydantic import BaseModel
from uipath.platform.guardrails import BaseGuardrail

from uipath_langchain.chat.types import UiPathPassthroughChatModel

from ..guardrails.actions import GuardrailAction
from .guardrails.guardrails_subgraph import (
create_agent_init_guardrails_subgraph,
Expand All @@ -33,6 +35,7 @@
AgentGraphConfig,
AgentGraphNode,
AgentGraphState,
AgentSettings,
)
from .utils import create_state_with_input

Expand Down Expand Up @@ -62,6 +65,17 @@ def create_agent(
"""
from ..tools import create_tool_node

if not isinstance(model, UiPathPassthroughChatModel):
raise TypeError(
f"Model {type(model).__name__} does not implement UiPathPassthroughChatModel. "
"The model must have llm_provider and api_flavor properties."
)

agent_settings = AgentSettings(
llm_provider=model.llm_provider,
api_flavor=model.api_flavor,
)

if config is None:
config = AgentGraphConfig()

Expand All @@ -71,7 +85,9 @@ def create_agent(
)
llm_tools: list[BaseTool] = [*agent_tools, *flow_control_tools]

init_node = create_init_node(messages, input_schema, config.is_conversational)
init_node = create_init_node(
messages, input_schema, config.is_conversational, agent_settings
)

tool_nodes = create_tool_node(agent_tools)
tool_nodes_with_guardrails = create_tools_guardrails_subgraph(
Expand Down
210 changes: 0 additions & 210 deletions src/uipath_langchain/agent/react/file_type_handler.py

This file was deleted.

3 changes: 3 additions & 0 deletions src/uipath_langchain/agent/react/init_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
from .job_attachments import (
get_job_attachments,
)
from .types import AgentSettings


def create_init_node(
messages: Sequence[SystemMessage | HumanMessage]
| Callable[[Any], Sequence[SystemMessage | HumanMessage]],
input_schema: type[BaseModel] | None,
is_conversational: bool = False,
agent_settings: AgentSettings | None = None,
):
def graph_state_init(state: Any) -> Any:
resolved_messages: Sequence[SystemMessage | HumanMessage] | Overwrite
Expand Down Expand Up @@ -46,6 +48,7 @@ def graph_state_init(state: Any) -> Any:
"messages": resolved_messages,
"inner_state": {
"job_attachments": job_attachments_dict,
"agent_settings": agent_settings,
},
}

Expand Down
28 changes: 4 additions & 24 deletions src/uipath_langchain/agent/react/llm_node.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""LLM node for ReAct Agent graph."""

from typing import Literal, Sequence, TypeVar
from typing import Sequence, TypeVar

from langchain_core.language_models import BaseChatModel
from langchain_core.messages import AIMessage, AnyMessage, ToolCall
Expand All @@ -14,6 +14,7 @@
from uipath_langchain.agent.tools.structured_tool_with_argument_properties import (
StructuredToolWithArgumentProperties,
)
from uipath_langchain.llm import get_payload_handler

from ..exceptions import AgentTerminationException
from .constants import (
Expand All @@ -23,28 +24,6 @@
from .types import FLOW_CONTROL_TOOLS, AgentGraphState
from .utils import count_consecutive_thinking_messages, extract_input_data_from_state

OPENAI_COMPATIBLE_CHAT_MODELS = (
"UiPathChatOpenAI",
"AzureChatOpenAI",
"ChatOpenAI",
"UiPathChat",
"UiPathAzureChatOpenAI",
)


def _get_required_tool_choice_by_model(
model: BaseChatModel,
) -> Literal["required", "any"]:
"""Get the appropriate tool_choice value to enforce tool usage based on model type.

"required" - OpenAI compatible required tool_choice value
"any" - Vertex and Bedrock parameter for required tool_choice value
"""
model_class_name = model.__class__.__name__
if model_class_name in OPENAI_COMPATIBLE_CHAT_MODELS:
return "required"
return "any"


def _filter_control_flow_tool_calls(
tool_calls: list[ToolCall],
Expand Down Expand Up @@ -82,7 +61,8 @@ def create_llm_node(
before enforcing tool usage. 0 = force tools every time.
"""
bindable_tools = list(tools) if tools else []
tool_choice_required_value = _get_required_tool_choice_by_model(model)
payload_handler = get_payload_handler(model)
tool_choice_required_value = payload_handler.get_required_tool_choice()

async def llm_node(state: StateT):
messages: list[AnyMessage] = state.messages
Expand Down
Loading