Skip to content

Commit dfe34bf

Browse files
authored
Merge pull request #180 from UiPath/fix/llamaindex-use-shared-runtime-utils
fix: use shared serialization and schema utils from uipath core
2 parents c3c60e2 + f4d99a7 commit dfe34bf

9 files changed

Lines changed: 23 additions & 152 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,3 +191,4 @@ playground.py
191191
**/samples/**/bindings.json
192192
**/samples/**/entry-points.json
193193
**/samples/**/uv.lock
194+
**/**/.claude/settings.local.json

packages/uipath-llamaindex/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-llamaindex"
3-
version = "0.5.4"
3+
version = "0.5.5"
44
description = "Python SDK that enables developers to build and deploy LlamaIndex agents to the UiPath Cloud Platform"
55
readme = { file = "README.md", content-type = "text/markdown" }
66
requires-python = ">=3.11"

packages/uipath-llamaindex/src/uipath_llamaindex/runtime/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ def create_factory(
2727
)
2828

2929

30-
register_runtime_factory()
31-
3230
__all__ = [
3331
"register_runtime_factory",
3432
"get_entrypoints_schema",

packages/uipath-llamaindex/src/uipath_llamaindex/runtime/_serialize.py

Lines changed: 0 additions & 46 deletions
This file was deleted.

packages/uipath-llamaindex/src/uipath_llamaindex/runtime/runtime.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
ToolCall,
1313
ToolCallResult,
1414
)
15+
from uipath.core.serialization import serialize_defaults
1516
from uipath.runtime import (
1617
UiPathExecuteOptions,
1718
UiPathRuntimeResult,
@@ -49,8 +50,6 @@
4950
from uipath_llamaindex.runtime.schema import get_entrypoints_schema, get_workflow_schema
5051
from uipath_llamaindex.runtime.storage import SqliteResumableStorage
5152

52-
from ._serialize import serialize_output
53-
5453

5554
class UiPathLlamaIndexRuntime:
5655
"""
@@ -179,14 +178,14 @@ async def _run_workflow(
179178
(AgentOutput, AgentInput, AgentStream, ToolCall, ToolCallResult),
180179
):
181180
message_event = UiPathRuntimeMessageEvent(
182-
payload=serialize_output(event),
181+
payload=serialize_defaults(event),
183182
node_name=node_name,
184183
execution_id=self.runtime_id,
185184
)
186185
yield message_event
187186
elif not isinstance(event, BreakpointEvent):
188187
state_event = UiPathRuntimeStateEvent(
189-
payload=serialize_output(event),
188+
payload=serialize_defaults(event),
190189
node_name=node_name,
191190
execution_id=self.runtime_id,
192191
)
@@ -256,7 +255,7 @@ def _create_breakpoint_result(
256255
return UiPathBreakpointResult(
257256
breakpoint_node=self._get_node_name(event),
258257
breakpoint_type="before",
259-
current_state=serialize_output(event),
258+
current_state=serialize_defaults(event),
260259
next_nodes=[], # We don't know what's next in the stream
261260
)
262261

@@ -285,11 +284,11 @@ def _create_success_result(self, output: Any) -> UiPathRuntimeResult:
285284
"""Create result for successful completion."""
286285
if isinstance(output, AgentOutput):
287286
if output.structured_response is not None:
288-
serialized_output = serialize_output(output.structured_response)
287+
serialized_output = serialize_defaults(output.structured_response)
289288
else:
290-
serialized_output = serialize_output(output)
289+
serialized_output = serialize_defaults(output)
291290
else:
292-
serialized_output = serialize_output(output)
291+
serialized_output = serialize_defaults(output)
293292

294293
if isinstance(serialized_output, str):
295294
serialized_output = {"result": serialized_output}

packages/uipath-llamaindex/src/uipath_llamaindex/runtime/schema.py

Lines changed: 8 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
UiPathRuntimeEdge,
1414
UiPathRuntimeGraph,
1515
UiPathRuntimeNode,
16+
transform_nullable_types,
17+
transform_references,
1618
)
1719
from workflows import Workflow
1820
from workflows.decorators import StepConfig
@@ -60,8 +62,8 @@ def get_entrypoints_schema(workflow: Workflow) -> dict[str, Any]:
6062
else:
6163
input_schema = start_event_class.model_json_schema()
6264
# Resolve references and handle nullable types
63-
unpacked_input = _resolve_refs(input_schema)
64-
schema["input"]["properties"] = _process_nullable_types(
65+
unpacked_input, _ = transform_references(input_schema)
66+
schema["input"]["properties"] = transform_nullable_types(
6567
unpacked_input.get("properties", {})
6668
)
6769
schema["input"]["required"] = unpacked_input.get("required", [])
@@ -75,8 +77,8 @@ def get_entrypoints_schema(workflow: Workflow) -> dict[str, Any]:
7577
try:
7678
output_schema = output_cls.model_json_schema()
7779
# Resolve references and handle nullable types
78-
unpacked_output = _resolve_refs(output_schema)
79-
schema["output"]["properties"] = _process_nullable_types(
80+
unpacked_output, _ = transform_references(output_schema)
81+
schema["output"]["properties"] = transform_nullable_types(
8082
unpacked_output.get("properties", {})
8183
)
8284
schema["output"]["required"] = unpacked_output.get("required", [])
@@ -100,8 +102,8 @@ def get_entrypoints_schema(workflow: Workflow) -> dict[str, Any]:
100102
try:
101103
output_schema = stop_event_class.model_json_schema()
102104
# Resolve references and handle nullable types
103-
unpacked_output = _resolve_refs(output_schema)
104-
schema["output"]["properties"] = _process_nullable_types(
105+
unpacked_output, _ = transform_references(output_schema)
106+
schema["output"]["properties"] = transform_nullable_types(
105107
unpacked_output.get("properties", {})
106108
)
107109
schema["output"]["required"] = unpacked_output.get("required", [])
@@ -305,92 +307,6 @@ def get_step_config(step_name: str, step_func: Any) -> StepConfig | None:
305307
)
306308

307309

308-
def _resolve_refs(
309-
schema: dict[str, Any],
310-
root: dict[str, Any] | None = None,
311-
visited: set[str] | None = None,
312-
) -> dict[str, Any]:
313-
"""
314-
Recursively resolves $ref references in a JSON schema.
315-
316-
Args:
317-
schema: The schema dictionary to resolve
318-
root: The root schema for reference resolution
319-
visited: Set of visited references to detect circular dependencies
320-
321-
Returns:
322-
Resolved schema dictionary
323-
"""
324-
if root is None:
325-
root = schema
326-
327-
if visited is None:
328-
visited = set()
329-
330-
if isinstance(schema, dict):
331-
if "$ref" in schema:
332-
ref_path = schema["$ref"]
333-
334-
if ref_path in visited:
335-
# Circular dependency detected
336-
return {
337-
"type": "object",
338-
"description": f"Circular reference to {ref_path}",
339-
}
340-
341-
visited.add(ref_path)
342-
343-
# Resolve the reference - handle both #/definitions/ and #/$defs/ formats
344-
ref_parts = ref_path.lstrip("#/").split("/")
345-
ref_schema = root
346-
for part in ref_parts:
347-
ref_schema = ref_schema.get(part, {})
348-
349-
result = _resolve_refs(ref_schema, root, visited)
350-
351-
# Remove from visited after resolution
352-
visited.discard(ref_path)
353-
354-
return result
355-
356-
return {k: _resolve_refs(v, root, visited) for k, v in schema.items()}
357-
358-
elif isinstance(schema, list):
359-
return [_resolve_refs(item, root, visited) for item in schema]
360-
361-
return schema
362-
363-
364-
def _process_nullable_types(properties: dict[str, Any]) -> dict[str, Any]:
365-
"""
366-
Process properties to handle nullable types correctly.
367-
368-
This matches the original implementation that adds "nullable": True
369-
instead of simplifying the schema structure.
370-
371-
Args:
372-
properties: The properties dictionary from a schema
373-
374-
Returns:
375-
Processed properties with nullable types marked
376-
"""
377-
result = {}
378-
for name, prop in properties.items():
379-
if "anyOf" in prop:
380-
types = [item.get("type") for item in prop["anyOf"] if "type" in item]
381-
if "null" in types:
382-
non_null_types = [t for t in types if t != "null"]
383-
if len(non_null_types) == 1:
384-
result[name] = {"type": non_null_types[0], "nullable": True}
385-
else:
386-
result[name] = {"type": non_null_types, "nullable": True}
387-
else:
388-
result[name] = prop
389-
else:
390-
result[name] = prop
391-
return result
392-
393-
394310
__all__ = [
395311
"get_entrypoints_schema",
396312
"get_workflow_schema",

packages/uipath-llamaindex/tests/cli/conftest.py

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

33
import pytest
44

5+
from uipath_llamaindex.runtime import register_runtime_factory
6+
7+
# Register the LlamaIndex runtime factory for CLI tests
8+
register_runtime_factory()
9+
510
# Get the tests directory path relative to this conftest file
611
TESTS_DIR = Path(__file__).parent.parent
712
MOCKS_DIR = TESTS_DIR / "mocks"

packages/uipath-llamaindex/tests/cli/test_init.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ def test_init_custom_config_generation(
104104

105105
assert "param" in props
106106
assert props["param"]["type"] == "string"
107-
assert props["param"]["nullable"]
108107

109108
# Verify required fields in input
110109
assert input_schema["required"] == ["topic"]
@@ -126,7 +125,6 @@ def test_init_custom_config_generation(
126125

127126
assert "param" in out_props
128127
assert out_props["param"]["type"] == "string"
129-
assert out_props["param"]["nullable"]
130128

131129
# Verify required fields in output
132130
assert output_schema["required"] == ["joke", "critique"]

packages/uipath-llamaindex/uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)