openapi-haystack 0.1.0__tar.gz → 1.0.0__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.
- openapi_haystack-1.0.0/CHANGELOG.md +9 -0
- {openapi_haystack-0.1.0 → openapi_haystack-1.0.0}/PKG-INFO +4 -2
- {openapi_haystack-0.1.0 → openapi_haystack-1.0.0}/README.md +2 -0
- {openapi_haystack-0.1.0 → openapi_haystack-1.0.0}/pyproject.toml +1 -0
- {openapi_haystack-0.1.0 → openapi_haystack-1.0.0}/src/haystack_integrations/components/connectors/openapi/openapi.py +15 -5
- {openapi_haystack-0.1.0 → openapi_haystack-1.0.0}/tests/test_openapi_connector.py +80 -50
- {openapi_haystack-0.1.0 → openapi_haystack-1.0.0}/.gitignore +0 -0
- {openapi_haystack-0.1.0 → openapi_haystack-1.0.0}/LICENSE.txt +0 -0
- {openapi_haystack-0.1.0 → openapi_haystack-1.0.0}/pydoc/config_docusaurus.yml +0 -0
- {openapi_haystack-0.1.0 → openapi_haystack-1.0.0}/src/haystack_integrations/components/connectors/openapi/__init__.py +0 -0
- {openapi_haystack-0.1.0 → openapi_haystack-1.0.0}/src/haystack_integrations/components/connectors/openapi/openapi_service.py +0 -0
- {openapi_haystack-0.1.0 → openapi_haystack-1.0.0}/src/haystack_integrations/components/connectors/py.typed +0 -0
- {openapi_haystack-0.1.0 → openapi_haystack-1.0.0}/src/haystack_integrations/components/converters/openapi/__init__.py +0 -0
- {openapi_haystack-0.1.0 → openapi_haystack-1.0.0}/src/haystack_integrations/components/converters/openapi/openapi_functions.py +0 -0
- {openapi_haystack-0.1.0 → openapi_haystack-1.0.0}/src/haystack_integrations/components/converters/py.typed +0 -0
- {openapi_haystack-0.1.0 → openapi_haystack-1.0.0}/tests/__init__.py +0 -0
- {openapi_haystack-0.1.0 → openapi_haystack-1.0.0}/tests/conftest.py +0 -0
- {openapi_haystack-0.1.0 → openapi_haystack-1.0.0}/tests/test_files/json/complex_types_openai_spec.json +0 -0
- {openapi_haystack-0.1.0 → openapi_haystack-1.0.0}/tests/test_files/json/complex_types_openapi_service.json +0 -0
- {openapi_haystack-0.1.0 → openapi_haystack-1.0.0}/tests/test_openapi_functions.py +0 -0
- {openapi_haystack-0.1.0 → openapi_haystack-1.0.0}/tests/test_openapi_service.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: openapi-haystack
|
|
3
|
-
Version:
|
|
3
|
+
Version: 1.0.0
|
|
4
4
|
Summary: Haystack integration for openapi
|
|
5
5
|
Project-URL: Documentation, https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/openapi#readme
|
|
6
6
|
Project-URL: Issues, https://github.com/deepset-ai/haystack-core-integrations/issues
|
|
@@ -38,3 +38,5 @@ Description-Content-Type: text/markdown
|
|
|
38
38
|
## Contributing
|
|
39
39
|
|
|
40
40
|
Refer to the general [Contribution Guidelines](https://github.com/deepset-ai/haystack-core-integrations/blob/main/CONTRIBUTING.md).
|
|
41
|
+
|
|
42
|
+
To run integration tests locally, you need to export the `OPENAI_API_KEY` environment variable.
|
|
@@ -61,11 +61,18 @@ class OpenAPIConnector:
|
|
|
61
61
|
self.credentials = credentials
|
|
62
62
|
self.service_kwargs = service_kwargs or {}
|
|
63
63
|
|
|
64
|
-
self.client =
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
self.client: OpenAPIClient | None = None
|
|
65
|
+
|
|
66
|
+
def warm_up(self) -> None:
|
|
67
|
+
"""
|
|
68
|
+
Initialize the OpenAPI client.
|
|
69
|
+
"""
|
|
70
|
+
if self.client is None:
|
|
71
|
+
self.client = OpenAPIClient.from_spec(
|
|
72
|
+
openapi_spec=self.openapi_spec,
|
|
73
|
+
credentials=self.credentials.resolve_value() if self.credentials else None,
|
|
74
|
+
**self.service_kwargs,
|
|
75
|
+
)
|
|
69
76
|
|
|
70
77
|
def to_dict(self) -> dict[str, Any]:
|
|
71
78
|
"""
|
|
@@ -91,6 +98,9 @@ class OpenAPIConnector:
|
|
|
91
98
|
:param arguments: Optional parameters for the endpoint (query, path, or body parameters)
|
|
92
99
|
:return: Dictionary containing the service response
|
|
93
100
|
"""
|
|
101
|
+
self.warm_up()
|
|
102
|
+
assert self.client is not None # noqa: S101
|
|
103
|
+
|
|
94
104
|
payload = {"name": operation_id, "arguments": arguments or {}}
|
|
95
105
|
|
|
96
106
|
# Invoke the endpoint using openapi-llm client
|
|
@@ -38,8 +38,8 @@ def mock_client():
|
|
|
38
38
|
yield client_instance
|
|
39
39
|
|
|
40
40
|
|
|
41
|
-
class
|
|
42
|
-
def test_init(self
|
|
41
|
+
class TestInitialization:
|
|
42
|
+
def test_init(self):
|
|
43
43
|
# Test initialization with credentials and service_kwargs
|
|
44
44
|
service_kwargs = {"allowed_operations": ["search"]}
|
|
45
45
|
connector = OpenAPIConnector(
|
|
@@ -48,13 +48,17 @@ class TestOpenAPIConnector:
|
|
|
48
48
|
assert connector.openapi_spec == MOCK_OPENAPI_SPEC
|
|
49
49
|
assert connector.credentials.resolve_value() == "test-token"
|
|
50
50
|
assert connector.service_kwargs == service_kwargs
|
|
51
|
+
assert connector.client is None
|
|
51
52
|
|
|
52
53
|
# Test initialization without credentials and service_kwargs
|
|
53
54
|
connector = OpenAPIConnector(openapi_spec=MOCK_OPENAPI_SPEC)
|
|
54
55
|
assert connector.credentials is None
|
|
55
56
|
assert connector.service_kwargs == {}
|
|
57
|
+
assert connector.client is None
|
|
56
58
|
|
|
57
|
-
|
|
59
|
+
|
|
60
|
+
class TestSerialization:
|
|
61
|
+
def test_to_dict(self, monkeypatch):
|
|
58
62
|
monkeypatch.setenv("ENV_VAR", "test-api-key")
|
|
59
63
|
service_kwargs = {"allowed_operations": ["search"]}
|
|
60
64
|
connector = OpenAPIConnector(
|
|
@@ -70,7 +74,7 @@ class TestOpenAPIConnector:
|
|
|
70
74
|
},
|
|
71
75
|
}
|
|
72
76
|
|
|
73
|
-
def test_from_dict(self, monkeypatch
|
|
77
|
+
def test_from_dict(self, monkeypatch):
|
|
74
78
|
monkeypatch.setenv("ENV_VAR", "test-api-key")
|
|
75
79
|
service_kwargs = {"allowed_operations": ["search"]}
|
|
76
80
|
data = {
|
|
@@ -85,52 +89,9 @@ class TestOpenAPIConnector:
|
|
|
85
89
|
assert connector.openapi_spec == MOCK_OPENAPI_SPEC
|
|
86
90
|
assert connector.credentials == Secret.from_env_var("ENV_VAR")
|
|
87
91
|
assert connector.service_kwargs == service_kwargs
|
|
92
|
+
assert connector.client is None
|
|
88
93
|
|
|
89
|
-
def
|
|
90
|
-
service_kwargs = {"allowed_operations": ["search"]}
|
|
91
|
-
connector = OpenAPIConnector(
|
|
92
|
-
openapi_spec=MOCK_OPENAPI_SPEC, credentials=Secret.from_token("test-token"), service_kwargs=service_kwargs
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
# Mock the response from the client
|
|
96
|
-
mock_client.invoke.return_value = {"results": ["test result"]}
|
|
97
|
-
|
|
98
|
-
# Test with arguments
|
|
99
|
-
response = connector.run(operation_id="search", arguments={"q": "test query"})
|
|
100
|
-
mock_client.invoke.assert_called_with({"name": "search", "arguments": {"q": "test query"}})
|
|
101
|
-
assert response == {"response": {"results": ["test result"]}}
|
|
102
|
-
|
|
103
|
-
# Test without arguments
|
|
104
|
-
response = connector.run(operation_id="search")
|
|
105
|
-
mock_client.invoke.assert_called_with({"name": "search", "arguments": {}})
|
|
106
|
-
assert response == {"response": {"results": ["test result"]}}
|
|
107
|
-
|
|
108
|
-
def test_in_pipeline(self, mock_client):
|
|
109
|
-
mock_client.invoke.return_value = {"results": ["test result"]}
|
|
110
|
-
|
|
111
|
-
connector = OpenAPIConnector(openapi_spec=MOCK_OPENAPI_SPEC, credentials=Secret.from_token("test-token"))
|
|
112
|
-
|
|
113
|
-
pipe = Pipeline()
|
|
114
|
-
pipe.add_component("api", connector)
|
|
115
|
-
|
|
116
|
-
# Test pipeline execution
|
|
117
|
-
results = pipe.run(data={"api": {"operation_id": "search", "arguments": {"q": "test query"}}})
|
|
118
|
-
|
|
119
|
-
assert results == {"api": {"response": {"results": ["test result"]}}}
|
|
120
|
-
|
|
121
|
-
def test_from_dict_fail_wo_env_var(self, monkeypatch, mock_client):
|
|
122
|
-
monkeypatch.delenv("ENV_VAR", raising=False)
|
|
123
|
-
data = {
|
|
124
|
-
"type": "haystack_integrations.components.connectors.openapi.openapi.OpenAPIConnector",
|
|
125
|
-
"init_parameters": {
|
|
126
|
-
"openapi_spec": MOCK_OPENAPI_SPEC,
|
|
127
|
-
"credentials": {"env_vars": ["ENV_VAR"], "type": "env_var", "strict": True},
|
|
128
|
-
},
|
|
129
|
-
}
|
|
130
|
-
with pytest.raises(ValueError, match=r"None of the .* environment variables are set"):
|
|
131
|
-
OpenAPIConnector.from_dict(data)
|
|
132
|
-
|
|
133
|
-
def test_serde_in_pipeline(self, monkeypatch, mock_client):
|
|
94
|
+
def test_serde_in_pipeline(self, monkeypatch):
|
|
134
95
|
"""
|
|
135
96
|
Test serialization/deserialization of OpenAPIConnector in a Pipeline,
|
|
136
97
|
including detailed dictionary validation
|
|
@@ -179,8 +140,77 @@ class TestOpenAPIConnector:
|
|
|
179
140
|
assert loaded_connector.service_kwargs == connector.service_kwargs
|
|
180
141
|
|
|
181
142
|
|
|
143
|
+
class TestComponentLifecycle:
|
|
144
|
+
def test_key_resolved_at_warm_up_not_init(self, monkeypatch):
|
|
145
|
+
monkeypatch.delenv("ENV_VAR", raising=False)
|
|
146
|
+
data = {
|
|
147
|
+
"type": "haystack_integrations.components.connectors.openapi.openapi.OpenAPIConnector",
|
|
148
|
+
"init_parameters": {
|
|
149
|
+
"openapi_spec": MOCK_OPENAPI_SPEC,
|
|
150
|
+
"credentials": {"env_vars": ["ENV_VAR"], "type": "env_var", "strict": True},
|
|
151
|
+
},
|
|
152
|
+
}
|
|
153
|
+
connector = OpenAPIConnector.from_dict(data)
|
|
154
|
+
assert connector.client is None
|
|
155
|
+
|
|
156
|
+
with pytest.raises(ValueError, match=r"None of the .* environment variables are set"):
|
|
157
|
+
connector.warm_up()
|
|
158
|
+
|
|
159
|
+
def test_warm_up_is_idempotent(self):
|
|
160
|
+
connector = OpenAPIConnector(
|
|
161
|
+
openapi_spec=MOCK_OPENAPI_SPEC,
|
|
162
|
+
credentials=Secret.from_token("test-token"),
|
|
163
|
+
service_kwargs={"allowed_operations": ["search"]},
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
with patch("haystack_integrations.components.connectors.openapi.openapi.OpenAPIClient") as client_class:
|
|
167
|
+
connector.warm_up()
|
|
168
|
+
connector.warm_up()
|
|
169
|
+
|
|
170
|
+
client_class.from_spec.assert_called_once_with(
|
|
171
|
+
openapi_spec=MOCK_OPENAPI_SPEC,
|
|
172
|
+
credentials="test-token",
|
|
173
|
+
allowed_operations=["search"],
|
|
174
|
+
)
|
|
175
|
+
assert connector.client is client_class.from_spec.return_value
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class TestRun:
|
|
179
|
+
def test_run(self, mock_client):
|
|
180
|
+
service_kwargs = {"allowed_operations": ["search"]}
|
|
181
|
+
connector = OpenAPIConnector(
|
|
182
|
+
openapi_spec=MOCK_OPENAPI_SPEC, credentials=Secret.from_token("test-token"), service_kwargs=service_kwargs
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
# Mock the response from the client
|
|
186
|
+
mock_client.invoke.return_value = {"results": ["test result"]}
|
|
187
|
+
|
|
188
|
+
# Test with arguments
|
|
189
|
+
response = connector.run(operation_id="search", arguments={"q": "test query"})
|
|
190
|
+
mock_client.invoke.assert_called_with({"name": "search", "arguments": {"q": "test query"}})
|
|
191
|
+
assert response == {"response": {"results": ["test result"]}}
|
|
192
|
+
|
|
193
|
+
# Test without arguments
|
|
194
|
+
response = connector.run(operation_id="search")
|
|
195
|
+
mock_client.invoke.assert_called_with({"name": "search", "arguments": {}})
|
|
196
|
+
assert response == {"response": {"results": ["test result"]}}
|
|
197
|
+
|
|
198
|
+
def test_run_in_pipeline(self, mock_client):
|
|
199
|
+
mock_client.invoke.return_value = {"results": ["test result"]}
|
|
200
|
+
|
|
201
|
+
connector = OpenAPIConnector(openapi_spec=MOCK_OPENAPI_SPEC, credentials=Secret.from_token("test-token"))
|
|
202
|
+
|
|
203
|
+
pipe = Pipeline()
|
|
204
|
+
pipe.add_component("api", connector)
|
|
205
|
+
|
|
206
|
+
# Test pipeline execution
|
|
207
|
+
results = pipe.run(data={"api": {"operation_id": "search", "arguments": {"q": "test query"}}})
|
|
208
|
+
|
|
209
|
+
assert results == {"api": {"response": {"results": ["test result"]}}}
|
|
210
|
+
|
|
211
|
+
|
|
182
212
|
@pytest.mark.integration
|
|
183
|
-
class
|
|
213
|
+
class TestIntegration:
|
|
184
214
|
@pytest.mark.integration
|
|
185
215
|
def test_open_meteo_integration(self):
|
|
186
216
|
open_meteo_spec = {
|
|
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
|