optimizely-opal.opal-tools-sdk 0.1.2.dev0__tar.gz → 0.1.4.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.

Potentially problematic release.


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

Files changed (18) hide show
  1. {optimizely_opal_opal_tools_sdk-0.1.2.dev0 → optimizely_opal_opal_tools_sdk-0.1.4.dev0}/PKG-INFO +1 -1
  2. optimizely_opal_opal_tools_sdk-0.1.4.dev0/opal_tools_sdk/__init__.py +7 -0
  3. {optimizely_opal_opal_tools_sdk-0.1.2.dev0 → optimizely_opal_opal_tools_sdk-0.1.4.dev0}/opal_tools_sdk/decorators.py +9 -8
  4. optimizely_opal_opal_tools_sdk-0.1.4.dev0/opal_tools_sdk/logging.py +37 -0
  5. {optimizely_opal_opal_tools_sdk-0.1.2.dev0 → optimizely_opal_opal_tools_sdk-0.1.4.dev0}/opal_tools_sdk/models.py +6 -6
  6. {optimizely_opal_opal_tools_sdk-0.1.2.dev0 → optimizely_opal_opal_tools_sdk-0.1.4.dev0}/opal_tools_sdk/service.py +10 -10
  7. {optimizely_opal_opal_tools_sdk-0.1.2.dev0 → optimizely_opal_opal_tools_sdk-0.1.4.dev0}/optimizely_opal.opal_tools_sdk.egg-info/PKG-INFO +1 -1
  8. {optimizely_opal_opal_tools_sdk-0.1.2.dev0 → optimizely_opal_opal_tools_sdk-0.1.4.dev0}/optimizely_opal.opal_tools_sdk.egg-info/SOURCES.txt +1 -0
  9. {optimizely_opal_opal_tools_sdk-0.1.2.dev0 → optimizely_opal_opal_tools_sdk-0.1.4.dev0}/pyproject.toml +1 -1
  10. {optimizely_opal_opal_tools_sdk-0.1.2.dev0 → optimizely_opal_opal_tools_sdk-0.1.4.dev0}/setup.py +1 -1
  11. optimizely_opal_opal_tools_sdk-0.1.2.dev0/opal_tools_sdk/__init__.py +0 -6
  12. {optimizely_opal_opal_tools_sdk-0.1.2.dev0 → optimizely_opal_opal_tools_sdk-0.1.4.dev0}/README.md +0 -0
  13. {optimizely_opal_opal_tools_sdk-0.1.2.dev0 → optimizely_opal_opal_tools_sdk-0.1.4.dev0}/opal_tools_sdk/_registry.py +0 -0
  14. {optimizely_opal_opal_tools_sdk-0.1.2.dev0 → optimizely_opal_opal_tools_sdk-0.1.4.dev0}/opal_tools_sdk/auth.py +0 -0
  15. {optimizely_opal_opal_tools_sdk-0.1.2.dev0 → optimizely_opal_opal_tools_sdk-0.1.4.dev0}/optimizely_opal.opal_tools_sdk.egg-info/dependency_links.txt +0 -0
  16. {optimizely_opal_opal_tools_sdk-0.1.2.dev0 → optimizely_opal_opal_tools_sdk-0.1.4.dev0}/optimizely_opal.opal_tools_sdk.egg-info/requires.txt +0 -0
  17. {optimizely_opal_opal_tools_sdk-0.1.2.dev0 → optimizely_opal_opal_tools_sdk-0.1.4.dev0}/optimizely_opal.opal_tools_sdk.egg-info/top_level.txt +0 -0
  18. {optimizely_opal_opal_tools_sdk-0.1.2.dev0 → optimizely_opal_opal_tools_sdk-0.1.4.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.2.dev0
3
+ Version: 0.1.4.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
@@ -0,0 +1,7 @@
1
+ from .service import ToolsService
2
+ from .decorators import tool
3
+ from .auth import requires_auth
4
+ from .logging import register_logger_factory
5
+
6
+ __version__ = "0.1.0"
7
+ __all__ = ["ToolsService", "tool", "requires_auth", "register_logger_factory"]
@@ -1,13 +1,14 @@
1
1
  import inspect
2
2
  import re
3
3
  import logging
4
- from typing import Callable, Any, List, Dict, Type, get_type_hints, Optional, Union
4
+ from typing import Callable, Any, List, Dict, Type, get_origin, get_type_hints, Optional, Union
5
5
  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.
@@ -69,9 +70,9 @@ def tool(name: str, description: str, auth_requirements: Optional[List[Dict[str,
69
70
  param_type = ParameterType.number
70
71
  elif field_type == bool:
71
72
  param_type = ParameterType.boolean
72
- elif field_type == list or field_type == List:
73
+ elif get_origin(field_type) is list:
73
74
  param_type = ParameterType.list
74
- elif field_type == dict or field_type == Dict:
75
+ elif get_origin(field_type) is dict:
75
76
  param_type = ParameterType.dictionary
76
77
 
77
78
  # Determine if required
@@ -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
@@ -8,7 +8,7 @@ class ParameterType(str, Enum):
8
8
  integer = "integer"
9
9
  number = "number"
10
10
  boolean = "boolean"
11
- list = "list" # Changed to match main service expectation
11
+ list = "array" # Changed to match main service expectation
12
12
  dictionary = "object" # Standard JSON schema type
13
13
 
14
14
  @dataclass
@@ -18,7 +18,7 @@ class Parameter:
18
18
  param_type: ParameterType
19
19
  description: str
20
20
  required: bool
21
-
21
+
22
22
  def to_dict(self) -> Dict[str, Any]:
23
23
  """Convert to dictionary for the discovery endpoint."""
24
24
  return {
@@ -34,7 +34,7 @@ class AuthRequirement:
34
34
  provider: str # e.g., "google", "microsoft"
35
35
  scope_bundle: str # e.g., "calendar", "drive"
36
36
  required: bool = True
37
-
37
+
38
38
  def to_dict(self) -> Dict[str, Any]:
39
39
  """Convert to dictionary for the discovery endpoint."""
40
40
  return {
@@ -52,7 +52,7 @@ class Function:
52
52
  endpoint: str
53
53
  auth_requirements: Optional[List[AuthRequirement]] = None
54
54
  http_method: str = "POST"
55
-
55
+
56
56
  def to_dict(self) -> Dict[str, Any]:
57
57
  """Convert to dictionary for the discovery endpoint."""
58
58
  result = {
@@ -62,8 +62,8 @@ class Function:
62
62
  "endpoint": self.endpoint,
63
63
  "http_method": self.http_method
64
64
  }
65
-
65
+
66
66
  if self.auth_requirements:
67
67
  result["auth_requirements"] = [auth.to_dict() for auth in self.auth_requirements]
68
-
68
+
69
69
  return result
@@ -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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: optimizely-opal.opal-tools-sdk
3
- Version: 0.1.2.dev0
3
+ Version: 0.1.4.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
@@ -5,6 +5,7 @@ opal_tools_sdk/__init__.py
5
5
  opal_tools_sdk/_registry.py
6
6
  opal_tools_sdk/auth.py
7
7
  opal_tools_sdk/decorators.py
8
+ opal_tools_sdk/logging.py
8
9
  opal_tools_sdk/models.py
9
10
  opal_tools_sdk/service.py
10
11
  optimizely_opal.opal_tools_sdk.egg-info/PKG-INFO
@@ -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.2-dev"
7
+ version = "0.1.4-dev"
8
8
  description = "SDK for creating Opal-compatible tools services"
9
9
  authors = [
10
10
  {name = "Optimizely", email = "opal-team@optimizely.com"}
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="optimizely-opal.opal-tools-sdk",
5
- version="0.1.1-dev",
5
+ version="0.1.4-dev",
6
6
  packages=find_packages(),
7
7
  install_requires=[
8
8
  "fastapi>=0.100.0",
@@ -1,6 +0,0 @@
1
- from .service import ToolsService
2
- from .decorators import tool
3
- from .auth import requires_auth
4
-
5
- __version__ = "0.1.0"
6
- __all__ = ["ToolsService", "tool", "requires_auth"]