optimizely-opal.opal-tools-sdk 0.1.41.dev0__tar.gz → 0.1.42.dev0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/PKG-INFO +1 -1
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/opal_tools_sdk/decorators.py +4 -0
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/opal_tools_sdk/models.py +9 -3
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/opal_tools_sdk/service.py +4 -0
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/optimizely_opal.opal_tools_sdk.egg-info/PKG-INFO +1 -1
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/pyproject.toml +1 -1
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/setup.py +1 -1
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/tests/test_integration.py +19 -0
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/README.md +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/opal_tools_sdk/__init__.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/opal_tools_sdk/_registry.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/opal_tools_sdk/config.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/opal_tools_sdk/context.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/opal_tools_sdk/logging.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/opal_tools_sdk/proteus.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/opal_tools_sdk/response.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/opal_tools_sdk/ui.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/optimizely_opal.opal_tools_sdk.egg-info/SOURCES.txt +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/optimizely_opal.opal_tools_sdk.egg-info/dependency_links.txt +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/optimizely_opal.opal_tools_sdk.egg-info/requires.txt +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/optimizely_opal.opal_tools_sdk.egg-info/top_level.txt +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/setup.cfg +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/tests/test_async_tool.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/tests/test_auth_middleware.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/tests/test_dynamic_ui.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/tests/test_hmac.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/tests/test_nested_schema.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/tests/test_proteus.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/tests/test_response.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/tests/test_rollback.py +0 -0
|
@@ -99,6 +99,7 @@ def tool(
|
|
|
99
99
|
timeout: int | None = None,
|
|
100
100
|
sensitive: str | bool | None = None,
|
|
101
101
|
wait: bool = False,
|
|
102
|
+
skippable_interaction: bool = False,
|
|
102
103
|
):
|
|
103
104
|
"""Decorator to register a function as an Opal tool.
|
|
104
105
|
|
|
@@ -114,6 +115,8 @@ def tool(
|
|
|
114
115
|
Example: "ui://my-app/create-form"
|
|
115
116
|
wait: When True, invoking this tool pauses the agent execution until the caller
|
|
116
117
|
signals completion.
|
|
118
|
+
skippable_interaction: When True, the tool's confirmation interaction (card) can be
|
|
119
|
+
skipped; a user who opts out has the tool run headless instead.
|
|
117
120
|
|
|
118
121
|
Returns:
|
|
119
122
|
Decorator function
|
|
@@ -309,6 +312,7 @@ def tool(
|
|
|
309
312
|
timeout=timeout,
|
|
310
313
|
sensitive=sensitive,
|
|
311
314
|
wait=wait,
|
|
315
|
+
skippable_interaction=skippable_interaction,
|
|
312
316
|
)
|
|
313
317
|
|
|
314
318
|
return func
|
|
@@ -3,7 +3,7 @@ from enum import Enum
|
|
|
3
3
|
from typing import Any, Literal
|
|
4
4
|
|
|
5
5
|
from pydantic import BaseModel, Field
|
|
6
|
-
from typing_extensions import TypedDict # pydantic requires this on Python < 3.12
|
|
6
|
+
from typing_extensions import NotRequired, TypedDict # pydantic requires this on Python < 3.12
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class ParameterType(str, Enum):
|
|
@@ -83,14 +83,17 @@ class InteractionContext:
|
|
|
83
83
|
auth_data: AuthData | None = None
|
|
84
84
|
|
|
85
85
|
|
|
86
|
-
class Environment(TypedDict
|
|
86
|
+
class Environment(TypedDict):
|
|
87
87
|
"""Execution environment for an Opal tool.
|
|
88
88
|
|
|
89
89
|
Interactive mode provides interaction islands, while headless does not.
|
|
90
|
+
`execution_mode` is always sent by TMS — required. `is_proteus_enabled`
|
|
91
|
+
was added later, so older TMS versions may omit it; mark it `NotRequired`
|
|
92
|
+
for cross-version forward compatibility.
|
|
90
93
|
"""
|
|
91
94
|
|
|
92
95
|
execution_mode: Literal["headless", "interactive"]
|
|
93
|
-
is_proteus_enabled: bool
|
|
96
|
+
is_proteus_enabled: NotRequired[bool]
|
|
94
97
|
|
|
95
98
|
|
|
96
99
|
@dataclass
|
|
@@ -110,6 +113,7 @@ class Function:
|
|
|
110
113
|
# to a non-empty advisory string when sensitive, else None.
|
|
111
114
|
sensitive: str | bool | None = None
|
|
112
115
|
wait: bool = False # When True, pauses the agent until the caller signals completion
|
|
116
|
+
skippable_interaction: bool = False # When True, the tool's confirmation card can be skipped
|
|
113
117
|
|
|
114
118
|
def __post_init__(self) -> None:
|
|
115
119
|
# Normalize `sensitive` to the wire shape: a single nullable advisory
|
|
@@ -144,6 +148,8 @@ class Function:
|
|
|
144
148
|
result["sensitive"] = self.sensitive
|
|
145
149
|
if self.wait:
|
|
146
150
|
result["wait"] = self.wait
|
|
151
|
+
if self.skippable_interaction:
|
|
152
|
+
result["skippable_interaction"] = self.skippable_interaction
|
|
147
153
|
|
|
148
154
|
return result
|
|
149
155
|
|
|
@@ -314,6 +314,7 @@ class ToolsService:
|
|
|
314
314
|
timeout: int | None = None,
|
|
315
315
|
sensitive: str | bool | None = None,
|
|
316
316
|
wait: bool = False,
|
|
317
|
+
skippable_interaction: bool = False,
|
|
317
318
|
) -> None:
|
|
318
319
|
"""Register a tool function.
|
|
319
320
|
|
|
@@ -329,6 +330,8 @@ class ToolsService:
|
|
|
329
330
|
timeout: Timeout in seconds for async mode
|
|
330
331
|
sensitive: Sensitive data field declaration
|
|
331
332
|
wait: When True, invoking this tool pauses the agent until the caller signals completion
|
|
333
|
+
skippable_interaction: When True, the tool's confirmation interaction (card) can be
|
|
334
|
+
skipped; a user who opts out has the tool run headless instead
|
|
332
335
|
"""
|
|
333
336
|
logger.info(f"Registering tool: {name} with endpoint: {endpoint}")
|
|
334
337
|
|
|
@@ -359,6 +362,7 @@ class ToolsService:
|
|
|
359
362
|
timeout=timeout,
|
|
360
363
|
sensitive=sensitive,
|
|
361
364
|
wait=wait,
|
|
365
|
+
skippable_interaction=skippable_interaction,
|
|
362
366
|
)
|
|
363
367
|
|
|
364
368
|
self.functions.append(function)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "optimizely-opal.opal-tools-sdk"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.42-dev"
|
|
8
8
|
description = "SDK for creating Opal-compatible tools services"
|
|
9
9
|
authors = [{ name = "Optimizely", email = "opal-team@optimizely.com" }]
|
|
10
10
|
readme = "README.md"
|
|
@@ -860,3 +860,22 @@ def test_v1_tool_bare_dict_unchanged():
|
|
|
860
860
|
assert "application/json" in resp.headers["content-type"]
|
|
861
861
|
# Should NOT be wrapped in envelope
|
|
862
862
|
assert resp.json() == {"v1": True, "name": "Dave"}
|
|
863
|
+
|
|
864
|
+
|
|
865
|
+
def test_tool_with_skippable_interaction_flag():
|
|
866
|
+
"""A tool marked skippable_interaction advertises it in discovery; others omit the key."""
|
|
867
|
+
app = FastAPI()
|
|
868
|
+
_ = ToolsService(app)
|
|
869
|
+
|
|
870
|
+
@tool(name="skip_me", description="skippable tool", skippable_interaction=True)
|
|
871
|
+
async def skip_me(params: SimpleParams) -> dict:
|
|
872
|
+
return {"ok": True}
|
|
873
|
+
|
|
874
|
+
@tool(name="plain", description="plain tool")
|
|
875
|
+
async def plain(params: SimpleParams) -> dict:
|
|
876
|
+
return {"ok": True}
|
|
877
|
+
|
|
878
|
+
discovery = TestClient(app).get("/discovery").json()
|
|
879
|
+
by_name = {f["name"]: f for f in discovery["functions"]}
|
|
880
|
+
assert by_name["skip_me"]["skippable_interaction"] is True
|
|
881
|
+
assert "skippable_interaction" not in by_name["plain"]
|
{optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/README.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/setup.cfg
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|