<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tuana Celik</title>
    <description>The latest articles on DEV Community by Tuana Celik (@tuanacelik).</description>
    <link>https://dev.to/tuanacelik</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F586180%2Ff360ddb9-125c-46b1-9006-d68cfc075bfa.jpeg</url>
      <title>DEV Community: Tuana Celik</title>
      <link>https://dev.to/tuanacelik</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tuanacelik"/>
    <language>en</language>
    <item>
      <title>Customizing RAG Pipelines to Summarize Latest Hacker News Posts with Haystack 2.0 Preview</title>
      <dc:creator>Tuana Celik</dc:creator>
      <pubDate>Fri, 22 Sep 2023 13:41:43 +0000</pubDate>
      <link>https://dev.to/tuanacelik/customizing-rag-pipelines-to-summarize-latest-hacker-news-posts-with-haystack-20-preview-4cm3</link>
      <guid>https://dev.to/tuanacelik/customizing-rag-pipelines-to-summarize-latest-hacker-news-posts-with-haystack-20-preview-4cm3</guid>
      <description>&lt;p&gt;&lt;em&gt;Take a look at how we are changing Haystack for advanced LLM pipelines, with an example that uses a custom component to fetch the latest Hacker News posts&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TPXIjEeC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/km1oeov1f63tgc5l2zo6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TPXIjEeC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/km1oeov1f63tgc5l2zo6.png" alt="Image description" width="663" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Over the last few months, the team at &lt;a href="https://deepset.ai/"&gt;deepset&lt;/a&gt; has been working on a major upgrade in the Haystack repository. Along the way, we’ve been sharing our updates and design process for the upcoming &lt;a href="https://github.com/deepset-ai/haystack/tree/main/haystack/preview"&gt;Haystack 2.0&lt;/a&gt; with the community, as well as releasing new components in a preview package. This means that you can already start exploring features coming to Haystack 2.0 using the preview components available in the &lt;code&gt;haystack-ai&lt;/code&gt; package (&lt;code&gt;pip install haystack-ai&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You can run the example code showcased in this article in the accompanying &lt;a href="https://colab.research.google.com/drive/1YWFvq29xkMAUCt5Aal0VPX0KxGM4xTku?usp=sharing"&gt;Colab notebook&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this article, I’ll cover two major concepts in Haystack 2.0.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Components:&lt;/strong&gt; These are the smallest building blocks in Haystack. They are meant to cover one simple task. As well as using components available in the core Haystack project, it will be easier than ever in 2.0, to create your own custom components.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pipelines:&lt;/strong&gt; These are made by connecting components to each other. Pipelines in 2.0 are more flexible than ever and enable you various new connection patterns between your components.
While components and pipelines have been at the core of Haystack since the beginning, Haystack 2.0 introduces some significant changes to how they are constructed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll look at how to create custom components and pipelines using the Haystack 2.0 preview. I’ll share a custom Haystack component that fetches the latest posts from Hacker News, and show how we can use it in a retrieval-augmented generative (RAG) pipeline to generate summaries of Hacker News posts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Components in Haystack 2.0
&lt;/h2&gt;

&lt;p&gt;A component is a class that does one thing. That thing could be to ‘prompt GPT3.5’, or ‘translate’, or ‘retrieve documents’, and so on.&lt;/p&gt;

&lt;p&gt;While Haystack comes with a set of components in the core project, we hope that with Haystack 2.0 you will also be able to easily build components to your own custom requirements.&lt;/p&gt;

&lt;p&gt;In Haystack 2.0, a class can become a component with just two additions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;@component&lt;/code&gt; decorator on the class declaration.
A run function with a decorator &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@component.output_types(my_output_name=my_output_type)&lt;/code&gt; that describes what output the pipeline should expect from this component.
And that’s about it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Building a Custom Hacker News Component
&lt;/h3&gt;

&lt;p&gt;I’ll admit, the idea for this custom component came from one of our amazing Haystack ambassadors on Discord during a live coding session (thanks rec 💙) — and it turned out pretty well! So let’s take a look at how we create a custom component that fetches the latest k posts from Hacker News.&lt;/p&gt;

&lt;p&gt;First, we create a &lt;code&gt;HackernewsNewestFetcher&lt;/code&gt;. For it to be a valid Haystack component, it will also need a run function. For now, let’s create a stub function that simply returns a dictionary containing a single key ‘articles’ with the value ‘Hello world!’.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;haystack.preview&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;component&lt;/span&gt;  

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;  
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HackernewsNewestFetcher&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;  

  &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;output_types&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;articles&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'articles'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'Hello world!'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let’s make our component actually fetch the latest posts from Hacker News. We can use the &lt;a href="https://newspaper.readthedocs.io/en/latest/"&gt;&lt;code&gt;newspapers3k&lt;/code&gt;&lt;/a&gt; package to crawl and get the contents of given URLs. We will also change the output type to return a list of Document objects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;  
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;haystack.preview&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Document&lt;/span&gt;  
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;newspaper&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Article&lt;/span&gt;  
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;requests&lt;/span&gt;  

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;  
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HackernewsNewestFetcher&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;  

  &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;output_types&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;articles&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;last_k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  
    &lt;span class="n"&gt;newest_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'https://hacker-news.firebaseio.com/v0/newstories.json?print=pretty'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
    &lt;span class="n"&gt;articles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;  
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;newest_list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;last_k&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;  
      &lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"https://hacker-news.firebaseio.com/v0/item/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.json?print=pretty"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s"&gt;'url'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;  
        &lt;span class="n"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="s"&gt;'url'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  

    &lt;span class="n"&gt;docs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;  
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  
      &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  
        &lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
        &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;download&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
        &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
        &lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'url'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;  
      &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"Couldn't download &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, skipped"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'articles'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now have a component that, when run, returns a list of Documents containing the contents of the (last_k) latest posts on Hacker News. Here we store the output in the articles key of the dictionary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pipelines in Haystack 2.0
&lt;/h2&gt;

&lt;p&gt;A pipeline is a structure that connects one component’s output to another component’s input until a final result is reached.&lt;/p&gt;

&lt;p&gt;A pipeline is created with a few steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a Pipeline:
&lt;code&gt;pipeline = Pipeline()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add components to the pipeline:
&lt;code&gt;pipeline.add_component(instance=component_a, name=”ComponentA”)&lt;/code&gt; &lt;code&gt;pipeline.add_component(instance=component_b, name=”ComponentB”)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Connect an output from one component to the input of another:
&lt;code&gt;pipeline.connect("component_a.output_a", "component_b.input_b")&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are already enough components available in the Haystack 2.0 preview for us to build a simple RAG pipeline that uses our new &lt;code&gt;HackernewsNewestFetcher&lt;/code&gt; for the retrieval augmentation step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building a RAG Pipeline to Generate Summaries of Hacker News Posts
&lt;/h3&gt;

&lt;p&gt;To build a RAG pipeline that can create a summary for each of the latest k posts on Hacker News, we will use two components from the Haystack 2.0 preview:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;PromptBuilder&lt;/code&gt;: This component allows us to create prompt templates using Jinja as our templating language.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;GPTGenerator&lt;/code&gt;: This component simply prompts the specified GPT model. We can connect the &lt;code&gt;PromptBuilder&lt;/code&gt; output to this component to customize how we interact with our chosen model.
First, we initialize all of the components we will need for the pipeline:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;haystack.preview&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pipeline&lt;/span&gt;  
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;haystack.preview.components.builders.prompt_builder&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PromptBuilder&lt;/span&gt;  
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;haystack.preview.components.generators.openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;GPTGenerator&lt;/span&gt;  

&lt;span class="n"&gt;prompt_template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""  
You will be provided a few of the latest posts in HackerNews, followed by their URL.  
For each post, provide a brief summary followed by the URL the full post can be found at.  

Posts:  
{% for article in articles %}  
  {{article.text}}  
  URL: {{article.metadata['url']}}  
{% endfor %}  
"""&lt;/span&gt;  

&lt;span class="n"&gt;prompt_builder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PromptBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;prompt_template&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GPTGenerator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"gpt-4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'YOUR_API_KEY'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="n"&gt;fetcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HackernewsNewestFetcher&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we add the components to a Pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Pipeline&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
&lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hackernews_fetcher"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fetcher&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"prompt_builder"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt_builder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"llm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, we connect the components to each other:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hackernews_fetcher.articles"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"prompt_builder.articles"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"prompt_builder"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"llm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, notice how we connect &lt;code&gt;hackernews_fetcher.articles&lt;/code&gt; to &lt;code&gt;prompt_builder.articles&lt;/code&gt;. This is because prompt_builder is expecting articles in its template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Posts:  
{% for article in articles %}  
  {{article.text}}  
  URL: {{article.metadata['url']}}  
{% endfor %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output and input keys do not need to have matching names. Additionally, &lt;code&gt;prompt_builder&lt;/code&gt; makes all of the input keys available to your prompt template. We could, for example, provide a &lt;code&gt;documents&lt;/code&gt; input to &lt;code&gt;prompt_builder&lt;/code&gt; instead of &lt;code&gt;articles&lt;/code&gt;. Then our code might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;prompt_template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""  
You will be provided a few of the latest posts in HackerNews, followed by their URL.  
For each post, provide a brief summary followed by the URL the full post can be found at.  

Posts:  
{% for document in documents %}  
  {{document.text}}  
  URL: {{document.metadata['url']}}  
{% endfor %}  
"""&lt;/span&gt;  

&lt;span class="p"&gt;[...]&lt;/span&gt;  

&lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hackernews_fetcher.articles"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"prompt_builder.documents"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how the prompt now refers to documents, and the connect call now attaches to the corresponding &lt;code&gt;prompt_builder.documents&lt;/code&gt; input.&lt;/p&gt;

&lt;p&gt;Now that we have a pipeline, we can run it. Here is what I got as a response at about 22:45 CET on September 21st 🤗&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"hackernews_fetcher"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="s"&gt;"last_k"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}})&lt;/span&gt;  
&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'llm'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;'replies'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. "The translation world has legends of its own, but not all legends involve greatness.   
Many provide pain, confusion, or comedy, as these examples of bad game translation prove."   
- This post shares a humorous look at some examples of poor video game translations that have   
resulted in confusion and comedy. The author seeks to highlight that while translation is often   
necessary in game localization, it can sometimes yield suboptimal results.  
Link: https://legendsoflocalization.com/bad-translation/  

2. “Recently, I found myself returning to a compelling series of   
blog posts titled Zero-cost futures in Rust by Aaron Turon about what would   
become the foundation of Rust's async ecosystem.”   
- This post provides an in-depth analysis of the current state of Rust's   
'async' ecosystem, drawing upon the author's own experiences and Aaron Turon's   
blog series, "Zero-cost futures in Rust". The author also discusses the benefits and   
negatives of the current async ecosystem, the problems with ecosystem fragmentation,   
the state and issue of async-std, alternative runtimes, the complexities of writing async code,   
the benefits of synchronous threads over async, and the obsessiveness of Rust landscape with an   
async-first approach. The post concludes with the notion that async Rust should be used only   
when necessary and that the smaller, simpler language inside Rust (the synchronous Rust)   
should be the default mode.  
Link: https://corrode.dev/blog/async/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Further Improvements
&lt;/h2&gt;

&lt;p&gt;This custom component was created as an experiment and you could certainly take it much further in a real-world application.&lt;/p&gt;

&lt;p&gt;For example, our experimental component does nothing to reduce the length of the content in each article. This means that GPT-4 may struggle to give a good response, especially when setting last_k to a high number.&lt;/p&gt;

</description>
      <category>llm</category>
      <category>opensource</category>
      <category>python</category>
      <category>haystack</category>
    </item>
    <item>
      <title>Talk to YouTube Videos with Haystack Pipelines</title>
      <dc:creator>Tuana Celik</dc:creator>
      <pubDate>Fri, 08 Sep 2023 14:12:41 +0000</pubDate>
      <link>https://dev.to/tuanacelik/talk-to-youtube-videos-with-haystack-pipelines-3b8n</link>
      <guid>https://dev.to/tuanacelik/talk-to-youtube-videos-with-haystack-pipelines-3b8n</guid>
      <description>&lt;p&gt;Use Whisper to provide YouTube videos as context for retrieval augmented generation&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4wF3dU0d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2t2wnv9fvnglbetdkf6f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4wF3dU0d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2t2wnv9fvnglbetdkf6f.png" alt="Talk to YouTube Videos with Haystack Pipelines" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You can use this&lt;/em&gt; &lt;a href="https://colab.research.google.com/drive/1sZM5Y1NkPOy3y8HCsecsmhjImrARIVru?usp=sharing"&gt;&lt;em&gt;Colab&lt;/em&gt;&lt;/a&gt; &lt;em&gt;for a working example of the application described in this article.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this article, I’ll be showing an example of how to leverage transcription models like OpenAI’s Whisper, so as to build a retrieval augmented generation (RAG) pipeline that will allow us to effectively search through video content.&lt;/p&gt;

&lt;p&gt;The example application I’ll showcase is able to answer questions based on the transcript extracted from the video. I’ll use the &lt;a href="https://www.youtube.com/watch?v=h5id4erwD4s"&gt;video by Erika Cardenas&lt;/a&gt; as an example. In the video, she talks about chunking and preprocessing documents for RAG pipelines. Once we’re done, we will be able to query a Haystack pipeline that will respond based on the contents of the video.&lt;/p&gt;

&lt;h2&gt;
  
  
  Transcribing and Storing the Video
&lt;/h2&gt;

&lt;p&gt;To get started, we first need to set up an &lt;a href="https://docs.haystack.deepset.ai/docs/pipelines#indexing-pipelines"&gt;indexing pipeline&lt;/a&gt;. These pipelines in Haystack are designed to be given files of some form (.pdf, .txt, .md and in our case, a YouTube link), and store them in a database. The indexing pipeline is also used to design and define how we would like files to be prepared. This often involves &lt;a href="https://docs.haystack.deepset.ai/docs/file_converters"&gt;file conversion&lt;/a&gt; steps, some &lt;a href="https://docs.haystack.deepset.ai/docs/preprocessor"&gt;preprocessing&lt;/a&gt;, and maybe also some &lt;a href="https://docs.haystack.deepset.ai/docs/retriever#embedding-retrieval-recommended"&gt;embedding&lt;/a&gt; creation and so on.&lt;/p&gt;

&lt;p&gt;The way we design the components and structure of this pipeline will also be important for another type of pipeline we will create in the next section: The RAG pipeline, also often referred to as the query or LLM pipeline too. While the indexing pipeline defines how we prepare and store data, an LLM pipeline &lt;strong&gt;&lt;em&gt;uses&lt;/em&gt;&lt;/strong&gt; said stored data. A simple example of the impact an indexing pipeline has on the RAG pipeline is that depending on the model we’re using, we may have to chunk our files to be longer or shorter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reusability
&lt;/h3&gt;

&lt;p&gt;The idea behind Haystack pipelines is that once created, they can be re-invoked when needed. This ensures that data is treated the same way each time. In terms of indexing pipelines, this means we have a way to keep our databases for RAG pipelines always up to date. In a practical sense for this example application, when there’s a new video we want to be able to query, we re-use the same indexing pipeline and run the new video through it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the Indexing Pipeline
&lt;/h3&gt;

&lt;p&gt;In this example, we’re using Weaviate as our vector database for storage. However, Haystack provides a number of &lt;a href="https://haystack.deepset.ai/integrations?type=Document+Store"&gt;Document Stores&lt;/a&gt; which you can pick from.&lt;/p&gt;

&lt;p&gt;First, we create our &lt;code&gt;WeaviateDocumentStore&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;weaviate&lt;/span&gt;  
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;weaviate.embedded&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;EmbeddedOptions&lt;/span&gt;  
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;haystack.document_stores&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;WeaviateDocumentStore&lt;/span&gt;  

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;weaviate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;  
  &lt;span class="n"&gt;embedded_options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;weaviate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;embedded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EmbeddedOptions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
&lt;span class="p"&gt;)&lt;/span&gt;  

&lt;span class="n"&gt;document_store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WeaviateDocumentStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;6666&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we build the indexing pipeline. Here, our aim is to create a pipeline that will create transcripts of YouTube videos. So, we use the &lt;a href="https://docs.haystack.deepset.ai/docs/whisper_transcriber"&gt;&lt;strong&gt;&lt;code&gt;WhisperTranscriber&lt;/code&gt;&lt;/strong&gt;&lt;/a&gt; as our first component. This component uses &lt;a href="https://openai.com/research/whisper"&gt;Whisper&lt;/a&gt; by OpenAI, an automatic speech recognition (ASR) system which can be used to transcribe audio into text. The component expects audio files, and returns transcripts in &lt;a href="https://docs.haystack.deepset.ai/docs/documents_answers_labels"&gt;Haystack Document&lt;/a&gt; form, ready to be used in any Haystack pipeline.&lt;/p&gt;

&lt;p&gt;We also include preprocessing, as well as embedding creations in our pipeline. This is because when it’s time to create the RAG pipeline, we would like to do semantic search on the indexed files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;haystack.nodes&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;EmbeddingRetriever&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PreProcessor&lt;/span&gt;  
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;haystack.nodes.audio&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;WhisperTranscriber&lt;/span&gt;  
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;haystack.pipelines&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pipeline&lt;/span&gt;  

&lt;span class="n"&gt;preprocessor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PreProcessor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
&lt;span class="n"&gt;embedder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EmbeddingRetriever&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document_store&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;document_store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   
                              &lt;span class="n"&gt;embedding_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"sentence-transformers/multi-qa-mpnet-base-dot-v1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="n"&gt;whisper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WhisperTranscriber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'OPENAI_API_KEY'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  

&lt;span class="n"&gt;indexing_pipeline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Pipeline&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
&lt;span class="n"&gt;indexing_pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;whisper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Whisper"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"File"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  
&lt;span class="n"&gt;indexing_pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;preprocessor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Preprocessor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Whisper"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  
&lt;span class="n"&gt;indexing_pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;embedder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Embedder"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Preprocessor"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  
&lt;span class="n"&gt;indexing_pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;document_store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"DocumentStore"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Embedder"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we create a helper function that extracts the audio of YouTube videos, and we can run the pipeline, for this, we install the &lt;code&gt;pytube&lt;/code&gt; package 👇&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pytube&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;YouTube&lt;/span&gt;  

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;youtube2audio&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  
    &lt;span class="n"&gt;yt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;YouTube&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
    &lt;span class="n"&gt;video&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;yt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;streams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;abr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'160kbps'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;video&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;download&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can run our indexing pipeline with a URL to a YouTube video:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;file_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;youtube2audio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://www.youtube.com/watch?v=h5id4erwD4s"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="n"&gt;indexing_pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_paths&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Retrieval Augmented Generative (RAG) Pipeline
&lt;/h2&gt;

&lt;p&gt;This part is certainly the fun part. We now define our RAG pipeline. This will be the pipeline that defines &lt;em&gt;how&lt;/em&gt; we query our videos. Although RAG pipelines often are built for question-answering, they can be designed for a number of other use cases. What the pipeline does in this case, is largely defined by what prompt you provide the LLM. You can find various prompts for different use cases in the &lt;a href="https://prompthub.deepset.ai/"&gt;PromptHub&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Prompt
&lt;/h3&gt;

&lt;p&gt;For this example, we’ve gone with a commonly used style of question-answering prompts, although you can of course change this prompt to do what you want to achieve. For example, changing it to a prompt that asks for a summary might be interesting. You could also make it more general. Here we’re also informing the model that the transcripts belong to Weaviate videos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You will be provided some transcripts from Weaviate YouTube videos.   
Please answer the query based on what is said in the videos.  
Video Transcripts: {join(documents)}  
Query: {query}  
Answer:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Haystack, these prompts can be included in a pipeline with the &lt;a href="https://docs.haystack.deepset.ai/docs/prompt_node#prompttemplates"&gt;&lt;code&gt;PromptTemplate&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://docs.haystack.deepset.ai/docs/prompt_node"&gt;&lt;code&gt;PromptNode&lt;/code&gt;&lt;/a&gt; components.&lt;/p&gt;

&lt;p&gt;While the &lt;code&gt;PromptTemplate&lt;/code&gt; is where we define the prompt and the variables the prompt expects as inputs (in our case &lt;em&gt;documents&lt;/em&gt; and &lt;em&gt;query&lt;/em&gt;), the &lt;code&gt;PromptNode&lt;/code&gt; is really the interface with which we interact with LLMs. In this example, we’re using GPT-4 as our model of choice, but you can &lt;a href="https://docs.haystack.deepset.ai/docs/prompt_node#models"&gt;change this to use other models from Hugging Face, SageMaker, Azure&lt;/a&gt; and so on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;haystack.nodes&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PromptNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PromptTemplate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AnswerParser&lt;/span&gt;  

&lt;span class="n"&gt;video_qa_prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PromptTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"You will be provided some transcripts from Weaviate YouTube videos. Please answer the query based on what is said in the videos.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;  
                                        &lt;span class="s"&gt;"Video Transcripts: {join(documents)}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;  
                                        &lt;span class="s"&gt;"Query: {query}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;  
                                        &lt;span class="s"&gt;"Answer:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output_parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AnswerParser&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;  

&lt;span class="n"&gt;prompt_node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PromptNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_name_or_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"gpt-4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                         &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'OPENAI_KEY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                         &lt;span class="n"&gt;default_prompt_template&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;video_qa_prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Pipeline
&lt;/h3&gt;

&lt;p&gt;Finally, we define our RAG pipeline. The important thing to note here is how the &lt;em&gt;documents&lt;/em&gt; input gets provided to the prompt we are using.&lt;/p&gt;

&lt;p&gt;Haystack retrievers always return &lt;code&gt;documents&lt;/code&gt;. Notice below how the first component to get the query is the same &lt;code&gt;EmbeddingRetriever&lt;/code&gt; that we used in the indexing pipeline above. This allows us to embed the query using the same model that was used for indexing the transcript. The embeddings of the query and indexed transcripts are then used to retrieve the most relevant parts of the transcript. Since these are returned by the retriever as &lt;strong&gt;&lt;em&gt;documents,&lt;/em&gt;&lt;/strong&gt; we are able to fill in the &lt;em&gt;documents&lt;/em&gt; parameter of the prompt with whatever the retriever returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;video_rag_pipeline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Pipeline&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
&lt;span class="n"&gt;video_rag_pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;embedder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Retriever"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Query"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  
&lt;span class="n"&gt;video_rag_pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;prompt_node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"PromptNode"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Retriever"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can run the pipeline with a query. The response will be based on what Erika said in the example video we’re using 🤗&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;video_rag_pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Why do we do chunking?"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result I got for this was the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Chunking is done to ensure that the language model is receiving the most   
relevant information and not going over the context window. It involves   
splitting up the text once it hits a certain token limit, depending on   
the model or the chunk size defined. This is especially useful in documents   
where subsequent sentences or sections may not make sense without the   
information from previous ones. Chunking can also help in providing extremely   
relevant information when making queries that are specific to titles or   
sections.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Further Improvements
&lt;/h2&gt;

&lt;p&gt;In this example, we’ve used a transcription model that is able to transcribe audio into text, but it is unable to distinguish between speakers. A follow up step I would like to try is to use a model that allows for speaker distinction. This would allow me to ask questions and in the response from the model, get an understanding of who provided that answer in the video.&lt;/p&gt;

&lt;p&gt;Another point I would like to make is that this pipeline, which was for demonstration purposes, uses a light-weight yet quite effective &lt;strong&gt;sentence-transformers&lt;/strong&gt; model for retrieval, and the default setting for preprocessing. More could definitely be done to find out what the best embedding model for retrieval would be. And taking inspiration from Erika’s video, chunking and preprocessing of the transcribed documents could be evaluated and improved.&lt;/p&gt;

&lt;p&gt;To discover more about the available pipelines and components that would help you build custom LLM applications, check out the &lt;a href="https://docs.haystack.deepset.ai/"&gt;Haystack documentation&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>llm</category>
      <category>opensource</category>
      <category>haystack</category>
      <category>python</category>
    </item>
    <item>
      <title>Strava Dashboards with Zapier and Cumul.io</title>
      <dc:creator>Tuana Celik</dc:creator>
      <pubDate>Fri, 15 Oct 2021 12:37:57 +0000</pubDate>
      <link>https://dev.to/tuanacelik/strava-dashboards-with-zapier-and-cumulio-ei7</link>
      <guid>https://dev.to/tuanacelik/strava-dashboards-with-zapier-and-cumulio-ei7</guid>
      <description>&lt;p&gt;Recently one of our &lt;a href="http://cumul.io/"&gt;Cumul.io&lt;/a&gt; Ambassadors shared a company Strava dashboard they built with Cumul.io and I had to build something similar for us. It's a nice way to keep motivated to go out for runs and great for those who are competitive when it comes to exercising (NOT me). And it's a fun way to use a data visualization tool like Cumul.io. So anyway, I followed the lead of Olivier de Lamotte who gave us the idea after sharing the one he built for his team at &lt;a href="https://qualifio.com/"&gt;Qualifio&lt;/a&gt; and built our own &lt;a href="https://app.cumul.io/s/cumulio-club-activities-on-strava-baxglzxfbgz91w3z"&gt;Cumul.io Team Strava Dashboard&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;For this exercise, again as Olivier did, I used &lt;a href="https://zapier.com/"&gt;Zapier&lt;/a&gt;. I initially thought I might just use the &lt;a href="https://developers.strava.com/"&gt;Strava API directly&lt;/a&gt;. However I soon found out that the &lt;a href="https://developers.strava.com/docs/reference/#api-Clubs-getClubActivitiesById"&gt;activities endpoint&lt;/a&gt; for club activities doesn't really provide a lot of data and misses some (imo) obvious ones, like the date of the activity. So instead of building around it myself, I went ahead with the Zapier workflow that is already available, which adds a column to a Google sheet whenever there is a new activity by a club member.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Side note: Both &lt;a href="http://cumul.io/"&gt;Cumul.io&lt;/a&gt; and &lt;a href="https://zapier.com/"&gt;Zapier&lt;/a&gt; have free trials so if you're not a paying user you can still get this Strava dashboard up pretty easily!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here's how it's set up:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a Zapier workflow&lt;/li&gt;
&lt;li&gt;Add the dataset to Cumul.io&lt;/li&gt;
&lt;li&gt;Let your creativity shine and create the most brutal athletes dashboard for your team&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Create a Zapier Workflow
&lt;/h3&gt;

&lt;p&gt;I'm new to Zapier, and the simplest way I can explain how this works is that you create 'Zap's (or workflows) which you can turn on or off. And these workflows are simply: Trigger -&amp;gt; Action. I.e.: "When this happens, do that"&lt;/p&gt;

&lt;p&gt;There are already a number of Strava based workflows available on Zapier. Just search for it and one of the first one that comes up will be the one I used for our dashboard, which is 'Add new Strava club activities as rows to Google Sheets':&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1qORkYbK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cj1k25in1jod5k498zus.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1qORkYbK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cj1k25in1jod5k498zus.png" alt="Create Zapier Strava Trigger" width="800" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you select that, the workflow will appear in your 'Zaps' tab and it's pretty straight forward. First you should set up your trigger, for which I picked 'New or Updated Club Activity':&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MWgSsP1c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6345r2sc2zipztbymhd6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MWgSsP1c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6345r2sc2zipztbymhd6.png" alt="Create Zapier Strava Trigger" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And then you will also have to select a Strava account to connect to. Next you'll be able to pick a Strava club that this account is a member of to get new activities from. Warning (from experience)! Be aware that if there is a member of the club that has a private Strava account, the owner of the account that sets up this trigger will also have to follow said member for their activities to be tracked!&lt;/p&gt;

&lt;p&gt;Once you have set this up, you can set up the action. This is pretty simple and Zapier will walk you through selecting a Google Sheet to add new activities to. Here's an example of what mine looks like: &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HDdrefVM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ci5xe08fg5h3l7hjji3b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HDdrefVM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ci5xe08fg5h3l7hjji3b.png" alt="Create Zapier Action" width="662" height="620"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, don't forget to turn this workflow (zap) ON!&lt;/p&gt;

&lt;p&gt;Once you've set this up, you should be able to see new activities showing up in the Google Sheet you selected. This sheet will be what we connect to Cumul.io.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add the Dataset to Cumul.io
&lt;/h3&gt;

&lt;p&gt;Now that we have a Google Sheet that lists activities by a club member on every row (and thanks to Zapier it's updated every time there is a new one), we just have to connect the dataset to Cumul.io and create a dashboard.&lt;/p&gt;

&lt;p&gt;In your Cumul.io account head over to 'Datasets' and select 'New Dataset'. Here, simply pick Google Drive and select the Google Sheet you just created:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1KHSuw_1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rykg1t2w855qwyoz9omz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1KHSuw_1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rykg1t2w855qwyoz9omz.png" alt="Add Dataset to Cumul.io" width="727" height="552"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a Dashboard
&lt;/h3&gt;

&lt;p&gt;Now this is quite simple. Just go ahead and add the dataset you created and let your creativity shine. Here's the dashboard I created for our team for inspiration. And a lot of that inspiration was taken from Olivier De Lamotte's Qualifio dashboard:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--onwuw7Pc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rc82qulewbkgu2hcgf42.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--onwuw7Pc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rc82qulewbkgu2hcgf42.png" alt="Cumul.io Strava Dashboard" width="800" height="802"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's all! Let me know if you create your own. Would love to see them so please do share 🎈&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>saas</category>
    </item>
    <item>
      <title>Building My First Python Package with Poetry</title>
      <dc:creator>Tuana Celik</dc:creator>
      <pubDate>Mon, 19 Apr 2021 15:57:46 +0000</pubDate>
      <link>https://dev.to/tuanacelik/building-my-first-python-package-with-poetry-1bgj</link>
      <guid>https://dev.to/tuanacelik/building-my-first-python-package-with-poetry-1bgj</guid>
      <description>&lt;p&gt;A lot of firsts happening for me here. First post on Dev.to AND first published Python package. So I though I'd take the opportunity to share my experience building and publishing the package with &lt;a href="https://python-poetry.org/docs/"&gt;Poetry&lt;/a&gt; 😊&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cumul.io"&gt;Cumul.io&lt;/a&gt; has a number of SDKs available for people to install and use, but we were missing one in Python. So I built one! It's a simple one that provides interaction with our Core API (For those of you who don't know I'll add some info about Cumul.io at the end of this post). This might not be surprising to a lot of you but as it was my first go, I soon discovered there are a plethora of routes you can take to publish a package in PyPI and so I put in some research time to decide which one would be the least painful for me. In the end I decided on going for Poetry. &lt;a href="https://youtu.be/tNlurLxcf68"&gt;This video by Black Hills Information Security&lt;/a&gt; was extremely helpful to understand the different options and the advantages and disadvantages that come with them. Here's my takeaway and experience:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Poetry makes the configuration of your project a lot simpler than some of the other methods out there. You end up with only a &lt;em&gt;pyproject.toml&lt;/em&gt; (not even a requirements.txt is needed any more) vs &lt;em&gt;setup.py&lt;/em&gt; and &lt;em&gt;reqiurements.txt&lt;/em&gt; that you need with Pipenv for example. Example &lt;em&gt;pyproject.toml&lt;/em&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[tool.poetry]
name = "cumulio"
version = "x.x.x"
description = "Cumulio Python SDK for the Core API"
authors = []
readme = "README.md"
homepage = "Link to your homepage"
repository = "Link to your repo"
exclude = ["test/*"]
include = ["LICENSE"]
classifiers = [
    "Topic :: Software Development :: Libraries :: Python Modules"
]
packages = [
    { include = "cumulio"}
]

[tool.poetry.scripts]

[tool.poetry.dependencies]
python = "^3.7"
requests = "^2.25.1"

[tool.poetry.dev-dependencies]
autopep8 = "^1.5.6"

[build-system]
requires = ["poetry-core&amp;gt;=1.0.0"]
build-backend = "poetry.core.masonry.api"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how you can set your classifiers for PyPI as well as your dependencies.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Not only does it manage your dependencies, it also manages your packaging and upload to PyPI.&lt;/li&gt;
&lt;li&gt;Super easy to work in a virtual environment and get shell access inside the virtual environment. Literally just &lt;code&gt;poetry shell&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The current &lt;a href="https://pypi.org/project/cumulio/"&gt;Cumul.io Python Package&lt;/a&gt; provides a simple endpoint to the Cumul.io API. The code and Poetry setup are all open source on  &lt;a href="https://github.com/cumulio/cumul.io-sdk-python"&gt;GitHub&lt;/a&gt;. Now I know how to upload packages to PyPI, I intend to expand the SDK! &lt;/p&gt;

&lt;p&gt;As a first timer, Poetry made my life a lot easier than it could have been. Let me know what your experiences were and if any what disadvantages to poetry you've noticed that I haven't yet. I would be interested to know! &lt;/p&gt;

&lt;p&gt;About Cumul.io:&lt;/p&gt;

&lt;p&gt;Cumul.io is an API first data analytics and dashboarding platform that makes integrating dashboards and charts into your own platforms super easy. It's designed so that anything you can do frim within Cumul.io's UI, you can also do via the API which makes it quite a customizable option for a data analytics tool. The SDKs are there and are being expanded to cover a wide range of languages that are most commonly used in web development and data science (hence the reason for a Python SDK).&lt;/p&gt;

</description>
      <category>python</category>
      <category>todayilearned</category>
    </item>
  </channel>
</rss>
