apify-haystack 0.1.3__tar.gz → 0.1.4__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: apify_haystack
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: Apify-haystack integration
5
5
  Home-page: https://apify.com
6
6
  License: Apache-2.0
@@ -97,7 +97,8 @@ See other examples in the [examples directory](https://github.com/apify/apify-ha
97
97
  - Load a dataset from Apify and convert it to a Haystack Document
98
98
  - Call [Website Content Crawler](https://apify.com/apify/website-content-crawler) and convert the data into the Haystack Documents
99
99
  - Crawl websites, retrieve text content, and store it in the `InMemoryDocumentStore`
100
- - Retrieval-Augmented Generation (RAG): Extracting text from a website & question answering
100
+ - 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>
101
+ - Analyze Your Instagram Comments’ Vibe with Apify and Haystack
101
102
 
102
103
  ## Support
103
104
 
@@ -68,7 +68,8 @@ See other examples in the [examples directory](https://github.com/apify/apify-ha
68
68
  - Load a dataset from Apify and convert it to a Haystack Document
69
69
  - Call [Website Content Crawler](https://apify.com/apify/website-content-crawler) and convert the data into the Haystack Documents
70
70
  - Crawl websites, retrieve text content, and store it in the `InMemoryDocumentStore`
71
- - Retrieval-Augmented Generation (RAG): Extracting text from a website & question answering
71
+ - 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>
72
+ - Analyze Your Instagram Comments’ Vibe with Apify and Haystack
72
73
 
73
74
  ## Support
74
75
 
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "apify_haystack"
7
- version = "0.1.3"
7
+ version = "0.1.4"
8
8
  description = "Apify-haystack integration"
9
9
  authors = ["Apify Technologies s.r.o. <support@apify.com>"]
10
10
  homepage = "https://apify.com"
@@ -55,6 +55,7 @@ pytest = "~8.3.0"
55
55
  pytest-cov = "~5.0.0"
56
56
  ruff = "~0.5.0"
57
57
  setuptools = "~70.3.0" # setuptools are used by pytest, but not explicitly required
58
+ black = "^24.8.0"
58
59
 
59
60
  [tool.ruff]
60
61
  line-length = 120
@@ -78,7 +78,7 @@ class ApifyDatasetFromActorCall:
78
78
  self,
79
79
  actor_id: str,
80
80
  dataset_mapping_function: Callable[[dict], Document],
81
- run_input: dict,
81
+ run_input: dict | None = None,
82
82
  *,
83
83
  build: str | None = None,
84
84
  memory_mbytes: int | None = None,
@@ -89,10 +89,12 @@ class ApifyDatasetFromActorCall:
89
89
 
90
90
  Args:
91
91
  actor_id (str): The ID or name of the Actor on the Apify platform.
92
- run_input (Dict): The input object of the Actor that you're trying to run.
93
92
  dataset_mapping_function (Callable): A function that takes a single
94
93
  dictionary (an Apify dataset item) and converts it to an
95
94
  instance of the Document class.
95
+ run_input (dict, optional): The input parameters for the Actor you want to run. This can be provided
96
+ either in the `run` method or during the class instantiation. If specified in both places,
97
+ the inputs will be merged.
96
98
  build (str, optional): Optionally specifies the actor build to run.
97
99
  It can be either a build tag or build number.
98
100
  memory_mbytes (int, optional): Optional memory limit for the run, in megabytes.
@@ -118,16 +120,33 @@ class ApifyDatasetFromActorCall:
118
120
  if httpx_client := getattr(self.client.http_client, "httpx_client", None):
119
121
  httpx_client.headers["user-agent"] += f"; {HAYSTACK_ATTRIBUTE_USER_AGENT}"
120
122
 
121
- @component.output_types(documents=list[Document])
122
- def run(self): # type: ignore[no-untyped-def] # noqa: ANN201
123
+ @component.output_types(documents=list[Document]) # type: ignore[misc]
124
+ def run(self, run_input: dict | None = None): # type: ignore[no-untyped-def] # noqa: ANN201
123
125
  """Run an Actor on the Apify platform and wait for results to be ready.
124
126
 
127
+ Args:
128
+ run_input (dict, optional): The input parameters for the Actor you want to run. This can be provided
129
+ either in the `run` method or during the class instantiation. If specified in both places,
130
+ the inputs will be merged.
131
+
132
+ Example:
133
+ .. code-block:: python
134
+
135
+ actor = ApifyDatasetFromActorCall(
136
+ actor_id="apify/website-content-crawler",
137
+ run_input={ "maxCrawlPages": 5 }
138
+ )
139
+ dataset = actor.run(run_input={ "startUrls": [{"url": "https://haystack.deepset.ai/"}] })
140
+
125
141
  Type-hint note: the output is not type-hinted, otherwise linting complains
126
142
  that `run` method is not haystack a component
127
143
  """
144
+ if not (run_input := _merge_inputs(run_input, self.run_input)):
145
+ raise ValueError("Please provide the run_input either in the constructor or in the run method.")
146
+
128
147
  if not (
129
148
  actor_call := self.client.actor(self.actor_id).call(
130
- run_input=self.run_input,
149
+ run_input=run_input,
131
150
  build=self.build,
132
151
  memory_mbytes=self.memory_mbytes,
133
152
  timeout_secs=self.timeout_secs,
@@ -156,7 +175,7 @@ class ApifyDatasetFromTaskCall:
156
175
  self,
157
176
  task_id: str,
158
177
  dataset_mapping_function: Callable[[dict], Document],
159
- task_input: dict,
178
+ task_input: dict | None = None,
160
179
  *,
161
180
  build: str | None = None,
162
181
  memory_mbytes: int | None = None,
@@ -167,10 +186,12 @@ class ApifyDatasetFromTaskCall:
167
186
 
168
187
  Args:
169
188
  task_id (str): The ID or name of the Actor on the Apify platform.
170
- task_input (Dict): The input object of the Actor that you're trying to run.
171
189
  dataset_mapping_function (Callable): A function that takes a single
172
190
  dictionary (an Apify dataset item) and converts it to an
173
191
  instance of the Document class.
192
+ task_input (dict, optional): The input parameters for the Actor you want to run. This can be provided
193
+ either in the `run` method or during the class instantiation. If specified in both places,
194
+ the inputs will be merged.
174
195
  build (str, optional): Optionally specifies the Actor build to run.
175
196
  It can be either a build tag or build number.
176
197
  memory_mbytes (int, optional): Optional memory limit for the run, in megabytes.
@@ -196,13 +217,22 @@ class ApifyDatasetFromTaskCall:
196
217
  if httpx_client := getattr(self.client.http_client, "httpx_client", None):
197
218
  httpx_client.headers["user-agent"] += f"; {HAYSTACK_ATTRIBUTE_USER_AGENT}"
198
219
 
199
- @component.output_types(documents=list[Document])
200
- def run(self): # type: ignore[no-untyped-def] # noqa: ANN201
220
+ @component.output_types(documents=list[Document]) # type: ignore[misc]
221
+ def run(self, task_input: dict | None = None): # type: ignore[no-untyped-def] # noqa: ANN201
201
222
  """Run an Actor on the Apify platform and wait for results to be ready.
202
223
 
224
+ Args:
225
+ task_input (dict, optional): The input parameters for the Actor you want to run. This can be provided
226
+ either in the `run` method or during the class instantiation. If specified in both places,
227
+ the inputs will be merged.
228
+
203
229
  Type-hint note: the output is not type-hinted, otherwise linting complains
204
230
  that `run` method is not haystack a component
205
231
  """
232
+ task_input = task_input or self.task_input
233
+ if not task_input:
234
+ raise ValueError("Please provide the task_input either in the constructor or in the run method.")
235
+
206
236
  if not (
207
237
  actor_call := self.client.task(self.task_id).call(
208
238
  task_input=self.task_input,
@@ -221,3 +251,9 @@ class ApifyDatasetFromTaskCall:
221
251
  dataset_mapping_function=self.dataset_mapping_function,
222
252
  )
223
253
  return loader.run()
254
+
255
+
256
+ def _merge_inputs(input1: dict | None, input2: dict | None) -> dict | None:
257
+ if input1 and input2:
258
+ return input1 | input2
259
+ return input1 or input2
@@ -16,6 +16,7 @@ meta: {'url': 'https://haystack.deepset.ai/overview/quick-start',
16
16
  'split_id': 0, 'split_idx_start': 0, '_split_overlap': ......
17
17
  .....
18
18
  """
19
+
19
20
  import os
20
21
 
21
22
  from haystack import Document, Pipeline
@@ -0,0 +1,85 @@
1
+ """
2
+ Analyze Your Instagram Comments' Vibe with Apify and Haystack
3
+
4
+ This script demonstrates how to extract comments from an Instagram post using Apify's Instagram Scraper.
5
+ The content is processed using the Haystack pipeline and the vibe of the comments is analyzed using the LLM model.
6
+
7
+ URL = "https://www.instagram.com/p/C_a9jcRuJZZ/" # @tiffintech on How to easily keep up with tech?
8
+
9
+ Expected output:
10
+ ......
11
+ Overall, the Instagram post seems to be generating positive energy and high engagement. The comments are filled with
12
+ emojis like 😍, 😂, ❤️, 🙏🏿, and 🙌 which show excitement and enthusiasm .....
13
+ .... Overall, the engagement patterns suggest that the post is vibrating with high energy and positivity.
14
+ """
15
+
16
+ import os
17
+
18
+ from haystack import Document, Pipeline
19
+ from haystack.components.builders import PromptBuilder
20
+ from haystack.components.generators import OpenAIGenerator
21
+ from haystack.components.preprocessors import DocumentCleaner
22
+
23
+ from apify_haystack import ApifyDatasetFromActorCall
24
+
25
+ os.environ["APIFY_API_TOKEN"] = "YOUR-APIFY-API-TOKEN"
26
+ os.environ["OPENAI_API_KEY"] = "YOUR-OPENAI-API-KEY"
27
+
28
+
29
+ def dataset_mapping_function(dataset_item: dict) -> Document:
30
+ return Document(
31
+ content=dataset_item.get("text"),
32
+ meta={"ownerUsername": dataset_item.get("ownerUsername")},
33
+ )
34
+
35
+
36
+ document_loader = ApifyDatasetFromActorCall(
37
+ actor_id="apify/instagram-comment-scraper",
38
+ run_input={"resultsLimit": 50},
39
+ dataset_mapping_function=dataset_mapping_function,
40
+ )
41
+
42
+ prompt = """
43
+ Analyze these Instagram comments to determine if the post is generating positive energy, excitement,
44
+ or high engagement. Focus on sentiment, emotional tone, and engagement patterns to conclude if
45
+ the post is 'vibrating' with high energy. Be concise."
46
+
47
+ Context:
48
+ {% for document in documents %}
49
+ {{ document.content }}
50
+ {% endfor %}
51
+
52
+ Analysis:
53
+ """
54
+
55
+ cleaner = DocumentCleaner(
56
+ remove_empty_lines=True,
57
+ remove_extra_whitespaces=True,
58
+ remove_repeated_substrings=True,
59
+ )
60
+ prompt_builder = PromptBuilder(template=prompt)
61
+ generator = OpenAIGenerator(model="gpt-3.5-turbo")
62
+
63
+
64
+ pipe = Pipeline()
65
+ pipe.add_component("loader", document_loader)
66
+ pipe.add_component("cleaner", cleaner)
67
+ pipe.add_component("prompt_builder", prompt_builder)
68
+ pipe.add_component("llm", generator)
69
+ pipe.connect("loader", "cleaner")
70
+ pipe.connect("cleaner", "prompt_builder")
71
+ pipe.connect("prompt_builder", "llm")
72
+
73
+ print("Vibe analysis:\n")
74
+
75
+ url1 = "https://www.instagram.com/p/C_a9jcRuJZZ/" # @tiffintech on How to easily keep up with tech?
76
+ print(f"\nScraping Instagram comments for: {url1} and running analysis (it might take around 30-60 secs) ...\n")
77
+
78
+ res = pipe.run({"loader": {"run_input": {"directUrls": [url1]}}})
79
+ print("Analysis:", res.get("llm", {}).get("replies", ["No response"])[0])
80
+
81
+ url2 = "https://www.instagram.com/p/C_RgBzogufK/" # @maharishis on Affordable Care Act
82
+ print(f"\nScraping Instagram comments for: {url2} and running analysis (it might take around 30-60 secs)... \n")
83
+
84
+ res = pipe.run({"loader": {"run_input": {"directUrls": [url2]}}})
85
+ print("Analysis:", res.get("llm", {}).get("replies", ["No response"])[0])
File without changes