apify-haystack 0.1.8b1__tar.gz → 0.1.8b2__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.
- {apify_haystack-0.1.8b1 → apify_haystack-0.1.8b2}/CHANGELOG.md +1 -0
- {apify_haystack-0.1.8b1 → apify_haystack-0.1.8b2}/CONTRIBUTING.md +2 -0
- {apify_haystack-0.1.8b1 → apify_haystack-0.1.8b2}/PKG-INFO +1 -1
- {apify_haystack-0.1.8b1 → apify_haystack-0.1.8b2}/pyproject.toml +9 -1
- {apify_haystack-0.1.8b1 → apify_haystack-0.1.8b2}/pyproject.toml.orig +3 -1
- {apify_haystack-0.1.8b1 → apify_haystack-0.1.8b2}/src/apify_haystack/__init__.py +3 -3
- apify_haystack-0.1.8b1/src/apify_haystack/actor_tool.py → apify_haystack-0.1.8b2/src/apify_haystack/_actor_tool.py +19 -15
- {apify_haystack-0.1.8b1 → apify_haystack-0.1.8b2}/src/apify_haystack/_client.py +7 -7
- apify_haystack-0.1.8b1/src/apify_haystack/apify_dataset.py → apify_haystack-0.1.8b2/src/apify_haystack/_dataset.py +26 -17
- apify_haystack-0.1.8b1/src/apify_haystack/web_search.py → apify_haystack-0.1.8b2/src/apify_haystack/_web_search.py +12 -9
- {apify_haystack-0.1.8b1 → apify_haystack-0.1.8b2}/LICENSE +0 -0
- {apify_haystack-0.1.8b1 → apify_haystack-0.1.8b2}/README.md +0 -0
- {apify_haystack-0.1.8b1 → apify_haystack-0.1.8b2}/src/apify_haystack/_constants.py +0 -0
- {apify_haystack-0.1.8b1 → apify_haystack-0.1.8b2}/src/apify_haystack/py.typed +0 -0
|
@@ -28,6 +28,7 @@ All notable changes to this project will be documented in this file.
|
|
|
28
28
|
### 🚜 Refactor
|
|
29
29
|
|
|
30
30
|
- [**breaking**] Make the constants module private ([#68](https://github.com/apify/apify-haystack/pull/68)) ([8f5dbbd](https://github.com/apify/apify-haystack/commit/8f5dbbd7a4fa0e8c3c7c8036799d40744ee47624)) by [@vdusek](https://github.com/vdusek)
|
|
31
|
+
- [**breaking**] Make all modules private and export the public API from the package root ([#77](https://github.com/apify/apify-haystack/pull/77)) ([4e6ccca](https://github.com/apify/apify-haystack/commit/4e6cccae287999f9ceb72fe9b67c2305b1206113)) by [@vdusek](https://github.com/vdusek)
|
|
31
32
|
|
|
32
33
|
### ⚙️ Miscellaneous Tasks
|
|
33
34
|
|
|
@@ -32,6 +32,8 @@ We use [ruff](https://docs.astral.sh/ruff/) for linting and formatting, [ty](htt
|
|
|
32
32
|
|
|
33
33
|
Unit tests (`tests/unit`) are isolated from the Apify API. Run them with `uv run poe unit-tests`, or a single test with `uv run pytest tests/unit/<path>.py::<test_name>`.
|
|
34
34
|
|
|
35
|
+
Integration tests (`tests/integration`) run the components against the Apify platform, so they need an Apify API token in the `APIFY_TOKEN` environment variable and are skipped without one. Run them with `uv run poe integration-tests`. They run real Actors on that account, which costs a little. CI runs them on pull requests from this repository and on pushes to `main`, with the test user's token.
|
|
36
|
+
|
|
35
37
|
## Documentation
|
|
36
38
|
|
|
37
39
|
We adhere to the [Google docstring format](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html) for documenting our codebase. Every user-facing class or method is documented. Documentation standards are enforced using [Ruff](https://docs.astral.sh/ruff/).
|
|
@@ -4,7 +4,7 @@ build-backend = "uv_build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "apify_haystack"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.8b2"
|
|
8
8
|
description = "Apify-haystack integration"
|
|
9
9
|
license = "Apache-2.0"
|
|
10
10
|
license-files = ["LICENSE"]
|
|
@@ -191,6 +191,14 @@ help = "Run unit tests"
|
|
|
191
191
|
cmd = "uv run pytest --numprocesses=${TESTS_CONCURRENCY:-auto} --cov=src/apify_haystack --cov-report=xml:coverage-unit.xml tests/unit"
|
|
192
192
|
help = "Run unit tests with an XML coverage report"
|
|
193
193
|
|
|
194
|
+
[tool.poe.tasks.integration-tests]
|
|
195
|
+
cmd = "uv run pytest --numprocesses=${TESTS_CONCURRENCY:-auto} tests/integration"
|
|
196
|
+
help = "Run integration tests against the Apify platform"
|
|
197
|
+
|
|
198
|
+
[tool.poe.tasks.integration-tests-cov]
|
|
199
|
+
cmd = "uv run pytest --numprocesses=${TESTS_CONCURRENCY:-auto} --cov=src/apify_haystack --cov-report=xml:coverage-integration.xml tests/integration"
|
|
200
|
+
help = "Run integration tests with an XML coverage report"
|
|
201
|
+
|
|
194
202
|
[tool.poe.tasks.check-code]
|
|
195
203
|
sequence = [
|
|
196
204
|
"lint",
|
|
@@ -4,7 +4,7 @@ build-backend = "uv_build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "apify_haystack"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.8b2"
|
|
8
8
|
description = "Apify-haystack integration"
|
|
9
9
|
authors = [{ name = "Apify Technologies s.r.o.", email = "support@apify.com" }]
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -156,6 +156,8 @@ build = { cmd = "uv build", help = "Build the package" }
|
|
|
156
156
|
type-check = { cmd = "uv run ty check", help = "Run the type checker" }
|
|
157
157
|
unit-tests = { cmd = "uv run pytest --numprocesses=${TESTS_CONCURRENCY:-auto} tests/unit", help = "Run unit tests" }
|
|
158
158
|
unit-tests-cov = { cmd = "uv run pytest --numprocesses=${TESTS_CONCURRENCY:-auto} --cov=src/apify_haystack --cov-report=xml:coverage-unit.xml tests/unit", help = "Run unit tests with an XML coverage report" }
|
|
159
|
+
integration-tests = { cmd = "uv run pytest --numprocesses=${TESTS_CONCURRENCY:-auto} tests/integration", help = "Run integration tests against the Apify platform" }
|
|
160
|
+
integration-tests-cov = { cmd = "uv run pytest --numprocesses=${TESTS_CONCURRENCY:-auto} --cov=src/apify_haystack --cov-report=xml:coverage-integration.xml tests/integration", help = "Run integration tests with an XML coverage report" }
|
|
159
161
|
check-code = { sequence = ["lint", "type-check", "unit-tests"], help = "Run lint, type check, and unit tests" }
|
|
160
162
|
|
|
161
163
|
[tool.poe.tasks.install-dev]
|
|
@@ -2,14 +2,14 @@ from importlib import metadata
|
|
|
2
2
|
|
|
3
3
|
__version__ = metadata.version('apify-haystack')
|
|
4
4
|
|
|
5
|
-
from apify_haystack.
|
|
6
|
-
from apify_haystack.
|
|
5
|
+
from apify_haystack._actor_tool import ApifyActorTool
|
|
6
|
+
from apify_haystack._dataset import (
|
|
7
7
|
ApifyDatasetFromActorCall,
|
|
8
8
|
ApifyDatasetFromTaskCall,
|
|
9
9
|
ApifyDatasetLoader,
|
|
10
10
|
default_dataset_mapping_function,
|
|
11
11
|
)
|
|
12
|
-
from apify_haystack.
|
|
12
|
+
from apify_haystack._web_search import ApifyWebSearch
|
|
13
13
|
|
|
14
14
|
__all__ = [
|
|
15
15
|
'ApifyActorTool',
|
|
@@ -10,11 +10,11 @@ from haystack.tools import Tool
|
|
|
10
10
|
from haystack.utils import Secret, deserialize_secrets_inplace
|
|
11
11
|
|
|
12
12
|
from apify_haystack._client import (
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
ApifyClientComponent,
|
|
14
|
+
create_client,
|
|
15
|
+
get_dataset_id,
|
|
16
|
+
to_decimal,
|
|
17
|
+
to_timedelta,
|
|
18
18
|
)
|
|
19
19
|
|
|
20
20
|
# OpenAI limits a function name to 64 characters and a description to 1024.
|
|
@@ -30,15 +30,16 @@ UNTRUSTED_CONTENT_END = '<<<END_EXTERNAL_UNTRUSTED_CONTENT>>>'
|
|
|
30
30
|
_PROPERTY_SCHEMA_KEYS = ('type', 'description', 'enum', 'default', 'items', 'minimum', 'maximum')
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
class ApifyActorTool(
|
|
33
|
+
class ApifyActorTool(ApifyClientComponent, Tool):
|
|
34
34
|
"""A Haystack tool that runs an Apify Actor and returns the items of its default dataset.
|
|
35
35
|
|
|
36
36
|
Use it to give a Haystack `Agent` access to any Actor from Apify Store. The tool takes its parameters from the
|
|
37
37
|
Actor's input schema and its description from the Actor's definition, so the LLM sees the same inputs as a user
|
|
38
38
|
of the Actor. Reading them needs one Apify API request when the tool is created without `parameters`.
|
|
39
39
|
|
|
40
|
-
The LLM gets the dataset items as JSON between the
|
|
41
|
-
since scraped content can carry prompt injections. `invoke` and
|
|
40
|
+
The LLM gets the dataset items as JSON between the `<<<EXTERNAL_UNTRUSTED_CONTENT>>>` and
|
|
41
|
+
`<<<END_EXTERNAL_UNTRUSTED_CONTENT>>>` markers, since scraped content can carry prompt injections. `invoke` and
|
|
42
|
+
`invoke_async` return the items unwrapped.
|
|
42
43
|
For details, see https://docs.apify.com/platform/integrations/haystack
|
|
43
44
|
|
|
44
45
|
Example:
|
|
@@ -53,6 +54,9 @@ class ApifyActorTool(_ApifyClientComponent, Tool):
|
|
|
53
54
|
agent = Agent(chat_generator=OpenAIChatGenerator(), tools=[tool])
|
|
54
55
|
"""
|
|
55
56
|
|
|
57
|
+
# Serialized pipelines store the class by this public path, so it does not depend on the module layout.
|
|
58
|
+
__module__ = 'apify_haystack'
|
|
59
|
+
|
|
56
60
|
def __init__(
|
|
57
61
|
self,
|
|
58
62
|
actor_id: str,
|
|
@@ -152,10 +156,10 @@ class ApifyActorTool(_ApifyClientComponent, Tool):
|
|
|
152
156
|
run_input=run_input,
|
|
153
157
|
build=self.build,
|
|
154
158
|
memory_mbytes=self.memory_mbytes,
|
|
155
|
-
run_timeout=
|
|
156
|
-
max_total_charge_usd=
|
|
159
|
+
run_timeout=to_timedelta(self.timeout_secs),
|
|
160
|
+
max_total_charge_usd=to_decimal(self.max_total_charge_usd),
|
|
157
161
|
)
|
|
158
|
-
dataset_id =
|
|
162
|
+
dataset_id = get_dataset_id(run, f'Actor {self.actor_id}')
|
|
159
163
|
return client.dataset(dataset_id).list_items(clean=True, limit=self.dataset_items_limit).items
|
|
160
164
|
|
|
161
165
|
async def _run_actor_async(self, **run_input: Any) -> list[dict]:
|
|
@@ -164,15 +168,15 @@ class ApifyActorTool(_ApifyClientComponent, Tool):
|
|
|
164
168
|
run_input=run_input,
|
|
165
169
|
build=self.build,
|
|
166
170
|
memory_mbytes=self.memory_mbytes,
|
|
167
|
-
run_timeout=
|
|
168
|
-
max_total_charge_usd=
|
|
171
|
+
run_timeout=to_timedelta(self.timeout_secs),
|
|
172
|
+
max_total_charge_usd=to_decimal(self.max_total_charge_usd),
|
|
169
173
|
)
|
|
170
|
-
dataset_id =
|
|
174
|
+
dataset_id = get_dataset_id(run, f'Actor {self.actor_id}')
|
|
171
175
|
return (await client.dataset(dataset_id).list_items(clean=True, limit=self.dataset_items_limit)).items
|
|
172
176
|
|
|
173
177
|
|
|
174
178
|
def _get_actor_definition(apify_token: Secret, actor_id: str) -> dict[str, Any]:
|
|
175
|
-
client =
|
|
179
|
+
client = create_client(ApifyClient, apify_token.resolve_value())
|
|
176
180
|
try:
|
|
177
181
|
build = client.actor(actor_id).default_build().get()
|
|
178
182
|
finally:
|
|
@@ -15,7 +15,7 @@ if TYPE_CHECKING:
|
|
|
15
15
|
_ClientT = TypeVar('_ClientT', ApifyClient, ApifyClientAsync)
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
class
|
|
18
|
+
class ApifyClientComponent:
|
|
19
19
|
"""Base for the components that call the Apify API.
|
|
20
20
|
|
|
21
21
|
The sync client lives between `warm_up` and `close`, the async one between `warm_up_async` and `close_async`.
|
|
@@ -47,16 +47,16 @@ class _ApifyClientComponent:
|
|
|
47
47
|
|
|
48
48
|
def _get_client(self) -> ApifyClient:
|
|
49
49
|
if self.client is None:
|
|
50
|
-
self.client =
|
|
50
|
+
self.client = create_client(ApifyClient, self.apify_token.resolve_value())
|
|
51
51
|
return self.client
|
|
52
52
|
|
|
53
53
|
def _get_async_client(self) -> ApifyClientAsync:
|
|
54
54
|
if self.async_client is None:
|
|
55
|
-
self.async_client =
|
|
55
|
+
self.async_client = create_client(ApifyClientAsync, self.apify_token.resolve_value())
|
|
56
56
|
return self.async_client
|
|
57
57
|
|
|
58
58
|
|
|
59
|
-
def
|
|
59
|
+
def create_client(client_class: type[_ClientT], token: str | None) -> _ClientT:
|
|
60
60
|
"""Create an Apify client that identifies its requests as coming from this integration."""
|
|
61
61
|
client = client_class(token, headers={'x-apify-integration-platform': HAYSTACK_INTEGRATION_PLATFORM})
|
|
62
62
|
|
|
@@ -66,7 +66,7 @@ def _create_client(client_class: type[_ClientT], token: str | None) -> _ClientT:
|
|
|
66
66
|
return client
|
|
67
67
|
|
|
68
68
|
|
|
69
|
-
def
|
|
69
|
+
def get_dataset_id(run: Run | None, label: str) -> str:
|
|
70
70
|
"""Return the default dataset of a finished run, raising unless the run succeeded."""
|
|
71
71
|
if run is None:
|
|
72
72
|
raise ValueError(f'No response found in the {label} call')
|
|
@@ -75,10 +75,10 @@ def _get_dataset_id(run: Run | None, label: str) -> str:
|
|
|
75
75
|
return run.default_dataset_id
|
|
76
76
|
|
|
77
77
|
|
|
78
|
-
def
|
|
78
|
+
def to_timedelta(seconds: int | None) -> timedelta | None:
|
|
79
79
|
return None if seconds is None else timedelta(seconds=seconds)
|
|
80
80
|
|
|
81
81
|
|
|
82
|
-
def
|
|
82
|
+
def to_decimal(value: float | None) -> Decimal | None:
|
|
83
83
|
# `str` keeps the amount as written; `Decimal(0.1)` would carry the binary float error.
|
|
84
84
|
return None if value is None else Decimal(str(value))
|
|
@@ -7,7 +7,7 @@ from haystack import component, default_from_dict, default_to_dict
|
|
|
7
7
|
from haystack.dataclasses import Document
|
|
8
8
|
from haystack.utils import Secret, deserialize_callable, serialize_callable
|
|
9
9
|
|
|
10
|
-
from apify_haystack._client import
|
|
10
|
+
from apify_haystack._client import ApifyClientComponent, get_dataset_id, to_decimal, to_timedelta
|
|
11
11
|
|
|
12
12
|
if TYPE_CHECKING:
|
|
13
13
|
from collections.abc import Callable
|
|
@@ -15,7 +15,7 @@ if TYPE_CHECKING:
|
|
|
15
15
|
from apify_client import ApifyClient, ApifyClientAsync
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
class
|
|
18
|
+
class ApifyDatasetComponent(ApifyClientComponent):
|
|
19
19
|
"""Base for the components that convert dataset items to Documents with a mapping function."""
|
|
20
20
|
|
|
21
21
|
@classmethod
|
|
@@ -29,7 +29,7 @@ class _ApifyDatasetComponent(_ApifyClientComponent):
|
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
@component
|
|
32
|
-
class ApifyDatasetLoader(
|
|
32
|
+
class ApifyDatasetLoader(ApifyDatasetComponent):
|
|
33
33
|
"""Load datasets produced by Apify Actors.
|
|
34
34
|
|
|
35
35
|
Actors are serverless functions that can be used to scrape, crawl, and extract data from the web.
|
|
@@ -49,6 +49,9 @@ class ApifyDatasetLoader(_ApifyDatasetComponent):
|
|
|
49
49
|
dataset = loader.run()
|
|
50
50
|
"""
|
|
51
51
|
|
|
52
|
+
# Serialized pipelines store the class by this public path, so it does not depend on the module layout.
|
|
53
|
+
__module__ = 'apify_haystack'
|
|
54
|
+
|
|
52
55
|
def __init__(
|
|
53
56
|
self,
|
|
54
57
|
dataset_id: str,
|
|
@@ -95,7 +98,7 @@ class ApifyDatasetLoader(_ApifyDatasetComponent):
|
|
|
95
98
|
|
|
96
99
|
|
|
97
100
|
@component
|
|
98
|
-
class ApifyDatasetFromActorCall(
|
|
101
|
+
class ApifyDatasetFromActorCall(ApifyDatasetComponent):
|
|
99
102
|
"""Get an Apify dataset by calling an Apify Actor.
|
|
100
103
|
|
|
101
104
|
Perform a call of an Apify Actor and load the produced dataset.
|
|
@@ -105,6 +108,9 @@ class ApifyDatasetFromActorCall(_ApifyDatasetComponent):
|
|
|
105
108
|
`apify_token`.
|
|
106
109
|
"""
|
|
107
110
|
|
|
111
|
+
# Serialized pipelines store the class by this public path, so it does not depend on the module layout.
|
|
112
|
+
__module__ = 'apify_haystack'
|
|
113
|
+
|
|
108
114
|
def __init__(
|
|
109
115
|
self,
|
|
110
116
|
actor_id: str,
|
|
@@ -174,10 +180,10 @@ class ApifyDatasetFromActorCall(_ApifyDatasetComponent):
|
|
|
174
180
|
run_input=run_input,
|
|
175
181
|
build=self.build,
|
|
176
182
|
memory_mbytes=self.memory_mbytes,
|
|
177
|
-
run_timeout=
|
|
178
|
-
max_total_charge_usd=
|
|
183
|
+
run_timeout=to_timedelta(self.timeout_secs),
|
|
184
|
+
max_total_charge_usd=to_decimal(self.max_total_charge_usd),
|
|
179
185
|
)
|
|
180
|
-
dataset_id =
|
|
186
|
+
dataset_id = get_dataset_id(run, f'Actor {self.actor_id}')
|
|
181
187
|
return {'documents': _load_documents(client, dataset_id, self.dataset_mapping_function)}
|
|
182
188
|
|
|
183
189
|
@component.output_types(documents=list[Document])
|
|
@@ -197,10 +203,10 @@ class ApifyDatasetFromActorCall(_ApifyDatasetComponent):
|
|
|
197
203
|
run_input=run_input,
|
|
198
204
|
build=self.build,
|
|
199
205
|
memory_mbytes=self.memory_mbytes,
|
|
200
|
-
run_timeout=
|
|
201
|
-
max_total_charge_usd=
|
|
206
|
+
run_timeout=to_timedelta(self.timeout_secs),
|
|
207
|
+
max_total_charge_usd=to_decimal(self.max_total_charge_usd),
|
|
202
208
|
)
|
|
203
|
-
dataset_id =
|
|
209
|
+
dataset_id = get_dataset_id(run, f'Actor {self.actor_id}')
|
|
204
210
|
documents = await _load_documents_async(client, dataset_id, self.dataset_mapping_function)
|
|
205
211
|
return {'documents': documents}
|
|
206
212
|
|
|
@@ -220,7 +226,7 @@ class ApifyDatasetFromActorCall(_ApifyDatasetComponent):
|
|
|
220
226
|
|
|
221
227
|
|
|
222
228
|
@component
|
|
223
|
-
class ApifyDatasetFromTaskCall(
|
|
229
|
+
class ApifyDatasetFromTaskCall(ApifyDatasetComponent):
|
|
224
230
|
"""Get an Apify dataset by calling an Apify task.
|
|
225
231
|
|
|
226
232
|
Perform a call of an Apify task and load the produced dataset.
|
|
@@ -230,6 +236,9 @@ class ApifyDatasetFromTaskCall(_ApifyDatasetComponent):
|
|
|
230
236
|
`apify_token`.
|
|
231
237
|
"""
|
|
232
238
|
|
|
239
|
+
# Serialized pipelines store the class by this public path, so it does not depend on the module layout.
|
|
240
|
+
__module__ = 'apify_haystack'
|
|
241
|
+
|
|
233
242
|
def __init__(
|
|
234
243
|
self,
|
|
235
244
|
task_id: str,
|
|
@@ -289,10 +298,10 @@ class ApifyDatasetFromTaskCall(_ApifyDatasetComponent):
|
|
|
289
298
|
task_input=task_input,
|
|
290
299
|
build=self.build,
|
|
291
300
|
memory_mbytes=self.memory_mbytes,
|
|
292
|
-
run_timeout=
|
|
293
|
-
max_total_charge_usd=
|
|
301
|
+
run_timeout=to_timedelta(self.timeout_secs),
|
|
302
|
+
max_total_charge_usd=to_decimal(self.max_total_charge_usd),
|
|
294
303
|
)
|
|
295
|
-
dataset_id =
|
|
304
|
+
dataset_id = get_dataset_id(run, f'task {self.task_id}')
|
|
296
305
|
return {'documents': _load_documents(client, dataset_id, self.dataset_mapping_function)}
|
|
297
306
|
|
|
298
307
|
@component.output_types(documents=list[Document])
|
|
@@ -312,10 +321,10 @@ class ApifyDatasetFromTaskCall(_ApifyDatasetComponent):
|
|
|
312
321
|
task_input=task_input,
|
|
313
322
|
build=self.build,
|
|
314
323
|
memory_mbytes=self.memory_mbytes,
|
|
315
|
-
run_timeout=
|
|
316
|
-
max_total_charge_usd=
|
|
324
|
+
run_timeout=to_timedelta(self.timeout_secs),
|
|
325
|
+
max_total_charge_usd=to_decimal(self.max_total_charge_usd),
|
|
317
326
|
)
|
|
318
|
-
dataset_id =
|
|
327
|
+
dataset_id = get_dataset_id(run, f'task {self.task_id}')
|
|
319
328
|
documents = await _load_documents_async(client, dataset_id, self.dataset_mapping_function)
|
|
320
329
|
return {'documents': documents}
|
|
321
330
|
|
|
@@ -6,8 +6,8 @@ from haystack import component, default_from_dict, default_to_dict
|
|
|
6
6
|
from haystack.dataclasses import Document
|
|
7
7
|
from haystack.utils import Secret
|
|
8
8
|
|
|
9
|
-
from apify_haystack._client import
|
|
10
|
-
from apify_haystack.
|
|
9
|
+
from apify_haystack._client import ApifyClientComponent, get_dataset_id, to_decimal, to_timedelta
|
|
10
|
+
from apify_haystack._dataset import default_dataset_mapping_function
|
|
11
11
|
|
|
12
12
|
RAG_WEB_BROWSER_ACTOR_ID = 'apify/rag-web-browser'
|
|
13
13
|
|
|
@@ -16,7 +16,7 @@ _MAX_TOP_K = 100
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
@component
|
|
19
|
-
class ApifyWebSearch(
|
|
19
|
+
class ApifyWebSearch(ApifyClientComponent):
|
|
20
20
|
"""Search the web with the Apify RAG Web Browser Actor and return the result pages as Documents.
|
|
21
21
|
|
|
22
22
|
The Actor searches Google, scrapes the top results, and returns their content as Markdown. Each result becomes
|
|
@@ -33,6 +33,9 @@ class ApifyWebSearch(_ApifyClientComponent):
|
|
|
33
33
|
documents, links = result['documents'], result['links']
|
|
34
34
|
"""
|
|
35
35
|
|
|
36
|
+
# Serialized pipelines store the class by this public path, so it does not depend on the module layout.
|
|
37
|
+
__module__ = 'apify_haystack'
|
|
38
|
+
|
|
36
39
|
def __init__(
|
|
37
40
|
self,
|
|
38
41
|
top_k: int = 5,
|
|
@@ -75,10 +78,10 @@ class ApifyWebSearch(_ApifyClientComponent):
|
|
|
75
78
|
client = self._get_client()
|
|
76
79
|
run = client.actor(RAG_WEB_BROWSER_ACTOR_ID).call(
|
|
77
80
|
run_input=self._run_input(query),
|
|
78
|
-
run_timeout=
|
|
79
|
-
max_total_charge_usd=
|
|
81
|
+
run_timeout=to_timedelta(self.timeout_secs),
|
|
82
|
+
max_total_charge_usd=to_decimal(self.max_total_charge_usd),
|
|
80
83
|
)
|
|
81
|
-
dataset_id =
|
|
84
|
+
dataset_id = get_dataset_id(run, f'Actor {RAG_WEB_BROWSER_ACTOR_ID}')
|
|
82
85
|
items = client.dataset(dataset_id).list_items(clean=True).items
|
|
83
86
|
return _to_output(items)
|
|
84
87
|
|
|
@@ -92,10 +95,10 @@ class ApifyWebSearch(_ApifyClientComponent):
|
|
|
92
95
|
client = self._get_async_client()
|
|
93
96
|
run = await client.actor(RAG_WEB_BROWSER_ACTOR_ID).call(
|
|
94
97
|
run_input=self._run_input(query),
|
|
95
|
-
run_timeout=
|
|
96
|
-
max_total_charge_usd=
|
|
98
|
+
run_timeout=to_timedelta(self.timeout_secs),
|
|
99
|
+
max_total_charge_usd=to_decimal(self.max_total_charge_usd),
|
|
97
100
|
)
|
|
98
|
-
dataset_id =
|
|
101
|
+
dataset_id = get_dataset_id(run, f'Actor {RAG_WEB_BROWSER_ACTOR_ID}')
|
|
99
102
|
items = (await client.dataset(dataset_id).list_items(clean=True)).items
|
|
100
103
|
return _to_output(items)
|
|
101
104
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|