Skip to content

Commit b5d462a

Browse files
authored
fix(uipath-core): accept minimal conversation message envelope on input (#1677)
1 parent f396de9 commit b5d462a

8 files changed

Lines changed: 86 additions & 17 deletions

File tree

packages/uipath-core/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-core"
3-
version = "0.5.15"
3+
version = "0.5.16"
44
description = "UiPath Core abstractions"
55
readme = { file = "README.md", content-type = "text/markdown" }
66
requires-python = ">=3.11"

packages/uipath-core/src/uipath/core/chat/content.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from __future__ import annotations
44

5+
import uuid
56
from typing import Any, Sequence
67

78
from pydantic import BaseModel, ConfigDict, Field
@@ -95,7 +96,7 @@ class UiPathConversationContentPartData(BaseModel):
9596

9697
mime_type: str = Field(..., alias="mimeType")
9798
data: InlineOrExternal
98-
citations: Sequence[UiPathConversationCitationData]
99+
citations: Sequence[UiPathConversationCitationData] = Field(default_factory=list)
99100
is_transcript: bool | None = Field(None, alias="isTranscript")
100101
is_incomplete: bool | None = Field(None, alias="isIncomplete")
101102
name: str | None = None
@@ -106,11 +107,13 @@ class UiPathConversationContentPartData(BaseModel):
106107
class UiPathConversationContentPart(UiPathConversationContentPartData):
107108
"""Represents a single part of message content."""
108109

109-
content_part_id: str = Field(..., alias="contentPartId")
110-
created_at: str = Field(..., alias="createdAt")
111-
updated_at: str = Field(..., alias="updatedAt")
110+
content_part_id: str = Field(
111+
default_factory=lambda: str(uuid.uuid4()), alias="contentPartId"
112+
)
113+
created_at: str | None = Field(None, alias="createdAt")
114+
updated_at: str | None = Field(None, alias="updatedAt")
112115

113116
# Override to use full type
114-
citations: Sequence[UiPathConversationCitation]
117+
citations: Sequence[UiPathConversationCitation] = Field(default_factory=list)
115118

116119
model_config = ConfigDict(validate_by_name=True, validate_by_alias=True)

packages/uipath-core/src/uipath/core/chat/message.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Message-level events."""
22

3+
import uuid
34
from typing import Any, Sequence
45

56
from pydantic import BaseModel, ConfigDict, Field
@@ -61,23 +62,29 @@ class UiPathConversationMessageData(BaseModel):
6162
content_parts: Sequence[UiPathConversationContentPartData] = Field(
6263
..., alias="contentParts"
6364
)
64-
tool_calls: Sequence[UiPathConversationToolCallData] = Field(..., alias="toolCalls")
65+
tool_calls: Sequence[UiPathConversationToolCallData] = Field(
66+
default_factory=list, alias="toolCalls"
67+
)
6568

6669
model_config = ConfigDict(validate_by_name=True, validate_by_alias=True)
6770

6871

6972
class UiPathConversationMessage(UiPathConversationMessageData):
7073
"""Represents a single message within an exchange."""
7174

72-
message_id: str = Field(..., alias="messageId")
73-
created_at: str = Field(..., alias="createdAt")
74-
updated_at: str = Field(..., alias="updatedAt")
75+
message_id: str = Field(
76+
default_factory=lambda: str(uuid.uuid4()), alias="messageId"
77+
)
78+
created_at: str | None = Field(None, alias="createdAt")
79+
updated_at: str | None = Field(None, alias="updatedAt")
7580
span_id: str | None = Field(None, alias="spanId")
7681

7782
# Overrides to use full types
7883
content_parts: Sequence[UiPathConversationContentPart] = Field(
7984
..., alias="contentParts"
8085
)
81-
tool_calls: Sequence[UiPathConversationToolCall] = Field(..., alias="toolCalls")
86+
tool_calls: Sequence[UiPathConversationToolCall] = Field(
87+
default_factory=list, alias="toolCalls"
88+
)
8289

8390
model_config = ConfigDict(validate_by_name=True, validate_by_alias=True)

packages/uipath-core/tests/chat/__init__.py

Whitespace-only changes.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
"""Tests for `UiPathConversationMessage` input validation.
2+
3+
Conversational Agent Service contract treats `role` + `contentParts` as
4+
the load-bearing fields for an inbound user message. `messageId` and
5+
`contentPartId` are GUIDs that identify entities in the conversation
6+
hierarchy; when omitted on input, the model fills them with fresh
7+
UUIDs (matching what `uipath dev` does server-side). `createdAt`,
8+
`updatedAt`, `spanId`, and `toolCalls` are server-allocated and absent
9+
from client input.
10+
11+
These tests pin that behavior so `--input-file` payloads from
12+
`uip codedagent run` validate against the model without requiring
13+
callers to hand-generate UUIDs.
14+
"""
15+
16+
from __future__ import annotations
17+
18+
from uipath.core.chat import UiPathConversationMessage
19+
20+
21+
def test_minimal_user_message_validates_and_fills_ids() -> None:
22+
msg = UiPathConversationMessage.model_validate(
23+
{
24+
"role": "user",
25+
"contentParts": [
26+
{
27+
"mimeType": "text/plain",
28+
"data": {"inline": "hello world"},
29+
}
30+
],
31+
}
32+
)
33+
assert msg.role == "user"
34+
assert msg.tool_calls == []
35+
assert msg.created_at is None
36+
assert msg.updated_at is None
37+
assert msg.message_id # auto-generated UUID
38+
assert msg.content_parts[0].content_part_id # auto-generated UUID
39+
assert msg.content_parts[0].citations == []
40+
41+
42+
def test_explicit_ids_are_preserved() -> None:
43+
msg = UiPathConversationMessage.model_validate(
44+
{
45+
"messageId": "00000000-0000-0000-0000-000000000001",
46+
"role": "user",
47+
"contentParts": [
48+
{
49+
"contentPartId": "00000000-0000-0000-0000-000000000002",
50+
"mimeType": "text/plain",
51+
"data": {"inline": "hello world"},
52+
}
53+
],
54+
}
55+
)
56+
assert msg.message_id == "00000000-0000-0000-0000-000000000001"
57+
assert (
58+
msg.content_parts[0].content_part_id == "00000000-0000-0000-0000-000000000002"
59+
)

packages/uipath-core/uv.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/uipath-platform/uv.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/uipath/uv.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)