apify-haystack 0.1.8b3__tar.gz → 1.0.1__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.
@@ -2,8 +2,14 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
- <!-- git-cliff-unreleased-start -->
6
- ## 0.1.8 - **not yet released**
5
+ ## [1.0.1](https://github.com/apify/apify-haystack/releases/tag/v1.0.1) (2026-10-29)
6
+
7
+ ### 🐛 Bug Fixes
8
+
9
+ - Allow running Actors and tasks without input ([#84](https://github.com/apify/apify-haystack/pull/84)) ([3036312](https://github.com/apify/apify-haystack/commit/3036312e567dc513529e1d822db009b44ca97041)) by [@vdusek](https://github.com/vdusek)
10
+
11
+
12
+ ## [1.0.0](https://github.com/apify/apify-haystack/releases/tag/v1.0.0) (2026-10-27)
7
13
 
8
14
  ### 🚀 Features
9
15
 
@@ -19,6 +25,7 @@ All notable changes to this project will be documented in this file.
19
25
  - Add a default dataset mapping function ([#64](https://github.com/apify/apify-haystack/pull/64)) ([c25c9a3](https://github.com/apify/apify-haystack/commit/c25c9a3160ca7425737abd52986d756d009df9d8)) by [@vdusek](https://github.com/vdusek)
20
26
  - Mark ApifyActorTool output as untrusted external content ([#65](https://github.com/apify/apify-haystack/pull/65)) ([ee93080](https://github.com/apify/apify-haystack/commit/ee930801ccf7ac88ed77be9c27b9dea57a02c038)) by [@vdusek](https://github.com/vdusek)
21
27
  - Add a max_total_charge_usd limit to the Actor runs ([#70](https://github.com/apify/apify-haystack/pull/70)) ([83a8184](https://github.com/apify/apify-haystack/commit/83a81847cc4050cabd44183979440ab944350d95)) by [@vdusek](https://github.com/vdusek)
28
+ - Add support for Python 3.15 ([#37](https://github.com/apify/apify-haystack/pull/37)) ([9a1856e](https://github.com/apify/apify-haystack/commit/9a1856e3a57dd36fc1e528392dc64f089a1d4e33)) by [@vdusek](https://github.com/vdusek)
22
29
 
23
30
  ### 🐛 Bug Fixes
24
31
 
@@ -37,7 +44,6 @@ All notable changes to this project will be documented in this file.
37
44
  - [**breaking**] Drop the unused python-dotenv dependency and move examples out of the package ([#43](https://github.com/apify/apify-haystack/pull/43)) ([9b3de5e](https://github.com/apify/apify-haystack/commit/9b3de5e81babcede7d3287fcacf1189f72d02224)) by [@vdusek](https://github.com/vdusek)
38
45
 
39
46
 
40
- <!-- git-cliff-unreleased-end -->
41
47
  # Changelog
42
48
 
43
49
  ## [0.1.7](https://github.com/apify/apify-haystack/releases/tag/0.1.7) (2024-12-09)
@@ -0,0 +1,172 @@
1
+ Metadata-Version: 2.4
2
+ Name: apify_haystack
3
+ Version: 1.0.1
4
+ Summary: Apify integration for Haystack: load Actor results as Documents, search the web, and give Agents access to Apify Actors.
5
+ Keywords: agents,apify,crawler,haystack,llm,rag,scraper,scraping,web-search
6
+ Author: Apify Technologies s.r.o.
7
+ Author-email: Apify Technologies s.r.o. <support@apify.com>
8
+ License-Expression: Apache-2.0
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Programming Language :: Python :: 3.15
18
+ Classifier: Topic :: Software Development :: Libraries
19
+ Classifier: Typing :: Typed
20
+ Requires-Dist: apify-client>=3.4.0,<4.0.0
21
+ Requires-Dist: haystack-ai>=3.0.0,<4.0.0
22
+ Requires-Python: >=3.11
23
+ Project-URL: Homepage, https://apify.com
24
+ Project-URL: Changelog, https://github.com/apify/apify-haystack/blob/main/CHANGELOG.md
25
+ Project-URL: Documentation, https://docs.apify.com/platform/integrations/haystack
26
+ Project-URL: Issue Tracker, https://github.com/apify/apify-haystack/issues
27
+ Project-URL: Repository, https://github.com/apify/apify-haystack
28
+ Description-Content-Type: text/markdown
29
+
30
+ <h1 align="center">Apify-Haystack integration</h1>
31
+
32
+ <p align="center">
33
+ <strong>The official integration of the <a href="https://apify.com/">Apify</a> platform with <a href="https://haystack.deepset.ai/">Haystack</a>.</strong>
34
+ </p>
35
+
36
+ <p align="center">
37
+ <a href="https://pypi.org/project/apify-haystack/"><img src="https://badge.fury.io/py/apify-haystack.svg" alt="PyPI version"></a>
38
+ <a href="https://pypi.org/project/apify-haystack/"><img src="https://img.shields.io/pypi/dm/apify-haystack" alt="PyPI downloads"></a>
39
+ <a href="https://pypi.org/project/apify-haystack/"><img src="https://img.shields.io/badge/python-3.11%2B-blue" alt="Python versions"></a>
40
+ <a href="https://github.com/apify/apify-haystack/actions/workflows/on_main.yaml"><img src="https://github.com/apify/apify-haystack/actions/workflows/on_main.yaml/badge.svg?branch=main" alt="Build status"></a>
41
+ <a href="https://github.com/apify/apify-haystack/blob/main/LICENSE"><img src="https://img.shields.io/pypi/l/apify-haystack" alt="License"></a>
42
+ <a href="https://discord.gg/jyEM2PRvMU"><img src="https://img.shields.io/discord/801163717915574323?label=discord" alt="Chat on Discord"></a>
43
+ </p>
44
+
45
+ Apify is a platform for web scraping, data extraction, and web automation tasks.
46
+ It provides serverless applications called Actors for tasks like crawling websites and scraping Facebook, Instagram, and Google results.
47
+
48
+ Haystack is an open-source framework for building LLM applications, RAG pipelines, and agents.
49
+
50
+ For a step-by-step guide, see the [Haystack integration](https://docs.apify.com/platform/integrations/haystack) page in the Apify documentation.
51
+
52
+ ## Installation
53
+
54
+ The integration is available as the [`apify-haystack`](https://pypi.org/project/apify-haystack/) package on PyPI. It requires Python 3.11 or newer and Haystack 3.
55
+
56
+ ```sh
57
+ pip install apify-haystack
58
+ ```
59
+
60
+ Upgrading from 0.1.x? See [UPGRADING.md](https://github.com/apify/apify-haystack/blob/main/UPGRADING.md).
61
+
62
+ ## Components
63
+
64
+ - `ApifyDatasetFromActorCall` runs an Actor, waits for it to finish, and loads its default dataset.
65
+ - `ApifyDatasetFromTaskCall` does the same for a saved Actor task.
66
+ - `ApifyDatasetLoader` loads an existing dataset by its ID.
67
+ - `ApifyWebSearch` searches the web with the [RAG Web Browser](https://apify.com/apify/rag-web-browser) Actor and returns the content of the top result pages.
68
+ - `ApifyActorTool` is a tool for Haystack Agents that runs any Actor and returns its dataset items. It takes its parameters from the Actor's input schema, and it marks the items it hands to the LLM as untrusted external content to guard against prompt injection.
69
+
70
+ The dataset components turn dataset items into Haystack `Document` objects with a mapping function. Without one, they use `default_dataset_mapping_function`. For website and search Actors, it takes the item's `markdown` or `text` as the content and the page `url`, `title`, and `description` as `meta`. For other Actors, the content is the whole item as JSON.
71
+
72
+ Every component also has `run_async`, which `Pipeline.run_async` uses to run it without blocking the event loop.
73
+
74
+ When an Actor or task run finishes with any status other than `SUCCEEDED`, the component raises a `RuntimeError`.
75
+
76
+ The components read your Apify API token from the `APIFY_TOKEN` environment variable, or from `APIFY_API_TOKEN` if `APIFY_TOKEN` isn't set. To pass the token differently, use a Haystack `Secret`, for example `apify_token=Secret.from_env_var('MY_APIFY_TOKEN')`.
77
+
78
+ ## Examples
79
+
80
+ The examples need an Apify account and API token. You can start with a free account at [Apify](https://apify.com/) and get your [API token](https://docs.apify.com/platform/integrations/api), then set it as the `APIFY_TOKEN` environment variable.
81
+
82
+ ### Crawl a website and load it as Documents
83
+
84
+ `ApifyDatasetFromActorCall` runs [Website Content Crawler](https://apify.com/apify/website-content-crawler) and loads the crawled pages as `Document` objects with the default mapping function:
85
+
86
+ ```python
87
+ from apify_haystack import ApifyDatasetFromActorCall
88
+
89
+ crawler = ApifyDatasetFromActorCall(
90
+ actor_id='apify/website-content-crawler',
91
+ run_input={
92
+ 'maxCrawlPages': 3,
93
+ 'startUrls': [{'url': 'https://haystack.deepset.ai/'}],
94
+ },
95
+ )
96
+
97
+ for document in crawler.run()['documents']:
98
+ print(document.meta['url'], document.content[:100])
99
+ ```
100
+
101
+ ### Search the web
102
+
103
+ `ApifyWebSearch` returns the top result pages as Markdown `Document` objects, with the page `url`, `title`, and `description` in `meta`, and their URLs in `links`:
104
+
105
+ ```python
106
+ from apify_haystack import ApifyWebSearch
107
+
108
+ search = ApifyWebSearch(top_k=3)
109
+ result = search.run(query='What is Haystack?')
110
+
111
+ for document in result['documents']:
112
+ print(document.meta['url'], document.meta['title'])
113
+ ```
114
+
115
+ ### Give an Agent access to an Actor
116
+
117
+ `ApifyActorTool` lets a Haystack `Agent` run an Actor. Limit `dataset_items_limit` to keep the tool output within the model's context window. For pay-per-event Actors, `max_total_charge_usd` caps what each run the Agent starts can cost:
118
+
119
+ ```python
120
+ from haystack.components.agents import Agent
121
+ from haystack.components.generators.chat import OpenAIChatGenerator
122
+ from haystack.dataclasses import ChatMessage
123
+
124
+ from apify_haystack import ApifyActorTool
125
+
126
+ tool = ApifyActorTool('apify/rag-web-browser', dataset_items_limit=3, max_total_charge_usd=0.5)
127
+ agent = Agent(chat_generator=OpenAIChatGenerator(), tools=[tool])
128
+
129
+ result = agent.run([ChatMessage.from_user('What is new in Haystack 3?')])
130
+ print(result['last_message'].text)
131
+ ```
132
+
133
+ ### Save and load a pipeline
134
+
135
+ The components and the tool support Haystack serialization. The token is saved as the name of its environment variable, never as its value. Haystack 3 only loads classes and functions from trusted modules, so pass `apify_haystack` in `allowed_modules`, along with the module of your mapping function if you use a custom one:
136
+
137
+ ```python
138
+ from haystack import Pipeline
139
+
140
+ from apify_haystack import ApifyDatasetLoader
141
+
142
+ pipeline = Pipeline()
143
+ pipeline.add_component('loader', ApifyDatasetLoader('YOUR-DATASET-ID'))
144
+
145
+ yaml = pipeline.dumps()
146
+ restored = Pipeline.loads(yaml, allowed_modules=['apify_haystack'])
147
+ ```
148
+
149
+ ### More examples
150
+
151
+ The [examples directory](https://github.com/apify/apify-haystack/tree/main/examples) has complete scripts:
152
+
153
+ - [Load an existing dataset](https://github.com/apify/apify-haystack/blob/main/examples/apify_dataset_load.py) and convert its items to Documents.
154
+ - [Call Website Content Crawler](https://github.com/apify/apify-haystack/blob/main/examples/apify_actor_call.py) with a custom mapping function.
155
+ - [Crawl a website, store the pages in `InMemoryDocumentStore`, and retrieve them](https://github.com/apify/apify-haystack/blob/main/examples/crawl_and_process_data.py).
156
+ - [Answer questions about a crawled website with RAG](https://github.com/apify/apify-haystack/blob/main/examples/rag_with_crawled_website.py). <a href="https://colab.research.google.com/github/deepset-ai/haystack-cookbook/blob/main/notebooks/apify_haystack_rag.ipynb" target="_parent"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a>
157
+ - [Analyze the sentiment of Instagram comments](https://github.com/apify/apify-haystack/blob/main/examples/instagram_comments_analysis.py). <a href="https://colab.research.google.com/github/deepset-ai/haystack-cookbook/blob/main/notebooks/apify_haystack_instagram_comments_analysis.ipynb" target="_parent"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a>
158
+
159
+ ## Support
160
+
161
+ If you find a bug, please [submit an issue on GitHub](https://github.com/apify/apify-haystack/issues).
162
+ For questions, ask on [Stack Overflow](https://stackoverflow.com/questions/tagged/apify) or join our [Discord server](https://discord.com/invite/jyEM2PRvMU).
163
+
164
+ ## Contributing
165
+
166
+ Code contributions are welcome.
167
+ If you have ideas for improvements, submit an issue or create a pull request.
168
+ For contribution guidelines, see [CONTRIBUTING.md](https://github.com/apify/apify-haystack/blob/main/CONTRIBUTING.md).
169
+
170
+ ## License
171
+
172
+ This project is licensed under the [Apache License 2.0](https://github.com/apify/apify-haystack/blob/main/LICENSE).
@@ -0,0 +1,143 @@
1
+ <h1 align="center">Apify-Haystack integration</h1>
2
+
3
+ <p align="center">
4
+ <strong>The official integration of the <a href="https://apify.com/">Apify</a> platform with <a href="https://haystack.deepset.ai/">Haystack</a>.</strong>
5
+ </p>
6
+
7
+ <p align="center">
8
+ <a href="https://pypi.org/project/apify-haystack/"><img src="https://badge.fury.io/py/apify-haystack.svg" alt="PyPI version"></a>
9
+ <a href="https://pypi.org/project/apify-haystack/"><img src="https://img.shields.io/pypi/dm/apify-haystack" alt="PyPI downloads"></a>
10
+ <a href="https://pypi.org/project/apify-haystack/"><img src="https://img.shields.io/badge/python-3.11%2B-blue" alt="Python versions"></a>
11
+ <a href="https://github.com/apify/apify-haystack/actions/workflows/on_main.yaml"><img src="https://github.com/apify/apify-haystack/actions/workflows/on_main.yaml/badge.svg?branch=main" alt="Build status"></a>
12
+ <a href="https://github.com/apify/apify-haystack/blob/main/LICENSE"><img src="https://img.shields.io/pypi/l/apify-haystack" alt="License"></a>
13
+ <a href="https://discord.gg/jyEM2PRvMU"><img src="https://img.shields.io/discord/801163717915574323?label=discord" alt="Chat on Discord"></a>
14
+ </p>
15
+
16
+ Apify is a platform for web scraping, data extraction, and web automation tasks.
17
+ It provides serverless applications called Actors for tasks like crawling websites and scraping Facebook, Instagram, and Google results.
18
+
19
+ Haystack is an open-source framework for building LLM applications, RAG pipelines, and agents.
20
+
21
+ For a step-by-step guide, see the [Haystack integration](https://docs.apify.com/platform/integrations/haystack) page in the Apify documentation.
22
+
23
+ ## Installation
24
+
25
+ The integration is available as the [`apify-haystack`](https://pypi.org/project/apify-haystack/) package on PyPI. It requires Python 3.11 or newer and Haystack 3.
26
+
27
+ ```sh
28
+ pip install apify-haystack
29
+ ```
30
+
31
+ Upgrading from 0.1.x? See [UPGRADING.md](https://github.com/apify/apify-haystack/blob/main/UPGRADING.md).
32
+
33
+ ## Components
34
+
35
+ - `ApifyDatasetFromActorCall` runs an Actor, waits for it to finish, and loads its default dataset.
36
+ - `ApifyDatasetFromTaskCall` does the same for a saved Actor task.
37
+ - `ApifyDatasetLoader` loads an existing dataset by its ID.
38
+ - `ApifyWebSearch` searches the web with the [RAG Web Browser](https://apify.com/apify/rag-web-browser) Actor and returns the content of the top result pages.
39
+ - `ApifyActorTool` is a tool for Haystack Agents that runs any Actor and returns its dataset items. It takes its parameters from the Actor's input schema, and it marks the items it hands to the LLM as untrusted external content to guard against prompt injection.
40
+
41
+ The dataset components turn dataset items into Haystack `Document` objects with a mapping function. Without one, they use `default_dataset_mapping_function`. For website and search Actors, it takes the item's `markdown` or `text` as the content and the page `url`, `title`, and `description` as `meta`. For other Actors, the content is the whole item as JSON.
42
+
43
+ Every component also has `run_async`, which `Pipeline.run_async` uses to run it without blocking the event loop.
44
+
45
+ When an Actor or task run finishes with any status other than `SUCCEEDED`, the component raises a `RuntimeError`.
46
+
47
+ The components read your Apify API token from the `APIFY_TOKEN` environment variable, or from `APIFY_API_TOKEN` if `APIFY_TOKEN` isn't set. To pass the token differently, use a Haystack `Secret`, for example `apify_token=Secret.from_env_var('MY_APIFY_TOKEN')`.
48
+
49
+ ## Examples
50
+
51
+ The examples need an Apify account and API token. You can start with a free account at [Apify](https://apify.com/) and get your [API token](https://docs.apify.com/platform/integrations/api), then set it as the `APIFY_TOKEN` environment variable.
52
+
53
+ ### Crawl a website and load it as Documents
54
+
55
+ `ApifyDatasetFromActorCall` runs [Website Content Crawler](https://apify.com/apify/website-content-crawler) and loads the crawled pages as `Document` objects with the default mapping function:
56
+
57
+ ```python
58
+ from apify_haystack import ApifyDatasetFromActorCall
59
+
60
+ crawler = ApifyDatasetFromActorCall(
61
+ actor_id='apify/website-content-crawler',
62
+ run_input={
63
+ 'maxCrawlPages': 3,
64
+ 'startUrls': [{'url': 'https://haystack.deepset.ai/'}],
65
+ },
66
+ )
67
+
68
+ for document in crawler.run()['documents']:
69
+ print(document.meta['url'], document.content[:100])
70
+ ```
71
+
72
+ ### Search the web
73
+
74
+ `ApifyWebSearch` returns the top result pages as Markdown `Document` objects, with the page `url`, `title`, and `description` in `meta`, and their URLs in `links`:
75
+
76
+ ```python
77
+ from apify_haystack import ApifyWebSearch
78
+
79
+ search = ApifyWebSearch(top_k=3)
80
+ result = search.run(query='What is Haystack?')
81
+
82
+ for document in result['documents']:
83
+ print(document.meta['url'], document.meta['title'])
84
+ ```
85
+
86
+ ### Give an Agent access to an Actor
87
+
88
+ `ApifyActorTool` lets a Haystack `Agent` run an Actor. Limit `dataset_items_limit` to keep the tool output within the model's context window. For pay-per-event Actors, `max_total_charge_usd` caps what each run the Agent starts can cost:
89
+
90
+ ```python
91
+ from haystack.components.agents import Agent
92
+ from haystack.components.generators.chat import OpenAIChatGenerator
93
+ from haystack.dataclasses import ChatMessage
94
+
95
+ from apify_haystack import ApifyActorTool
96
+
97
+ tool = ApifyActorTool('apify/rag-web-browser', dataset_items_limit=3, max_total_charge_usd=0.5)
98
+ agent = Agent(chat_generator=OpenAIChatGenerator(), tools=[tool])
99
+
100
+ result = agent.run([ChatMessage.from_user('What is new in Haystack 3?')])
101
+ print(result['last_message'].text)
102
+ ```
103
+
104
+ ### Save and load a pipeline
105
+
106
+ The components and the tool support Haystack serialization. The token is saved as the name of its environment variable, never as its value. Haystack 3 only loads classes and functions from trusted modules, so pass `apify_haystack` in `allowed_modules`, along with the module of your mapping function if you use a custom one:
107
+
108
+ ```python
109
+ from haystack import Pipeline
110
+
111
+ from apify_haystack import ApifyDatasetLoader
112
+
113
+ pipeline = Pipeline()
114
+ pipeline.add_component('loader', ApifyDatasetLoader('YOUR-DATASET-ID'))
115
+
116
+ yaml = pipeline.dumps()
117
+ restored = Pipeline.loads(yaml, allowed_modules=['apify_haystack'])
118
+ ```
119
+
120
+ ### More examples
121
+
122
+ The [examples directory](https://github.com/apify/apify-haystack/tree/main/examples) has complete scripts:
123
+
124
+ - [Load an existing dataset](https://github.com/apify/apify-haystack/blob/main/examples/apify_dataset_load.py) and convert its items to Documents.
125
+ - [Call Website Content Crawler](https://github.com/apify/apify-haystack/blob/main/examples/apify_actor_call.py) with a custom mapping function.
126
+ - [Crawl a website, store the pages in `InMemoryDocumentStore`, and retrieve them](https://github.com/apify/apify-haystack/blob/main/examples/crawl_and_process_data.py).
127
+ - [Answer questions about a crawled website with RAG](https://github.com/apify/apify-haystack/blob/main/examples/rag_with_crawled_website.py). <a href="https://colab.research.google.com/github/deepset-ai/haystack-cookbook/blob/main/notebooks/apify_haystack_rag.ipynb" target="_parent"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a>
128
+ - [Analyze the sentiment of Instagram comments](https://github.com/apify/apify-haystack/blob/main/examples/instagram_comments_analysis.py). <a href="https://colab.research.google.com/github/deepset-ai/haystack-cookbook/blob/main/notebooks/apify_haystack_instagram_comments_analysis.ipynb" target="_parent"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a>
129
+
130
+ ## Support
131
+
132
+ If you find a bug, please [submit an issue on GitHub](https://github.com/apify/apify-haystack/issues).
133
+ For questions, ask on [Stack Overflow](https://stackoverflow.com/questions/tagged/apify) or join our [Discord server](https://discord.com/invite/jyEM2PRvMU).
134
+
135
+ ## Contributing
136
+
137
+ Code contributions are welcome.
138
+ If you have ideas for improvements, submit an issue or create a pull request.
139
+ For contribution guidelines, see [CONTRIBUTING.md](https://github.com/apify/apify-haystack/blob/main/CONTRIBUTING.md).
140
+
141
+ ## License
142
+
143
+ This project is licensed under the [Apache License 2.0](https://github.com/apify/apify-haystack/blob/main/LICENSE).
@@ -4,8 +4,8 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "apify_haystack"
7
- version = "0.1.8b3"
8
- description = "Apify-haystack integration"
7
+ version = "1.0.1"
8
+ description = "Apify integration for Haystack: load Actor results as Documents, search the web, and give Agents access to Apify Actors."
9
9
  license = "Apache-2.0"
10
10
  license-files = ["LICENSE"]
11
11
  readme = "README.md"
@@ -18,16 +18,20 @@ classifiers = [
18
18
  "Programming Language :: Python :: 3.12",
19
19
  "Programming Language :: Python :: 3.13",
20
20
  "Programming Language :: Python :: 3.14",
21
+ "Programming Language :: Python :: 3.15",
21
22
  "Topic :: Software Development :: Libraries",
22
23
  "Typing :: Typed",
23
24
  ]
24
25
  keywords = [
26
+ "agents",
25
27
  "apify",
26
28
  "crawler",
27
29
  "haystack",
30
+ "llm",
28
31
  "rag",
29
32
  "scraper",
30
33
  "scraping",
34
+ "web-search",
31
35
  ]
32
36
  dependencies = [
33
37
  "apify-client>=3.4.0,<4.0.0",
@@ -4,8 +4,8 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "apify_haystack"
7
- version = "0.1.8b3"
8
- description = "Apify-haystack integration"
7
+ version = "1.0.1"
8
+ description = "Apify integration for Haystack: load Actor results as Documents, search the web, and give Agents access to Apify Actors."
9
9
  authors = [{ name = "Apify Technologies s.r.o.", email = "support@apify.com" }]
10
10
  license = "Apache-2.0"
11
11
  license-files = ["LICENSE"]
@@ -19,10 +19,11 @@ classifiers = [
19
19
  "Programming Language :: Python :: 3.12",
20
20
  "Programming Language :: Python :: 3.13",
21
21
  "Programming Language :: Python :: 3.14",
22
+ "Programming Language :: Python :: 3.15",
22
23
  "Topic :: Software Development :: Libraries",
23
24
  "Typing :: Typed",
24
25
  ]
25
- keywords = ["apify", "crawler", "haystack", "rag", "scraper", "scraping"]
26
+ keywords = ["agents", "apify", "crawler", "haystack", "llm", "rag", "scraper", "scraping", "web-search"]
26
27
  dependencies = [
27
28
  "apify-client>=3.4.0,<4.0.0",
28
29
  "haystack-ai>=3.0.0,<4.0.0",
@@ -132,7 +132,8 @@ class ApifyDatasetFromActorCall(ApifyDatasetComponent):
132
132
  `default_dataset_mapping_function`.
133
133
  run_input (dict, optional): The input parameters for the Actor you want to run. This can be provided
134
134
  either in the `run` method or during the class instantiation. If specified in both places,
135
- the inputs are merged and the values from `run` take precedence.
135
+ the inputs are merged and the values from `run` take precedence. Without any input, the
136
+ Actor runs with its default input.
136
137
  build (str, optional): Optionally specifies the Actor build to run.
137
138
  It can be either a build tag or build number.
138
139
  memory_mbytes (int, optional): Optional memory limit for the run, in megabytes.
@@ -160,7 +161,8 @@ class ApifyDatasetFromActorCall(ApifyDatasetComponent):
160
161
  Args:
161
162
  run_input (dict, optional): The input parameters for the Actor you want to run. This can be provided
162
163
  either in the `run` method or during the class instantiation. If specified in both places,
163
- the inputs are merged and the values from `run` take precedence.
164
+ the inputs are merged and the values from `run` take precedence. Without any input, the
165
+ Actor runs with its default input.
164
166
 
165
167
  Example:
166
168
  .. code-block:: python
@@ -172,8 +174,7 @@ class ApifyDatasetFromActorCall(ApifyDatasetComponent):
172
174
  )
173
175
  dataset = actor.run(run_input={"startUrls": [{"url": "https://haystack.deepset.ai/"}]})
174
176
  """
175
- if not (run_input := _merge_inputs(self.run_input, run_input)):
176
- raise ValueError('Please provide the run_input either in the constructor or in the run method.')
177
+ run_input = _merge_inputs(self.run_input, run_input)
177
178
 
178
179
  client = self._get_client()
179
180
  run = client.actor(self.actor_id).call(
@@ -193,10 +194,10 @@ class ApifyDatasetFromActorCall(ApifyDatasetComponent):
193
194
  Args:
194
195
  run_input (dict, optional): The input parameters for the Actor you want to run. This can be provided
195
196
  either in the `run_async` method or during the class instantiation. If specified in both places,
196
- the inputs are merged and the values from `run_async` take precedence.
197
+ the inputs are merged and the values from `run_async` take precedence. Without any input, the
198
+ Actor runs with its default input.
197
199
  """
198
- if not (run_input := _merge_inputs(self.run_input, run_input)):
199
- raise ValueError('Please provide the run_input either in the constructor or in the run method.')
200
+ run_input = _merge_inputs(self.run_input, run_input)
200
201
 
201
202
  client = self._get_async_client()
202
203
  run = await client.actor(self.actor_id).call(
@@ -260,7 +261,8 @@ class ApifyDatasetFromTaskCall(ApifyDatasetComponent):
260
261
  `default_dataset_mapping_function`.
261
262
  task_input (dict, optional): The input parameters for the task you want to run. This can be provided
262
263
  either in the `run` method or during the class instantiation. If specified in both places,
263
- the inputs are merged and the values from `run` take precedence.
264
+ the inputs are merged and the values from `run` take precedence. Without any input, the
265
+ task runs with its saved input.
264
266
  build (str, optional): Optionally specifies the Actor build to run.
265
267
  It can be either a build tag or build number.
266
268
  memory_mbytes (int, optional): Optional memory limit for the run, in megabytes.
@@ -288,10 +290,10 @@ class ApifyDatasetFromTaskCall(ApifyDatasetComponent):
288
290
  Args:
289
291
  task_input (dict, optional): The input parameters for the task you want to run. This can be provided
290
292
  either in the `run` method or during the class instantiation. If specified in both places,
291
- the inputs are merged and the values from `run` take precedence.
293
+ the inputs are merged and the values from `run` take precedence. Without any input, the
294
+ task runs with its saved input.
292
295
  """
293
- if not (task_input := _merge_inputs(self.task_input, task_input)):
294
- raise ValueError('Please provide the task_input either in the constructor or in the run method.')
296
+ task_input = _merge_inputs(self.task_input, task_input)
295
297
 
296
298
  client = self._get_client()
297
299
  run = client.task(self.task_id).call(
@@ -311,10 +313,10 @@ class ApifyDatasetFromTaskCall(ApifyDatasetComponent):
311
313
  Args:
312
314
  task_input (dict, optional): The input parameters for the task you want to run. This can be provided
313
315
  either in the `run_async` method or during the class instantiation. If specified in both places,
314
- the inputs are merged and the values from `run_async` take precedence.
316
+ the inputs are merged and the values from `run_async` take precedence. Without any input, the
317
+ task runs with its saved input.
315
318
  """
316
- if not (task_input := _merge_inputs(self.task_input, task_input)):
317
- raise ValueError('Please provide the task_input either in the constructor or in the run method.')
319
+ task_input = _merge_inputs(self.task_input, task_input)
318
320
 
319
321
  client = self._get_async_client()
320
322
  run = await client.task(self.task_id).call(
@@ -375,9 +377,8 @@ def _serialize_mapping_function(dataset_mapping_function: Callable[[dict], Docum
375
377
 
376
378
 
377
379
  def _merge_inputs(input1: dict | None, input2: dict | None) -> dict | None:
378
- if input1 and input2:
379
- return input1 | input2
380
- return input1 or input2
380
+ merged = (input1 or {}) | (input2 or {})
381
+ return merged or None
381
382
 
382
383
 
383
384
  def _load_documents(
@@ -1,166 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: apify_haystack
3
- Version: 0.1.8b3
4
- Summary: Apify-haystack integration
5
- Keywords: apify,crawler,haystack,rag,scraper,scraping
6
- Author: Apify Technologies s.r.o.
7
- Author-email: Apify Technologies s.r.o. <support@apify.com>
8
- License-Expression: Apache-2.0
9
- License-File: LICENSE
10
- Classifier: Development Status :: 5 - Production/Stable
11
- Classifier: Intended Audience :: Developers
12
- Classifier: Operating System :: OS Independent
13
- Classifier: Programming Language :: Python :: 3.11
14
- Classifier: Programming Language :: Python :: 3.12
15
- Classifier: Programming Language :: Python :: 3.13
16
- Classifier: Programming Language :: Python :: 3.14
17
- Classifier: Topic :: Software Development :: Libraries
18
- Classifier: Typing :: Typed
19
- Requires-Dist: apify-client>=3.4.0,<4.0.0
20
- Requires-Dist: haystack-ai>=3.0.0,<4.0.0
21
- Requires-Python: >=3.11
22
- Project-URL: Homepage, https://apify.com
23
- Project-URL: Changelog, https://github.com/apify/apify-haystack/blob/main/CHANGELOG.md
24
- Project-URL: Documentation, https://docs.apify.com/platform/integrations/haystack
25
- Project-URL: Issue Tracker, https://github.com/apify/apify-haystack/issues
26
- Project-URL: Repository, https://github.com/apify/apify-haystack
27
- Description-Content-Type: text/markdown
28
-
29
- # Apify-Haystack integration
30
-
31
- [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://github.com/apify/apify-haystack/blob/main/LICENSE)
32
- [![PyPi Package](https://badge.fury.io/py/apify-haystack.svg)](https://badge.fury.io/py/apify-haystack)
33
- [![Python](https://img.shields.io/pypi/pyversions/apify-haystack)](https://pypi.org/project/apify-haystack)
34
-
35
- The Apify-Haystack integration allows easy interaction between the [Apify](https://apify.com/) platform and [Haystack](https://haystack.deepset.ai/).
36
-
37
- Apify is a platform for web scraping, data extraction, and web automation tasks.
38
- It provides serverless applications called Actors for tasks like crawling websites and scraping Facebook, Instagram, and Google results.
39
-
40
- Haystack offers an ecosystem of tools for building, managing, and deploying search engines and LLM applications.
41
-
42
- ## Installation
43
-
44
- Apify-haystack is available as the [`apify-haystack`](https://pypi.org/project/apify-haystack/) package on PyPI. It requires Python 3.11 or newer and Haystack 3.
45
-
46
- ```sh
47
- pip install apify-haystack
48
- ```
49
-
50
- ## Components
51
-
52
- - `ApifyDatasetFromActorCall` runs an Actor, waits for it to finish, and loads its default dataset.
53
- - `ApifyDatasetFromTaskCall` does the same for a saved Actor task.
54
- - `ApifyDatasetLoader` loads an existing dataset by its ID.
55
- - `ApifyWebSearch` searches the web with the [RAG Web Browser](https://apify.com/apify/rag-web-browser) Actor and returns the content of the top result pages.
56
- - `ApifyActorTool` is a tool for Haystack Agents that runs any Actor and returns its dataset items. It takes its parameters from the Actor's input schema, and it marks the items it hands to the LLM as untrusted external content to guard against prompt injection.
57
-
58
- The dataset components turn dataset items into Haystack `Document` objects with a mapping function. Without one, they use `default_dataset_mapping_function`, which takes the `markdown` or `text` of website and search Actors with the page `url`, `title`, and `description` as `meta`, and the whole item as JSON for other Actors. Every component also has `run_async`, which `Pipeline.run_async` uses to run it without blocking the event loop. When an Actor or task run finishes with any status other than `SUCCEEDED`, the component raises a `RuntimeError` instead of loading a partial dataset.
59
-
60
- The components read your Apify API token from the `APIFY_TOKEN` environment variable, or from `APIFY_API_TOKEN` if `APIFY_TOKEN` isn't set. To pass the token differently, use a Haystack `Secret`, for example `apify_token=Secret.from_env_var('MY_APIFY_TOKEN')`.
61
-
62
- ## Examples
63
-
64
- ### Crawl a website using Apify's Website Content Crawler and convert it to Haystack Documents
65
-
66
- You need an Apify account and API token to run this example.
67
- You can start with a free account at [Apify](https://apify.com/) and get your [API token](https://docs.apify.com/platform/integrations/api).
68
-
69
- In the example below, set the `APIFY_TOKEN` environment variable and run the script:
70
-
71
- ```python
72
- from haystack import Document
73
-
74
- from apify_haystack import ApifyDatasetFromActorCall
75
-
76
- actor_id = 'apify/website-content-crawler'
77
- run_input = {
78
- 'maxCrawlPages': 3, # limit the number of pages to crawl
79
- 'startUrls': [{'url': 'https://haystack.deepset.ai/'}],
80
- }
81
-
82
-
83
- def dataset_mapping_function(dataset_item: dict) -> Document:
84
- return Document(content=dataset_item.get('text'), meta={'url': dataset_item.get('url')})
85
-
86
-
87
- actor = ApifyDatasetFromActorCall(
88
- actor_id=actor_id, run_input=run_input, dataset_mapping_function=dataset_mapping_function
89
- )
90
- print(f'Calling the Apify Actor {actor_id}. Crawling will take some time.')
91
- print('You can monitor the progress at: https://console.apify.com/actors/runs')
92
-
93
- dataset = actor.run()['documents']
94
-
95
- print(f'Loaded {len(dataset)} documents from the Apify Actor {actor_id}:')
96
- for d in dataset:
97
- print(d)
98
- ```
99
-
100
- ### Search the web
101
-
102
- `ApifyWebSearch` returns the top result pages as Markdown `Document` objects, with the page `url`, `title`, and `description` in `meta`, and their URLs in `links`:
103
-
104
- ```python
105
- from apify_haystack import ApifyWebSearch
106
-
107
- search = ApifyWebSearch(top_k=3)
108
- result = search.run(query='What is Haystack?')
109
-
110
- for document in result['documents']:
111
- print(document.meta['url'], document.meta['title'])
112
- ```
113
-
114
- ### Give an Agent access to an Actor
115
-
116
- `ApifyActorTool` lets a Haystack `Agent` run an Actor. Limit `dataset_items_limit` to keep the tool output within the model's context window. For pay-per-event Actors, `max_total_charge_usd` caps what each run the Agent starts can cost:
117
-
118
- ```python
119
- from haystack.components.agents import Agent
120
- from haystack.components.generators.chat import OpenAIChatGenerator
121
- from haystack.dataclasses import ChatMessage
122
-
123
- from apify_haystack import ApifyActorTool
124
-
125
- tool = ApifyActorTool('apify/rag-web-browser', dataset_items_limit=3, max_total_charge_usd=0.5)
126
- agent = Agent(chat_generator=OpenAIChatGenerator(), tools=[tool])
127
-
128
- result = agent.run([ChatMessage.from_user('What is new in Haystack 3?')])
129
- print(result['last_message'].text)
130
- ```
131
-
132
- ### Save and load a pipeline
133
-
134
- The components and the tool support Haystack serialization. The token is saved as the name of its environment variable, never as its value. Haystack 3 only loads classes and functions from trusted modules, so allow `apify_haystack` and the module with your mapping function:
135
-
136
- ```python
137
- from haystack import Pipeline
138
-
139
- yaml = pipeline.dumps()
140
- restored = Pipeline.loads(yaml, allowed_modules=['apify_haystack', 'my_project.mapping'])
141
- ```
142
-
143
- ### More examples
144
-
145
- The [examples directory](https://github.com/apify/apify-haystack/tree/main/examples) has more examples:
146
-
147
- - Load a dataset from Apify and convert it to a Haystack Document
148
- - Call [Website Content Crawler](https://apify.com/apify/website-content-crawler) and convert the data into the Haystack Documents
149
- - Crawl websites, retrieve text content, and store it in the `InMemoryDocumentStore`
150
- - Retrieval-Augmented Generation (RAG): Extracting text from a website & question answering <a href="https://colab.research.google.com/github/deepset-ai/haystack-cookbook/blob/main/notebooks/apify_haystack_rag.ipynb" target="_parent"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a>
151
- - Analyze your Instagram comments' vibe with Apify and Haystack <a href="https://colab.research.google.com/github/deepset-ai/haystack-cookbook/blob/main/notebooks/apify_haystack_instagram_comments_analysis.ipynb" target="_parent"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a>
152
-
153
- ## Support
154
-
155
- If you find any bug or issue, please [submit an issue on GitHub](https://github.com/apify/apify-haystack/issues).
156
- For questions, you can ask on [Stack Overflow](https://stackoverflow.com/questions/tagged/apify), in GitHub Discussions or you can join our [Discord server](https://discord.com/invite/jyEM2PRvMU).
157
-
158
- ## Contributing
159
-
160
- Code contributions are welcome.
161
- If you have ideas for improvements, submit an issue or create a pull request.
162
- For contribution guidelines, see [CONTRIBUTING.md](https://github.com/apify/apify-haystack/blob/main/CONTRIBUTING.md).
163
-
164
- ## License
165
-
166
- This project is licensed under the Apache License 2.0 - see the [LICENSE](https://github.com/apify/apify-haystack/blob/main/LICENSE) file for details.
@@ -1,138 +0,0 @@
1
- # Apify-Haystack integration
2
-
3
- [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://github.com/apify/apify-haystack/blob/main/LICENSE)
4
- [![PyPi Package](https://badge.fury.io/py/apify-haystack.svg)](https://badge.fury.io/py/apify-haystack)
5
- [![Python](https://img.shields.io/pypi/pyversions/apify-haystack)](https://pypi.org/project/apify-haystack)
6
-
7
- The Apify-Haystack integration allows easy interaction between the [Apify](https://apify.com/) platform and [Haystack](https://haystack.deepset.ai/).
8
-
9
- Apify is a platform for web scraping, data extraction, and web automation tasks.
10
- It provides serverless applications called Actors for tasks like crawling websites and scraping Facebook, Instagram, and Google results.
11
-
12
- Haystack offers an ecosystem of tools for building, managing, and deploying search engines and LLM applications.
13
-
14
- ## Installation
15
-
16
- Apify-haystack is available as the [`apify-haystack`](https://pypi.org/project/apify-haystack/) package on PyPI. It requires Python 3.11 or newer and Haystack 3.
17
-
18
- ```sh
19
- pip install apify-haystack
20
- ```
21
-
22
- ## Components
23
-
24
- - `ApifyDatasetFromActorCall` runs an Actor, waits for it to finish, and loads its default dataset.
25
- - `ApifyDatasetFromTaskCall` does the same for a saved Actor task.
26
- - `ApifyDatasetLoader` loads an existing dataset by its ID.
27
- - `ApifyWebSearch` searches the web with the [RAG Web Browser](https://apify.com/apify/rag-web-browser) Actor and returns the content of the top result pages.
28
- - `ApifyActorTool` is a tool for Haystack Agents that runs any Actor and returns its dataset items. It takes its parameters from the Actor's input schema, and it marks the items it hands to the LLM as untrusted external content to guard against prompt injection.
29
-
30
- The dataset components turn dataset items into Haystack `Document` objects with a mapping function. Without one, they use `default_dataset_mapping_function`, which takes the `markdown` or `text` of website and search Actors with the page `url`, `title`, and `description` as `meta`, and the whole item as JSON for other Actors. Every component also has `run_async`, which `Pipeline.run_async` uses to run it without blocking the event loop. When an Actor or task run finishes with any status other than `SUCCEEDED`, the component raises a `RuntimeError` instead of loading a partial dataset.
31
-
32
- The components read your Apify API token from the `APIFY_TOKEN` environment variable, or from `APIFY_API_TOKEN` if `APIFY_TOKEN` isn't set. To pass the token differently, use a Haystack `Secret`, for example `apify_token=Secret.from_env_var('MY_APIFY_TOKEN')`.
33
-
34
- ## Examples
35
-
36
- ### Crawl a website using Apify's Website Content Crawler and convert it to Haystack Documents
37
-
38
- You need an Apify account and API token to run this example.
39
- You can start with a free account at [Apify](https://apify.com/) and get your [API token](https://docs.apify.com/platform/integrations/api).
40
-
41
- In the example below, set the `APIFY_TOKEN` environment variable and run the script:
42
-
43
- ```python
44
- from haystack import Document
45
-
46
- from apify_haystack import ApifyDatasetFromActorCall
47
-
48
- actor_id = 'apify/website-content-crawler'
49
- run_input = {
50
- 'maxCrawlPages': 3, # limit the number of pages to crawl
51
- 'startUrls': [{'url': 'https://haystack.deepset.ai/'}],
52
- }
53
-
54
-
55
- def dataset_mapping_function(dataset_item: dict) -> Document:
56
- return Document(content=dataset_item.get('text'), meta={'url': dataset_item.get('url')})
57
-
58
-
59
- actor = ApifyDatasetFromActorCall(
60
- actor_id=actor_id, run_input=run_input, dataset_mapping_function=dataset_mapping_function
61
- )
62
- print(f'Calling the Apify Actor {actor_id}. Crawling will take some time.')
63
- print('You can monitor the progress at: https://console.apify.com/actors/runs')
64
-
65
- dataset = actor.run()['documents']
66
-
67
- print(f'Loaded {len(dataset)} documents from the Apify Actor {actor_id}:')
68
- for d in dataset:
69
- print(d)
70
- ```
71
-
72
- ### Search the web
73
-
74
- `ApifyWebSearch` returns the top result pages as Markdown `Document` objects, with the page `url`, `title`, and `description` in `meta`, and their URLs in `links`:
75
-
76
- ```python
77
- from apify_haystack import ApifyWebSearch
78
-
79
- search = ApifyWebSearch(top_k=3)
80
- result = search.run(query='What is Haystack?')
81
-
82
- for document in result['documents']:
83
- print(document.meta['url'], document.meta['title'])
84
- ```
85
-
86
- ### Give an Agent access to an Actor
87
-
88
- `ApifyActorTool` lets a Haystack `Agent` run an Actor. Limit `dataset_items_limit` to keep the tool output within the model's context window. For pay-per-event Actors, `max_total_charge_usd` caps what each run the Agent starts can cost:
89
-
90
- ```python
91
- from haystack.components.agents import Agent
92
- from haystack.components.generators.chat import OpenAIChatGenerator
93
- from haystack.dataclasses import ChatMessage
94
-
95
- from apify_haystack import ApifyActorTool
96
-
97
- tool = ApifyActorTool('apify/rag-web-browser', dataset_items_limit=3, max_total_charge_usd=0.5)
98
- agent = Agent(chat_generator=OpenAIChatGenerator(), tools=[tool])
99
-
100
- result = agent.run([ChatMessage.from_user('What is new in Haystack 3?')])
101
- print(result['last_message'].text)
102
- ```
103
-
104
- ### Save and load a pipeline
105
-
106
- The components and the tool support Haystack serialization. The token is saved as the name of its environment variable, never as its value. Haystack 3 only loads classes and functions from trusted modules, so allow `apify_haystack` and the module with your mapping function:
107
-
108
- ```python
109
- from haystack import Pipeline
110
-
111
- yaml = pipeline.dumps()
112
- restored = Pipeline.loads(yaml, allowed_modules=['apify_haystack', 'my_project.mapping'])
113
- ```
114
-
115
- ### More examples
116
-
117
- The [examples directory](https://github.com/apify/apify-haystack/tree/main/examples) has more examples:
118
-
119
- - Load a dataset from Apify and convert it to a Haystack Document
120
- - Call [Website Content Crawler](https://apify.com/apify/website-content-crawler) and convert the data into the Haystack Documents
121
- - Crawl websites, retrieve text content, and store it in the `InMemoryDocumentStore`
122
- - Retrieval-Augmented Generation (RAG): Extracting text from a website & question answering <a href="https://colab.research.google.com/github/deepset-ai/haystack-cookbook/blob/main/notebooks/apify_haystack_rag.ipynb" target="_parent"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a>
123
- - Analyze your Instagram comments' vibe with Apify and Haystack <a href="https://colab.research.google.com/github/deepset-ai/haystack-cookbook/blob/main/notebooks/apify_haystack_instagram_comments_analysis.ipynb" target="_parent"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a>
124
-
125
- ## Support
126
-
127
- If you find any bug or issue, please [submit an issue on GitHub](https://github.com/apify/apify-haystack/issues).
128
- For questions, you can ask on [Stack Overflow](https://stackoverflow.com/questions/tagged/apify), in GitHub Discussions or you can join our [Discord server](https://discord.com/invite/jyEM2PRvMU).
129
-
130
- ## Contributing
131
-
132
- Code contributions are welcome.
133
- If you have ideas for improvements, submit an issue or create a pull request.
134
- For contribution guidelines, see [CONTRIBUTING.md](https://github.com/apify/apify-haystack/blob/main/CONTRIBUTING.md).
135
-
136
- ## License
137
-
138
- This project is licensed under the Apache License 2.0 - see the [LICENSE](https://github.com/apify/apify-haystack/blob/main/LICENSE) file for details.
File without changes