optimizely-opal.opal-tools-sdk 0.1.5.dev0__tar.gz → 0.1.10.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.5.dev0 → optimizely_opal_opal_tools_sdk-0.1.10.dev0}/PKG-INFO +3 -1
- {optimizely_opal_opal_tools_sdk-0.1.5.dev0 → optimizely_opal_opal_tools_sdk-0.1.10.dev0}/README.md +2 -0
- {optimizely_opal_opal_tools_sdk-0.1.5.dev0 → optimizely_opal_opal_tools_sdk-0.1.10.dev0}/opal_tools_sdk/decorators.py +29 -6
- {optimizely_opal_opal_tools_sdk-0.1.5.dev0 → optimizely_opal_opal_tools_sdk-0.1.10.dev0}/opal_tools_sdk/models.py +5 -2
- {optimizely_opal_opal_tools_sdk-0.1.5.dev0 → optimizely_opal_opal_tools_sdk-0.1.10.dev0}/opal_tools_sdk/service.py +26 -27
- {optimizely_opal_opal_tools_sdk-0.1.5.dev0 → optimizely_opal_opal_tools_sdk-0.1.10.dev0}/optimizely_opal.opal_tools_sdk.egg-info/PKG-INFO +3 -1
- {optimizely_opal_opal_tools_sdk-0.1.5.dev0 → optimizely_opal_opal_tools_sdk-0.1.10.dev0}/pyproject.toml +1 -1
- {optimizely_opal_opal_tools_sdk-0.1.5.dev0 → optimizely_opal_opal_tools_sdk-0.1.10.dev0}/setup.py +1 -1
- {optimizely_opal_opal_tools_sdk-0.1.5.dev0 → optimizely_opal_opal_tools_sdk-0.1.10.dev0}/opal_tools_sdk/__init__.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.5.dev0 → optimizely_opal_opal_tools_sdk-0.1.10.dev0}/opal_tools_sdk/_registry.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.5.dev0 → optimizely_opal_opal_tools_sdk-0.1.10.dev0}/opal_tools_sdk/auth.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.5.dev0 → optimizely_opal_opal_tools_sdk-0.1.10.dev0}/opal_tools_sdk/logging.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.5.dev0 → optimizely_opal_opal_tools_sdk-0.1.10.dev0}/optimizely_opal.opal_tools_sdk.egg-info/SOURCES.txt +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.5.dev0 → optimizely_opal_opal_tools_sdk-0.1.10.dev0}/optimizely_opal.opal_tools_sdk.egg-info/dependency_links.txt +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.5.dev0 → optimizely_opal_opal_tools_sdk-0.1.10.dev0}/optimizely_opal.opal_tools_sdk.egg-info/requires.txt +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.5.dev0 → optimizely_opal_opal_tools_sdk-0.1.10.dev0}/optimizely_opal.opal_tools_sdk.egg-info/top_level.txt +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.5.dev0 → optimizely_opal_opal_tools_sdk-0.1.10.dev0}/setup.cfg +0 -0
{optimizely_opal_opal_tools_sdk-0.1.5.dev0 → optimizely_opal_opal_tools_sdk-0.1.10.dev0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: optimizely-opal.opal-tools-sdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.10.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
|
|
@@ -223,6 +223,8 @@ Actions represent buttons or operations:
|
|
|
223
223
|
Contains the complete island configuration:
|
|
224
224
|
- `fields`: List of IslandConfig.Field objects
|
|
225
225
|
- `actions`: List of IslandConfig.Action objects
|
|
226
|
+
- `type`: Island type for UI rendering (optional, default: `None`)
|
|
227
|
+
- `icon`: Icon to display in the island (optional, default: `None`)
|
|
226
228
|
|
|
227
229
|
#### IslandResponse
|
|
228
230
|
The response wrapper for islands:
|
{optimizely_opal_opal_tools_sdk-0.1.5.dev0 → optimizely_opal_opal_tools_sdk-0.1.10.dev0}/README.md
RENAMED
|
@@ -199,6 +199,8 @@ Actions represent buttons or operations:
|
|
|
199
199
|
Contains the complete island configuration:
|
|
200
200
|
- `fields`: List of IslandConfig.Field objects
|
|
201
201
|
- `actions`: List of IslandConfig.Action objects
|
|
202
|
+
- `type`: Island type for UI rendering (optional, default: `None`)
|
|
203
|
+
- `icon`: Icon to display in the island (optional, default: `None`)
|
|
202
204
|
|
|
203
205
|
#### IslandResponse
|
|
204
206
|
The response wrapper for islands:
|
|
@@ -62,25 +62,48 @@ def tool(name: str, description: str, auth_requirements: Optional[List[Dict[str,
|
|
|
62
62
|
else:
|
|
63
63
|
field_type = str
|
|
64
64
|
|
|
65
|
+
# Check if the field is Optional (Union with None)
|
|
66
|
+
# Optional[X] is equivalent to Union[X, None]
|
|
67
|
+
type_args = getattr(field_type, '__args__', ())
|
|
68
|
+
is_optional = get_origin(field_type) is Union and type(None) in type_args
|
|
69
|
+
|
|
70
|
+
# Extract the actual type from Optional[T]
|
|
71
|
+
if is_optional and type_args:
|
|
72
|
+
# Get the non-None type from Union[T, None]
|
|
73
|
+
field_type = next(
|
|
74
|
+
(arg for arg in type_args if arg is not type(None)),
|
|
75
|
+
field_type,
|
|
76
|
+
)
|
|
77
|
+
|
|
65
78
|
# Map Python type to Parameter type
|
|
66
79
|
param_type = ParameterType.string
|
|
67
|
-
if field_type
|
|
80
|
+
if field_type is int:
|
|
68
81
|
param_type = ParameterType.integer
|
|
69
|
-
elif field_type
|
|
82
|
+
elif field_type is float:
|
|
70
83
|
param_type = ParameterType.number
|
|
71
|
-
elif field_type
|
|
84
|
+
elif field_type is bool:
|
|
72
85
|
param_type = ParameterType.boolean
|
|
73
|
-
elif get_origin(field_type) is list:
|
|
86
|
+
elif field_type is list or get_origin(field_type) is list:
|
|
74
87
|
param_type = ParameterType.list
|
|
75
|
-
elif get_origin(field_type) is dict:
|
|
88
|
+
elif field_type is dict or get_origin(field_type) is dict:
|
|
76
89
|
param_type = ParameterType.dictionary
|
|
77
90
|
|
|
78
91
|
# Determine if required
|
|
79
92
|
field_info_extra = getattr(field_info, "json_schema_extra") or {}
|
|
80
93
|
if "required" in field_info_extra:
|
|
81
94
|
required = field_info_extra["required"]
|
|
95
|
+
# If the field is typed as Optional, it's not required (check this FIRST)
|
|
96
|
+
elif is_optional:
|
|
97
|
+
required = False
|
|
98
|
+
# Check for Pydantic v2 is_required() method
|
|
99
|
+
elif hasattr(field_info, 'is_required'):
|
|
100
|
+
required = field_info.is_required()
|
|
101
|
+
# Fall back to checking if default is ... (Pydantic v1/v2 compatibility)
|
|
102
|
+
elif hasattr(field_info, 'default'):
|
|
103
|
+
required = field_info.default is ...
|
|
82
104
|
else:
|
|
83
|
-
|
|
105
|
+
# If no default attribute at all, assume required
|
|
106
|
+
required = True
|
|
84
107
|
|
|
85
108
|
# Get description
|
|
86
109
|
description_text = ""
|
|
@@ -112,6 +112,8 @@ class IslandConfig(BaseModel):
|
|
|
112
112
|
|
|
113
113
|
fields: list[Field]
|
|
114
114
|
actions: list[Action]
|
|
115
|
+
type: Optional[str] = None
|
|
116
|
+
icon: Optional[str] = None
|
|
115
117
|
|
|
116
118
|
|
|
117
119
|
class IslandResponse(BaseModel):
|
|
@@ -120,7 +122,8 @@ class IslandResponse(BaseModel):
|
|
|
120
122
|
|
|
121
123
|
type: Literal["island"]
|
|
122
124
|
config: ResponseConfig
|
|
125
|
+
message: Optional[str] = None
|
|
123
126
|
|
|
124
127
|
@classmethod
|
|
125
|
-
def create(cls, islands: list[IslandConfig]):
|
|
126
|
-
return cls(type="island", config=cls.ResponseConfig(islands=islands))
|
|
128
|
+
def create(cls, islands: list[IslandConfig], message: Optional[str] = None):
|
|
129
|
+
return cls(type="island", config=cls.ResponseConfig(islands=islands), message=message)
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
from typing import Dict, List, Any, Callable,
|
|
1
|
+
from typing import Dict, List, Any, Callable, Optional, get_type_hints
|
|
2
2
|
import inspect
|
|
3
3
|
import logging
|
|
4
|
-
from fastapi import FastAPI, APIRouter,
|
|
4
|
+
from fastapi import FastAPI, APIRouter, HTTPException, Request
|
|
5
5
|
from fastapi.routing import APIRoute
|
|
6
|
-
from pydantic import BaseModel,
|
|
6
|
+
from pydantic import BaseModel, ValidationError
|
|
7
7
|
|
|
8
|
-
from .models import Function, Parameter,
|
|
8
|
+
from .models import Function, Parameter, AuthRequirement
|
|
9
9
|
from . import _registry
|
|
10
10
|
|
|
11
11
|
logger = logging.getLogger(__name__)
|
|
12
12
|
|
|
13
|
+
|
|
13
14
|
class ToolsService:
|
|
14
15
|
"""Main class for managing Opal tools."""
|
|
15
16
|
|
|
@@ -33,15 +34,12 @@ class ToolsService:
|
|
|
33
34
|
routes = []
|
|
34
35
|
for route in app.routes:
|
|
35
36
|
if isinstance(route, APIRoute):
|
|
36
|
-
routes.append({
|
|
37
|
-
"path": route.path,
|
|
38
|
-
"name": route.name,
|
|
39
|
-
"methods": route.methods
|
|
40
|
-
})
|
|
37
|
+
routes.append({"path": route.path, "name": route.name, "methods": route.methods})
|
|
41
38
|
return {"routes": routes}
|
|
42
39
|
|
|
43
40
|
def _init_routes(self) -> None:
|
|
44
41
|
"""Initialize the discovery endpoint."""
|
|
42
|
+
|
|
45
43
|
@self.router.get("/discovery")
|
|
46
44
|
async def discovery() -> Dict[str, Any]:
|
|
47
45
|
"""Return the discovery information for this tools service."""
|
|
@@ -66,21 +64,23 @@ class ToolsService:
|
|
|
66
64
|
# Auth requirements should always be a list
|
|
67
65
|
if isinstance(handler.__auth_requirements__, list):
|
|
68
66
|
for req in handler.__auth_requirements__:
|
|
69
|
-
auth_requirements.append(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
)
|
|
67
|
+
auth_requirements.append(
|
|
68
|
+
AuthRequirement(
|
|
69
|
+
provider=req.get("provider", ""), scope_bundle=req.get("scope_bundle", ""), required=req.get("required", True)
|
|
70
|
+
)
|
|
71
|
+
)
|
|
74
72
|
|
|
75
73
|
return auth_requirements
|
|
76
74
|
|
|
77
|
-
def register_tool(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
75
|
+
def register_tool(
|
|
76
|
+
self,
|
|
77
|
+
name: str,
|
|
78
|
+
description: str,
|
|
79
|
+
handler: Callable,
|
|
80
|
+
parameters: List[Parameter],
|
|
81
|
+
endpoint: str,
|
|
82
|
+
auth_requirements: Optional[List[AuthRequirement]] = None,
|
|
83
|
+
) -> None:
|
|
84
84
|
"""Register a tool function.
|
|
85
85
|
|
|
86
86
|
Args:
|
|
@@ -101,11 +101,7 @@ class ToolsService:
|
|
|
101
101
|
final_auth_requirements = auth_requirements if auth_requirements else handler_auth_requirements
|
|
102
102
|
|
|
103
103
|
function = Function(
|
|
104
|
-
name=name,
|
|
105
|
-
description=description,
|
|
106
|
-
parameters=parameters,
|
|
107
|
-
endpoint=endpoint,
|
|
108
|
-
auth_requirements=final_auth_requirements
|
|
104
|
+
name=name, description=description, parameters=parameters, endpoint=endpoint, auth_requirements=final_auth_requirements
|
|
109
105
|
)
|
|
110
106
|
|
|
111
107
|
self.functions.append(function)
|
|
@@ -144,7 +140,6 @@ class ToolsService:
|
|
|
144
140
|
param_name = list(sig.parameters.keys())[0]
|
|
145
141
|
param_type = get_type_hints(handler).get(param_name)
|
|
146
142
|
|
|
147
|
-
|
|
148
143
|
args = []
|
|
149
144
|
kwargs = {}
|
|
150
145
|
if param_type:
|
|
@@ -168,8 +163,12 @@ class ToolsService:
|
|
|
168
163
|
|
|
169
164
|
logger.info(f"Tool {name} returned: {result}")
|
|
170
165
|
return result
|
|
166
|
+
except ValidationError as e:
|
|
167
|
+
logger.warning(f"Invalid parameters predicted by LLM for tool {name}: {str(e)}")
|
|
168
|
+
raise HTTPException(status_code=400, detail=str(e))
|
|
171
169
|
except Exception as e:
|
|
172
170
|
import traceback
|
|
171
|
+
|
|
173
172
|
logger.error(f"Error in tool {name}: {str(e)}")
|
|
174
173
|
logger.error(traceback.format_exc())
|
|
175
174
|
raise HTTPException(status_code=500, detail=str(e))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: optimizely-opal.opal-tools-sdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.10.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
|
|
@@ -223,6 +223,8 @@ Actions represent buttons or operations:
|
|
|
223
223
|
Contains the complete island configuration:
|
|
224
224
|
- `fields`: List of IslandConfig.Field objects
|
|
225
225
|
- `actions`: List of IslandConfig.Action objects
|
|
226
|
+
- `type`: Island type for UI rendering (optional, default: `None`)
|
|
227
|
+
- `icon`: Icon to display in the island (optional, default: `None`)
|
|
226
228
|
|
|
227
229
|
#### IslandResponse
|
|
228
230
|
The response wrapper for islands:
|
|
@@ -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.10-dev"
|
|
8
8
|
description = "SDK for creating Opal-compatible tools services"
|
|
9
9
|
authors = [
|
|
10
10
|
{name = "Optimizely", email = "opal-team@optimizely.com"}
|
|
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.5.dev0 → optimizely_opal_opal_tools_sdk-0.1.10.dev0}/setup.cfg
RENAMED
|
File without changes
|