Langchain.rb

repository·main·Indexed 24 days ago

https://github.com/patterns-ai-core/langchainrb

A Ruby implementation of the LangChain framework providing a unified interface for LLM providers including OpenAI, Anthropic, Google Gemini, and others. It features tools for managing conversation state, dynamic prompt templates, structured JSON output parsing, and a unified interface for vector search databases such as Chroma, Pinecone, and Weaviate to support Retrieval Augmented Generation (RAG). Includes the Langchain::Assistant class for high-level orchestration of LLMs, tools, and multi-turn conversations.

Tokens
10.9K
Snippets
24
Records
69
Agent score
83%

What's inside langchainrb

  1. How Langchain::Assistant works

    main

    The Langchain::Assistant class is a high-level abstraction that combines Large Language Models (LLMs), tools, and conversation management. It is designed to handle complex, multi-turn conversations, manage conversation threads, and execute tools (either automatically or manually) to provide coherent responses based on context.

    An assistant can be initialized with an LLM, a set of tools, and system instructions. It supports features like automatic tool execution, streaming responses (where supported by the LLM), and image input.

    llm = Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
    assistant = Langchain::Assistant.new(
      llm: llm,
      instructions: "You're a helpful AI assistant",
      tools: [Langchain::Tool::NewsRetriever.new(api_key: ENV["NEWS_API_KEY"])]
    )
    
    # Run the assistant with automatic tool execution
    assistant.add_message_and_run!(content: "What's the latest news about AI?")
    assistant.run(auto_tool_execution: true)
  2. How the unified LLM interface works

    main

    The Langchain::LLM module provides a unified abstraction layer for interacting with various Large Language Model (LLM) providers. All LLM classes inherit from Langchain::LLM::Base, ensuring a consistent interface for core operations regardless of the backend.

    This abstraction allows you to switch between providers (e.g., moving from OpenAI to Anthropic) by simply changing the class you instantiate, without needing to rewrite your application's logic for embeddings, completions, or chat.

    Supported Providers:

    • Anthropic
    • AWS Bedrock
    • Azure OpenAI
    • Cohere
    • Google Gemini
    • Google Vertex AI
    • HuggingFace
    • Mistral AI
    • Ollama
    • OpenAI
    • Replicate
    require "langchain"
    
    # Using Anthropic
    anthropic_llm = Langchain::LLM::Anthropic.new(api_key: ENV["ANTHROPIC_API_KEY"])
    
    # Using Google Gemini
    gemini_llm = Langchain::LLM::GoogleGemini.new(api_key: ENV["GOOGLE_GEMINI_API_KEY"])
    
    # Using OpenAI
    openai_llm = Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
  3. Access results from LLM response objects

    main

    Every LLM method in Langchain.rb returns a response object with a consistent interface. Use these methods to extract specific data from the LLM's output:

    • embedding: Returns the embedding vector.
    • completion: Returns the generated text completion.
    • chat_completion: Returns the generated chat completion.
    • tool_calls: Returns tool calls made by the LLM.
    • prompt_tokens: Returns the number of tokens in the prompt.
    • completion_tokens: Returns the number of tokens in the completion.
    • total_tokens: Returns the total number of tokens used.
  4. Install the langchainrb gem

    main

    To use Langchain.rb in your Ruby application, install the gem using Bundler or by installing it directly via gem.

    Using Bundler:

    bundle add langchainrb

    Using gem directly:

    gem install langchainrb

    Note: Additional gems may be required depending on the specific LLM providers you intend to use, as they are not included by default.

    bundle add langchainrb
  5. Create custom tools for Langchain::Assistant

    main

    You can extend Langchain::Assistant by creating custom tool classes. Your class must extend Langchain::ToolDefinition and use define_function to declare the interface. The methods defined in define_function must be implemented in your class.

    Example structure:

    1. Use define_function to specify the name, description, and properties (arguments) of the tool.
    2. Implement the corresponding method in the class to handle the logic.
    class MovieInfoTool
      extend Langchain::ToolDefinition
    
      define_function :search_movie, description: "MovieInfoTool: Search for a movie by title" do
        property :query, type: "string", description: "The movie title to search for", required: true
      end
    
      define_function :get_movie_details, description: "MovieInfoTool: Get detailed information about a specific movie" do
        property :movie_id, type: "integer", description: "The TMDb ID of the movie", required: true
      end
    
      def initialize(api_key:)
        @api_key = api_key
      end
    
      def search_movie(query:)
        # implementation
      end
    
      def get_movie_details(movie_id:)
        # implementation
      end
    end
  6. Parse LLM responses into structured JSON

    main

    Use Langchain::OutputParsers::StructuredOutputParser.from_json_schema(json_schema) to instruct an LLM to return data in a specific JSON format.

    1. Define a JSON schema.
    2. Create a PromptTemplate that includes {format_instructions}.
    3. Use parser.get_format_instructions to populate the template.
    4. Pass the LLM's text response to parser.parse(llm_response) to convert it into a Ruby hash.

    If parsing fails with a Langchain::OutputParsers::OutputParserException, use Langchain::OutputParsers::OutputFixingParser.from_llm to automatically attempt to fix the response using the LLM.

    json_schema = {
      type: "object",
      properties: {
        name: { type: "string", description: "Persons name" },
        age: { type: "number", description: "Persons age" },
        interests: {
          type: "array",
          items: {
            type: "object",
            properties: {
              interest: { type: "string", description: "A topic of interest" },
              levelOfInterest: { type: "number", description: "A value between 0 and 100" }
            },
            required: ["interest", "levelOfInterest"],
            additionalProperties: false
          },
          minItems: 1,
          maxItems: 3
        }
      },
      required: ["name", "age", "interests"],
      additionalProperties: false
    }
    
    parser = Langchain::OutputParsers::StructuredOutputParser.from_json_schema(json_schema)
    llm = Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
    
    prompt = Langchain::Prompt::PromptTemplate.new(
      template: "Generate details of a fictional character.\n{format_instructions}\nCharacter description: {description}", 
      input_variables: ["description", "format_instructions"]
    )
    
    prompt_text = prompt.format(
      description: "Korean chemistry student", 
      format_instructions: parser.get_format_instructions
    )
    
    llm_response = llm.chat(messages: [{role: "user", content: prompt_text}]).completion
    result = parser.parse(llm_response)
  7. Configure and use Vector Search Databases for RAG

    main

    Langchain.rb provides a unified interface for various vector databases to support Retrieval Augmented Generation (RAG).

    Supported Databases

    • Chroma: Open-source & Cloud
    • Hnswlib: Open-source
    • Milvus: Open-source & Cloud (Zilliz)
    • Pinecone: Cloud
    • Pgvector: Open-source & Cloud
    • Qdrant: Open-source & Cloud
    • Weaviate: Open-source & Cloud
    • Elasticsearch: Open-source & Cloud

    Workflow

    1. Instantiate Client: Pass an LLM provider (for embeddings) and database credentials.
    2. Initialize Schema: Call client.create_default_schema.
    3. Add Data: Use client.add_texts(texts: [...]) for raw strings or client.add_data(paths: [...]) for files (supports docx, html, pdf, text, json, jsonl, csv, xlsx, eml, pptx).
    4. Query:
      • client.similarity_search(query: "...", k: n): Standard similarity search.
      • client.similarity_search_with_hyde(): Search using the HyDE technique.
      • client.similarity_search_by_vector(embedding: vector, k: n): Search using a pre-computed vector.
      • client.ask(question: "..."): High-level RAG-based querying.

    Example: Weaviate Setup

    # Requires gem "weaviate-ruby", "~> 0.8.9"
    llm = Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
    client = Langchain::Vectorsearch::Weaviate.new(
        url: ENV["WEAVIATE_URL"],
        api_key: ENV["WEAVIATE_API_KEY"],
        index_name: "Documents",
        llm: llm
    )
    client.create_default_schema
    client.add_texts(texts: ["Some text content..."])
    # Example Weaviate setup
    llm = Langchain::LLM::OpenAI.new(api_key: ENV["OPENAI_API_KEY"])
    client = Langchain::Vectorsearch::Weaviate.new(
        url: ENV["WEAVIATE_URL"],
        api_key: ENV["WEAVIATE_API_KEY"],
        index_name: "Documents",
        llm: llm
    )
    client.create_default_schema
    client.add_texts(texts: ["Example text content."])
    client.ask(question: "What is the content about?")
  8. Configure Langchain.rb logging

    main

    Langchain.rb uses the standard Ruby Logger. By default, it logs to STDOUT at the DEBUG level.

    To change the log level:

    Langchain.logger.level = Logger::DEBUG

    To redirect logs to a file:

    Langchain.logger = Logger.new("path/to/file", **Langchain::LOGGER_OPTIONS)
  9. Configure Langchain::Assistant

    main

    When initializing Langchain::Assistant, you can use the following configuration options:

    OptionTypeDescription
    llmLangchain::LLMThe LLM instance to use (required)
    toolsArrayAn array of tool instances (optional)
    instructionsStringSystem instructions for the assistant (optional)
    tool_choiceStringSpecifies how tools are selected. Default: "auto". You can pass a specific tool function name to force the assistant to always use it.
    parallel_tool_callsBooleanWhether to make multiple parallel tool calls. Default: true
    add_message_callbackProc/LambdaCalled when any message is added to the conversation (optional)
    tool_execution_callbackProc/LambdaCalled right before a tool is executed (optional)
    assistant.add_message_callback = -> (message) { puts "New message: #{message}" }
    
    assistant.tool_execution_callback = -> (tool_call_id, tool_name, method_name, tool_arguments) { 
      puts "Executing tool_call_id: #{tool_call_id}, tool_name: #{tool_name}, method_name: #{method_name}, tool_arguments: #{tool_arguments}" 
    }
  10. Define a custom tool using ToolDefinition

    main

    To create a tool that an LLM agent can use, extend your class with Langchain::ToolDefinition. A tool is a collection of functions (methods) that perform specific tasks. You define these functions using the define_function method, which takes a method name, a description, and a block to define the parameter schema.

    When you extend a class, the tool's name is automatically derived from the class name in snake_case format.