Skip to content
Closed
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: 2 additions & 1 deletion src/uipath_langchain/agent/react/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""UiPath ReAct Agent implementation"""

from .agent import create_agent
from .types import AgentGraphConfig, AgentGraphNode, AgentGraphState
from .types import AgentGraphConfig, AgentGraphNode, AgentGraphState, AgentResources
from .utils import resolve_input_model, resolve_output_model

__all__ = [
Expand All @@ -11,4 +11,5 @@
"AgentGraphNode",
"AgentGraphState",
"AgentGraphConfig",
"AgentResources",
]
10 changes: 8 additions & 2 deletions src/uipath_langchain/agent/react/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
AgentGraphConfig,
AgentGraphNode,
AgentGraphState,
AgentResources,
)
from .utils import create_state_with_input

Expand All @@ -53,6 +54,7 @@ def create_agent(
output_schema: Type[OutputT] | None = None,
config: AgentGraphConfig | None = None,
guardrails: Sequence[tuple[BaseGuardrail, GuardrailAction]] | None = None,
resources: AgentResources | None = None,
) -> StateGraph[AgentGraphState, None, InputT, OutputT]:
"""Build agent graph with INIT -> AGENT (subgraph) <-> TOOLS loop, terminated by control flow tools.

Expand All @@ -74,8 +76,12 @@ 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,
resources_for_init=resources,
)
tool_nodes = create_tool_node(
agent_tools, handle_tool_errors=config.is_conversational
)
Expand Down
38 changes: 35 additions & 3 deletions src/uipath_langchain/agent/react/init_node.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""State initialization node for the ReAct Agent graph."""

import logging
from typing import Any, Callable, Sequence

from langchain_core.messages import HumanMessage, SystemMessage
Expand All @@ -10,20 +11,51 @@
get_job_attachments,
parse_attachments_from_conversation_messages,
)
from .types import AgentResources

logger = logging.getLogger(__name__)


def create_init_node(
messages: Sequence[SystemMessage | HumanMessage]
| Callable[[Any], Sequence[SystemMessage | HumanMessage]],
| Callable[..., Sequence[SystemMessage | HumanMessage]],
input_schema: type[BaseModel] | None,
is_conversational: bool = False,
resources_for_init: AgentResources | None = None,
):
def graph_state_init(state: Any) -> Any:
async def graph_state_init(state: Any) -> Any:
# --- Data Fabric schema fetch (INIT-time) ---
schema_context: str | None = None
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets move this to a helper method behind datafabric_tool folder. Few principles:

  1. Resources_for_init is a new extension getting introduced in the infra, so that it can be used by other resources if required.
  2. DataFabric may not be the tool always for resoures_for_init. Check for resource and then invoke the helper.

Similar pattern as tools_factory.py.

if resources_for_init:
from uipath_langchain.agent.tools.datafabric_tool import (
fetch_entity_schemas,
format_schemas_for_context,
get_datafabric_entity_identifiers_from_resources,
)

entity_identifiers = get_datafabric_entity_identifiers_from_resources(
resources_for_init
)
if entity_identifiers:
logger.info(
"Fetching Data Fabric schemas for %d identifier(s)",
len(entity_identifiers),
)
entities = await fetch_entity_schemas(entity_identifiers)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be single api outside tool

schema_context = format_schemas_for_context(entities)

# --- Resolve messages ---
resolved_messages: Sequence[SystemMessage | HumanMessage] | Overwrite
if callable(messages):
resolved_messages = list(messages(state))
if schema_context:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be keyed to additional_context_available. That derives from schema_context.

resolved_messages = list(
messages(state, additional_context=schema_context)
)
else:
resolved_messages = list(messages(state))
else:
resolved_messages = list(messages)

if is_conversational:
# For conversational agents we need to reorder the messages so that the system message is first, followed by
# the initial user message. When resuming the conversation, the state will have the entire message history,
Expand Down
5 changes: 4 additions & 1 deletion src/uipath_langchain/agent/react/types.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from enum import StrEnum
from typing import Annotated, Any, Hashable, Literal, Optional
from typing import Annotated, Any, Hashable, Literal, Optional, Sequence

from langchain_core.messages import AnyMessage
from langgraph.graph.message import add_messages
from pydantic import BaseModel, Field
from uipath.agent.models.agent import BaseAgentResourceConfig
from uipath.agent.react import END_EXECUTION_TOOL, RAISE_ERROR_TOOL
from uipath.platform.attachments import Attachment

Expand All @@ -14,6 +15,8 @@

FLOW_CONTROL_TOOLS = [END_EXECUTION_TOOL.name, RAISE_ERROR_TOOL.name]

AgentResources = Sequence[BaseAgentResourceConfig]


class InnerAgentGraphState(BaseModel):
job_attachments: Annotated[dict[str, Attachment], merge_dicts] = {}
Expand Down
12 changes: 12 additions & 0 deletions src/uipath_langchain/agent/tools/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
"""Tool creation and management for LowCode agents."""

from .context_tool import create_context_tool
from .datafabric_tool import (
create_datafabric_tools,
fetch_entity_schemas,
format_schemas_for_context,
get_datafabric_contexts,
get_datafabric_entity_identifiers_from_resources,
)
from .escalation_tool import create_escalation_tool
from .extraction_tool import create_ixp_extraction_tool
from .integration_tool import create_integration_tool
Expand All @@ -16,12 +23,17 @@
"create_tools_from_resources",
"create_tool_node",
"create_context_tool",
"create_datafabric_tools",
"open_mcp_tools",
"create_process_tool",
"create_integration_tool",
"create_escalation_tool",
"create_ixp_extraction_tool",
"create_ixp_escalation_tool",
"fetch_entity_schemas",
"format_schemas_for_context",
"get_datafabric_contexts",
"get_datafabric_entity_identifiers_from_resources",
"UiPathToolNode",
"ToolWrapperMixin",
]
5 changes: 5 additions & 0 deletions src/uipath_langchain/agent/tools/context_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ def create_context_tool(resource: AgentContextResourceConfig) -> StructuredTool:
return handle_deep_rag(tool_name, resource)
elif retrieval_mode == AgentContextRetrievalMode.BATCH_TRANSFORM.value.lower():
return handle_batch_transform(tool_name, resource)
elif retrieval_mode == AgentContextRetrievalMode.DATA_FABRIC.value.lower():
raise ValueError(
"Data Fabric context should be handled via create_datafabric_tools(), "
"not create_context_tool()"
)
else:
return handle_semantic_search(tool_name, resource)

Expand Down
17 changes: 17 additions & 0 deletions src/uipath_langchain/agent/tools/datafabric_tool/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""Data Fabric tool module for entity-based SQL queries."""

from .datafabric_tool import (
create_datafabric_tools,
fetch_entity_schemas,
format_schemas_for_context,
get_datafabric_contexts,
get_datafabric_entity_identifiers_from_resources,
)

__all__ = [
"create_datafabric_tools",
"fetch_entity_schemas",
"format_schemas_for_context",
"get_datafabric_contexts",
"get_datafabric_entity_identifiers_from_resources",
]
Loading
Loading