langchain-rust

repository·main·Indexed 23 days ago

https://github.com/abraxas-365/langchain-rust

A Rust implementation of the LangChain framework for building LLM-powered applications. Version 4.6.0 provides composable components for LLMs (OpenAI, Azure OpenAI, Ollama, Anthropic Claude), embeddings, vector stores (OpenSearch, Postgres, Qdrant, Sqlite, SurrealDB), chains, agents, and document loaders for PDF, HTML, CSV, and source code. It includes specialized implementations such as SQLDatabaseChain for natural language database queries and ConversationalRetrieverChain for QA over retrieved documents.

Tokens
15.7K
Snippets
29
Records
65
Agent score
79%

What's inside langchain-rust

  1. Overview of langchain-rust features

    main

    LangChain Rust is a Rust implementation of LangChain designed for building LLM applications through composability.

    Supported feature categories include:

    • LLMs: OpenAI, Azure OpenAI, Ollama, Anthropic Claude.
    • Embeddings: OpenAI, Azure OpenAI, Ollama, Local FastEmbed, MistralAI.
    • VectorStores: OpenSearch, Postgres, Qdrant, Sqlite, SurrealDB.
    • Chains: LLM Chain, Conversational Chain, Conversational Retriever (Simple and with Vector Store), Sequential Chain, Q&A Chain, SQL Chain.
    • Agents: Chat Agent with Tools, OpenAI Compatible Tools Agent.
    • Tools: Serpapi/Google, DuckDuckGo Search, Wolfram/Math, Command line, Text2Speech.
    • Semantic Routing: Static and Dynamic Routing.
    • Document Loaders: PDF, Pandoc, HTML, HTML to Markdown, CSV, Git commits, and Source code.
  2. Install langchain-rust

    main

    To use langchain-rust, you must first add serde_json as a dependency, as the library relies heavily on it. You can then install langchain-rust using cargo add.

    Depending on your requirements, you can enable optional features for specific vector stores or databases like Postgres, SurrealDB, or Qdrant.

  3. Explore the LangChain Rust module hierarchy

    main

    The langchain-rust library is organized into several core modules that provide the building blocks for LLM applications. Key modules include:

    • agent: For creating autonomous agents that use tools.
    • chain: For composing multiple components into a sequence of operations.
    • document_loaders: For importing data from various sources into the system.
    • embedding: For generating vector representations of text.
    • language_models & llm: For interacting with Large Language Models.
    • memory: For maintaining state and context in conversational applications.
    • output_parsers: For transforming LLM raw text into structured data.
    • prompt: For managing and constructing prompt templates.
    • schemas: For core data structures and types.
    • semantic_router: For routing queries based on semantic meaning.
    • text_splitter: For breaking down large documents into smaller chunks.
    • tools: For defining capabilities that agents can invoke.
    • vectorstore: For storing and retrieving embeddings.
  4. Define tool parameters for OpenAI-like function calling

    main

    When implementing parameters(&self) -> Value, you should return a JSON Schema that describes the tool's arguments. This allows LLMs to perform structured function calling.

    If you do not implement this method, the default schema is:

    {
        "type": "object",
        "properties": {
            "input": {
                "type": "string",
                "description": "<self.description()>"
            }
        },
        "required": ["input"]
    }

    To support custom arguments (e.g., a command), implement parameters to return a schema like this:

    {
        "type": "object",
        "properties": {
            "command": {
                "type": "string",
                "description": "The raw command you want executed"
            }
        },
        "required": ["command"]
    }
  5. Use StuffDocument to pass multiple documents into a prompt

    main

    The StuffDocument chain is used to combine multiple Document objects into a single string (using a separator) and inject that string into a prompt variable. This is commonly used for Question Answering (QA) tasks where you want the LLM to answer questions based on a provided context of documents.

    Key Configuration Constants

    • Input Key: input_documents (the key expected in the input PromptArgs containing the list of documents).
    • Output Key: text.
    • Document Variable Name: context (the variable name used within the prompt template to receive the joined document text).
    • Default Separator: \n\n.
  6. Ensure compatibility with Python LangChain SurrealDB stores

    main

    If you are attempting to connect to a SurrealDB vector store that was previously created using the Python version of LangChain, you must use the new_with_compatiblity() constructor.

    This mode applies the following settings to match the Python implementation:

    • collection_name is set to "documents".
    • collection_table_name is set to None.
    • collection_metadata_key_name is set to None.
    • schemafull is set to false.
  7. Use SQLDatabaseChain to query databases with natural language

    main

    The SQLDatabaseChain allows you to interact with a SQL database using human language. It works by taking a natural language query, using an LLM to generate the corresponding SQL, executing that SQL against the database, and then using the LLM again to format the final answer.

    Key Input Variables

    • query: The natural language question (e.g., "What is the phone number of Luis?"). This is the primary input required.
    • table_names (optional): A JSON array of strings representing the specific tables to include in the context. If not provided, the chain uses its internal logic to determine table information.

    Usage Pattern

    You can provide inputs using the prompt_builder() helper or by manually constructing PromptArgs using the prompt_args! macro.

    // Using the prompt builder
    let input_variables = chain.prompt_builder()
        .query("Whats the phone number of luis")
        .build();
    
    match chain.invoke(input_variables).await {
       Ok(result) => println!("Result: {}", result),
       Err(e) => panic!("Error: {:?}", e),
    }
    
    // OR using prompt_args! macro
    let input_variables = prompt_args! {
        "query" => "Whats the phone number of luis"
    };
  8. Quick Start: Create a Conversational Chain

    main

    This example demonstrates how to initialize an OpenAI model, define a prompt template using macros like message_formatter!, fmt_message!, and fmt_template!, and build an LLMChain using LLMChainBuilder. It also shows how to handle conversation history using the fmt_placeholder! macro and prompt_args! for input injection.

    use langchain_rust::{
        chain::{Chain, LLMChainBuilder},
        fmt_message, fmt_placeholder, fmt_template, 
        language_models::llm::LLM,
        llm::openai::{OpenAI, OpenAIModel},
        message_formatter,
        prompt::HumanMessagePromptTemplate,
        prompt_args,
        schemas::messages::Message,
        template_fstring,
    };
    
    #[tokio::main]
    async fn main() {
        // Initialize the model
        let open_ai = OpenAI::default().with_model(OpenAIModel::Gpt4oMini.to_string());
    
        // Define a prompt template with a system message and a human message template
        let prompt = message_formatter![
            fmt_message!(Message::new_system_message(
                "You are world class technical documentation writer."
            )),
            fmt_template!(HumanMessagePromptTemplate::new(template_fstring!(
                "{input}", "input"
            )))
        ];
    
        // Build the LLM chain
        let chain = LLMChainBuilder::new()
            .prompt(prompt.clone())
            .llm(open_ai.clone())
            .build()
            .unwrap();
    
        // Invoke the chain with arguments
        match chain
            .invoke(prompt_args! {
            "input" => "Quien es el escritor de 20000 millas de viaje submarino",
               })
            .await
        {
            Ok(result) => println!("Result: {:?}", result),
            Err(e) => panic!("Error invoking LLMChain: {:?}", e),
        }
    
        // Example with conversation history using fmt_placeholder!
        let prompt_with_history = message_formatter![
            fmt_message!(Message::new_system_message(
                "You are world class technical documentation writer."
            )),
            fmt_placeholder!("history"),
            fmt_template!(HumanMessagePromptTemplate::new(template_fstring!(
                "{input}", "input"
            ))),
        ];
    
        let chain_with_history = LLMChainBuilder::new()
            .prompt(prompt_with_history.clone())
            .llm(open_ai)
            .build()
            .unwrap();
    
        match chain_with_history
            .invoke(prompt_args! {
            "input" => "Who is the writer of 20,000 Leagues Under the Sea, and what is my name?”,
            "history" => vec![
                    Message::new_human_message("My name is: luis"),
                    Message::new_ai_message("Hi luis"),
                    ],
            })
            .await
        {
            Ok(result) => println!("Result: {:?}", result),
            Err(e) => panic!("Error invoking LLMChain: {:?}", e),
        }
    }
  9. Load CSV documents

    main

    Use CsvLoader to load CSV files. You must provide a vector of column names to define the schema.

    use futures_util::StreamExt;
    
    async fn main() {
        let path = "./src/document_loaders/test_data/test.csv";
        let columns = vec![
            "name".to_string(),
            "age".to_string(),
            "city".to_string(),
            "country".to_string(),
        ];
        let csv_loader = CsvLoader::from_path(path, columns).expect("Failed to create csv loader");
    
        let documents = csv_loader
            .load()
            .await
            .unwrap()
            .map(|x| x.unwrap())
            .collect::<Vec<_>>()
            .await;
    }
  10. Load PDF documents

    main

    Use PdfExtractLoader or LoPdfLoader to extract content from PDF files. The load() method returns a stream of documents.

    use futures_util::StreamExt;
    
    async fn main() {
        let path = "./src/document_loaders/test_data/sample.pdf";
    
        let loader = PdfExtractLoader::from_path(path).expect("Failed to create PdfExtractLoader");
        // let loader = LoPdfLoader::from_path(path).expect("Failed to create LoPdfLoader");
    
        let docs = loader
            .load()
            .await
            .unwrap()
            .map(|d| d.unwrap())
            .collect::<Vec<_>>()
            .await;
    }
  11. Load HTML documents

    main

    Use HtmlLoader to load HTML files. You must provide a base URL for parsing.

    use futures_util::StreamExt;
    use url::Url;
    
    async fn main() {
        let path = "./src/document_loaders/test_data/example.html";
        let html_loader = HtmlLoader::from_path(path, Url::parse("https://example.com/").unwrap())
            .expect("Failed to create html loader");
    
        let documents = html_loader
            .load()
            .await
            .unwrap()
            .map(|x| x.unwrap())
            .collect::<Vec<_>>()
            .await;
    }