airbyte-agent-zendesk-support 0.18.29__tar.gz → 0.18.37__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.
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/CHANGELOG.md +40 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/PKG-INFO +4 -8
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/README.md +2 -2
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/__init__.py +0 -3
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/connector_model_loader.py +10 -2
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/constants.py +1 -1
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/executor/local_executor.py +54 -4
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/extensions.py +42 -3
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/introspection.py +2 -2
- airbyte_agent_zendesk_support-0.18.37/airbyte_agent_zendesk_support/_vendored/connector_sdk/observability/config.py +179 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/observability/session.py +35 -28
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/schema/components.py +2 -1
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/schema/operations.py +1 -1
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/schema/security.py +10 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/telemetry/events.py +2 -1
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/telemetry/tracker.py +3 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/types.py +5 -1
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/connector.py +45 -7
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/connector_model.py +2 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/types.py +1 -1
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/pyproject.toml +2 -6
- airbyte_agent_zendesk_support-0.18.29/airbyte_agent_zendesk_support/_vendored/connector_sdk/decorators.py +0 -128
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/.gitignore +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/REFERENCE.md +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/__init__.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/__init__.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/auth_strategies.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/auth_template.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/cloud_utils/__init__.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/cloud_utils/client.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/exceptions.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/executor/__init__.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/executor/hosted_executor.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/executor/models.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/http/__init__.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/http/adapters/__init__.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/http/adapters/httpx_adapter.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/http/config.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/http/exceptions.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/http/protocols.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/http/response.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/http_client.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/logging/__init__.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/logging/logger.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/logging/types.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/observability/__init__.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/observability/models.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/observability/redactor.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/performance/__init__.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/performance/instrumentation.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/performance/metrics.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/schema/__init__.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/schema/base.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/schema/connector.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/schema/extensions.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/secrets.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/telemetry/__init__.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/telemetry/config.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/utils.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/_vendored/connector_sdk/validation.py +0 -0
- {airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/airbyte_agent_zendesk_support/models.py +0 -0
{airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/CHANGELOG.md
RENAMED
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
# Zendesk Support changelog
|
|
2
2
|
|
|
3
|
+
## [0.18.37] - 2026-01-14
|
|
4
|
+
- Updated connector definition (YAML version 0.1.4)
|
|
5
|
+
- Source commit: e6285db5
|
|
6
|
+
- SDK version: 0.1.0
|
|
7
|
+
|
|
8
|
+
## [0.18.36] - 2026-01-14
|
|
9
|
+
- Updated connector definition (YAML version 0.1.4)
|
|
10
|
+
- Source commit: 31de238d
|
|
11
|
+
- SDK version: 0.1.0
|
|
12
|
+
|
|
13
|
+
## [0.18.35] - 2026-01-13
|
|
14
|
+
- Updated connector definition (YAML version 0.1.4)
|
|
15
|
+
- Source commit: e80a226e
|
|
16
|
+
- SDK version: 0.1.0
|
|
17
|
+
|
|
18
|
+
## [0.18.34] - 2026-01-13
|
|
19
|
+
- Updated connector definition (YAML version 0.1.4)
|
|
20
|
+
- Source commit: 78b1be67
|
|
21
|
+
- SDK version: 0.1.0
|
|
22
|
+
|
|
23
|
+
## [0.18.33] - 2026-01-11
|
|
24
|
+
- Updated connector definition (YAML version 0.1.4)
|
|
25
|
+
- Source commit: e519b73d
|
|
26
|
+
- SDK version: 0.1.0
|
|
27
|
+
|
|
28
|
+
## [0.18.32] - 2026-01-09
|
|
29
|
+
- Updated connector definition (YAML version 0.1.4)
|
|
30
|
+
- Source commit: 3c7bfdfd
|
|
31
|
+
- SDK version: 0.1.0
|
|
32
|
+
|
|
33
|
+
## [0.18.31] - 2026-01-09
|
|
34
|
+
- Updated connector definition (YAML version 0.1.4)
|
|
35
|
+
- Source commit: 3bcb33e8
|
|
36
|
+
- SDK version: 0.1.0
|
|
37
|
+
|
|
38
|
+
## [0.18.30] - 2026-01-09
|
|
39
|
+
- Updated connector definition (YAML version 0.1.4)
|
|
40
|
+
- Source commit: da9b741b
|
|
41
|
+
- SDK version: 0.1.0
|
|
42
|
+
|
|
3
43
|
## [0.18.29] - 2026-01-07
|
|
4
44
|
- Updated connector definition (YAML version 0.1.4)
|
|
5
45
|
- Source commit: d023e05f
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: airbyte-agent-zendesk-support
|
|
3
|
-
Version: 0.18.
|
|
3
|
+
Version: 0.18.37
|
|
4
4
|
Summary: Airbyte Zendesk-Support Connector for AI platforms
|
|
5
5
|
Project-URL: Homepage, https://github.com/airbytehq/airbyte-embedded
|
|
6
6
|
Project-URL: Documentation, https://github.com/airbytehq/airbyte-embedded/tree/main/integrations
|
|
@@ -13,13 +13,9 @@ Classifier: Development Status :: 3 - Alpha
|
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
14
|
Classifier: License :: Other/Proprietary License
|
|
15
15
|
Classifier: Programming Language :: Python :: 3
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
20
16
|
Classifier: Programming Language :: Python :: 3.13
|
|
21
17
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
-
Requires-Python: >=3.
|
|
18
|
+
Requires-Python: >=3.13
|
|
23
19
|
Requires-Dist: httpx>=0.24.0
|
|
24
20
|
Requires-Dist: jinja2>=3.0.0
|
|
25
21
|
Requires-Dist: jsonpath-ng>=1.6.1
|
|
@@ -141,6 +137,6 @@ For the service's official API docs, see the [Zendesk-Support API reference](htt
|
|
|
141
137
|
|
|
142
138
|
## Version information
|
|
143
139
|
|
|
144
|
-
- **Package version:** 0.18.
|
|
140
|
+
- **Package version:** 0.18.37
|
|
145
141
|
- **Connector version:** 0.1.4
|
|
146
|
-
- **Generated with Connector SDK commit SHA:**
|
|
142
|
+
- **Generated with Connector SDK commit SHA:** e6285db53fdc1d0dd43d30ef752a009226e47d48
|
|
@@ -107,6 +107,6 @@ For the service's official API docs, see the [Zendesk-Support API reference](htt
|
|
|
107
107
|
|
|
108
108
|
## Version information
|
|
109
109
|
|
|
110
|
-
- **Package version:** 0.18.
|
|
110
|
+
- **Package version:** 0.18.37
|
|
111
111
|
- **Connector version:** 0.1.4
|
|
112
|
-
- **Generated with Connector SDK commit SHA:**
|
|
112
|
+
- **Generated with Connector SDK commit SHA:** e6285db53fdc1d0dd43d30ef752a009226e47d48
|
|
@@ -13,7 +13,6 @@ from __future__ import annotations
|
|
|
13
13
|
from .auth_strategies import AuthStrategy
|
|
14
14
|
from .connector_model_loader import load_connector_model
|
|
15
15
|
from .constants import SDK_VERSION
|
|
16
|
-
from .decorators import airbyte_description
|
|
17
16
|
from .exceptions import (
|
|
18
17
|
AuthenticationError,
|
|
19
18
|
HTTPClientError,
|
|
@@ -80,6 +79,4 @@ __all__ = [
|
|
|
80
79
|
"instrument",
|
|
81
80
|
# Utilities
|
|
82
81
|
"save_download",
|
|
83
|
-
# Decorators for AI integration
|
|
84
|
-
"airbyte_description",
|
|
85
82
|
]
|
|
@@ -393,16 +393,24 @@ def convert_openapi_to_connector_model(spec: OpenAPIConnector) -> ConnectorModel
|
|
|
393
393
|
for entity_name, endpoints_dict in entities_map.items():
|
|
394
394
|
actions = list(endpoints_dict.keys())
|
|
395
395
|
|
|
396
|
-
# Get schema from components if available
|
|
396
|
+
# Get schema and stream_name from components if available
|
|
397
397
|
schema = None
|
|
398
|
+
entity_stream_name = None
|
|
398
399
|
if spec.components:
|
|
399
400
|
# Look for a schema matching the entity name
|
|
400
401
|
for schema_name, schema_def in spec.components.schemas.items():
|
|
401
402
|
if schema_def.x_airbyte_entity_name == entity_name or schema_name.lower() == entity_name.lower():
|
|
402
403
|
schema = schema_def.model_dump(by_alias=True)
|
|
404
|
+
entity_stream_name = schema_def.x_airbyte_stream_name
|
|
403
405
|
break
|
|
404
406
|
|
|
405
|
-
entity = EntityDefinition(
|
|
407
|
+
entity = EntityDefinition(
|
|
408
|
+
name=entity_name,
|
|
409
|
+
stream_name=entity_stream_name,
|
|
410
|
+
actions=actions,
|
|
411
|
+
endpoints=endpoints_dict,
|
|
412
|
+
schema=schema,
|
|
413
|
+
)
|
|
406
414
|
entities.append(entity)
|
|
407
415
|
|
|
408
416
|
# Extract retry config from x-airbyte-retry-config extension
|
|
@@ -844,10 +844,58 @@ class LocalExecutor:
|
|
|
844
844
|
return self._extract_body(endpoint.body_fields, params)
|
|
845
845
|
return None
|
|
846
846
|
|
|
847
|
+
def _flatten_form_data(self, data: dict[str, Any], parent_key: str = "") -> dict[str, Any]:
|
|
848
|
+
"""Flatten nested dict/list structures into bracket notation for form encoding.
|
|
849
|
+
|
|
850
|
+
Stripe and similar APIs require nested arrays/objects to be encoded using bracket
|
|
851
|
+
notation when using application/x-www-form-urlencoded content type.
|
|
852
|
+
|
|
853
|
+
Args:
|
|
854
|
+
data: Nested dict with arrays/objects to flatten
|
|
855
|
+
parent_key: Parent key for nested structures (used in recursion)
|
|
856
|
+
|
|
857
|
+
Returns:
|
|
858
|
+
Flattened dict with bracket notation keys
|
|
859
|
+
|
|
860
|
+
Examples:
|
|
861
|
+
>>> _flatten_form_data({"items": [{"price": "p1", "qty": 1}]})
|
|
862
|
+
{"items[0][price]": "p1", "items[0][qty]": 1}
|
|
863
|
+
|
|
864
|
+
>>> _flatten_form_data({"customer": "cus_123", "metadata": {"key": "value"}})
|
|
865
|
+
{"customer": "cus_123", "metadata[key]": "value"}
|
|
866
|
+
"""
|
|
867
|
+
flattened = {}
|
|
868
|
+
|
|
869
|
+
for key, value in data.items():
|
|
870
|
+
new_key = f"{parent_key}[{key}]" if parent_key else key
|
|
871
|
+
|
|
872
|
+
if isinstance(value, dict):
|
|
873
|
+
# Recursively flatten nested dicts
|
|
874
|
+
flattened.update(self._flatten_form_data(value, new_key))
|
|
875
|
+
elif isinstance(value, list):
|
|
876
|
+
# Flatten arrays with indexed bracket notation
|
|
877
|
+
for i, item in enumerate(value):
|
|
878
|
+
indexed_key = f"{new_key}[{i}]"
|
|
879
|
+
if isinstance(item, dict):
|
|
880
|
+
# Nested dict in array - recurse
|
|
881
|
+
flattened.update(self._flatten_form_data(item, indexed_key))
|
|
882
|
+
elif isinstance(item, list):
|
|
883
|
+
# Nested list in array - recurse
|
|
884
|
+
flattened.update(self._flatten_form_data({str(i): item}, new_key))
|
|
885
|
+
else:
|
|
886
|
+
# Primitive value in array
|
|
887
|
+
flattened[indexed_key] = item
|
|
888
|
+
else:
|
|
889
|
+
# Primitive value - add directly
|
|
890
|
+
flattened[new_key] = value
|
|
891
|
+
|
|
892
|
+
return flattened
|
|
893
|
+
|
|
847
894
|
def _determine_request_format(self, endpoint: EndpointDefinition, body: dict[str, Any] | None) -> dict[str, Any]:
|
|
848
895
|
"""Determine json/data parameters for HTTP request.
|
|
849
896
|
|
|
850
897
|
GraphQL always uses JSON, regardless of content_type setting.
|
|
898
|
+
For form-encoded requests, nested structures are flattened into bracket notation.
|
|
851
899
|
|
|
852
900
|
Args:
|
|
853
901
|
endpoint: Endpoint definition
|
|
@@ -864,7 +912,9 @@ class LocalExecutor:
|
|
|
864
912
|
if is_graphql or endpoint.content_type.value == "application/json":
|
|
865
913
|
return {"json": body}
|
|
866
914
|
elif endpoint.content_type.value == "application/x-www-form-urlencoded":
|
|
867
|
-
|
|
915
|
+
# Flatten nested structures for form encoding
|
|
916
|
+
flattened_body = self._flatten_form_data(body)
|
|
917
|
+
return {"data": flattened_body}
|
|
868
918
|
|
|
869
919
|
return {}
|
|
870
920
|
|
|
@@ -1079,7 +1129,7 @@ class LocalExecutor:
|
|
|
1079
1129
|
if not action:
|
|
1080
1130
|
return response_data
|
|
1081
1131
|
|
|
1082
|
-
is_array_action = action in (Action.LIST, Action.
|
|
1132
|
+
is_array_action = action in (Action.LIST, Action.API_SEARCH)
|
|
1083
1133
|
|
|
1084
1134
|
try:
|
|
1085
1135
|
# Parse and apply JSONPath expression
|
|
@@ -1209,7 +1259,7 @@ class LocalExecutor:
|
|
|
1209
1259
|
|
|
1210
1260
|
|
|
1211
1261
|
class _StandardOperationHandler:
|
|
1212
|
-
"""Handler for standard REST operations (GET, LIST, CREATE, UPDATE, DELETE,
|
|
1262
|
+
"""Handler for standard REST operations (GET, LIST, CREATE, UPDATE, DELETE, API_SEARCH, AUTHORIZE)."""
|
|
1213
1263
|
|
|
1214
1264
|
def __init__(self, context: _OperationContext):
|
|
1215
1265
|
self.ctx = context
|
|
@@ -1222,7 +1272,7 @@ class _StandardOperationHandler:
|
|
|
1222
1272
|
Action.CREATE,
|
|
1223
1273
|
Action.UPDATE,
|
|
1224
1274
|
Action.DELETE,
|
|
1225
|
-
Action.
|
|
1275
|
+
Action.API_SEARCH,
|
|
1226
1276
|
Action.AUTHORIZE,
|
|
1227
1277
|
}
|
|
1228
1278
|
|
|
@@ -159,6 +159,38 @@ Example:
|
|
|
159
159
|
```
|
|
160
160
|
"""
|
|
161
161
|
|
|
162
|
+
AIRBYTE_STREAM_NAME = "x-airbyte-stream-name"
|
|
163
|
+
"""
|
|
164
|
+
Extension: x-airbyte-stream-name
|
|
165
|
+
Location: Schema object (in components.schemas)
|
|
166
|
+
Type: string
|
|
167
|
+
Required: No
|
|
168
|
+
|
|
169
|
+
Description:
|
|
170
|
+
Specifies the Airbyte stream name for cache lookup purposes. This maps the entity
|
|
171
|
+
to the corresponding Airbyte stream, enabling cache-based data retrieval. When
|
|
172
|
+
specified, the EntityDefinition.stream_name field will be populated with this value.
|
|
173
|
+
|
|
174
|
+
This extension is placed on Schema objects alongside x-airbyte-entity-name, following
|
|
175
|
+
the same pattern. The stream name is an entity-level property (not operation-level)
|
|
176
|
+
since an entity maps to exactly one Airbyte stream.
|
|
177
|
+
|
|
178
|
+
Example:
|
|
179
|
+
```yaml
|
|
180
|
+
components:
|
|
181
|
+
schemas:
|
|
182
|
+
Customer:
|
|
183
|
+
type: object
|
|
184
|
+
x-airbyte-entity-name: customers
|
|
185
|
+
x-airbyte-stream-name: customers
|
|
186
|
+
properties:
|
|
187
|
+
id:
|
|
188
|
+
type: string
|
|
189
|
+
name:
|
|
190
|
+
type: string
|
|
191
|
+
```
|
|
192
|
+
"""
|
|
193
|
+
|
|
162
194
|
AIRBYTE_TOKEN_PATH = "x-airbyte-token-path"
|
|
163
195
|
"""
|
|
164
196
|
Extension: x-airbyte-token-path
|
|
@@ -495,8 +527,8 @@ class ActionType(str, Enum):
|
|
|
495
527
|
DELETE = "delete"
|
|
496
528
|
"""Delete a record"""
|
|
497
529
|
|
|
498
|
-
|
|
499
|
-
"""Search for records matching specific query criteria"""
|
|
530
|
+
API_SEARCH = "api_search"
|
|
531
|
+
"""Search for records matching specific query criteria via API"""
|
|
500
532
|
|
|
501
533
|
DOWNLOAD = "download"
|
|
502
534
|
"""Download file content from a URL specified in the metadata response"""
|
|
@@ -514,7 +546,7 @@ class BodyType(str, Enum):
|
|
|
514
546
|
|
|
515
547
|
|
|
516
548
|
# Type alias for use in Pydantic models
|
|
517
|
-
ActionTypeLiteral = Literal["get", "list", "create", "update", "delete", "
|
|
549
|
+
ActionTypeLiteral = Literal["get", "list", "create", "update", "delete", "api_search", "download"]
|
|
518
550
|
|
|
519
551
|
|
|
520
552
|
# =============================================================================
|
|
@@ -548,6 +580,7 @@ def get_all_extension_names() -> list[str]:
|
|
|
548
580
|
AIRBYTE_ENTITY,
|
|
549
581
|
AIRBYTE_ACTION,
|
|
550
582
|
AIRBYTE_ENTITY_NAME,
|
|
583
|
+
AIRBYTE_STREAM_NAME,
|
|
551
584
|
AIRBYTE_TOKEN_PATH,
|
|
552
585
|
AIRBYTE_BODY_TYPE,
|
|
553
586
|
AIRBYTE_PATH_OVERRIDE,
|
|
@@ -594,6 +627,12 @@ EXTENSION_REGISTRY = {
|
|
|
594
627
|
"required": False,
|
|
595
628
|
"description": "Links schema to an entity/stream",
|
|
596
629
|
},
|
|
630
|
+
AIRBYTE_STREAM_NAME: {
|
|
631
|
+
"location": "schema",
|
|
632
|
+
"type": "string",
|
|
633
|
+
"required": False,
|
|
634
|
+
"description": "Maps entity to Airbyte stream for cache lookup",
|
|
635
|
+
},
|
|
597
636
|
AIRBYTE_TOKEN_PATH: {
|
|
598
637
|
"location": "securityScheme",
|
|
599
638
|
"type": "string",
|
|
@@ -203,7 +203,7 @@ def generate_tool_description(model: ConnectorModelProtocol) -> str:
|
|
|
203
203
|
- Response structure documentation with pagination hints
|
|
204
204
|
- Example questions if available in the OpenAPI spec
|
|
205
205
|
|
|
206
|
-
This is used by the
|
|
206
|
+
This is used by the Connector.describe class method decorator to populate
|
|
207
207
|
function docstrings for AI framework integration.
|
|
208
208
|
|
|
209
209
|
Args:
|
|
@@ -232,7 +232,7 @@ def generate_tool_description(model: ConnectorModelProtocol) -> str:
|
|
|
232
232
|
# Response structure (brief, includes pagination hint)
|
|
233
233
|
lines.append("")
|
|
234
234
|
lines.append("RESPONSE STRUCTURE:")
|
|
235
|
-
lines.append(" - list/
|
|
235
|
+
lines.append(" - list/api_search: {data: [...], meta: {has_more: bool}}")
|
|
236
236
|
lines.append(" - get: Returns entity directly (no envelope)")
|
|
237
237
|
lines.append(" To paginate: pass starting_after=<last_id> while has_more is true")
|
|
238
238
|
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""Unified configuration for connector-sdk."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
import tempfile
|
|
6
|
+
import uuid
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any, Optional
|
|
10
|
+
|
|
11
|
+
import yaml
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
# New config location
|
|
16
|
+
CONFIG_DIR = Path.home() / ".airbyte" / "connector-sdk"
|
|
17
|
+
CONFIG_PATH = CONFIG_DIR / "config.yaml"
|
|
18
|
+
|
|
19
|
+
# Legacy file locations (for migration)
|
|
20
|
+
LEGACY_USER_ID_PATH = Path.home() / ".airbyte" / "ai_sdk_user_id"
|
|
21
|
+
LEGACY_INTERNAL_MARKER_PATH = Path.home() / ".airbyte" / "internal_user"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class SDKConfig:
|
|
26
|
+
"""Connector SDK configuration."""
|
|
27
|
+
|
|
28
|
+
user_id: str = field(default_factory=lambda: str(uuid.uuid4()))
|
|
29
|
+
is_internal_user: bool = False
|
|
30
|
+
|
|
31
|
+
def to_dict(self) -> dict[str, Any]:
|
|
32
|
+
"""Convert to dictionary for YAML serialization."""
|
|
33
|
+
return {
|
|
34
|
+
"user_id": self.user_id,
|
|
35
|
+
"is_internal_user": self.is_internal_user,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _delete_legacy_files() -> None:
|
|
40
|
+
"""
|
|
41
|
+
Delete legacy config files after successful migration.
|
|
42
|
+
|
|
43
|
+
Removes:
|
|
44
|
+
- ~/.airbyte/ai_sdk_user_id
|
|
45
|
+
- ~/.airbyte/internal_user
|
|
46
|
+
"""
|
|
47
|
+
for legacy_path in [LEGACY_USER_ID_PATH, LEGACY_INTERNAL_MARKER_PATH]:
|
|
48
|
+
try:
|
|
49
|
+
if legacy_path.exists():
|
|
50
|
+
legacy_path.unlink()
|
|
51
|
+
logger.debug(f"Deleted legacy config file: {legacy_path}")
|
|
52
|
+
except Exception as e:
|
|
53
|
+
logger.debug(f"Could not delete legacy file {legacy_path}: {e}")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _migrate_legacy_config() -> Optional[SDKConfig]:
|
|
57
|
+
"""
|
|
58
|
+
Migrate from legacy file-based config to new YAML format.
|
|
59
|
+
|
|
60
|
+
Reads from:
|
|
61
|
+
- ~/.airbyte/ai_sdk_user_id (user_id)
|
|
62
|
+
- ~/.airbyte/internal_user (is_internal_user marker)
|
|
63
|
+
|
|
64
|
+
Returns SDKConfig if migration was successful, None otherwise.
|
|
65
|
+
"""
|
|
66
|
+
user_id = None
|
|
67
|
+
is_internal = False
|
|
68
|
+
|
|
69
|
+
# Try to read legacy user_id
|
|
70
|
+
try:
|
|
71
|
+
if LEGACY_USER_ID_PATH.exists():
|
|
72
|
+
user_id = LEGACY_USER_ID_PATH.read_text().strip()
|
|
73
|
+
if not user_id:
|
|
74
|
+
user_id = None
|
|
75
|
+
except Exception:
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
# Check legacy internal_user marker
|
|
79
|
+
try:
|
|
80
|
+
is_internal = LEGACY_INTERNAL_MARKER_PATH.exists()
|
|
81
|
+
except Exception:
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
if user_id or is_internal:
|
|
85
|
+
return SDKConfig(
|
|
86
|
+
user_id=user_id or str(uuid.uuid4()),
|
|
87
|
+
is_internal_user=is_internal,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def load_config() -> SDKConfig:
|
|
94
|
+
"""
|
|
95
|
+
Load SDK configuration from config file.
|
|
96
|
+
|
|
97
|
+
Checks (in order):
|
|
98
|
+
1. New config file at ~/.airbyte/connector-sdk/config.yaml
|
|
99
|
+
2. Legacy files at ~/.airbyte/ai_sdk_user_id and ~/.airbyte/internal_user
|
|
100
|
+
3. Creates new config with generated user_id if nothing exists
|
|
101
|
+
|
|
102
|
+
Environment variable AIRBYTE_INTERNAL_USER can override is_internal_user.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
SDKConfig with user_id and is_internal_user
|
|
106
|
+
"""
|
|
107
|
+
config = None
|
|
108
|
+
|
|
109
|
+
# Try to load from new config file
|
|
110
|
+
try:
|
|
111
|
+
if CONFIG_PATH.exists():
|
|
112
|
+
content = CONFIG_PATH.read_text()
|
|
113
|
+
data = yaml.safe_load(content) or {}
|
|
114
|
+
config = SDKConfig(
|
|
115
|
+
user_id=data.get("user_id", str(uuid.uuid4())),
|
|
116
|
+
is_internal_user=data.get("is_internal_user", False),
|
|
117
|
+
)
|
|
118
|
+
# Always clean up legacy files if they exist (even if new config exists)
|
|
119
|
+
_delete_legacy_files()
|
|
120
|
+
except Exception as e:
|
|
121
|
+
logger.debug(f"Could not load config from {CONFIG_PATH}: {e}")
|
|
122
|
+
|
|
123
|
+
# Try to migrate from legacy files if new config doesn't exist
|
|
124
|
+
if config is None:
|
|
125
|
+
config = _migrate_legacy_config()
|
|
126
|
+
if config:
|
|
127
|
+
# Save migrated config to new location
|
|
128
|
+
try:
|
|
129
|
+
save_config(config)
|
|
130
|
+
logger.debug("Migrated legacy config to new location")
|
|
131
|
+
# Delete legacy files after successful migration
|
|
132
|
+
_delete_legacy_files()
|
|
133
|
+
except Exception as e:
|
|
134
|
+
logger.debug(f"Could not save migrated config: {e}")
|
|
135
|
+
|
|
136
|
+
# Create new config if nothing exists
|
|
137
|
+
if config is None:
|
|
138
|
+
config = SDKConfig()
|
|
139
|
+
try:
|
|
140
|
+
save_config(config)
|
|
141
|
+
except Exception as e:
|
|
142
|
+
logger.debug(f"Could not save new config: {e}")
|
|
143
|
+
|
|
144
|
+
# Environment variable override for is_internal_user
|
|
145
|
+
env_value = os.getenv("AIRBYTE_INTERNAL_USER", "").lower()
|
|
146
|
+
if env_value in ("true", "1", "yes"):
|
|
147
|
+
config.is_internal_user = True
|
|
148
|
+
elif env_value:
|
|
149
|
+
# Any other non-empty value (including "false", "0", "no") defaults to False
|
|
150
|
+
config.is_internal_user = False
|
|
151
|
+
|
|
152
|
+
return config
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def save_config(config: SDKConfig) -> None:
|
|
156
|
+
"""
|
|
157
|
+
Save SDK configuration to config file.
|
|
158
|
+
|
|
159
|
+
Creates the config directory if it doesn't exist.
|
|
160
|
+
Uses atomic writes to prevent corruption from concurrent access.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
config: SDKConfig to save
|
|
164
|
+
"""
|
|
165
|
+
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
166
|
+
|
|
167
|
+
# Use atomic write: write to temp file then rename (atomic on POSIX)
|
|
168
|
+
fd, temp_path = tempfile.mkstemp(dir=CONFIG_DIR, suffix=".tmp")
|
|
169
|
+
try:
|
|
170
|
+
with os.fdopen(fd, "w") as f:
|
|
171
|
+
yaml.dump(config.to_dict(), f, default_flow_style=False)
|
|
172
|
+
os.rename(temp_path, CONFIG_PATH)
|
|
173
|
+
except Exception:
|
|
174
|
+
# Clean up temp file on failure
|
|
175
|
+
try:
|
|
176
|
+
os.unlink(temp_path)
|
|
177
|
+
except OSError:
|
|
178
|
+
pass
|
|
179
|
+
raise
|
|
@@ -3,46 +3,40 @@
|
|
|
3
3
|
import logging
|
|
4
4
|
import uuid
|
|
5
5
|
from datetime import UTC, datetime
|
|
6
|
-
from pathlib import Path
|
|
7
6
|
from typing import Any, Dict, Optional
|
|
8
7
|
|
|
8
|
+
from .config import SDKConfig, load_config
|
|
9
|
+
|
|
9
10
|
logger = logging.getLogger(__name__)
|
|
10
11
|
|
|
12
|
+
# Cache the config at module level to avoid repeated reads
|
|
13
|
+
_cached_config: Optional[SDKConfig] = None
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _get_config() -> SDKConfig:
|
|
17
|
+
"""Get cached SDK config or load from file."""
|
|
18
|
+
global _cached_config
|
|
19
|
+
if _cached_config is None:
|
|
20
|
+
_cached_config = load_config()
|
|
21
|
+
return _cached_config
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _clear_config_cache() -> None:
|
|
25
|
+
"""Clear the cached config. Used for testing."""
|
|
26
|
+
global _cached_config
|
|
27
|
+
_cached_config = None
|
|
28
|
+
|
|
11
29
|
|
|
12
30
|
def get_persistent_user_id() -> str:
|
|
13
31
|
"""
|
|
14
|
-
Get
|
|
32
|
+
Get the persistent anonymous user ID.
|
|
15
33
|
|
|
16
|
-
|
|
17
|
-
If the file doesn't exist, a new UUID is generated and saved.
|
|
34
|
+
Now reads from ~/.airbyte/connector-sdk/config.yaml
|
|
18
35
|
|
|
19
36
|
Returns:
|
|
20
37
|
An anonymous UUID string that uniquely identifies this user across sessions.
|
|
21
38
|
"""
|
|
22
|
-
|
|
23
|
-
# Create .airbyte directory in home folder if it doesn't exist
|
|
24
|
-
airbyte_dir = Path.home() / ".airbyte"
|
|
25
|
-
airbyte_dir.mkdir(exist_ok=True)
|
|
26
|
-
|
|
27
|
-
# Path to user ID file
|
|
28
|
-
user_id_file = airbyte_dir / "ai_sdk_user_id"
|
|
29
|
-
|
|
30
|
-
# Try to read existing user ID
|
|
31
|
-
if user_id_file.exists():
|
|
32
|
-
user_id = user_id_file.read_text().strip()
|
|
33
|
-
if user_id: # Validate it's not empty
|
|
34
|
-
return user_id
|
|
35
|
-
|
|
36
|
-
# Generate new user ID if file doesn't exist or is empty
|
|
37
|
-
user_id = str(uuid.uuid4())
|
|
38
|
-
user_id_file.write_text(user_id)
|
|
39
|
-
logger.debug(f"Generated new anonymous user ID: {user_id}")
|
|
40
|
-
|
|
41
|
-
return user_id
|
|
42
|
-
except Exception as e:
|
|
43
|
-
# If we can't read/write the file, generate a session-only ID
|
|
44
|
-
logger.debug(f"Could not access anonymous user ID file: {e}")
|
|
45
|
-
return str(uuid.uuid4())
|
|
39
|
+
return _get_config().user_id
|
|
46
40
|
|
|
47
41
|
|
|
48
42
|
def get_public_ip() -> Optional[str]:
|
|
@@ -65,6 +59,18 @@ def get_public_ip() -> Optional[str]:
|
|
|
65
59
|
return None
|
|
66
60
|
|
|
67
61
|
|
|
62
|
+
def get_is_internal_user() -> bool:
|
|
63
|
+
"""
|
|
64
|
+
Check if the current user is an internal Airbyte user.
|
|
65
|
+
|
|
66
|
+
Now reads from ~/.airbyte/connector-sdk/config.yaml
|
|
67
|
+
Environment variable AIRBYTE_INTERNAL_USER can override.
|
|
68
|
+
|
|
69
|
+
Returns False if not set or on any error.
|
|
70
|
+
"""
|
|
71
|
+
return _get_config().is_internal_user
|
|
72
|
+
|
|
73
|
+
|
|
68
74
|
class ObservabilitySession:
|
|
69
75
|
"""Shared session context for both logging and telemetry."""
|
|
70
76
|
|
|
@@ -84,6 +90,7 @@ class ObservabilitySession:
|
|
|
84
90
|
self.operation_count = 0
|
|
85
91
|
self.metadata: Dict[str, Any] = {}
|
|
86
92
|
self.public_ip = get_public_ip()
|
|
93
|
+
self.is_internal_user = get_is_internal_user()
|
|
87
94
|
|
|
88
95
|
def increment_operations(self):
|
|
89
96
|
"""Increment the operation counter."""
|
|
@@ -65,8 +65,9 @@ class Schema(BaseModel):
|
|
|
65
65
|
write_only: Optional[bool] = Field(None, alias="writeOnly")
|
|
66
66
|
deprecated: Optional[bool] = None
|
|
67
67
|
|
|
68
|
-
# Airbyte
|
|
68
|
+
# Airbyte extensions
|
|
69
69
|
x_airbyte_entity_name: Optional[str] = Field(None, alias="x-airbyte-entity-name")
|
|
70
|
+
x_airbyte_stream_name: Optional[str] = Field(None, alias="x-airbyte-stream-name")
|
|
70
71
|
|
|
71
72
|
|
|
72
73
|
class Parameter(BaseModel):
|
|
@@ -61,7 +61,7 @@ class Operation(BaseModel):
|
|
|
61
61
|
description=(
|
|
62
62
|
"JSONPath expression to extract records from API response envelopes. "
|
|
63
63
|
"When specified, executor extracts data at this path instead of returning "
|
|
64
|
-
"full response. Returns array for list/
|
|
64
|
+
"full response. Returns array for list/api_search actions, single record for "
|
|
65
65
|
"get/create/update/delete actions."
|
|
66
66
|
),
|
|
67
67
|
)
|
|
@@ -77,6 +77,10 @@ class AuthConfigOption(BaseModel):
|
|
|
77
77
|
default_factory=dict,
|
|
78
78
|
description="Mapping from auth parameters (e.g., 'username', 'password', 'token') to template strings using ${field} syntax",
|
|
79
79
|
)
|
|
80
|
+
replication_auth_key_mapping: Optional[Dict[str, str]] = Field(
|
|
81
|
+
None,
|
|
82
|
+
description="Mapping from source config paths (e.g., 'credentials.api_key') to auth config keys for direct connectors",
|
|
83
|
+
)
|
|
80
84
|
|
|
81
85
|
|
|
82
86
|
class AirbyteAuthConfig(BaseModel):
|
|
@@ -99,6 +103,12 @@ class AirbyteAuthConfig(BaseModel):
|
|
|
99
103
|
properties: Optional[Dict[str, AuthConfigFieldSpec]] = None
|
|
100
104
|
auth_mapping: Optional[Dict[str, str]] = None
|
|
101
105
|
|
|
106
|
+
# Replication connector auth mapping
|
|
107
|
+
replication_auth_key_mapping: Optional[Dict[str, str]] = Field(
|
|
108
|
+
None,
|
|
109
|
+
description="Mapping from source config paths (e.g., 'credentials.api_key') to auth config keys for direct connectors",
|
|
110
|
+
)
|
|
111
|
+
|
|
102
112
|
# Multiple options (oneOf)
|
|
103
113
|
one_of: Optional[List[AuthConfigOption]] = Field(None, alias="oneOf")
|
|
104
114
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Telemetry event models."""
|
|
2
2
|
|
|
3
|
-
from dataclasses import asdict, dataclass
|
|
3
|
+
from dataclasses import asdict, dataclass, field
|
|
4
4
|
from datetime import datetime
|
|
5
5
|
from typing import Any, Dict, Optional
|
|
6
6
|
|
|
@@ -13,6 +13,7 @@ class BaseEvent:
|
|
|
13
13
|
session_id: str
|
|
14
14
|
user_id: str
|
|
15
15
|
execution_context: str
|
|
16
|
+
is_internal_user: bool = field(default=False, kw_only=True)
|
|
16
17
|
|
|
17
18
|
def to_dict(self) -> Dict[str, Any]:
|
|
18
19
|
"""Convert event to dictionary with ISO formatted timestamp."""
|
|
@@ -59,6 +59,7 @@ class SegmentTracker:
|
|
|
59
59
|
session_id=self.session.session_id,
|
|
60
60
|
user_id=self.session.user_id,
|
|
61
61
|
execution_context=self.session.execution_context,
|
|
62
|
+
is_internal_user=self.session.is_internal_user,
|
|
62
63
|
public_ip=self.session.public_ip,
|
|
63
64
|
connector_name=self.session.connector_name,
|
|
64
65
|
connector_version=connector_version,
|
|
@@ -101,6 +102,7 @@ class SegmentTracker:
|
|
|
101
102
|
session_id=self.session.session_id,
|
|
102
103
|
user_id=self.session.user_id,
|
|
103
104
|
execution_context=self.session.execution_context,
|
|
105
|
+
is_internal_user=self.session.is_internal_user,
|
|
104
106
|
public_ip=self.session.public_ip,
|
|
105
107
|
connector_name=self.session.connector_name,
|
|
106
108
|
entity=entity,
|
|
@@ -130,6 +132,7 @@ class SegmentTracker:
|
|
|
130
132
|
session_id=self.session.session_id,
|
|
131
133
|
user_id=self.session.user_id,
|
|
132
134
|
execution_context=self.session.execution_context,
|
|
135
|
+
is_internal_user=self.session.is_internal_user,
|
|
133
136
|
public_ip=self.session.public_ip,
|
|
134
137
|
connector_name=self.session.connector_name,
|
|
135
138
|
duration_seconds=self.session.duration_seconds(),
|
|
@@ -22,7 +22,7 @@ class Action(str, Enum):
|
|
|
22
22
|
UPDATE = "update"
|
|
23
23
|
DELETE = "delete"
|
|
24
24
|
LIST = "list"
|
|
25
|
-
|
|
25
|
+
API_SEARCH = "api_search"
|
|
26
26
|
DOWNLOAD = "download"
|
|
27
27
|
AUTHORIZE = "authorize"
|
|
28
28
|
|
|
@@ -221,6 +221,10 @@ class EntityDefinition(BaseModel):
|
|
|
221
221
|
model_config = {"populate_by_name": True}
|
|
222
222
|
|
|
223
223
|
name: str
|
|
224
|
+
stream_name: str | None = Field(
|
|
225
|
+
default=None,
|
|
226
|
+
description="Airbyte stream name for cache lookup (from x-airbyte-stream-name schema extension)",
|
|
227
|
+
)
|
|
224
228
|
actions: list[Action]
|
|
225
229
|
endpoints: dict[Action, EndpointDefinition]
|
|
226
230
|
entity_schema: dict[str, Any] | None = Field(default=None, alias="schema")
|
|
@@ -5,15 +5,14 @@ zendesk-support connector.
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
7
|
import logging
|
|
8
|
-
from typing import TYPE_CHECKING, Any, AsyncIterator, overload
|
|
8
|
+
from typing import TYPE_CHECKING, Any, Callable, TypeVar, AsyncIterator, overload
|
|
9
9
|
try:
|
|
10
10
|
from typing import Literal
|
|
11
11
|
except ImportError:
|
|
12
12
|
from typing_extensions import Literal
|
|
13
13
|
|
|
14
14
|
from .connector_model import ZendeskSupportConnectorModel
|
|
15
|
-
from ._vendored.connector_sdk.introspection import describe_entities
|
|
16
|
-
|
|
15
|
+
from ._vendored.connector_sdk.introspection import describe_entities, generate_tool_description
|
|
17
16
|
from .types import (
|
|
18
17
|
ArticleAttachmentsDownloadParams,
|
|
19
18
|
ArticleAttachmentsGetParams,
|
|
@@ -55,7 +54,6 @@ from .types import (
|
|
|
55
54
|
ViewsGetParams,
|
|
56
55
|
ViewsListParams,
|
|
57
56
|
)
|
|
58
|
-
|
|
59
57
|
if TYPE_CHECKING:
|
|
60
58
|
from .models import ZendeskSupportAuthConfig
|
|
61
59
|
# Import specific auth config classes for multi-auth isinstance checks
|
|
@@ -104,6 +102,9 @@ from .models import (
|
|
|
104
102
|
ArticleAttachmentsGetResult,
|
|
105
103
|
)
|
|
106
104
|
|
|
105
|
+
# TypeVar for decorator type preservation
|
|
106
|
+
_F = TypeVar("_F", bound=Callable[..., Any])
|
|
107
|
+
|
|
107
108
|
|
|
108
109
|
class ZendeskSupportConnector:
|
|
109
110
|
"""
|
|
@@ -716,9 +717,46 @@ class ZendeskSupportConnector:
|
|
|
716
717
|
|
|
717
718
|
# ===== INTROSPECTION METHODS =====
|
|
718
719
|
|
|
719
|
-
|
|
720
|
+
@classmethod
|
|
721
|
+
def describe(cls, func: _F) -> _F:
|
|
722
|
+
"""
|
|
723
|
+
Decorator that populates a function's docstring with connector capabilities.
|
|
724
|
+
|
|
725
|
+
This class method can be used as a decorator to automatically generate
|
|
726
|
+
comprehensive documentation for AI tool functions.
|
|
727
|
+
|
|
728
|
+
Usage:
|
|
729
|
+
@mcp.tool()
|
|
730
|
+
@ZendeskSupportConnector.describe
|
|
731
|
+
async def execute(entity: str, action: str, params: dict):
|
|
732
|
+
'''Execute operations.'''
|
|
733
|
+
...
|
|
734
|
+
|
|
735
|
+
The decorated function's __doc__ will be updated with:
|
|
736
|
+
- Available entities and their actions
|
|
737
|
+
- Parameter signatures with required (*) and optional (?) markers
|
|
738
|
+
- Response structure documentation
|
|
739
|
+
- Example questions (if available in OpenAPI spec)
|
|
740
|
+
|
|
741
|
+
Args:
|
|
742
|
+
func: The function to decorate
|
|
743
|
+
|
|
744
|
+
Returns:
|
|
745
|
+
The same function with updated __doc__
|
|
746
|
+
"""
|
|
747
|
+
description = generate_tool_description(ZendeskSupportConnectorModel)
|
|
748
|
+
|
|
749
|
+
original_doc = func.__doc__ or ""
|
|
750
|
+
if original_doc.strip():
|
|
751
|
+
func.__doc__ = f"{original_doc.strip()}\n{description}"
|
|
752
|
+
else:
|
|
753
|
+
func.__doc__ = description
|
|
754
|
+
|
|
755
|
+
return func
|
|
756
|
+
|
|
757
|
+
def list_entities(self) -> list[dict[str, Any]]:
|
|
720
758
|
"""
|
|
721
|
-
|
|
759
|
+
Get structured data about available entities, actions, and parameters.
|
|
722
760
|
|
|
723
761
|
Returns a list of entity descriptions with:
|
|
724
762
|
- entity_name: Name of the entity (e.g., "contacts", "deals")
|
|
@@ -727,7 +765,7 @@ class ZendeskSupportConnector:
|
|
|
727
765
|
- parameters: Dict mapping action -> list of parameter dicts
|
|
728
766
|
|
|
729
767
|
Example:
|
|
730
|
-
entities = connector.
|
|
768
|
+
entities = connector.list_entities()
|
|
731
769
|
for entity in entities:
|
|
732
770
|
print(f"{entity['entity_name']}: {entity['available_actions']}")
|
|
733
771
|
"""
|
|
@@ -60,6 +60,7 @@ ZendeskSupportConnectorModel: ConnectorModel = ConnectorModel(
|
|
|
60
60
|
),
|
|
61
61
|
},
|
|
62
62
|
auth_mapping={'access_token': '${access_token}', 'refresh_token': '${refresh_token}'},
|
|
63
|
+
replication_auth_key_mapping={'access_token': 'access_token'},
|
|
63
64
|
),
|
|
64
65
|
),
|
|
65
66
|
AuthOption(
|
|
@@ -83,6 +84,7 @@ ZendeskSupportConnectorModel: ConnectorModel = ConnectorModel(
|
|
|
83
84
|
),
|
|
84
85
|
},
|
|
85
86
|
auth_mapping={'username': '${email}/token', 'password': '${api_token}'},
|
|
87
|
+
replication_auth_key_mapping={'email': 'email', 'api_token': 'api_token'},
|
|
86
88
|
),
|
|
87
89
|
),
|
|
88
90
|
],
|
|
@@ -3,7 +3,7 @@ Type definitions for zendesk-support connector.
|
|
|
3
3
|
"""
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
|
-
# Use typing_extensions.TypedDict for Pydantic compatibility
|
|
6
|
+
# Use typing_extensions.TypedDict for Pydantic compatibility
|
|
7
7
|
try:
|
|
8
8
|
from typing_extensions import TypedDict, NotRequired
|
|
9
9
|
except ImportError:
|
{airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/pyproject.toml
RENAMED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "airbyte-agent-zendesk-support"
|
|
3
|
-
version = "0.18.
|
|
3
|
+
version = "0.18.37"
|
|
4
4
|
description = "Airbyte Zendesk-Support Connector for AI platforms"
|
|
5
5
|
readme = "README.md"
|
|
6
|
-
requires-python = ">=3.
|
|
6
|
+
requires-python = ">=3.13"
|
|
7
7
|
license = {text = "Elastic-2.0"}
|
|
8
8
|
authors = [
|
|
9
9
|
{name = "Airbyte", email = "contact@airbyte.io"}
|
|
@@ -14,10 +14,6 @@ classifiers = [
|
|
|
14
14
|
"Intended Audience :: Developers",
|
|
15
15
|
"License :: Other/Proprietary License",
|
|
16
16
|
"Programming Language :: Python :: 3",
|
|
17
|
-
"Programming Language :: Python :: 3.9",
|
|
18
|
-
"Programming Language :: Python :: 3.10",
|
|
19
|
-
"Programming Language :: Python :: 3.11",
|
|
20
|
-
"Programming Language :: Python :: 3.12",
|
|
21
17
|
"Programming Language :: Python :: 3.13",
|
|
22
18
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
23
19
|
]
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Decorators for AI agent tool integration.
|
|
3
|
-
|
|
4
|
-
Provides utilities to auto-generate comprehensive tool descriptions
|
|
5
|
-
from connector metadata, enabling easy integration with AI frameworks.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from __future__ import annotations
|
|
9
|
-
|
|
10
|
-
import importlib
|
|
11
|
-
from typing import Any, Callable, TypeVar
|
|
12
|
-
|
|
13
|
-
from .introspection import (
|
|
14
|
-
MAX_EXAMPLE_QUESTIONS,
|
|
15
|
-
ConnectorModelProtocol,
|
|
16
|
-
EndpointProtocol,
|
|
17
|
-
generate_tool_description,
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
F = TypeVar("F", bound=Callable[..., Any])
|
|
21
|
-
|
|
22
|
-
__all__ = [
|
|
23
|
-
"airbyte_description",
|
|
24
|
-
"EndpointProtocol",
|
|
25
|
-
"ConnectorModelProtocol",
|
|
26
|
-
"MAX_EXAMPLE_QUESTIONS",
|
|
27
|
-
# Private function exposed for testing
|
|
28
|
-
"_load_connector_model",
|
|
29
|
-
]
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def airbyte_description(connector_name: str) -> Callable[[F], F]:
|
|
33
|
-
"""
|
|
34
|
-
Decorator that generates comprehensive tool descriptions from connector metadata.
|
|
35
|
-
|
|
36
|
-
Automatically populates the function's docstring with:
|
|
37
|
-
- Connector description
|
|
38
|
-
- Available entities and their actions
|
|
39
|
-
- Example questions the connector can answer
|
|
40
|
-
|
|
41
|
-
Args:
|
|
42
|
-
connector_name: Name of the connector (e.g., "hubspot", "stripe")
|
|
43
|
-
Must match the generated package name pattern:
|
|
44
|
-
airbyte_agent_{connector_name}
|
|
45
|
-
|
|
46
|
-
Returns:
|
|
47
|
-
Decorator that updates the function's __doc__ attribute
|
|
48
|
-
|
|
49
|
-
Example:
|
|
50
|
-
from airbyte_agent_hubspot import HubspotConnector
|
|
51
|
-
|
|
52
|
-
connector = HubspotConnector(
|
|
53
|
-
external_user_id=external_user_id,
|
|
54
|
-
airbyte_client_id=airbyte_client_id,
|
|
55
|
-
airbyte_client_secret=airbyte_client_secret
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
# IMPORTANT: @airbyte_description must be the INNER decorator (closest to function)
|
|
59
|
-
# This ensures __doc__ is expanded BEFORE frameworks like FastMCP capture it
|
|
60
|
-
@agent.tool_plain # or @mcp.tool() for FastMCP
|
|
61
|
-
@airbyte_description("hubspot")
|
|
62
|
-
async def hubspot_exec(entity: str, action: str, params: dict | None = None):
|
|
63
|
-
'''Execute HubSpot operations.'''
|
|
64
|
-
return await connector.execute(entity, action, params or {})
|
|
65
|
-
|
|
66
|
-
The decorator will update hubspot_exec.__doc__ with a comprehensive
|
|
67
|
-
description including all available entities, actions, and example questions.
|
|
68
|
-
"""
|
|
69
|
-
|
|
70
|
-
def decorator(func: F) -> F:
|
|
71
|
-
# Load connector model from generated package
|
|
72
|
-
model = _load_connector_model(connector_name)
|
|
73
|
-
|
|
74
|
-
# Generate description using shared introspection module
|
|
75
|
-
description = generate_tool_description(model)
|
|
76
|
-
|
|
77
|
-
# Preserve original docstring if present, append to it
|
|
78
|
-
original_doc = func.__doc__ or ""
|
|
79
|
-
if original_doc.strip():
|
|
80
|
-
func.__doc__ = f"{original_doc.strip()}\n\n{description}"
|
|
81
|
-
else:
|
|
82
|
-
func.__doc__ = description
|
|
83
|
-
|
|
84
|
-
return func
|
|
85
|
-
|
|
86
|
-
return decorator
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def _load_connector_model(connector_name: str) -> Any:
|
|
90
|
-
"""
|
|
91
|
-
Load connector model from generated package.
|
|
92
|
-
|
|
93
|
-
Args:
|
|
94
|
-
connector_name: Connector name (e.g., "hubspot")
|
|
95
|
-
|
|
96
|
-
Returns:
|
|
97
|
-
ConnectorModel instance from the generated package
|
|
98
|
-
|
|
99
|
-
Raises:
|
|
100
|
-
ImportError: If connector package is not installed
|
|
101
|
-
AttributeError: If connector model constant not found
|
|
102
|
-
"""
|
|
103
|
-
# Normalize connector name to package name
|
|
104
|
-
package_name = f"airbyte_agent_{connector_name.replace('-', '_')}"
|
|
105
|
-
|
|
106
|
-
try:
|
|
107
|
-
# Import the connector_model module from the generated package
|
|
108
|
-
module = importlib.import_module(f"{package_name}.connector_model")
|
|
109
|
-
except ImportError as e:
|
|
110
|
-
raise ImportError(f"Could not import connector package '{package_name}'. " f"Ensure the package is installed. Error: {e}") from e
|
|
111
|
-
|
|
112
|
-
# Find the ConnectorModel constant (named like HubspotConnectorModel)
|
|
113
|
-
# Convention: {PascalCase connector name}ConnectorModel
|
|
114
|
-
pascal_name = "".join(word.capitalize() for word in connector_name.replace("-", "_").split("_"))
|
|
115
|
-
model_name = f"{pascal_name}ConnectorModel"
|
|
116
|
-
|
|
117
|
-
model = getattr(module, model_name, None)
|
|
118
|
-
if model is None:
|
|
119
|
-
# Fallback: look for any ConnectorModel attribute
|
|
120
|
-
for attr_name in dir(module):
|
|
121
|
-
if attr_name.endswith("ConnectorModel"):
|
|
122
|
-
model = getattr(module, attr_name)
|
|
123
|
-
break
|
|
124
|
-
|
|
125
|
-
if model is None:
|
|
126
|
-
raise AttributeError(f"Could not find ConnectorModel in {package_name}.connector_model. " f"Expected constant named '{model_name}'")
|
|
127
|
-
|
|
128
|
-
return model
|
|
File without changes
|
{airbyte_agent_zendesk_support-0.18.29 → airbyte_agent_zendesk_support-0.18.37}/REFERENCE.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|