Skip to content

Commit 34bf679

Browse files
fix: files messages stack (#436)
1 parent bcc1dc6 commit 34bf679

File tree

22 files changed

+212
-83
lines changed

22 files changed

+212
-83
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "uipath-langchain"
3-
version = "0.4.25"
3+
version = "0.4.26"
44
description = "Python SDK that enables developers to build and deploy LangGraph agents to the UiPath Cloud Platform"
55
readme = { file = "README.md", content-type = "text/markdown" }
66
requires-python = ">=3.11"

src/uipath_langchain/agent/react/multimodal/__init__.py renamed to src/uipath_langchain/agent/multimodal/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
"""Multimodal LLM input handling (images, PDFs, etc.)."""
22

3-
from .invoke import build_file_content_block, llm_call_with_files
3+
from .invoke import (
4+
build_file_content_block,
5+
llm_call_with_files,
6+
)
47
from .types import IMAGE_MIME_TYPES, FileInfo
58
from .utils import download_file_base64, is_image, is_pdf, sanitize_filename
69

src/uipath_langchain/agent/react/multimodal/invoke.py renamed to src/uipath_langchain/agent/multimodal/invoke.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""LLM invocation with multimodal file attachments."""
22

3+
import asyncio
34
from typing import Any
45

56
from langchain_core.language_models import BaseChatModel
@@ -43,6 +44,24 @@ async def build_file_content_block(
4344
raise ValueError(f"Unsupported mime_type={file_info.mime_type}")
4445

4546

47+
async def build_file_content_blocks(files: list[FileInfo]) -> list[DataContentBlock]:
48+
"""Build content blocks from file attachments.
49+
50+
Args:
51+
files: List of file information to convert to content blocks
52+
53+
Returns:
54+
List of DataContentBlock instances for the files
55+
"""
56+
if not files:
57+
return []
58+
59+
file_content_blocks: list[DataContentBlock] = await asyncio.gather(
60+
*[build_file_content_block(file) for file in files]
61+
)
62+
return file_content_blocks
63+
64+
4665
async def llm_call_with_files(
4766
messages: list[AnyMessage],
4867
files: list[FileInfo],
File renamed without changes.
File renamed without changes.

src/uipath_langchain/agent/react/llm_node.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from uipath_langchain.agent.tools.structured_tool_with_argument_properties import (
1515
StructuredToolWithArgumentProperties,
1616
)
17-
from uipath_langchain.llm import get_payload_handler
17+
from uipath_langchain.chat.handlers import get_payload_handler
1818

1919
from ..exceptions import AgentTerminationException
2020
from .constants import (

src/uipath_langchain/agent/tools/internal_tools/analyze_files_tool.py

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1+
import asyncio
12
import uuid
2-
from typing import Any
3+
from typing import Any, cast
34

45
from langchain_core.language_models import BaseChatModel
5-
from langchain_core.messages import AnyMessage, HumanMessage, SystemMessage
6+
from langchain_core.messages import (
7+
AnyMessage,
8+
BaseMessage,
9+
ContentBlock,
10+
DataContentBlock,
11+
HumanMessage,
12+
SystemMessage,
13+
)
614
from langchain_core.messages.tool import ToolCall
715
from langchain_core.tools import BaseTool, StructuredTool
816
from uipath.agent.models.agent import (
@@ -11,18 +19,20 @@
1119
from uipath.eval.mocks import mockable
1220
from uipath.platform import UiPath
1321

22+
from uipath_langchain.agent.multimodal import FileInfo, build_file_content_block
1423
from uipath_langchain.agent.react.jsonschema_pydantic_converter import create_model
15-
from uipath_langchain.agent.react.multimodal import FileInfo, llm_call_with_files
1624
from uipath_langchain.agent.react.types import AgentGraphState
1725
from uipath_langchain.agent.tools.static_args import handle_static_args
1826
from uipath_langchain.agent.tools.structured_tool_with_argument_properties import (
1927
StructuredToolWithArgumentProperties,
2028
)
21-
from uipath_langchain.agent.tools.tool_node import (
22-
ToolWrapperReturnType,
23-
)
29+
from uipath_langchain.agent.tools.tool_node import ToolWrapperReturnType
2430
from uipath_langchain.agent.tools.utils import sanitize_tool_name
2531
from uipath_langchain.agent.wrappers import get_job_attachment_wrapper
32+
from uipath_langchain.chat.helpers import (
33+
append_content_blocks_to_message,
34+
extract_text_content,
35+
)
2636

2737
ANALYZE_FILES_SYSTEM_MESSAGE = (
2838
"Process the provided files to complete the given task. "
@@ -50,8 +60,8 @@ async def tool_fn(**kwargs: Any):
5060
if "attachments" not in kwargs:
5161
raise ValueError("Argument 'attachments' is not available")
5262

53-
analysisTask = kwargs["analysisTask"]
54-
if not analysisTask:
63+
analysis_task = kwargs["analysisTask"]
64+
if not analysis_task:
5565
raise ValueError("Argument 'analysisTask' is not available")
5666

5767
attachments = kwargs["attachments"]
@@ -60,12 +70,17 @@ async def tool_fn(**kwargs: Any):
6070
if not files:
6171
return {"analysisResult": "No attachments provided to analyze."}
6272

73+
human_message = HumanMessage(content=analysis_task)
74+
human_message_with_files = await add_files_to_message(human_message, files)
75+
6376
messages: list[AnyMessage] = [
6477
SystemMessage(content=ANALYZE_FILES_SYSTEM_MESSAGE),
65-
HumanMessage(content=analysisTask),
78+
cast(AnyMessage, human_message_with_files),
6679
]
67-
result = await llm_call_with_files(messages, files, llm)
68-
return result
80+
result = await llm.ainvoke(messages)
81+
82+
analysis_result = extract_text_content(result)
83+
return analysis_result
6984

7085
job_attachment_wrapper = get_job_attachment_wrapper(output_type=output_model)
7186

@@ -125,3 +140,27 @@ async def _resolve_job_attachment_arguments(
125140
file_infos.append(file_info)
126141

127142
return file_infos
143+
144+
145+
async def add_files_to_message(
146+
message: BaseMessage,
147+
files: list[FileInfo],
148+
) -> BaseMessage:
149+
"""Add file attachments to a message.
150+
151+
Args:
152+
message: The message to add files to (any BaseMessage subclass)
153+
files: List of file attachments to add
154+
155+
Returns:
156+
New message of the same type with file content blocks appended
157+
"""
158+
if not files:
159+
return message
160+
161+
file_content_blocks: list[DataContentBlock] = await asyncio.gather(
162+
*[build_file_content_block(file) for file in files]
163+
)
164+
return append_content_blocks_to_message(
165+
message, cast(list[ContentBlock], file_content_blocks)
166+
)

src/uipath_langchain/llm/passthrough_factory.py renamed to src/uipath_langchain/chat/chat_model_factory.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def _create_openai_llm(
3030
agenthub_config: str,
3131
byo_connection_id: str | None = None,
3232
) -> BaseChatModel:
33-
"""Create UiPathChatOpenAI for OpenAI models via passthrough."""
33+
"""Create UiPathChatOpenAI for OpenAI models via LLMGateway."""
3434
from uipath_langchain.chat.openai import UiPathChatOpenAI
3535

3636
azure_open_ai_latest_api_version = "2025-04-01-preview"
@@ -70,7 +70,7 @@ def _create_bedrock_llm(
7070
agenthub_config: str,
7171
byo_connection_id: str | None = None,
7272
) -> BaseChatModel:
73-
"""Create UiPathChatBedrockConverse for Claude models via passthrough."""
73+
"""Create UiPathChatBedrockConverse for Claude models via LLMGateway."""
7474
from uipath_langchain.chat.bedrock import (
7575
UiPathChatBedrock,
7676
UiPathChatBedrockConverse,
@@ -107,7 +107,7 @@ def _create_vertex_llm(
107107
agenthub_config: str,
108108
byo_connection_id: str | None = None,
109109
) -> BaseChatModel:
110-
"""Create UiPathChatVertex for Gemini models via passthrough."""
110+
"""Create UiPathChatVertex for Gemini models via LLMGateway."""
111111
from uipath_langchain.chat.vertex import UiPathChatVertex
112112

113113
match api_flavor:
@@ -185,7 +185,7 @@ def get_chat_model(
185185
agenthub_config: str,
186186
byo_connection_id: str | None = None,
187187
) -> BaseChatModel:
188-
"""Create and configure LLM instance using passthrough API.
188+
"""Create and configure LLM instance using LLMGateway API.
189189
190190
Fetches available models from the discovery API and selects the appropriate
191191
LLM class based on the apiFlavor field from the matching model configuration.

src/uipath_langchain/llm/handlers/__init__.py renamed to src/uipath_langchain/chat/handlers/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from .base import ModelPayloadHandler
44
from .bedrock_converse import BedrockConversePayloadHandler
55
from .bedrock_invoke import BedrockInvokePayloadHandler
6+
from .handler_factory import get_payload_handler
67
from .openai_completions import OpenAICompletionsPayloadHandler
78
from .openai_responses import OpenAIResponsesPayloadHandler
89
from .vertex_gemini import VertexGeminiPayloadHandler
@@ -14,4 +15,5 @@
1415
"OpenAICompletionsPayloadHandler",
1516
"OpenAIResponsesPayloadHandler",
1617
"VertexGeminiPayloadHandler",
18+
"get_payload_handler",
1719
]
File renamed without changes.

0 commit comments

Comments
 (0)