Skip to content

Commit 4edc45b

Browse files
committed
fix: extend factory protocol
1 parent 9e7f9cc commit 4edc45b

File tree

9 files changed

+156
-89
lines changed

9 files changed

+156
-89
lines changed

README.md

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,6 @@ class MyRuntimeFactory:
169169
async def new_runtime(self, entrypoint: str, runtime_id: str) -> UiPathRuntimeProtocol:
170170
return MyRuntime()
171171

172-
async def discover_runtimes(self) -> list[UiPathRuntimeProtocol]:
173-
return []
174-
175172
def discover_entrypoints(self) -> list[str]:
176173
return []
177174

@@ -315,9 +312,6 @@ class ChildRuntimeFactory:
315312
async def new_runtime(self, entrypoint: str) -> UiPathRuntimeProtocol:
316313
return ChildRuntime(name=entrypoint)
317314

318-
async def discover_runtimes(self) -> list[UiPathRuntimeProtocol]:
319-
return []
320-
321315
def discover_entrypoints(self) -> list[str]:
322316
return []
323317

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
[project]
22
name = "uipath-runtime"
3-
version = "0.5.1"
3+
version = "0.6.0"
44
description = "Runtime abstractions and interfaces for building agents and automation scripts in the UiPath ecosystem"
55
readme = { file = "README.md", content-type = "text/markdown" }
66
requires-python = ">=3.11"
77
dependencies = [
8-
"uipath-core>=0.1.1, <0.2.0",
8+
"uipath-core>=0.2.0, <0.3.0",
99
]
1010
classifiers = [
1111
"Intended Audience :: Developers",

src/uipath/runtime/__init__.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,8 @@
1818
)
1919
from uipath.runtime.events import UiPathRuntimeEvent
2020
from uipath.runtime.factory import (
21-
UiPathRuntimeCreatorProtocol,
2221
UiPathRuntimeFactoryProtocol,
23-
UiPathRuntimeScannerProtocol,
22+
UiPathRuntimeFactorySettings,
2423
)
2524
from uipath.runtime.registry import UiPathRuntimeFactoryRegistry
2625
from uipath.runtime.result import (
@@ -41,17 +40,18 @@
4140
UiPathResumeTriggerType,
4241
)
4342
from uipath.runtime.schema import UiPathRuntimeSchema
43+
from uipath.runtime.storage import UiPathRuntimeStorageProtocol
4444

4545
__all__ = [
4646
"UiPathExecuteOptions",
4747
"UiPathStreamOptions",
4848
"UiPathRuntimeContext",
4949
"UiPathRuntimeProtocol",
5050
"UiPathExecutionRuntime",
51-
"UiPathRuntimeCreatorProtocol",
52-
"UiPathRuntimeScannerProtocol",
51+
"UiPathRuntimeStorageProtocol",
5352
"UiPathRuntimeFactoryProtocol",
5453
"UiPathRuntimeFactoryRegistry",
54+
"UiPathRuntimeFactorySettings",
5555
"UiPathRuntimeResult",
5656
"UiPathRuntimeStatus",
5757
"UiPathRuntimeEvent",

src/uipath/runtime/factory.py

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,44 @@
22

33
from typing import Protocol
44

5-
from uipath.runtime.base import UiPathDisposableProtocol, UiPathRuntimeProtocol
5+
from pydantic import BaseModel
6+
from uipath.core.tracing import UiPathTraceSettings
67

8+
from uipath.runtime.base import (
9+
UiPathDisposableProtocol,
10+
UiPathRuntimeProtocol,
11+
)
12+
from uipath.runtime.storage import UiPathRuntimeStorageProtocol
713

8-
class UiPathRuntimeScannerProtocol(Protocol):
9-
"""Protocol for discovering all UiPath runtime instances."""
1014

11-
async def discover_runtimes(self) -> list[UiPathRuntimeProtocol]:
12-
"""Discover all runtime classes."""
13-
...
15+
class UiPathRuntimeFactorySettings(BaseModel):
16+
"""Runtime settings for execution behavior."""
17+
18+
model_config = {"arbitrary_types_allowed": True} # Needed for Callable
19+
20+
trace_settings: UiPathTraceSettings | None = None
21+
22+
23+
class UiPathRuntimeFactoryProtocol(
24+
UiPathDisposableProtocol,
25+
Protocol,
26+
):
27+
"""Protocol for discovering and creating UiPath runtime instances."""
1428

1529
def discover_entrypoints(self) -> list[str]:
1630
"""Discover all runtime entrypoints."""
1731
...
1832

19-
20-
class UiPathRuntimeCreatorProtocol(Protocol):
21-
"""Protocol for creating a UiPath runtime given an entrypoint."""
22-
2333
async def new_runtime(
2434
self, entrypoint: str, runtime_id: str, **kwargs
2535
) -> UiPathRuntimeProtocol:
2636
"""Create a new runtime instance."""
2737
...
2838

39+
async def get_storage(self) -> UiPathRuntimeStorageProtocol | None:
40+
"""Get the factory storage."""
41+
...
2942

30-
class UiPathRuntimeFactoryProtocol(
31-
UiPathRuntimeCreatorProtocol,
32-
UiPathRuntimeScannerProtocol,
33-
UiPathDisposableProtocol,
34-
Protocol,
35-
):
36-
"""Protocol for discovering and creating UiPath runtime instances."""
43+
async def get_settings(self) -> UiPathRuntimeFactorySettings | None:
44+
"""Get factory settings."""
45+
...

src/uipath/runtime/resumable/protocols.py

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
from typing import Any, Protocol
44

55
from uipath.runtime.resumable.trigger import UiPathResumeTrigger
6+
from uipath.runtime.storage import UiPathRuntimeStorageProtocol
67

78

8-
class UiPathResumableStorageProtocol(Protocol):
9+
class UiPathResumableStorageProtocol(UiPathRuntimeStorageProtocol, Protocol):
910
"""Protocol for storing and retrieving resume triggers."""
1011

1112
async def save_triggers(
@@ -46,38 +47,6 @@ async def delete_trigger(
4647
"""
4748
...
4849

49-
async def set_value(
50-
self, runtime_id: str, namespace: str, key: str, value: Any
51-
) -> None:
52-
"""Store values for a specific runtime.
53-
54-
Args:
55-
runtime_id: The runtime ID
56-
namespace: The namespace of the persisted value
57-
key: The key associated with the persisted value
58-
value: The value to persist
59-
60-
Raises:
61-
Exception: If storage operation fails
62-
"""
63-
...
64-
65-
async def get_value(self, runtime_id: str, namespace: str, key: str) -> Any:
66-
"""Retrieve values for a specific runtime from storage.
67-
68-
Args:
69-
runtime_id: The runtime ID
70-
namespace: The namespace of the persisted value
71-
key: The key associated with the persisted value
72-
73-
Returns:
74-
The value matching the method's parameters, or None if it does not exist
75-
76-
Raises:
77-
Exception: If retrieval operation fails
78-
"""
79-
...
80-
8150

8251
class UiPathResumeTriggerCreatorProtocol(Protocol):
8352
"""Protocol for creating resume triggers from suspend values."""

src/uipath/runtime/storage.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""Runtime storage protocol definition."""
2+
3+
from typing import (
4+
Any,
5+
Protocol,
6+
)
7+
8+
9+
class UiPathRuntimeStorageProtocol(Protocol):
10+
"""Protocol for runtime storage operations."""
11+
12+
async def set_value(
13+
self, runtime_id: str, namespace: str, key: str, value: Any
14+
) -> None:
15+
"""Store values for a specific runtime.
16+
17+
Args:
18+
runtime_id: The runtime ID
19+
namespace: The namespace of the persisted value
20+
key: The key associated with the persisted value
21+
value: The value to persist
22+
23+
Raises:
24+
Exception: If storage operation fails
25+
"""
26+
...
27+
28+
async def get_value(self, runtime_id: str, namespace: str, key: str) -> Any:
29+
"""Retrieve values for a specific runtime from storage.
30+
31+
Args:
32+
runtime_id: The runtime ID
33+
namespace: The namespace of the persisted value
34+
key: The key associated with the persisted value
35+
36+
Returns:
37+
The value matching the method's parameters, or None if it does not exist
38+
39+
Raises:
40+
Exception: If retrieval operation fails
41+
"""
42+
...

tests/test_factory.py

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,29 @@
88
UiPathRuntimeProtocol,
99
UiPathRuntimeResult,
1010
UiPathRuntimeSchema,
11+
UiPathRuntimeStorageProtocol,
1112
UiPathStreamOptions,
1213
)
13-
from uipath.runtime.factory import UiPathRuntimeCreatorProtocol
14+
from uipath.runtime.factory import (
15+
UiPathRuntimeFactoryProtocol,
16+
UiPathRuntimeFactorySettings,
17+
)
18+
19+
20+
class MockStorage(UiPathRuntimeStorageProtocol):
21+
"""Mock storage implementation"""
22+
23+
def __init__(self):
24+
self._store = {}
25+
26+
async def set_value(self, runtime_id, namespace, key, value):
27+
self._store.setdefault(runtime_id, {}).setdefault(namespace, {})[key] = value
28+
29+
async def get_value(self, runtime_id, namespace, key):
30+
return self._store.get(runtime_id, {}).get(namespace, {}).get(key)
1431

1532

16-
class MockRuntime:
33+
class MockRuntime(UiPathRuntimeProtocol):
1734
"""Mock runtime that implements UiPathRuntimeProtocol."""
1835

1936
def __init__(self, settings: dict[str, Any] | None = None) -> None:
@@ -49,24 +66,36 @@ async def dispose(self) -> None:
4966
class CreatorWithKwargs:
5067
"""Implementation with kwargs."""
5168

69+
def discover_entrypoints(self) -> list[str]:
70+
return ["main.py"]
71+
5272
async def new_runtime(
5373
self, entrypoint: str, runtime_id: str, **kwargs
5474
) -> UiPathRuntimeProtocol:
5575
return MockRuntime(kwargs.get("settings"))
5676

77+
async def get_storage(self) -> UiPathRuntimeStorageProtocol | None:
78+
return MockStorage()
79+
80+
async def get_settings(self) -> UiPathRuntimeFactorySettings | None:
81+
return UiPathRuntimeFactorySettings()
82+
83+
async def dispose(self) -> None:
84+
pass
85+
5786

5887
@pytest.mark.asyncio
5988
async def test_protocol_works_with_kwargs_not_specified():
6089
"""Test protocol works with implementation that has kwargs."""
61-
creator: UiPathRuntimeCreatorProtocol = CreatorWithKwargs()
90+
creator: UiPathRuntimeFactoryProtocol = CreatorWithKwargs()
6291
runtime = await creator.new_runtime("main.py", "runtime-123")
6392
assert isinstance(runtime, MockRuntime)
6493

6594

6695
@pytest.mark.asyncio
6796
async def test_protocol_works_with_kwargs_specified():
6897
"""Test protocol works with implementation that has kwargs."""
69-
creator: UiPathRuntimeCreatorProtocol = CreatorWithKwargs()
98+
creator: UiPathRuntimeFactoryProtocol = CreatorWithKwargs()
7099
runtime = await creator.new_runtime(
71100
"main.py", "runtime-123", settings={"timeout": 30, "model": "gpt-4"}
72101
)

tests/test_registry.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,29 @@
1010
UiPathRuntimeEvent,
1111
UiPathRuntimeFactoryProtocol,
1212
UiPathRuntimeFactoryRegistry,
13+
UiPathRuntimeFactorySettings,
1314
UiPathRuntimeProtocol,
1415
UiPathRuntimeResult,
1516
UiPathRuntimeSchema,
1617
UiPathRuntimeStatus,
18+
UiPathRuntimeStorageProtocol,
1719
UiPathStreamOptions,
1820
)
1921

2022

23+
class MockStorage(UiPathRuntimeStorageProtocol):
24+
"""Mock storage implementation"""
25+
26+
def __init__(self):
27+
self._store = {}
28+
29+
async def set_value(self, runtime_id, namespace, key, value):
30+
self._store.setdefault(runtime_id, {}).setdefault(namespace, {})[key] = value
31+
32+
async def get_value(self, runtime_id, namespace, key):
33+
return self._store.get(runtime_id, {}).get(namespace, {}).get(key)
34+
35+
2136
class MockRuntime(UiPathRuntimeProtocol):
2237
"""Mock runtime instance"""
2338

@@ -59,8 +74,11 @@ def __init__(self, context: Optional[UiPathRuntimeContext] = None):
5974
def discover_entrypoints(self) -> list[str]:
6075
return ["main.py", "handler.py"]
6176

62-
async def discover_runtimes(self) -> list[UiPathRuntimeProtocol]:
63-
return []
77+
async def get_storage(self) -> UiPathRuntimeStorageProtocol | None:
78+
return MockStorage()
79+
80+
async def get_settings(self) -> UiPathRuntimeFactorySettings | None:
81+
return UiPathRuntimeFactorySettings()
6482

6583
async def new_runtime(
6684
self, entrypoint: str, runtime_id: str, **kwargs
@@ -81,8 +99,11 @@ def __init__(self, context: Optional[UiPathRuntimeContext] = None):
8199
def discover_entrypoints(self) -> list[str]:
82100
return ["agent", "workflow"]
83101

84-
async def discover_runtimes(self) -> list[UiPathRuntimeProtocol]:
85-
return []
102+
async def get_storage(self) -> UiPathRuntimeStorageProtocol | None:
103+
return MockStorage()
104+
105+
async def get_settings(self) -> UiPathRuntimeFactorySettings | None:
106+
return UiPathRuntimeFactorySettings()
86107

87108
async def new_runtime(
88109
self, entrypoint: str, runtime_id: str, **kwargs
@@ -103,8 +124,11 @@ def __init__(self, context: Optional[UiPathRuntimeContext] = None):
103124
def discover_entrypoints(self) -> list[str]:
104125
return ["chatbot", "rag"]
105126

106-
async def discover_runtimes(self) -> list[UiPathRuntimeProtocol]:
107-
return []
127+
async def get_storage(self) -> UiPathRuntimeStorageProtocol | None:
128+
return MockStorage()
129+
130+
async def get_settings(self) -> UiPathRuntimeFactorySettings | None:
131+
return UiPathRuntimeFactorySettings()
108132

109133
async def new_runtime(
110134
self, entrypoint: str, runtime_id: str, **kwargs

0 commit comments

Comments
 (0)