optimizely-opal.opal-tools-sdk 0.1.10.dev0__tar.gz → 0.1.12.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 (21) hide show
  1. {optimizely_opal_opal_tools_sdk-0.1.10.dev0 → optimizely_opal_opal_tools_sdk-0.1.12.dev0}/PKG-INFO +99 -6
  2. {optimizely_opal_opal_tools_sdk-0.1.10.dev0 → optimizely_opal_opal_tools_sdk-0.1.12.dev0}/README.md +94 -5
  3. {optimizely_opal_opal_tools_sdk-0.1.10.dev0 → optimizely_opal_opal_tools_sdk-0.1.12.dev0}/opal_tools_sdk/__init__.py +4 -0
  4. optimizely_opal_opal_tools_sdk-0.1.12.dev0/opal_tools_sdk/block.py +9985 -0
  5. {optimizely_opal_opal_tools_sdk-0.1.10.dev0 → optimizely_opal_opal_tools_sdk-0.1.12.dev0}/opal_tools_sdk/decorators.py +16 -3
  6. {optimizely_opal_opal_tools_sdk-0.1.10.dev0 → optimizely_opal_opal_tools_sdk-0.1.12.dev0}/opal_tools_sdk/models.py +8 -1
  7. {optimizely_opal_opal_tools_sdk-0.1.10.dev0 → optimizely_opal_opal_tools_sdk-0.1.12.dev0}/opal_tools_sdk/service.py +23 -1
  8. {optimizely_opal_opal_tools_sdk-0.1.10.dev0 → optimizely_opal_opal_tools_sdk-0.1.12.dev0}/optimizely_opal.opal_tools_sdk.egg-info/PKG-INFO +99 -6
  9. {optimizely_opal_opal_tools_sdk-0.1.10.dev0 → optimizely_opal_opal_tools_sdk-0.1.12.dev0}/optimizely_opal.opal_tools_sdk.egg-info/SOURCES.txt +4 -1
  10. optimizely_opal_opal_tools_sdk-0.1.12.dev0/optimizely_opal.opal_tools_sdk.egg-info/requires.txt +8 -0
  11. {optimizely_opal_opal_tools_sdk-0.1.10.dev0 → optimizely_opal_opal_tools_sdk-0.1.12.dev0}/pyproject.toml +10 -9
  12. {optimizely_opal_opal_tools_sdk-0.1.10.dev0 → optimizely_opal_opal_tools_sdk-0.1.12.dev0}/setup.py +1 -1
  13. optimizely_opal_opal_tools_sdk-0.1.12.dev0/tests/test_block.py +105 -0
  14. optimizely_opal_opal_tools_sdk-0.1.12.dev0/tests/test_integration.py +181 -0
  15. optimizely_opal_opal_tools_sdk-0.1.10.dev0/optimizely_opal.opal_tools_sdk.egg-info/requires.txt +0 -3
  16. {optimizely_opal_opal_tools_sdk-0.1.10.dev0 → optimizely_opal_opal_tools_sdk-0.1.12.dev0}/opal_tools_sdk/_registry.py +0 -0
  17. {optimizely_opal_opal_tools_sdk-0.1.10.dev0 → optimizely_opal_opal_tools_sdk-0.1.12.dev0}/opal_tools_sdk/auth.py +0 -0
  18. {optimizely_opal_opal_tools_sdk-0.1.10.dev0 → optimizely_opal_opal_tools_sdk-0.1.12.dev0}/opal_tools_sdk/logging.py +0 -0
  19. {optimizely_opal_opal_tools_sdk-0.1.10.dev0 → optimizely_opal_opal_tools_sdk-0.1.12.dev0}/optimizely_opal.opal_tools_sdk.egg-info/dependency_links.txt +0 -0
  20. {optimizely_opal_opal_tools_sdk-0.1.10.dev0 → optimizely_opal_opal_tools_sdk-0.1.12.dev0}/optimizely_opal.opal_tools_sdk.egg-info/top_level.txt +0 -0
  21. {optimizely_opal_opal_tools_sdk-0.1.10.dev0 → optimizely_opal_opal_tools_sdk-0.1.12.dev0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: optimizely-opal.opal-tools-sdk
3
- Version: 0.1.10.dev0
3
+ Version: 0.1.12.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
@@ -18,6 +18,10 @@ Description-Content-Type: text/markdown
18
18
  Requires-Dist: fastapi>=0.100.0
19
19
  Requires-Dist: pydantic>=2.0.0
20
20
  Requires-Dist: httpx>=0.24.1
21
+ Provides-Extra: dev
22
+ Requires-Dist: datamodel-code-generator>=0.25.0; extra == "dev"
23
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
24
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
21
25
  Dynamic: author
22
26
  Dynamic: home-page
23
27
  Dynamic: requires-python
@@ -33,7 +37,8 @@ This SDK simplifies the creation of tools services compatible with the Opal Tool
33
37
  - Parameter validation and type checking
34
38
  - Authentication helpers
35
39
  - FastAPI integration
36
- - Island components for interactive UI responses
40
+ - **Adaptive Blocks** for interactive forms and UI flows (new!)
41
+ - Island components for interactive UI responses (legacy)
37
42
 
38
43
  ## Installation
39
44
 
@@ -45,7 +50,7 @@ Note: While the package is installed as `optimizely-opal.opal-tools-sdk`, you'll
45
50
 
46
51
  ```python
47
52
  # Import using the package name
48
- from opal_tools_sdk import ToolsService, tool, IslandResponse, IslandConfig
53
+ from opal_tools_sdk import ToolsService, tool, Block, BlockResponse, IslandResponse, IslandConfig
49
54
  ```
50
55
 
51
56
  ## Usage
@@ -70,6 +75,65 @@ async def get_weather(parameters: WeatherParameters):
70
75
  # Discovery endpoint is automatically created at /discovery
71
76
  ```
72
77
 
78
+ ## Adaptive Blocks
79
+
80
+ Adaptive Blocks provide an interactive UI framework for tools to capture user input directly through forms, rather than having the LLM gather parameters through chat messages.
81
+
82
+ ### Basic Example
83
+
84
+ ```python
85
+ from opal_tools_sdk import Block, BlockResponse, tool
86
+ from pydantic import BaseModel, Field
87
+
88
+ class MyToolParams(BaseModel):
89
+ user_input: str | None = Field(default=None, description="User input")
90
+
91
+ @tool(
92
+ name="my_tool",
93
+ description="Example tool with Block Document",
94
+ type="block" # Important: Specify type="block"
95
+ )
96
+ async def my_tool(params: MyToolParams) -> BlockResponse:
97
+ # If parameters are missing, show a form
98
+ if not params.user_input:
99
+ return BlockResponse(
100
+ content=Block.Document(
101
+ children=[
102
+ Block.Heading(children="Enter Details", level="2"),
103
+ Block.Field(
104
+ label="User Input",
105
+ required=True,
106
+ children=Block.Input(
107
+ name="user_input",
108
+ placeholder="Enter something..."
109
+ )
110
+ ),
111
+ ],
112
+ actions=[
113
+ Block.Action(name="submit", children="Submit"),
114
+ Block.CancelAction(children="Cancel")
115
+ ],
116
+ blocking=True # Hide chat prompt, force form interaction
117
+ )
118
+ )
119
+
120
+ # Process the input and return as Block Document
121
+ return BlockResponse(
122
+ content=Block.Document(
123
+ children=[
124
+ Block.Heading(children="Result", level="2"),
125
+ Block.Text(children=f"You entered: {params.user_input}")
126
+ ]
127
+ )
128
+ )
129
+ ```
130
+
131
+ **Important:** When using `type="block"`, the function MUST:
132
+
133
+ 1. Have a return type annotation of `-> BlockResponse`
134
+ 2. Always return a `BlockResponse` object (even for success/error cases)
135
+ 3. Wrap all responses in `Block.Document()` for consistency
136
+
73
137
  ## Authentication
74
138
 
75
139
  The SDK provides two ways to require authentication for your tools:
@@ -110,7 +174,7 @@ async def get_calendar_events(parameters: CalendarParameters, auth_data: Optiona
110
174
  async def get_calendar_availability(parameters: CalendarParameters, auth_data: Optional[AuthData] = None):
111
175
  provider = ""
112
176
  token = ""
113
-
177
+
114
178
  if auth_data:
115
179
  provider = auth_data["provider"]
116
180
  token = auth_data["credentials"]["access_token"]
@@ -162,7 +226,7 @@ class WeatherParameters(BaseModel):
162
226
  async def get_weather(parameters: WeatherParameters):
163
227
  # Get weather data (implementation details omitted)
164
228
  weather_data = {"temperature": 22, "condition": "sunny", "humidity": 65}
165
-
229
+
166
230
  # Create an interactive island for weather settings
167
231
  island = IslandConfig(
168
232
  fields=[
@@ -196,14 +260,16 @@ async def get_weather(parameters: WeatherParameters):
196
260
  )
197
261
  ]
198
262
  )
199
-
263
+
200
264
  return IslandResponse.create([island])
201
265
  ```
202
266
 
203
267
  ### Island Components
204
268
 
205
269
  #### IslandConfig.Field
270
+
206
271
  Fields represent data inputs in the UI:
272
+
207
273
  - `name`: Programmatic field identifier
208
274
  - `label`: Human-readable label
209
275
  - `type`: Field type (`"string"`, `"boolean"`, `"json"`)
@@ -212,7 +278,9 @@ Fields represent data inputs in the UI:
212
278
  - `options`: Available options for selection (optional)
213
279
 
214
280
  #### IslandConfig.Action
281
+
215
282
  Actions represent buttons or operations:
283
+
216
284
  - `name`: Programmatic action identifier
217
285
  - `label`: Human-readable button label
218
286
  - `type`: UI element type (typically `"button"`)
@@ -220,14 +288,18 @@ Actions represent buttons or operations:
220
288
  - `operation`: Operation type (default: `"create"`)
221
289
 
222
290
  #### IslandConfig
291
+
223
292
  Contains the complete island configuration:
293
+
224
294
  - `fields`: List of IslandConfig.Field objects
225
295
  - `actions`: List of IslandConfig.Action objects
226
296
  - `type`: Island type for UI rendering (optional, default: `None`)
227
297
  - `icon`: Icon to display in the island (optional, default: `None`)
228
298
 
229
299
  #### IslandResponse
300
+
230
301
  The response wrapper for islands:
302
+
231
303
  - Use `IslandResponse.create([islands])` to create responses
232
304
  - Supports multiple islands per response
233
305
 
@@ -236,20 +308,41 @@ The response wrapper for islands:
236
308
  The SDK provides several TypedDict and dataclass definitions for better type safety:
237
309
 
238
310
  ### Authentication Types
311
+
239
312
  - `AuthData`: TypedDict containing provider and credentials information
240
313
  - `Credentials`: TypedDict with access_token, org_sso_id, customer_id, instance_id, and product_sku
241
314
  - `AuthRequirement`: Dataclass for specifying authentication requirements
242
315
 
243
316
  ### Execution Environment
317
+
244
318
  - `Environment`: TypedDict specifying execution mode (`"headless"` or `"interactive"`)
245
319
 
246
320
  ### Parameter Types
321
+
247
322
  - `ParameterType`: Enum for supported parameter types (string, integer, number, boolean, list, dictionary)
248
323
  - `Parameter`: Dataclass for tool parameter definitions
249
324
  - `Function`: Dataclass for complete tool function definitions
250
325
 
251
326
  These types are automatically imported when you import from `opal_tools_sdk` and provide better IDE support and type checking.
252
327
 
328
+ ## Code Generation
329
+
330
+ The Block Document types is automatically generated from the JSON schema for full type safety and Pydantic validation.
331
+
332
+ ### Generating Types
333
+
334
+ To use the code generation:
335
+
336
+ ```bash
337
+ # Install dev dependencies
338
+ pip install -e ".[dev]"
339
+
340
+ # Generate types from schema
341
+ make generate-block
342
+ ```
343
+
344
+ This generates Pydantic v2 models from `block-document-spec.json` to `opal_tools_sdk/block.py` with builder methods included.
345
+
253
346
  ## Documentation
254
347
 
255
348
  See full documentation for more examples and configuration options.
@@ -9,7 +9,8 @@ This SDK simplifies the creation of tools services compatible with the Opal Tool
9
9
  - Parameter validation and type checking
10
10
  - Authentication helpers
11
11
  - FastAPI integration
12
- - Island components for interactive UI responses
12
+ - **Adaptive Blocks** for interactive forms and UI flows (new!)
13
+ - Island components for interactive UI responses (legacy)
13
14
 
14
15
  ## Installation
15
16
 
@@ -21,7 +22,7 @@ Note: While the package is installed as `optimizely-opal.opal-tools-sdk`, you'll
21
22
 
22
23
  ```python
23
24
  # Import using the package name
24
- from opal_tools_sdk import ToolsService, tool, IslandResponse, IslandConfig
25
+ from opal_tools_sdk import ToolsService, tool, Block, BlockResponse, IslandResponse, IslandConfig
25
26
  ```
26
27
 
27
28
  ## Usage
@@ -46,6 +47,65 @@ async def get_weather(parameters: WeatherParameters):
46
47
  # Discovery endpoint is automatically created at /discovery
47
48
  ```
48
49
 
50
+ ## Adaptive Blocks
51
+
52
+ Adaptive Blocks provide an interactive UI framework for tools to capture user input directly through forms, rather than having the LLM gather parameters through chat messages.
53
+
54
+ ### Basic Example
55
+
56
+ ```python
57
+ from opal_tools_sdk import Block, BlockResponse, tool
58
+ from pydantic import BaseModel, Field
59
+
60
+ class MyToolParams(BaseModel):
61
+ user_input: str | None = Field(default=None, description="User input")
62
+
63
+ @tool(
64
+ name="my_tool",
65
+ description="Example tool with Block Document",
66
+ type="block" # Important: Specify type="block"
67
+ )
68
+ async def my_tool(params: MyToolParams) -> BlockResponse:
69
+ # If parameters are missing, show a form
70
+ if not params.user_input:
71
+ return BlockResponse(
72
+ content=Block.Document(
73
+ children=[
74
+ Block.Heading(children="Enter Details", level="2"),
75
+ Block.Field(
76
+ label="User Input",
77
+ required=True,
78
+ children=Block.Input(
79
+ name="user_input",
80
+ placeholder="Enter something..."
81
+ )
82
+ ),
83
+ ],
84
+ actions=[
85
+ Block.Action(name="submit", children="Submit"),
86
+ Block.CancelAction(children="Cancel")
87
+ ],
88
+ blocking=True # Hide chat prompt, force form interaction
89
+ )
90
+ )
91
+
92
+ # Process the input and return as Block Document
93
+ return BlockResponse(
94
+ content=Block.Document(
95
+ children=[
96
+ Block.Heading(children="Result", level="2"),
97
+ Block.Text(children=f"You entered: {params.user_input}")
98
+ ]
99
+ )
100
+ )
101
+ ```
102
+
103
+ **Important:** When using `type="block"`, the function MUST:
104
+
105
+ 1. Have a return type annotation of `-> BlockResponse`
106
+ 2. Always return a `BlockResponse` object (even for success/error cases)
107
+ 3. Wrap all responses in `Block.Document()` for consistency
108
+
49
109
  ## Authentication
50
110
 
51
111
  The SDK provides two ways to require authentication for your tools:
@@ -86,7 +146,7 @@ async def get_calendar_events(parameters: CalendarParameters, auth_data: Optiona
86
146
  async def get_calendar_availability(parameters: CalendarParameters, auth_data: Optional[AuthData] = None):
87
147
  provider = ""
88
148
  token = ""
89
-
149
+
90
150
  if auth_data:
91
151
  provider = auth_data["provider"]
92
152
  token = auth_data["credentials"]["access_token"]
@@ -138,7 +198,7 @@ class WeatherParameters(BaseModel):
138
198
  async def get_weather(parameters: WeatherParameters):
139
199
  # Get weather data (implementation details omitted)
140
200
  weather_data = {"temperature": 22, "condition": "sunny", "humidity": 65}
141
-
201
+
142
202
  # Create an interactive island for weather settings
143
203
  island = IslandConfig(
144
204
  fields=[
@@ -172,14 +232,16 @@ async def get_weather(parameters: WeatherParameters):
172
232
  )
173
233
  ]
174
234
  )
175
-
235
+
176
236
  return IslandResponse.create([island])
177
237
  ```
178
238
 
179
239
  ### Island Components
180
240
 
181
241
  #### IslandConfig.Field
242
+
182
243
  Fields represent data inputs in the UI:
244
+
183
245
  - `name`: Programmatic field identifier
184
246
  - `label`: Human-readable label
185
247
  - `type`: Field type (`"string"`, `"boolean"`, `"json"`)
@@ -188,7 +250,9 @@ Fields represent data inputs in the UI:
188
250
  - `options`: Available options for selection (optional)
189
251
 
190
252
  #### IslandConfig.Action
253
+
191
254
  Actions represent buttons or operations:
255
+
192
256
  - `name`: Programmatic action identifier
193
257
  - `label`: Human-readable button label
194
258
  - `type`: UI element type (typically `"button"`)
@@ -196,14 +260,18 @@ Actions represent buttons or operations:
196
260
  - `operation`: Operation type (default: `"create"`)
197
261
 
198
262
  #### IslandConfig
263
+
199
264
  Contains the complete island configuration:
265
+
200
266
  - `fields`: List of IslandConfig.Field objects
201
267
  - `actions`: List of IslandConfig.Action objects
202
268
  - `type`: Island type for UI rendering (optional, default: `None`)
203
269
  - `icon`: Icon to display in the island (optional, default: `None`)
204
270
 
205
271
  #### IslandResponse
272
+
206
273
  The response wrapper for islands:
274
+
207
275
  - Use `IslandResponse.create([islands])` to create responses
208
276
  - Supports multiple islands per response
209
277
 
@@ -212,20 +280,41 @@ The response wrapper for islands:
212
280
  The SDK provides several TypedDict and dataclass definitions for better type safety:
213
281
 
214
282
  ### Authentication Types
283
+
215
284
  - `AuthData`: TypedDict containing provider and credentials information
216
285
  - `Credentials`: TypedDict with access_token, org_sso_id, customer_id, instance_id, and product_sku
217
286
  - `AuthRequirement`: Dataclass for specifying authentication requirements
218
287
 
219
288
  ### Execution Environment
289
+
220
290
  - `Environment`: TypedDict specifying execution mode (`"headless"` or `"interactive"`)
221
291
 
222
292
  ### Parameter Types
293
+
223
294
  - `ParameterType`: Enum for supported parameter types (string, integer, number, boolean, list, dictionary)
224
295
  - `Parameter`: Dataclass for tool parameter definitions
225
296
  - `Function`: Dataclass for complete tool function definitions
226
297
 
227
298
  These types are automatically imported when you import from `opal_tools_sdk` and provide better IDE support and type checking.
228
299
 
300
+ ## Code Generation
301
+
302
+ The Block Document types is automatically generated from the JSON schema for full type safety and Pydantic validation.
303
+
304
+ ### Generating Types
305
+
306
+ To use the code generation:
307
+
308
+ ```bash
309
+ # Install dev dependencies
310
+ pip install -e ".[dev]"
311
+
312
+ # Generate types from schema
313
+ make generate-block
314
+ ```
315
+
316
+ This generates Pydantic v2 models from `block-document-spec.json` to `opal_tools_sdk/block.py` with builder methods included.
317
+
229
318
  ## Documentation
230
319
 
231
320
  See full documentation for more examples and configuration options.
@@ -3,6 +3,7 @@ from .decorators import tool
3
3
  from .auth import requires_auth
4
4
  from .logging import register_logger_factory
5
5
  from .models import AuthData, AuthRequirement, Credentials, Environment, IslandConfig, IslandResponse
6
+ from .block import Block, BlockResponse
6
7
 
7
8
  __version__ = "0.1.0"
8
9
  __all__ = [
@@ -17,4 +18,7 @@ __all__ = [
17
18
  "Environment",
18
19
  "IslandConfig",
19
20
  "IslandResponse",
21
+ # Block
22
+ "Block",
23
+ "BlockResponse",
20
24
  ]