optimizely-opal.opal-tools-sdk 0.1.3.dev0__py3-none-any.whl → 0.1.5.dev0__py3-none-any.whl

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.

Potentially problematic release.


This version of optimizely-opal.opal-tools-sdk might be problematic. Click here for more details.

@@ -1,6 +1,20 @@
1
1
  from .service import ToolsService
2
2
  from .decorators import tool
3
3
  from .auth import requires_auth
4
+ from .logging import register_logger_factory
5
+ from .models import AuthData, AuthRequirement, Credentials, Environment, IslandConfig, IslandResponse
4
6
 
5
7
  __version__ = "0.1.0"
6
- __all__ = ["ToolsService", "tool", "requires_auth"]
8
+ __all__ = [
9
+ "ToolsService",
10
+ "tool",
11
+ "requires_auth",
12
+ "register_logger_factory",
13
+ # Models
14
+ "AuthData",
15
+ "AuthRequirement",
16
+ "Credentials",
17
+ "Environment",
18
+ "IslandConfig",
19
+ "IslandResponse",
20
+ ]
@@ -6,8 +6,9 @@ from fastapi import APIRouter, Depends, Header, HTTPException
6
6
  from pydantic import BaseModel
7
7
 
8
8
  from .models import Parameter, ParameterType, AuthRequirement
9
+ from .logging import get_logger
9
10
 
10
- logger = logging.getLogger("opal_tools_sdk")
11
+ logger = get_logger(__name__)
11
12
 
12
13
  def tool(name: str, description: str, auth_requirements: Optional[List[Dict[str, Any]]] = None):
13
14
  """Decorator to register a function as an Opal tool.
@@ -95,9 +96,9 @@ def tool(name: str, description: str, auth_requirements: Optional[List[Dict[str,
95
96
  required=required
96
97
  ))
97
98
 
98
- print(f"Registered parameter: {field_name} of type {param_type.value}, required: {required}")
99
+ logger.info(f"Registered parameter: {field_name} of type {param_type.value}, required: {required}")
99
100
  else:
100
- print(f"Warning: No parameter model found for {name}")
101
+ logger.warning(f"Warning: No parameter model found for {name}")
101
102
 
102
103
  endpoint = f"/tools/{name}"
103
104
 
@@ -112,10 +113,10 @@ def tool(name: str, description: str, auth_requirements: Optional[List[Dict[str,
112
113
  required=auth_req.get("required", True)
113
114
  ))
114
115
 
115
- print(f"Registering tool {name} with endpoint {endpoint}")
116
+ logger.info(f"Registering tool {name} with endpoint {endpoint}")
116
117
 
117
118
  if not _registry.services:
118
- print("No services registered in registry! Make sure to create ToolsService before decorating functions.")
119
+ logger.warning("No services registered in registry! Make sure to create ToolsService before decorating functions.")
119
120
 
120
121
  for service in _registry.services:
121
122
  service.register_tool(
@@ -0,0 +1,37 @@
1
+ import logging
2
+ import sys
3
+ from typing import Optional, Callable
4
+
5
+ # Type alias for a logger factory function
6
+ type LoggerFactory = Callable[[Optional[str]], logging.Logger]
7
+
8
+ # Internal variable to hold a custom logger factory
9
+ _custom_logger_factory: Optional[LoggerFactory] = None
10
+
11
+ def register_logger_factory(factory: LoggerFactory):
12
+ """
13
+ Register a custom logger factory function. This function should accept a name (str or None)
14
+ and return a logger instance (e.g., structlog or standard logger).
15
+ """
16
+ global _custom_logger_factory
17
+ _custom_logger_factory = factory
18
+
19
+ def get_logger(name: str = None) -> logging.Logger:
20
+ """
21
+ Returns a logger configured to output to the console, or uses a registered custom logger factory.
22
+ If no name is provided, uses the root logger.
23
+ """
24
+ if _custom_logger_factory is not None:
25
+ return _custom_logger_factory(name)
26
+ logger = logging.getLogger(name)
27
+ if not logger.handlers:
28
+ handler = logging.StreamHandler(sys.stdout)
29
+ formatter = logging.Formatter(
30
+ fmt='%(asctime)s %(levelname)s [%(name)s] %(message)s',
31
+ datefmt='%Y-%m-%d %H:%M:%S'
32
+ )
33
+ handler.setFormatter(formatter)
34
+ logger.addHandler(handler)
35
+ logger.setLevel(logging.INFO)
36
+ logger.propagate = False
37
+ return logger
opal_tools_sdk/models.py CHANGED
@@ -1,9 +1,13 @@
1
1
  from enum import Enum
2
- from typing import List, Dict, Any, Optional
2
+ from typing import List, Dict, Any, Optional, Literal, TypedDict
3
3
  from dataclasses import dataclass
4
4
 
5
+ from pydantic import BaseModel, Field
6
+
7
+
5
8
  class ParameterType(str, Enum):
6
9
  """Types of parameters supported by Opal tools."""
10
+
7
11
  string = "string"
8
12
  integer = "integer"
9
13
  number = "number"
@@ -11,9 +15,11 @@ class ParameterType(str, Enum):
11
15
  list = "array" # Changed to match main service expectation
12
16
  dictionary = "object" # Standard JSON schema type
13
17
 
18
+
14
19
  @dataclass
15
20
  class Parameter:
16
21
  """Parameter definition for an Opal tool."""
22
+
17
23
  name: str
18
24
  param_type: ParameterType
19
25
  description: str
@@ -21,31 +27,49 @@ class Parameter:
21
27
 
22
28
  def to_dict(self) -> Dict[str, Any]:
23
29
  """Convert to dictionary for the discovery endpoint."""
24
- return {
25
- "name": self.name,
26
- "type": self.param_type.value,
27
- "description": self.description,
28
- "required": self.required
29
- }
30
+ return {"name": self.name, "type": self.param_type.value, "description": self.description, "required": self.required}
31
+
30
32
 
31
33
  @dataclass
32
34
  class AuthRequirement:
33
35
  """Authentication requirements for an Opal tool."""
36
+
34
37
  provider: str # e.g., "google", "microsoft"
35
38
  scope_bundle: str # e.g., "calendar", "drive"
36
39
  required: bool = True
37
40
 
38
41
  def to_dict(self) -> Dict[str, Any]:
39
42
  """Convert to dictionary for the discovery endpoint."""
40
- return {
41
- "provider": self.provider,
42
- "scope_bundle": self.scope_bundle,
43
- "required": self.required
44
- }
43
+ return {"provider": self.provider, "scope_bundle": self.scope_bundle, "required": self.required}
44
+
45
+
46
+ class Credentials(TypedDict):
47
+ """AuthData credentials."""
48
+
49
+ access_token: str
50
+ org_sso_id: Optional[str]
51
+ customer_id: str
52
+ instance_id: str
53
+ product_sku: str
54
+
55
+
56
+ class AuthData(TypedDict):
57
+ """Authentication data for an Opal tool."""
58
+
59
+ provider: str
60
+ credentials: Credentials
61
+
62
+
63
+ class Environment(TypedDict):
64
+ """Execution environment for an Opal tool. Interactive will provide interaction islands, while headless will not."""
65
+
66
+ execution_mode: Literal["headless", "interactive"]
67
+
45
68
 
46
69
  @dataclass
47
70
  class Function:
48
71
  """Function definition for an Opal tool."""
72
+
49
73
  name: str
50
74
  description: str
51
75
  parameters: List[Parameter]
@@ -60,10 +84,43 @@ class Function:
60
84
  "description": self.description,
61
85
  "parameters": [p.to_dict() for p in self.parameters],
62
86
  "endpoint": self.endpoint,
63
- "http_method": self.http_method
87
+ "http_method": self.http_method,
64
88
  }
65
89
 
66
90
  if self.auth_requirements:
67
91
  result["auth_requirements"] = [auth.to_dict() for auth in self.auth_requirements]
68
92
 
69
93
  return result
94
+
95
+
96
+ # Interaction island related classes
97
+ class IslandConfig(BaseModel):
98
+ class Field(BaseModel):
99
+ name: str
100
+ label: str
101
+ type: Literal["string", "boolean", "json"]
102
+ value: str = Field(default="")
103
+ hidden: bool = Field(default=False)
104
+ options: list[str] = Field(default=[])
105
+
106
+ class Action(BaseModel):
107
+ name: str
108
+ label: str
109
+ type: str
110
+ endpoint: str
111
+ operation: str = Field(default="create")
112
+
113
+ fields: list[Field]
114
+ actions: list[Action]
115
+
116
+
117
+ class IslandResponse(BaseModel):
118
+ class ResponseConfig(BaseModel):
119
+ islands: list[IslandConfig]
120
+
121
+ type: Literal["island"]
122
+ config: ResponseConfig
123
+
124
+ @classmethod
125
+ def create(cls, islands: list[IslandConfig]):
126
+ return cls(type="island", config=cls.ResponseConfig(islands=islands))
opal_tools_sdk/service.py CHANGED
@@ -8,7 +8,7 @@ from pydantic import BaseModel, create_model
8
8
  from .models import Function, Parameter, ParameterType, AuthRequirement
9
9
  from . import _registry
10
10
 
11
- logger = logging.getLogger("opal_tools_sdk")
11
+ logger = logging.getLogger(__name__)
12
12
 
13
13
  class ToolsService:
14
14
  """Main class for managing Opal tools."""
@@ -91,7 +91,7 @@ class ToolsService:
91
91
  endpoint: API endpoint for the tool
92
92
  auth_requirements: List of authentication requirements (optional)
93
93
  """
94
- print(f"Registering tool: {name} with endpoint: {endpoint}")
94
+ logger.info(f"Registering tool: {name} with endpoint: {endpoint}")
95
95
 
96
96
  # Extract auth requirements from handler if decorated with @requires_auth
97
97
  handler_auth_requirements = self._extract_auth_requirements(handler)
@@ -116,7 +116,7 @@ class ToolsService:
116
116
  try:
117
117
  # Parse JSON body
118
118
  body = await request.json()
119
- print(f"Received request for {endpoint} with body: {body}")
119
+ logger.info(f"Received request for {endpoint} with body: {body}")
120
120
 
121
121
  # Parameters should be in the "parameters" key according to the spec
122
122
  # This matches how the tools-mgmt-service calls tools
@@ -124,20 +124,20 @@ class ToolsService:
124
124
  params = body["parameters"]
125
125
  else:
126
126
  # For backward compatibility with direct test calls
127
- print(f"Warning: 'parameters' key not found in request body. Using body directly.")
127
+ logger.warning(f"Warning: 'parameters' key not found in request body. Using body directly.")
128
128
  params = body
129
129
 
130
130
  # Extract auth data if available
131
131
  auth_data = body.get("auth")
132
132
  if auth_data:
133
- print(f"Auth data provided for provider: {auth_data.get('provider', 'unknown')}")
133
+ logger.info(f"Auth data provided for provider: {auth_data.get('provider', 'unknown')}")
134
134
 
135
135
  # Extract environment data if available
136
136
  environment = body.get("environment", {})
137
137
  if environment:
138
- print(f"Environment data provided: {environment}")
138
+ logger.info(f"Environment data provided: {environment}")
139
139
 
140
- print(f"Extracted parameters: {params}")
140
+ logger.info(f"Extracted parameters: {params}")
141
141
 
142
142
  # Get the parameter model from handler's signature
143
143
  sig = inspect.signature(handler)
@@ -166,12 +166,12 @@ class ToolsService:
166
166
 
167
167
  result = await handler(*args, **kwargs)
168
168
 
169
- print(f"Tool {name} returned: {result}")
169
+ logger.info(f"Tool {name} returned: {result}")
170
170
  return result
171
171
  except Exception as e:
172
172
  import traceback
173
- print(f"Error in tool {name}: {str(e)}")
174
- print(traceback.format_exc())
173
+ logger.error(f"Error in tool {name}: {str(e)}")
174
+ logger.error(traceback.format_exc())
175
175
  raise HTTPException(status_code=500, detail=str(e))
176
176
 
177
177
  # Update the route function name and docstring
@@ -0,0 +1,253 @@
1
+ Metadata-Version: 2.4
2
+ Name: optimizely-opal.opal-tools-sdk
3
+ Version: 0.1.5.dev0
4
+ Summary: SDK for creating Opal-compatible tools services
5
+ Home-page: https://github.com/optimizely/opal-tools-sdk
6
+ Author: Optimizely
7
+ Author-email: Optimizely <opal-team@optimizely.com>
8
+ License: MIT
9
+ Project-URL: Homepage, https://github.com/optimizely/opal-tools-sdk
10
+ Project-URL: Bug Tracker, https://github.com/optimizely/opal-tools-sdk/issues
11
+ Keywords: opal,tools,sdk,ai,llm
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Requires-Python: >=3.10
17
+ Description-Content-Type: text/markdown
18
+ Requires-Dist: fastapi>=0.100.0
19
+ Requires-Dist: pydantic>=2.0.0
20
+ Requires-Dist: httpx>=0.24.1
21
+ Dynamic: author
22
+ Dynamic: home-page
23
+ Dynamic: requires-python
24
+
25
+ # Opal Tools SDK for Python
26
+
27
+ This SDK simplifies the creation of tools services compatible with the Opal Tools Management Service.
28
+
29
+ ## Features
30
+
31
+ - Easy definition of tool functions with decorators
32
+ - Automatic generation of discovery endpoints
33
+ - Parameter validation and type checking
34
+ - Authentication helpers
35
+ - FastAPI integration
36
+ - Island components for interactive UI responses
37
+
38
+ ## Installation
39
+
40
+ ```bash
41
+ pip install optimizely-opal.opal-tools-sdk
42
+ ```
43
+
44
+ Note: While the package is installed as `optimizely-opal.opal-tools-sdk`, you'll still import it in your code as `opal_tools_sdk`:
45
+
46
+ ```python
47
+ # Import using the package name
48
+ from opal_tools_sdk import ToolsService, tool, IslandResponse, IslandConfig
49
+ ```
50
+
51
+ ## Usage
52
+
53
+ ```python
54
+ from opal_tools_sdk import ToolsService, tool
55
+ from pydantic import BaseModel
56
+ from fastapi import FastAPI
57
+
58
+ app = FastAPI()
59
+ tools_service = ToolsService(app)
60
+
61
+ class WeatherParameters(BaseModel):
62
+ location: str
63
+ units: str = "metric"
64
+
65
+ @tool("get_weather", "Gets current weather for a location")
66
+ async def get_weather(parameters: WeatherParameters):
67
+ # Implementation...
68
+ return {"temperature": 22, "condition": "sunny"}
69
+
70
+ # Discovery endpoint is automatically created at /discovery
71
+ ```
72
+
73
+ ## Authentication
74
+
75
+ The SDK provides two ways to require authentication for your tools:
76
+
77
+ ### 1. Using the `@requires_auth` decorator
78
+
79
+ ```python
80
+ from opal_tools_sdk import ToolsService, tool, AuthData
81
+ from opal_tools_sdk.auth import requires_auth
82
+ from pydantic import BaseModel
83
+ from fastapi import FastAPI
84
+ from typing import Optional
85
+
86
+ app = FastAPI()
87
+ tools_service = ToolsService(app)
88
+
89
+ class CalendarParameters(BaseModel):
90
+ date: str
91
+ timezone: str = "UTC"
92
+
93
+ # Single authentication requirement
94
+ @requires_auth(provider="google", scope_bundle="calendar", required=True)
95
+ @tool("get_calendar_events", "Gets calendar events for a date")
96
+ async def get_calendar_events(parameters: CalendarParameters, auth_data: Optional[AuthData] = None):
97
+ # The auth_data parameter contains authentication information
98
+ if auth_data:
99
+ token = auth_data["credentials"]["access_token"]
100
+
101
+ # Use the token to make authenticated requests
102
+ # ...
103
+
104
+ return {"events": ["Meeting at 10:00", "Lunch at 12:00"]}
105
+
106
+ # Multiple authentication requirements (tool can work with either provider)
107
+ @requires_auth(provider="google", scope_bundle="calendar", required=True)
108
+ @requires_auth(provider="microsoft", scope_bundle="outlook", required=True)
109
+ @tool("get_calendar_availability", "Check calendar availability")
110
+ async def get_calendar_availability(parameters: CalendarParameters, auth_data: Optional[AuthData] = None):
111
+ provider = ""
112
+ token = ""
113
+
114
+ if auth_data:
115
+ provider = auth_data["provider"]
116
+ token = auth_data["credentials"]["access_token"]
117
+
118
+ if provider == "google":
119
+ # Use Google Calendar API
120
+ pass
121
+ elif provider == "microsoft":
122
+ # Use Microsoft Outlook API
123
+ pass
124
+
125
+ return {"available": True, "provider_used": provider}
126
+ ```
127
+
128
+ ### 2. Specifying auth requirements in the `@tool` decorator
129
+
130
+ ```python
131
+ @tool(
132
+ "get_email",
133
+ "Gets emails from the user's inbox",
134
+ auth_requirements=[
135
+ {"provider": "google", "scope_bundle": "gmail", "required": True}
136
+ ]
137
+ )
138
+ async def get_email(parameters: EmailParameters, auth_data: Optional[AuthData] = None):
139
+ # Implementation...
140
+ return {"emails": ["Email 1", "Email 2"]}
141
+ ```
142
+
143
+ ## Island Components
144
+
145
+ The SDK includes Island components for creating interactive UI responses that allow users to input data and trigger actions.
146
+
147
+ ### Weather Tool with Interactive Island
148
+
149
+ ```python
150
+ from opal_tools_sdk import ToolsService, tool, IslandResponse, IslandConfig
151
+ from pydantic import BaseModel
152
+ from fastapi import FastAPI
153
+
154
+ app = FastAPI()
155
+ tools_service = ToolsService(app)
156
+
157
+ class WeatherParameters(BaseModel):
158
+ location: str
159
+ units: str = "metric"
160
+
161
+ @tool("get_weather", "Gets current weather for a location")
162
+ async def get_weather(parameters: WeatherParameters):
163
+ # Get weather data (implementation details omitted)
164
+ weather_data = {"temperature": 22, "condition": "sunny", "humidity": 65}
165
+
166
+ # Create an interactive island for weather settings
167
+ island = IslandConfig(
168
+ fields=[
169
+ IslandConfig.Field(
170
+ name="location",
171
+ label="Location",
172
+ type="string",
173
+ value=parameters.location
174
+ ),
175
+ IslandConfig.Field(
176
+ name="units",
177
+ label="Temperature Units",
178
+ type="string",
179
+ value=parameters.units,
180
+ options=["metric", "imperial", "kelvin"]
181
+ ),
182
+ IslandConfig.Field(
183
+ name="current_temp",
184
+ label="Current Temperature",
185
+ type="string",
186
+ value=f"{weather_data['temperature']}°{'C' if parameters.units == 'metric' else 'F'}"
187
+ )
188
+ ],
189
+ actions=[
190
+ IslandConfig.Action(
191
+ name="refresh_weather",
192
+ label="Refresh Weather",
193
+ type="button",
194
+ endpoint="/tools/get_weather",
195
+ operation="update"
196
+ )
197
+ ]
198
+ )
199
+
200
+ return IslandResponse.create([island])
201
+ ```
202
+
203
+ ### Island Components
204
+
205
+ #### IslandConfig.Field
206
+ Fields represent data inputs in the UI:
207
+ - `name`: Programmatic field identifier
208
+ - `label`: Human-readable label
209
+ - `type`: Field type (`"string"`, `"boolean"`, `"json"`)
210
+ - `value`: Current field value (optional)
211
+ - `hidden`: Whether to hide from user (optional, default: False)
212
+ - `options`: Available options for selection (optional)
213
+
214
+ #### IslandConfig.Action
215
+ Actions represent buttons or operations:
216
+ - `name`: Programmatic action identifier
217
+ - `label`: Human-readable button label
218
+ - `type`: UI element type (typically `"button"`)
219
+ - `endpoint`: API endpoint to call
220
+ - `operation`: Operation type (default: `"create"`)
221
+
222
+ #### IslandConfig
223
+ Contains the complete island configuration:
224
+ - `fields`: List of IslandConfig.Field objects
225
+ - `actions`: List of IslandConfig.Action objects
226
+
227
+ #### IslandResponse
228
+ The response wrapper for islands:
229
+ - Use `IslandResponse.create([islands])` to create responses
230
+ - Supports multiple islands per response
231
+
232
+ ## Type Definitions
233
+
234
+ The SDK provides several TypedDict and dataclass definitions for better type safety:
235
+
236
+ ### Authentication Types
237
+ - `AuthData`: TypedDict containing provider and credentials information
238
+ - `Credentials`: TypedDict with access_token, org_sso_id, customer_id, instance_id, and product_sku
239
+ - `AuthRequirement`: Dataclass for specifying authentication requirements
240
+
241
+ ### Execution Environment
242
+ - `Environment`: TypedDict specifying execution mode (`"headless"` or `"interactive"`)
243
+
244
+ ### Parameter Types
245
+ - `ParameterType`: Enum for supported parameter types (string, integer, number, boolean, list, dictionary)
246
+ - `Parameter`: Dataclass for tool parameter definitions
247
+ - `Function`: Dataclass for complete tool function definitions
248
+
249
+ These types are automatically imported when you import from `opal_tools_sdk` and provide better IDE support and type checking.
250
+
251
+ ## Documentation
252
+
253
+ See full documentation for more examples and configuration options.
@@ -0,0 +1,11 @@
1
+ opal_tools_sdk/__init__.py,sha256=0B0c53fyCRMuI_zOfwiePzQk5ion1BSKCqGLVsY0TVk,495
2
+ opal_tools_sdk/_registry.py,sha256=YE3eD4kcS09QDe4RccBYAzXPo9znEU7fblrsB-g3o-Y,67
3
+ opal_tools_sdk/auth.py,sha256=9aMiZv6n6_iu7hQA0sKg4hgNr5DzYFFuP0SWUoZf_Vw,1520
4
+ opal_tools_sdk/decorators.py,sha256=iHal7RElOSCWjqKHIK-Hc4GKOsjuGyUg0_lsQKVQShg,5315
5
+ opal_tools_sdk/logging.py,sha256=krvkhdql2GWh0aCWj5gfqyn5plsyLJ773WzIirK7tkU,1325
6
+ opal_tools_sdk/models.py,sha256=YESFXdIPIFoVqP6OXtswXzHQ1tYMFVSnb8rFVe9cFj4,3403
7
+ opal_tools_sdk/service.py,sha256=wVYe40zEZc1ynIkvowD9APU8BFHQ5fdy3j8qlsdZB1U,6965
8
+ optimizely_opal_opal_tools_sdk-0.1.5.dev0.dist-info/METADATA,sha256=KS830xd4wFliLcbfJBuWSxj49pIH_AfajJJOrm_UG0Q,8085
9
+ optimizely_opal_opal_tools_sdk-0.1.5.dev0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
10
+ optimizely_opal_opal_tools_sdk-0.1.5.dev0.dist-info/top_level.txt,sha256=nCJ5PxF0rgoV6yNJvvuUaZPx4D3EWkl7gpu-6xafH1E,15
11
+ optimizely_opal_opal_tools_sdk-0.1.5.dev0.dist-info/RECORD,,
@@ -1,138 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: optimizely-opal.opal-tools-sdk
3
- Version: 0.1.3.dev0
4
- Summary: SDK for creating Opal-compatible tools services
5
- Home-page: https://github.com/optimizely/opal-tools-sdk
6
- Author: Optimizely
7
- Author-email: Optimizely <opal-team@optimizely.com>
8
- License: MIT
9
- Project-URL: Homepage, https://github.com/optimizely/opal-tools-sdk
10
- Project-URL: Bug Tracker, https://github.com/optimizely/opal-tools-sdk/issues
11
- Keywords: opal,tools,sdk,ai,llm
12
- Classifier: Development Status :: 3 - Alpha
13
- Classifier: Intended Audience :: Developers
14
- Classifier: Programming Language :: Python :: 3.10
15
- Classifier: License :: OSI Approved :: MIT License
16
- Requires-Python: >=3.10
17
- Description-Content-Type: text/markdown
18
- Requires-Dist: fastapi>=0.100.0
19
- Requires-Dist: pydantic>=2.0.0
20
- Requires-Dist: httpx>=0.24.1
21
- Dynamic: author
22
- Dynamic: home-page
23
- Dynamic: requires-python
24
-
25
- # Opal Tools SDK for Python
26
-
27
- This SDK simplifies the creation of tools services compatible with the Opal Tools Management Service.
28
-
29
- ## Features
30
-
31
- - Easy definition of tool functions with decorators
32
- - Automatic generation of discovery endpoints
33
- - Parameter validation and type checking
34
- - Authentication helpers
35
- - FastAPI integration
36
-
37
- ## Installation
38
-
39
- ```bash
40
- pip install optimizely-opal.opal-tools-sdk
41
- ```
42
-
43
- Note: While the package is installed as `optimizely-opal.opal-tools-sdk`, you'll still import it in your code as `opal_tools_sdk`:
44
-
45
- ```python
46
- # Import using the package name
47
- from opal_tools_sdk import ToolsService, tool
48
- ```
49
-
50
- ## Usage
51
-
52
- ```python
53
- from opal_tools_sdk import ToolsService, tool
54
- from pydantic import BaseModel
55
- from fastapi import FastAPI
56
-
57
- app = FastAPI()
58
- tools_service = ToolsService(app)
59
-
60
- class WeatherParameters(BaseModel):
61
- location: str
62
- units: str = "metric"
63
-
64
- @tool("get_weather", "Gets current weather for a location")
65
- async def get_weather(parameters: WeatherParameters):
66
- # Implementation...
67
- return {"temperature": 22, "condition": "sunny"}
68
-
69
- # Discovery endpoint is automatically created at /discovery
70
- ```
71
-
72
- ## Authentication
73
-
74
- The SDK provides two ways to require authentication for your tools:
75
-
76
- ### 1. Using the `@requires_auth` decorator
77
-
78
- ```python
79
- from opal_tools_sdk import ToolsService, tool
80
- from opal_tools_sdk.auth import requires_auth
81
- from pydantic import BaseModel
82
- from fastapi import FastAPI
83
-
84
- app = FastAPI()
85
- tools_service = ToolsService(app)
86
-
87
- class CalendarParameters(BaseModel):
88
- date: str
89
- timezone: str = "UTC"
90
-
91
- # Single authentication requirement
92
- @requires_auth(provider="google", scope_bundle="calendar", required=True)
93
- @tool("get_calendar_events", "Gets calendar events for a date")
94
- async def get_calendar_events(parameters: CalendarParameters, auth_data=None):
95
- # The auth_data parameter contains authentication information
96
- token = auth_data.get("credentials", {}).get("token", "")
97
-
98
- # Use the token to make authenticated requests
99
- # ...
100
-
101
- return {"events": ["Meeting at 10:00", "Lunch at 12:00"]}
102
-
103
- # Multiple authentication requirements (tool can work with either provider)
104
- @requires_auth(provider="google", scope_bundle="calendar", required=True)
105
- @requires_auth(provider="microsoft", scope_bundle="outlook", required=True)
106
- @tool("get_calendar_availability", "Check calendar availability")
107
- async def get_calendar_availability(parameters: CalendarParameters, auth_data=None):
108
- provider = auth_data.get("provider", "")
109
- token = auth_data.get("credentials", {}).get("token", "")
110
-
111
- if provider == "google":
112
- # Use Google Calendar API
113
- pass
114
- elif provider == "microsoft":
115
- # Use Microsoft Outlook API
116
- pass
117
-
118
- return {"available": True, "provider_used": provider}
119
- ```
120
-
121
- ### 2. Specifying auth requirements in the `@tool` decorator
122
-
123
- ```python
124
- @tool(
125
- "get_email",
126
- "Gets emails from the user's inbox",
127
- auth_requirements=[
128
- {"provider": "google", "scope_bundle": "gmail", "required": True}
129
- ]
130
- )
131
- async def get_email(parameters: EmailParameters, auth_data=None):
132
- # Implementation...
133
- return {"emails": ["Email 1", "Email 2"]}
134
- ```
135
-
136
- ## Documentation
137
-
138
- See full documentation for more examples and configuration options.
@@ -1,10 +0,0 @@
1
- opal_tools_sdk/__init__.py,sha256=Eu141xM3QVq1O_BLGZS9m_asU1b_KaOApFtwvaYll6E,170
2
- opal_tools_sdk/_registry.py,sha256=YE3eD4kcS09QDe4RccBYAzXPo9znEU7fblrsB-g3o-Y,67
3
- opal_tools_sdk/auth.py,sha256=9aMiZv6n6_iu7hQA0sKg4hgNr5DzYFFuP0SWUoZf_Vw,1520
4
- opal_tools_sdk/decorators.py,sha256=EE5gjpLRENSZv8cz7wHIIfTCvBh7WqvBaiQtDfMaZig,5268
5
- opal_tools_sdk/models.py,sha256=lVumBjEWbKtkRBzwXt7m5uagqinJ6Y96K9a5Uw39yaM,2092
6
- opal_tools_sdk/service.py,sha256=KBqdBHYJ5wgxky0mSv0NJPbbzVhq4YSafgaTbsndTrE,6914
7
- optimizely_opal_opal_tools_sdk-0.1.3.dev0.dist-info/METADATA,sha256=1pXFL2AInL1gJGBgEwCA5xKu8EoH5pyu0TH_m3y9G8U,4115
8
- optimizely_opal_opal_tools_sdk-0.1.3.dev0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
- optimizely_opal_opal_tools_sdk-0.1.3.dev0.dist-info/top_level.txt,sha256=nCJ5PxF0rgoV6yNJvvuUaZPx4D3EWkl7gpu-6xafH1E,15
10
- optimizely_opal_opal_tools_sdk-0.1.3.dev0.dist-info/RECORD,,