diff --git a/lib/chains/text_to_title_chain.ex b/lib/chains/text_to_title_chain.ex index 94f82a80..4857a905 100644 --- a/lib/chains/text_to_title_chain.ex +++ b/lib/chains/text_to_title_chain.ex @@ -2,6 +2,48 @@ defmodule LangChain.Chains.TextToTitleChain do @moduledoc """ A convenience chain for turning a user's prompt text into a summarized title for the anticipated conversation. + + ## Examples + A basic example that generates a title + + llm = ChatOpenAI.new!(%{model: "gpt-3.5-turbo", stream: false, seed: 0}) + user_text = "Let's start a new blog post about the magical properties of pineapple cookies." + + %{ + llm: llm, + input_text: user_text + } + |> TextToTitleChain.new!() + |> TextToTitleChain.evaluate() + + #=> "Magical Properties of Pineapple Cookies Blog Post" + + Want to get more consistent titles? + + LLMs are pretty bad at following instructions for text length. However, we can + provide examples titles for the LLM to follow in format style and length. We + get the added benefit of getting more consistently formatted titles. + + This is the same example, however now we provide other title examples to the + LLM to follow for consistency. + + llm = ChatOpenAI.new!(%{model: "gpt-3.5-turbo", stream: false, seed: 0}) + user_text = "Let's start a new blog post about the magical properties of + pineapple cookies." + + %{ + llm: llm, + input_text: user_text, + examples: [ + "Blog Post: Making Delicious and Healthy Smoothies", + "System Email: Notifying Users of Planned Downtime" + ] + } + |> TextToTitleChain.new!() + |> TextToTitleChain.evaluate() + + #=> "Blog Post: Exploring the Magic of Pineapple Cookies" + """ use Ecto.Schema import Ecto.Changeset @@ -10,7 +52,6 @@ defmodule LangChain.Chains.TextToTitleChain do alias __MODULE__ alias LangChain.Chains.LLMChain alias LangChain.LangChainError - alias LangChain.Message alias LangChain.Utils alias LangChain.Utils.ChainResult @@ -19,12 +60,13 @@ defmodule LangChain.Chains.TextToTitleChain do field :llm, :any, virtual: true field :input_text, :string field :fallback_title, :string, default: "New topic" + field :examples, {:array, :string}, default: [] field :verbose, :boolean, default: false end @type t :: %TextToTitleChain{} - @create_fields [:llm, :input_text, :fallback_title, :verbose] + @create_fields [:llm, :input_text, :fallback_title, :examples, :verbose] @required_fields [:llm, :input_text] @doc """ @@ -86,15 +128,22 @@ defmodule LangChain.Chains.TextToTitleChain do def run(%TextToTitleChain{} = chain, opts \\ []) do messages = [ - Message.new_system!( - "You expertly summarize a user's prompt into a short title or phrase to represent a conversation." - ), + PromptTemplate.new!(%{ + role: :system, + text: """ + You expertly summarize the User Text into a short title or phrase to represent a conversation. + + <%= if @examples != [] do %>Follow the style, approximate length, and format of the following examples: + <%= for example <- @examples do %>- <%= example %> + <% end %><% end %> + """ + }), PromptTemplate.new!(%{ role: :user, - text: "Generate and return the title and nothing else. Prompt:\n<%= @input %>" + text: "Generate and return the title and nothing else. User Text:\n<%= @input %>" }) ] - |> PromptTemplate.to_messages!(%{input: chain.input_text}) + |> PromptTemplate.to_messages!(%{input: chain.input_text, examples: chain.examples}) %{llm: chain.llm, verbose: chain.verbose} |> LLMChain.new!() diff --git a/test/chains/text_to_title_chain_test.exs b/test/chains/text_to_title_chain_test.exs index ca54b5d1..7e411f9e 100644 --- a/test/chains/text_to_title_chain_test.exs +++ b/test/chains/text_to_title_chain_test.exs @@ -94,5 +94,27 @@ defmodule LangChain.Chains.TextToTitleChainTest do assert fallback_title == TextToTitleChain.evaluate(title_chain) end + + @tag live_call: true, live_open_ai: true + test "supports using examples", %{llm: llm, input_text: input_text} do + data = %{ + llm: llm, + input_text: input_text, + fallback_title: "Default new title", + examples: [ + "Blog Post: Making Delicious and Healthy Smoothies", + "System Email: Notifying Users of Planned Downtime" + ], + verbose: true + } + + result_title = + data + |> TextToTitleChain.new!() + |> TextToTitleChain.evaluate() + + assert String.starts_with?(result_title, "Blog Post:") + assert String.contains?(result_title, "Pineapple") + end end end