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.
Files changed (30) hide show
  1. {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/PKG-INFO +1 -1
  2. {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
  3. {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
  4. {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
  5. {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
  6. {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/pyproject.toml +1 -1
  7. {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/setup.py +1 -1
  8. {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/tests/test_integration.py +19 -0
  9. {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/README.md +0 -0
  10. {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
  11. {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
  12. {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
  13. {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
  14. {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
  15. {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
  16. {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
  17. {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
  18. {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
  19. {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
  20. {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
  21. {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
  22. {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/setup.cfg +0 -0
  23. {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
  24. {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
  25. {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
  26. {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/tests/test_hmac.py +0 -0
  27. {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
  28. {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/tests/test_proteus.py +0 -0
  29. {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/tests/test_response.py +0 -0
  30. {optimizely_opal_opal_tools_sdk-0.1.41.dev0 → optimizely_opal_opal_tools_sdk-0.1.42.dev0}/tests/test_rollback.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: optimizely-opal.opal-tools-sdk
3
- Version: 0.1.41.dev0
3
+ Version: 0.1.42.dev0
4
4
  Summary: SDK for creating Opal-compatible tools services
5
5
  Home-page: https://github.com/optimizely/opal-tools-sdk
6
6
  Author: Optimizely
@@ -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, total=False):
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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: optimizely-opal.opal-tools-sdk
3
- Version: 0.1.41.dev0
3
+ Version: 0.1.42.dev0
4
4
  Summary: SDK for creating Opal-compatible tools services
5
5
  Home-page: https://github.com/optimizely/opal-tools-sdk
6
6
  Author: Optimizely
@@ -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.41-dev"
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"
@@ -2,7 +2,7 @@ from setuptools import find_packages, setup
2
2
 
3
3
  setup(
4
4
  name="optimizely-opal.opal-tools-sdk",
5
- version="0.1.41-dev",
5
+ version="0.1.42-dev",
6
6
  packages=find_packages(),
7
7
  install_requires=[
8
8
  "fastapi>=0.100.0",
@@ -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"]