Sponsored by AppSignal
Would you like to see your link here? Contact us
Notesclub

LangChain

livebooks/langchain_get_started.livemd

LangChain

Mix.install(
  [
    :jason,
    :req,
    :langchain
  ],
  config: [
    langchain: [
      openai_key: System.fetch_env!("LB_OPENAI_API_KEY"),
      openai_org_id: System.fetch_env!("LB_OPENAI_ORG_ID")
    ]
  ]
)

# Application.put_env(:langchain, :openai_key, System.fetch_env!("LB_OPENAI_API_KEY"))
# Application.put_env(:langchain, :openai_org_id, System.fetch_env!("LB_OPENAI_ORG_ID"))

Getting Started

alias LangChain.Chains.LLMChain
alias LangChain.ChatModels.ChatOpenAI
alias LangChain.Message

sys_prompt = """
You are an elixir developer obsests with elixir. You reply to every question/greeting with elixir code.
Even if you give simple reply you will use something like IO.puts("Hi!").
"""

{:ok, updated_chain, response} =
  %{llm: ChatOpenAI.new!(%{model: "gpt-3.5-turbo-0125"})}
  |> LLMChain.new!()
  |> LLMChain.add_message(Message.new_system!(sys_prompt))
  |> LLMChain.add_message(Message.new_user!("Hello world"))
  |> LLMChain.run()
{response.content, response.status}

response.content
|> String.replace(~r/```elixir/, "")
|> String.replace(~r/```/, "")
|> Code.eval_string()

Streaming response

alias LangChain.MessageDelta

callback = fn
  %MessageDelta{} = data ->
    # we received a piece of data
    IO.write(data.content)

  %Message{} = data ->
    IO.puts("")
    IO.puts("")
    IO.inspect(data.content, label: "COMPLETED MESSAGE")
end

{:ok, _updated_chain, response} =
  %{llm: ChatOpenAI.new!(%{model: "gpt-3.5-turbo-0125", stream: true})}
  |> LLMChain.new!()
  |> LLMChain.add_messages([
    Message.new_system!("You are a helpful assistant."),
    Message.new_user!("Write a haiku about the capital of the United Kingdom")
  ])
  |> LLMChain.run(callback_fn: callback)
defmodule OllamaClient do
  # 5 mins
  @timeout 300_000
  @url "http://localhost:11434/api/generate"
  def call(model, task, system_prompt) do
    prompt = "#{system_prompt}#{task}"

    payload =
      Jason.encode!(%{
        "model" => model,
        "prompt" => prompt,
        "stream" => false
      })

    Req.post!(@url, body: payload, receive_timeout: @timeout).body["response"]
  end
end
task = """
package main

import (
	"fmt"
	"os"
	"strings"
)

func main() {
	file, err := os.ReadFile("day1/input.txt")
	if err != nil {
		panic(err)
	}
	input := strings.TrimSpace(string(file))
	floor := 0
	for _, c := range input {
		if c == '(' {
			floor++
		} else if c == ')' {
			floor--
		}
	}
	fmt.Println(floor)
}
"""

system_prompt = """
You are an expert programmer that writes simple, concise code and no comments or explanation.
Given this golang program write a .cpp version that it reads its input from a file "input.txt" and solve the following task.
The program should print the answer on the stdout and can be run `/usr/bin/c++ solution.cpp`.

Ensure all functions or variables are declared before use. If a function is used before its definition, declare it at the beginning of your code or in a header file.
Always insert a space between consecutive right angle brackets in template declarations to ensure compatibility with older C++ standards. For example, use std::vector > instead of std::vector>.
Use appropriate constructors for non-aggregate types like std::vector or std::unordered_map instead of brace initialization when not directly supported.
Include the correct header for the function you are trying to use. For std::accumulate, include  at the beginning of your file.

Golang Task:
"""

OllamaClient.call("qwen:14b-chat", task, system_prompt)