airbyte-agent-zendesk-support 0.18.58__tar.gz → 0.18.61__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/AUTH.md +1 -1
  2. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/CHANGELOG.md +15 -0
  3. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/PKG-INFO +7 -6
  4. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/README.md +6 -5
  5. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/REFERENCE.md +15 -14
  6. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/connector_model_loader.py +84 -0
  7. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/executor/local_executor.py +3 -1
  8. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/introspection.py +222 -10
  9. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/schema/base.py +34 -2
  10. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/schema/components.py +5 -0
  11. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/schema/extensions.py +71 -0
  12. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/types.py +1 -0
  13. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/connector.py +101 -39
  14. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/connector_model.py +287 -1
  15. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/models.py +7 -7
  16. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/types.py +35 -35
  17. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/pyproject.toml +1 -1
  18. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/.gitignore +0 -0
  19. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/__init__.py +0 -0
  20. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/__init__.py +0 -0
  21. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/__init__.py +0 -0
  22. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/auth_strategies.py +0 -0
  23. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/auth_template.py +0 -0
  24. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/cloud_utils/__init__.py +0 -0
  25. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/cloud_utils/client.py +0 -0
  26. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/constants.py +0 -0
  27. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/exceptions.py +0 -0
  28. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/executor/__init__.py +0 -0
  29. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/executor/hosted_executor.py +0 -0
  30. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/executor/models.py +0 -0
  31. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/extensions.py +0 -0
  32. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/http/__init__.py +0 -0
  33. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/http/adapters/__init__.py +0 -0
  34. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/http/adapters/httpx_adapter.py +0 -0
  35. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/http/config.py +0 -0
  36. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/http/exceptions.py +0 -0
  37. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/http/protocols.py +0 -0
  38. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/http/response.py +0 -0
  39. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/http_client.py +0 -0
  40. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/logging/__init__.py +0 -0
  41. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/logging/logger.py +0 -0
  42. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/logging/types.py +0 -0
  43. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/observability/__init__.py +0 -0
  44. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/observability/config.py +0 -0
  45. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/observability/models.py +0 -0
  46. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/observability/redactor.py +0 -0
  47. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/observability/session.py +0 -0
  48. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/performance/__init__.py +0 -0
  49. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/performance/instrumentation.py +0 -0
  50. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/performance/metrics.py +0 -0
  51. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/schema/__init__.py +0 -0
  52. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/schema/connector.py +0 -0
  53. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/schema/operations.py +0 -0
  54. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/schema/security.py +0 -0
  55. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/secrets.py +0 -0
  56. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/telemetry/__init__.py +0 -0
  57. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/telemetry/config.py +0 -0
  58. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/telemetry/events.py +0 -0
  59. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/telemetry/tracker.py +0 -0
  60. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/utils.py +0 -0
  61. {airbyte_agent_zendesk_support-0.18.58 → airbyte_agent_zendesk_support-0.18.61}/airbyte_agent_zendesk_support/_vendored/connector_sdk/validation.py +0 -0
@@ -103,7 +103,7 @@ connector = ZendeskSupportConnector(
103
103
  )
104
104
 
105
105
  @agent.tool_plain # assumes you're using Pydantic AI
106
- @ZendeskSupportConnector.describe
106
+ @ZendeskSupportConnector.tool_utils
107
107
  async def zendesk-support_execute(entity: str, action: str, params: dict | None = None):
108
108
  return await connector.execute(entity, action, params or {})
109
109
  ```
@@ -1,5 +1,20 @@
1
1
  # Zendesk Support changelog
2
2
 
3
+ ## [0.18.61] - 2026-01-24
4
+ - Updated connector definition (YAML version 0.1.8)
5
+ - Source commit: 609c1d86
6
+ - SDK version: 0.1.0
7
+
8
+ ## [0.18.60] - 2026-01-23
9
+ - Updated connector definition (YAML version 0.1.8)
10
+ - Source commit: 592446b8
11
+ - SDK version: 0.1.0
12
+
13
+ ## [0.18.59] - 2026-01-23
14
+ - Updated connector definition (YAML version 0.1.7)
15
+ - Source commit: 416466da
16
+ - SDK version: 0.1.0
17
+
3
18
  ## [0.18.58] - 2026-01-23
4
19
  - Updated connector definition (YAML version 0.1.7)
5
20
  - Source commit: f17cdd8c
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: airbyte-agent-zendesk-support
3
- Version: 0.18.58
3
+ Version: 0.18.61
4
4
  Summary: Airbyte Zendesk-Support Connector for AI platforms
5
5
  Project-URL: Homepage, https://github.com/airbytehq/airbyte-agent-connectors
6
6
  Project-URL: Documentation, https://docs.airbyte.com/ai-agents/
@@ -89,7 +89,7 @@ connector = ZendeskSupportConnector(
89
89
  )
90
90
 
91
91
  @agent.tool_plain # assumes you're using Pydantic AI
92
- @ZendeskSupportConnector.describe
92
+ @ZendeskSupportConnector.tool_utils
93
93
  async def zendesk-support_execute(entity: str, action: str, params: dict | None = None):
94
94
  return await connector.execute(entity, action, params or {})
95
95
  ```
@@ -110,11 +110,12 @@ connector = ZendeskSupportConnector(
110
110
  )
111
111
 
112
112
  @agent.tool_plain # assumes you're using Pydantic AI
113
- @ZendeskSupportConnector.describe
113
+ @ZendeskSupportConnector.tool_utils
114
114
  async def zendesk-support_execute(entity: str, action: str, params: dict | None = None):
115
115
  return await connector.execute(entity, action, params or {})
116
116
  ```
117
117
 
118
+
118
119
  ## Full documentation
119
120
 
120
121
  This connector supports the following entities and actions.
@@ -153,6 +154,6 @@ For the service's official API docs, see the [Zendesk-Support API reference](htt
153
154
 
154
155
  ## Version information
155
156
 
156
- - **Package version:** 0.18.58
157
- - **Connector version:** 0.1.7
158
- - **Generated with Connector SDK commit SHA:** f17cdd8c92950751a75daff9c6bf74df05774ede
157
+ - **Package version:** 0.18.61
158
+ - **Connector version:** 0.1.8
159
+ - **Generated with Connector SDK commit SHA:** 609c1d86c76b36ff699b57123a5a8c2050d958c3
@@ -56,7 +56,7 @@ connector = ZendeskSupportConnector(
56
56
  )
57
57
 
58
58
  @agent.tool_plain # assumes you're using Pydantic AI
59
- @ZendeskSupportConnector.describe
59
+ @ZendeskSupportConnector.tool_utils
60
60
  async def zendesk-support_execute(entity: str, action: str, params: dict | None = None):
61
61
  return await connector.execute(entity, action, params or {})
62
62
  ```
@@ -77,11 +77,12 @@ connector = ZendeskSupportConnector(
77
77
  )
78
78
 
79
79
  @agent.tool_plain # assumes you're using Pydantic AI
80
- @ZendeskSupportConnector.describe
80
+ @ZendeskSupportConnector.tool_utils
81
81
  async def zendesk-support_execute(entity: str, action: str, params: dict | None = None):
82
82
  return await connector.execute(entity, action, params or {})
83
83
  ```
84
84
 
85
+
85
86
  ## Full documentation
86
87
 
87
88
  This connector supports the following entities and actions.
@@ -120,6 +121,6 @@ For the service's official API docs, see the [Zendesk-Support API reference](htt
120
121
 
121
122
  ## Version information
122
123
 
123
- - **Package version:** 0.18.58
124
- - **Connector version:** 0.1.7
125
- - **Generated with Connector SDK commit SHA:** f17cdd8c92950751a75daff9c6bf74df05774ede
124
+ - **Package version:** 0.18.61
125
+ - **Connector version:** 0.1.8
126
+ - **Generated with Connector SDK commit SHA:** 609c1d86c76b36ff699b57123a5a8c2050d958c3
@@ -258,7 +258,7 @@ curl --location 'https://api.airbyte.ai/api/v1/connectors/sources/{your_source_i
258
258
  | `created_at` | `string` | Timestamp indicating when the ticket was created |
259
259
  | `custom_fields` | `array` | Array of custom field values specific to the account's ticket configuration |
260
260
  | `custom_status_id` | `integer` | Unique identifier of the custom status applied to the ticket |
261
- | `deleted_ticket_form_id` | `integer` | Unique identifier of the ticket form if it was deleted after the ticket was created |
261
+ | `deleted_ticket_form_id` | `integer` | The ID of the ticket form that was previously associated with this ticket but has since been deleted |
262
262
  | `description` | `string` | Initial description or content of the ticket when it was created |
263
263
  | `due_at` | `string` | Timestamp indicating when the ticket is due for completion or resolution |
264
264
  | `email_cc_ids` | `array` | Array of user identifiers who are CC'd on ticket email notifications |
@@ -268,7 +268,7 @@ curl --location 'https://api.airbyte.ai/api/v1/connectors/sources/{your_source_i
268
268
  | `followup_ids` | `array` | Array of identifiers for follow-up tickets related to this ticket |
269
269
  | `forum_topic_id` | `integer` | Unique identifier linking the ticket to a forum topic if applicable |
270
270
  | `from_messaging_channel` | `boolean` | Boolean indicating whether the ticket originated from a messaging channel |
271
- | `generated_timestamp` | `integer` | Timestamp updated for all ticket updates including system changes, used for incremental export co... |
271
+ | `generated_timestamp` | `integer` | Timestamp updated for all ticket updates including system changes, used for incremental export |
272
272
  | `group_id` | `integer` | Unique identifier of the agent group assigned to handle the ticket |
273
273
  | `has_incidents` | `boolean` | Boolean indicating whether this problem ticket has related incident tickets |
274
274
  | `id` | `integer` | Unique identifier for the ticket |
@@ -308,7 +308,7 @@ curl --location 'https://api.airbyte.ai/api/v1/connectors/sources/{your_source_i
308
308
  | `hits[].data.created_at` | `string` | Timestamp indicating when the ticket was created |
309
309
  | `hits[].data.custom_fields` | `array` | Array of custom field values specific to the account's ticket configuration |
310
310
  | `hits[].data.custom_status_id` | `integer` | Unique identifier of the custom status applied to the ticket |
311
- | `hits[].data.deleted_ticket_form_id` | `integer` | Unique identifier of the ticket form if it was deleted after the ticket was created |
311
+ | `hits[].data.deleted_ticket_form_id` | `integer` | The ID of the ticket form that was previously associated with this ticket but has since been deleted |
312
312
  | `hits[].data.description` | `string` | Initial description or content of the ticket when it was created |
313
313
  | `hits[].data.due_at` | `string` | Timestamp indicating when the ticket is due for completion or resolution |
314
314
  | `hits[].data.email_cc_ids` | `array` | Array of user identifiers who are CC'd on ticket email notifications |
@@ -318,7 +318,7 @@ curl --location 'https://api.airbyte.ai/api/v1/connectors/sources/{your_source_i
318
318
  | `hits[].data.followup_ids` | `array` | Array of identifiers for follow-up tickets related to this ticket |
319
319
  | `hits[].data.forum_topic_id` | `integer` | Unique identifier linking the ticket to a forum topic if applicable |
320
320
  | `hits[].data.from_messaging_channel` | `boolean` | Boolean indicating whether the ticket originated from a messaging channel |
321
- | `hits[].data.generated_timestamp` | `integer` | Timestamp updated for all ticket updates including system changes, used for incremental export co... |
321
+ | `hits[].data.generated_timestamp` | `integer` | Timestamp updated for all ticket updates including system changes, used for incremental export |
322
322
  | `hits[].data.group_id` | `integer` | Unique identifier of the agent group assigned to handle the ticket |
323
323
  | `hits[].data.has_incidents` | `boolean` | Boolean indicating whether this problem ticket has related incident tickets |
324
324
  | `hits[].data.id` | `integer` | Unique identifier for the ticket |
@@ -1894,9 +1894,9 @@ curl --location 'https://api.airbyte.ai/api/v1/connectors/sources/{your_source_i
1894
1894
  |------------|------|-------------|
1895
1895
  | `active` | `boolean` | Whether this field is currently available for use |
1896
1896
  | `agent_description` | `string` | A description of the ticket field that only agents can see |
1897
- | `collapsed_for_agents` | `boolean` | If true, the field is shown to agents by default; if false, it is hidden alongside infrequently u... |
1897
+ | `collapsed_for_agents` | `boolean` | If true, the field is shown to agents by default; if false, it is hidden alongside infrequently used fields |
1898
1898
  | `created_at` | `string` | Timestamp when the custom ticket field was created |
1899
- | `custom_field_options` | `array` | Array of option objects for custom ticket fields of type multiselect or tagger, containing name a... |
1899
+ | `custom_field_options` | `array` | Array of option objects for custom ticket fields of type multiselect or tagger |
1900
1900
  | `custom_statuses` | `array` | List of customized ticket statuses, only present for system ticket fields of type custom_status |
1901
1901
  | `description` | `string` | Text describing the purpose of the ticket field to users |
1902
1902
  | `editable_in_portal` | `boolean` | Whether this field is editable by end users in Help Center |
@@ -1915,7 +1915,7 @@ curl --location 'https://api.airbyte.ai/api/v1/connectors/sources/{your_source_i
1915
1915
  | `tag` | `string` | For checkbox fields only, a tag added to tickets when the checkbox field is selected |
1916
1916
  | `title` | `string` | The title of the ticket field displayed to agents |
1917
1917
  | `title_in_portal` | `string` | The title of the ticket field displayed to end users in Help Center |
1918
- | `type` | `string` | Field type such as text, textarea, checkbox, date, integer, decimal, regexp, multiselect, tagger,... |
1918
+ | `type` | `string` | Field type such as text, textarea, checkbox, date, integer, decimal, regexp, multiselect, or tagger |
1919
1919
  | `updated_at` | `string` | Timestamp when the custom ticket field was last updated |
1920
1920
  | `url` | `string` | The API URL for this ticket field resource |
1921
1921
  | `visible_in_portal` | `boolean` | Whether this field is visible to end users in Help Center |
@@ -1931,9 +1931,9 @@ curl --location 'https://api.airbyte.ai/api/v1/connectors/sources/{your_source_i
1931
1931
  | `hits[].data` | `object` | Record data containing the searchable fields listed above |
1932
1932
  | `hits[].data.active` | `boolean` | Whether this field is currently available for use |
1933
1933
  | `hits[].data.agent_description` | `string` | A description of the ticket field that only agents can see |
1934
- | `hits[].data.collapsed_for_agents` | `boolean` | If true, the field is shown to agents by default; if false, it is hidden alongside infrequently u... |
1934
+ | `hits[].data.collapsed_for_agents` | `boolean` | If true, the field is shown to agents by default; if false, it is hidden alongside infrequently used fields |
1935
1935
  | `hits[].data.created_at` | `string` | Timestamp when the custom ticket field was created |
1936
- | `hits[].data.custom_field_options` | `array` | Array of option objects for custom ticket fields of type multiselect or tagger, containing name a... |
1936
+ | `hits[].data.custom_field_options` | `array` | Array of option objects for custom ticket fields of type multiselect or tagger |
1937
1937
  | `hits[].data.custom_statuses` | `array` | List of customized ticket statuses, only present for system ticket fields of type custom_status |
1938
1938
  | `hits[].data.description` | `string` | Text describing the purpose of the ticket field to users |
1939
1939
  | `hits[].data.editable_in_portal` | `boolean` | Whether this field is editable by end users in Help Center |
@@ -1952,7 +1952,7 @@ curl --location 'https://api.airbyte.ai/api/v1/connectors/sources/{your_source_i
1952
1952
  | `hits[].data.tag` | `string` | For checkbox fields only, a tag added to tickets when the checkbox field is selected |
1953
1953
  | `hits[].data.title` | `string` | The title of the ticket field displayed to agents |
1954
1954
  | `hits[].data.title_in_portal` | `string` | The title of the ticket field displayed to end users in Help Center |
1955
- | `hits[].data.type` | `string` | Field type such as text, textarea, checkbox, date, integer, decimal, regexp, multiselect, tagger,... |
1955
+ | `hits[].data.type` | `string` | Field type such as text, textarea, checkbox, date, integer, decimal, regexp, multiselect, or tagger |
1956
1956
  | `hits[].data.updated_at` | `string` | Timestamp when the custom ticket field was last updated |
1957
1957
  | `hits[].data.url` | `string` | The API URL for this ticket field resource |
1958
1958
  | `hits[].data.visible_in_portal` | `boolean` | Whether this field is visible to end users in Help Center |
@@ -2962,7 +2962,7 @@ curl --location 'https://api.airbyte.ai/api/v1/connectors/sources/{your_source_i
2962
2962
  | `group_id` | `integer` | The identifier of the group assigned to the ticket at the time the rating was submitted |
2963
2963
  | `id` | `integer` | Unique identifier for the satisfaction rating, automatically assigned upon creation |
2964
2964
  | `reason` | `string` | Free-text reason for a bad rating provided by the requester in a follow-up question |
2965
- | `reason_id` | `integer` | Identifier for the predefined reason given for a negative rating, only applicable when score is '... |
2965
+ | `reason_id` | `integer` | Identifier for the predefined reason given for a negative rating |
2966
2966
  | `requester_id` | `integer` | The identifier of the ticket requester who submitted the satisfaction rating |
2967
2967
  | `score` | `string` | The satisfaction rating value: 'offered', 'unoffered', 'good', or 'bad' |
2968
2968
  | `ticket_id` | `integer` | The identifier of the ticket being rated |
@@ -2984,7 +2984,7 @@ curl --location 'https://api.airbyte.ai/api/v1/connectors/sources/{your_source_i
2984
2984
  | `hits[].data.group_id` | `integer` | The identifier of the group assigned to the ticket at the time the rating was submitted |
2985
2985
  | `hits[].data.id` | `integer` | Unique identifier for the satisfaction rating, automatically assigned upon creation |
2986
2986
  | `hits[].data.reason` | `string` | Free-text reason for a bad rating provided by the requester in a follow-up question |
2987
- | `hits[].data.reason_id` | `integer` | Identifier for the predefined reason given for a negative rating, only applicable when score is '... |
2987
+ | `hits[].data.reason_id` | `integer` | Identifier for the predefined reason given for a negative rating |
2988
2988
  | `hits[].data.requester_id` | `integer` | The identifier of the ticket requester who submitted the satisfaction rating |
2989
2989
  | `hits[].data.score` | `string` | The satisfaction rating value: 'offered', 'unoffered', 'good', or 'bad' |
2990
2990
  | `hits[].data.ticket_id` | `integer` | The identifier of the ticket being rated |
@@ -3417,7 +3417,7 @@ curl --location 'https://api.airbyte.ai/api/v1/connectors/sources/{your_source_i
3417
3417
  | `raw_display_name` | `string` | The dynamic content placeholder if present, or the display_name value if not |
3418
3418
  | `raw_name` | `string` | The dynamic content placeholder if present, or the name value if not |
3419
3419
  | `restricted_brand_ids` | `array` | IDs of all brands that this ticket form is restricted to |
3420
- | `ticket_field_ids` | `array` | IDs of all ticket fields included in this ticket form, ordered to determine field display sequenc... |
3420
+ | `ticket_field_ids` | `array` | IDs of all ticket fields included in this ticket form |
3421
3421
  | `updated_at` | `string` | Timestamp of the last update to the ticket form |
3422
3422
  | `url` | `string` | URL of the ticket form |
3423
3423
 
@@ -3444,7 +3444,7 @@ curl --location 'https://api.airbyte.ai/api/v1/connectors/sources/{your_source_i
3444
3444
  | `hits[].data.raw_display_name` | `string` | The dynamic content placeholder if present, or the display_name value if not |
3445
3445
  | `hits[].data.raw_name` | `string` | The dynamic content placeholder if present, or the name value if not |
3446
3446
  | `hits[].data.restricted_brand_ids` | `array` | IDs of all brands that this ticket form is restricted to |
3447
- | `hits[].data.ticket_field_ids` | `array` | IDs of all ticket fields included in this ticket form, ordered to determine field display sequenc... |
3447
+ | `hits[].data.ticket_field_ids` | `array` | IDs of all ticket fields included in this ticket form |
3448
3448
  | `hits[].data.updated_at` | `string` | Timestamp of the last update to the ticket form |
3449
3449
  | `hits[].data.url` | `string` | URL of the ticket form |
3450
3450
  | `next_cursor` | `string \| null` | Cursor for next page of results |
@@ -3748,3 +3748,4 @@ curl --location 'https://api.airbyte.ai/api/v1/connectors/sources/{your_source_i
3748
3748
  | `range_header` | `string` | No | Optional Range header for partial downloads (e.g., 'bytes=0-99') |
3749
3749
 
3750
3750
 
3751
+
@@ -145,6 +145,87 @@ def _deproxy_schema(obj: Any) -> Any:
145
145
  return obj
146
146
 
147
147
 
148
+ def _type_includes(type_value: Any, target: str) -> bool:
149
+ if isinstance(type_value, list):
150
+ return target in type_value
151
+ return type_value == target
152
+
153
+
154
+ def _flatten_cache_properties(properties: dict[str, Any], prefix: str) -> list[str]:
155
+ entries: list[str] = []
156
+ for prop_name, prop in properties.items():
157
+ path = f"{prefix}{prop_name}" if prefix else prop_name
158
+ entries.append(path)
159
+
160
+ prop_type = getattr(prop, "type", None) if not isinstance(prop, dict) else prop.get("type")
161
+ prop_properties = getattr(prop, "properties", None) if not isinstance(prop, dict) else prop.get("properties")
162
+
163
+ if _type_includes(prop_type, "array"):
164
+ array_path = f"{path}[]"
165
+ entries.append(array_path)
166
+ if isinstance(prop_properties, dict):
167
+ entries.extend(_flatten_cache_properties(prop_properties, prefix=f"{array_path}."))
168
+ elif isinstance(prop_properties, dict):
169
+ entries.extend(_flatten_cache_properties(prop_properties, prefix=f"{path}."))
170
+
171
+ return entries
172
+
173
+
174
+ def _flatten_cache_field_paths(field: Any) -> list[str]:
175
+ field_name = getattr(field, "name", None) if not isinstance(field, dict) else field.get("name")
176
+ if not isinstance(field_name, str) or not field_name:
177
+ return []
178
+
179
+ field_type = getattr(field, "type", None) if not isinstance(field, dict) else field.get("type")
180
+ field_properties = getattr(field, "properties", None) if not isinstance(field, dict) else field.get("properties")
181
+
182
+ entries = [field_name]
183
+ if _type_includes(field_type, "array"):
184
+ array_path = f"{field_name}[]"
185
+ entries.append(array_path)
186
+ if isinstance(field_properties, dict):
187
+ entries.extend(_flatten_cache_properties(field_properties, prefix=f"{array_path}."))
188
+ elif isinstance(field_properties, dict):
189
+ entries.extend(_flatten_cache_properties(field_properties, prefix=f"{field_name}."))
190
+
191
+ return entries
192
+
193
+
194
+ def _dedupe_strings(values: list[str]) -> list[str]:
195
+ seen: set[str] = set()
196
+ ordered: list[str] = []
197
+ for value in values:
198
+ if value not in seen:
199
+ seen.add(value)
200
+ ordered.append(value)
201
+ return ordered
202
+
203
+
204
+ def _extract_search_field_paths(spec: OpenAPIConnector) -> dict[str, list[str]]:
205
+ cache_config = getattr(spec.info, "x_airbyte_cache", None)
206
+ entities = getattr(cache_config, "entities", None)
207
+ if not isinstance(entities, list):
208
+ return {}
209
+
210
+ search_fields: dict[str, list[str]] = {}
211
+ for entity in entities:
212
+ entity_name = getattr(entity, "entity", None) if not isinstance(entity, dict) else entity.get("entity")
213
+ if not isinstance(entity_name, str) or not entity_name:
214
+ continue
215
+
216
+ fields = getattr(entity, "fields", None) if not isinstance(entity, dict) else entity.get("fields")
217
+ if not isinstance(fields, list):
218
+ continue
219
+
220
+ field_paths: list[str] = []
221
+ for field in fields:
222
+ field_paths.extend(_flatten_cache_field_paths(field))
223
+
224
+ search_fields[entity_name] = _dedupe_strings(field_paths)
225
+
226
+ return search_fields
227
+
228
+
148
229
  def parse_openapi_spec(raw_config: dict) -> OpenAPIConnector:
149
230
  """Parse OpenAPI specification from YAML.
150
231
 
@@ -434,6 +515,8 @@ def convert_openapi_to_connector_model(spec: OpenAPIConnector) -> ConnectorModel
434
515
  if not connector_id:
435
516
  raise InvalidOpenAPIError("Missing required x-airbyte-connector-id field")
436
517
 
518
+ search_field_paths = _extract_search_field_paths(spec)
519
+
437
520
  # Create ConnectorModel
438
521
  model = ConnectorModel(
439
522
  id=connector_id,
@@ -444,6 +527,7 @@ def convert_openapi_to_connector_model(spec: OpenAPIConnector) -> ConnectorModel
444
527
  entities=entities,
445
528
  openapi_spec=spec,
446
529
  retry_config=retry_config,
530
+ search_field_paths=search_field_paths,
447
531
  )
448
532
 
449
533
  return model
@@ -1032,7 +1032,9 @@ class LocalExecutor:
1032
1032
  if "variables" in graphql_config and graphql_config["variables"]:
1033
1033
  variables = self._interpolate_variables(graphql_config["variables"], params, param_defaults)
1034
1034
  # Filter out None values (optional fields not provided) - matches REST _extract_body() behavior
1035
- body["variables"] = {k: v for k, v in variables.items() if v is not None}
1035
+ # But preserve None for variables explicitly marked as nullable (e.g., to unassign a user)
1036
+ nullable_vars = set(graphql_config.get("x-airbyte-nullable-variables") or [])
1037
+ body["variables"] = {k: v for k, v in variables.items() if v is not None or k in nullable_vars}
1036
1038
 
1037
1039
  # Add operation name if specified
1038
1040
  if "operationName" in graphql_config:
@@ -18,6 +18,185 @@ from typing import Any, Protocol
18
18
  MAX_EXAMPLE_QUESTIONS = 5 # Maximum number of example questions to include in description
19
19
 
20
20
 
21
+ def _type_includes(type_value: Any, target: str) -> bool:
22
+ if isinstance(type_value, list):
23
+ return target in type_value
24
+ return type_value == target
25
+
26
+
27
+ def _is_object_schema(schema: dict[str, Any]) -> bool:
28
+ if "properties" in schema:
29
+ return True
30
+ return _type_includes(schema.get("type"), "object")
31
+
32
+
33
+ def _is_array_schema(schema: dict[str, Any]) -> bool:
34
+ if "items" in schema:
35
+ return True
36
+ return _type_includes(schema.get("type"), "array")
37
+
38
+
39
+ def _dedupe_param_entries(entries: list[tuple[str, bool]]) -> list[tuple[str, bool]]:
40
+ seen: dict[str, bool] = {}
41
+ ordered: list[str] = []
42
+ for name, required in entries:
43
+ if name not in seen:
44
+ seen[name] = required
45
+ ordered.append(name)
46
+ else:
47
+ seen[name] = seen[name] or required
48
+ return [(name, seen[name]) for name in ordered]
49
+
50
+
51
+ def _flatten_schema_params(
52
+ schema: dict[str, Any],
53
+ prefix: str = "",
54
+ parent_required: bool = True,
55
+ seen_stack: set[int] | None = None,
56
+ ) -> list[tuple[str, bool]]:
57
+ if not isinstance(schema, dict):
58
+ return []
59
+
60
+ if seen_stack is None:
61
+ seen_stack = set()
62
+
63
+ schema_id = id(schema)
64
+ if schema_id in seen_stack:
65
+ return []
66
+
67
+ seen_stack.add(schema_id)
68
+ try:
69
+ entries: list[tuple[str, bool]] = []
70
+
71
+ for subschema in schema.get("allOf", []) or []:
72
+ if isinstance(subschema, dict):
73
+ entries.extend(_flatten_schema_params(subschema, prefix, parent_required, seen_stack))
74
+
75
+ for keyword in ("anyOf", "oneOf"):
76
+ for subschema in schema.get(keyword, []) or []:
77
+ if isinstance(subschema, dict):
78
+ entries.extend(_flatten_schema_params(subschema, prefix, False, seen_stack))
79
+
80
+ properties = schema.get("properties")
81
+ if isinstance(properties, dict):
82
+ required_fields = set(schema.get("required", [])) if isinstance(schema.get("required"), list) else set()
83
+ for prop_name, prop_schema in properties.items():
84
+ path = f"{prefix}{prop_name}" if prefix else prop_name
85
+ is_required = parent_required and prop_name in required_fields
86
+ entries.append((path, is_required))
87
+
88
+ if isinstance(prop_schema, dict):
89
+ if _is_array_schema(prop_schema):
90
+ array_path = f"{path}[]"
91
+ entries.append((array_path, is_required))
92
+ items = prop_schema.get("items")
93
+ if isinstance(items, dict):
94
+ entries.extend(_flatten_schema_params(items, prefix=f"{array_path}.", parent_required=is_required, seen_stack=seen_stack))
95
+ if _is_object_schema(prop_schema):
96
+ entries.extend(_flatten_schema_params(prop_schema, prefix=f"{path}.", parent_required=is_required, seen_stack=seen_stack))
97
+
98
+ return _dedupe_param_entries(entries)
99
+ finally:
100
+ seen_stack.remove(schema_id)
101
+
102
+
103
+ def _cache_field_value(field: Any, key: str) -> Any:
104
+ if isinstance(field, dict):
105
+ return field.get(key)
106
+ return getattr(field, key, None)
107
+
108
+
109
+ def _flatten_cache_properties(properties: dict[str, Any], prefix: str) -> list[str]:
110
+ entries: list[str] = []
111
+ for prop_name, prop in properties.items():
112
+ path = f"{prefix}{prop_name}" if prefix else prop_name
113
+ entries.append(path)
114
+
115
+ prop_type = _cache_field_value(prop, "type")
116
+ prop_properties = _cache_field_value(prop, "properties")
117
+
118
+ if _type_includes(prop_type, "array"):
119
+ array_path = f"{path}[]"
120
+ entries.append(array_path)
121
+ if isinstance(prop_properties, dict):
122
+ entries.extend(_flatten_cache_properties(prop_properties, prefix=f"{array_path}."))
123
+ elif isinstance(prop_properties, dict):
124
+ entries.extend(_flatten_cache_properties(prop_properties, prefix=f"{path}."))
125
+
126
+ return entries
127
+
128
+
129
+ def _flatten_cache_field_paths(field: Any) -> list[str]:
130
+ field_name = _cache_field_value(field, "name")
131
+ if not isinstance(field_name, str) or not field_name:
132
+ return []
133
+
134
+ field_type = _cache_field_value(field, "type")
135
+ field_properties = _cache_field_value(field, "properties")
136
+
137
+ entries = [field_name]
138
+ if _type_includes(field_type, "array"):
139
+ array_path = f"{field_name}[]"
140
+ entries.append(array_path)
141
+ if isinstance(field_properties, dict):
142
+ entries.extend(_flatten_cache_properties(field_properties, prefix=f"{array_path}."))
143
+ elif isinstance(field_properties, dict):
144
+ entries.extend(_flatten_cache_properties(field_properties, prefix=f"{field_name}."))
145
+
146
+ return entries
147
+
148
+
149
+ def _dedupe_strings(values: list[str]) -> list[str]:
150
+ seen: set[str] = set()
151
+ ordered: list[str] = []
152
+ for value in values:
153
+ if value not in seen:
154
+ seen.add(value)
155
+ ordered.append(value)
156
+ return ordered
157
+
158
+
159
+ def _collect_search_field_paths(model: ConnectorModelProtocol) -> dict[str, list[str]]:
160
+ search_field_paths = getattr(model, "search_field_paths", None)
161
+ if isinstance(search_field_paths, dict) and search_field_paths:
162
+ normalized: dict[str, list[str]] = {}
163
+ for entity, fields in search_field_paths.items():
164
+ if not isinstance(entity, str) or not entity:
165
+ continue
166
+ if isinstance(fields, list):
167
+ normalized[entity] = _dedupe_strings([field for field in fields if isinstance(field, str) and field])
168
+ return normalized
169
+
170
+ openapi_spec = getattr(model, "openapi_spec", None)
171
+ info = getattr(openapi_spec, "info", None)
172
+ cache_config = getattr(info, "x_airbyte_cache", None)
173
+ entities = getattr(cache_config, "entities", None)
174
+ if not isinstance(entities, list):
175
+ return {}
176
+
177
+ search_fields: dict[str, list[str]] = {}
178
+ for entity in entities:
179
+ entity_name = _cache_field_value(entity, "entity")
180
+ if not isinstance(entity_name, str) or not entity_name:
181
+ continue
182
+
183
+ fields = _cache_field_value(entity, "fields") or []
184
+ if not isinstance(fields, list):
185
+ continue
186
+ field_paths: list[str] = []
187
+ for field in fields:
188
+ field_paths.extend(_flatten_cache_field_paths(field))
189
+
190
+ search_fields[entity_name] = _dedupe_strings(field_paths)
191
+
192
+ return search_fields
193
+
194
+
195
+ def _format_search_param_signature() -> str:
196
+ params = ["query*", "limit?", "cursor?", "fields?"]
197
+ return f"({', '.join(params)})"
198
+
199
+
21
200
  class EndpointProtocol(Protocol):
22
201
  """Protocol defining the expected interface for endpoint parameters.
23
202
 
@@ -54,6 +233,9 @@ class ConnectorModelProtocol(Protocol):
54
233
  @property
55
234
  def openapi_spec(self) -> Any: ...
56
235
 
236
+ @property
237
+ def search_field_paths(self) -> dict[str, list[str]] | None: ...
238
+
57
239
 
58
240
  def format_param_signature(endpoint: EndpointProtocol) -> str:
59
241
  """Format parameter signature for an endpoint action.
@@ -86,9 +268,12 @@ def format_param_signature(endpoint: EndpointProtocol) -> str:
86
268
  required = schema.get("required", False)
87
269
  params.append(f"{name}{'*' if required else '?'}")
88
270
 
89
- # Body fields
90
- if request_schema:
91
- required_fields = set(request_schema.get("required", []))
271
+ # Body fields (include nested params from schema when available)
272
+ if isinstance(request_schema, dict):
273
+ for name, required in _flatten_schema_params(request_schema):
274
+ params.append(f"{name}{'*' if required else '?'}")
275
+ elif request_schema:
276
+ required_fields = set(request_schema.get("required", [])) if isinstance(request_schema, dict) else set()
92
277
  for name in body_fields:
93
278
  params.append(f"{name}{'*' if name in required_fields else '?'}")
94
279
 
@@ -99,7 +284,7 @@ def describe_entities(model: ConnectorModelProtocol) -> list[dict[str, Any]]:
99
284
  """Generate entity descriptions from ConnectorModel.
100
285
 
101
286
  Returns a list of entity descriptions with detailed parameter information
102
- for each action. This is used by generated connectors' describe() method.
287
+ for each action. This is used by generated connectors' list_entities() method.
103
288
 
104
289
  Args:
105
290
  model: Object conforming to ConnectorModelProtocol (e.g., ConnectorModel)
@@ -203,8 +388,8 @@ def generate_tool_description(model: ConnectorModelProtocol) -> str:
203
388
  - Response structure documentation with pagination hints
204
389
  - Example questions if available in the OpenAPI spec
205
390
 
206
- This is used by the Connector.describe class method decorator to populate
207
- function docstrings for AI framework integration.
391
+ This is used by the Connector.tool_utils decorator to populate function
392
+ docstrings for AI framework integration.
208
393
 
209
394
  Args:
210
395
  model: Object conforming to ConnectorModelProtocol (e.g., ConnectorModel)
@@ -213,8 +398,11 @@ def generate_tool_description(model: ConnectorModelProtocol) -> str:
213
398
  Formatted description string suitable for AI tool documentation
214
399
  """
215
400
  lines = []
401
+ # NOTE: Do not insert blank lines in the docstring; pydantic-ai parsing truncates
402
+ # at the first empty line and only keeps the initial section.
216
403
 
217
404
  # Entity/action parameter details (including pagination params like limit, starting_after)
405
+ search_field_paths = _collect_search_field_paths(model)
218
406
  lines.append("ENTITIES AND PARAMETERS:")
219
407
  for entity in model.entities:
220
408
  lines.append(f" {entity.name}:")
@@ -228,14 +416,41 @@ def generate_tool_description(model: ConnectorModelProtocol) -> str:
228
416
  lines.append(f" - {action_str}{param_sig}")
229
417
  else:
230
418
  lines.append(f" - {action_str}()")
419
+ if entity.name in search_field_paths:
420
+ search_sig = _format_search_param_signature()
421
+ lines.append(f" - search{search_sig}")
231
422
 
232
423
  # Response structure (brief, includes pagination hint)
233
- lines.append("")
234
424
  lines.append("RESPONSE STRUCTURE:")
235
425
  lines.append(" - list/api_search: {data: [...], meta: {has_more: bool}}")
236
426
  lines.append(" - get: Returns entity directly (no envelope)")
237
427
  lines.append(" To paginate: pass starting_after=<last_id> while has_more is true")
238
428
 
429
+ lines.append("GUIDELINES:")
430
+ lines.append(' - Prefer cached search over direct API calls when using execute(): action="search" whenever possible.')
431
+ lines.append(" - Direct API actions (list/get/download) are slower and should be used only if search cannot answer the query.")
432
+ lines.append(" - Keep results small: use params.fields, params.query.filter, small params.limit, and cursor pagination.")
433
+ lines.append(" - If output is too large, refine the query with tighter filters/fields/limit.")
434
+
435
+ if search_field_paths:
436
+ lines.append("SEARCH (PREFERRED):")
437
+ lines.append(' execute(entity, action="search", params={')
438
+ lines.append(' "query": {"filter": <condition>, "sort": [{"field": "asc|desc"}, ...]},')
439
+ lines.append(' "limit": <int>, "cursor": <str>, "fields": ["field", "nested.field", ...]')
440
+ lines.append(" })")
441
+ lines.append(' Example: {"query": {"filter": {"eq": {"title": "Intro to Airbyte | Miinto"}}}, "limit": 1,')
442
+ lines.append(' "fields": ["id", "title", "started", "primaryUserId"]}')
443
+ lines.append(" Conditions are composable:")
444
+ lines.append(" - eq, neq, gt, gte, lt, lte, in, like, fuzzy, keyword, contains, any")
445
+ lines.append(' - and/or/not to combine conditions (e.g., {"and": [cond1, cond2]})')
446
+
447
+ lines.append("SEARCHABLE FIELDS:")
448
+ for entity_name, field_paths in search_field_paths.items():
449
+ if field_paths:
450
+ lines.append(f" {entity_name}: {', '.join(field_paths)}")
451
+ else:
452
+ lines.append(f" {entity_name}: (no fields listed)")
453
+
239
454
  # Add example questions if available in openapi_spec
240
455
  openapi_spec = getattr(model, "openapi_spec", None)
241
456
  if openapi_spec:
@@ -245,18 +460,15 @@ def generate_tool_description(model: ConnectorModelProtocol) -> str:
245
460
  if example_questions:
246
461
  supported = getattr(example_questions, "supported", None)
247
462
  if supported:
248
- lines.append("")
249
463
  lines.append("EXAMPLE QUESTIONS:")
250
464
  for q in supported[:MAX_EXAMPLE_QUESTIONS]:
251
465
  lines.append(f" - {q}")
252
466
 
253
467
  # Generic parameter description for function signature
254
- lines.append("")
255
468
  lines.append("FUNCTION PARAMETERS:")
256
469
  lines.append(" - entity: Entity name (string)")
257
470
  lines.append(" - action: Operation to perform (string)")
258
471
  lines.append(" - params: Operation parameters (dict) - see entity details above")
259
- lines.append("")
260
472
  lines.append("Parameter markers: * = required, ? = optional")
261
473
 
262
474
  return "\n".join(lines)