LangChain

Full LangChain integration with LLM, embeddings, TTS, and STT. Build chains, agents, and voice-enabled pipelines with local models.

ClassCapability
GerbilLLMText generation + Vision
GerbilEmbeddingsVector embeddings
Note: The LangChain integration runs in Node.GerbilLLM and GerbilEmbeddings use native models (Qwen3.5-0.8B, EmbeddingGemma-300M), running on the WebGPU engine. For speech (Kani-TTS-2 TTS, Moonshine STT) and browser inference, see the WebGPUEngine.

Installation

Terminal
npm install @tryhamster/gerbil langchain

Quick Start

quick-start.ts
01import {
02 GerbilLLM,
03 GerbilEmbeddings,
04} from "@tryhamster/gerbil/langchain";
05
06// Text generation
07const llm = new GerbilLLM({ model: "qwen3.5-0.8b" });
08const result = await llm.invoke("Write a haiku about coding");
09
10// Embeddings
11const embeddings = new GerbilEmbeddings();
12const vector = await embeddings.embedQuery("Hello world");

GerbilLLM

Text generation with optional vision support:

llm-config.ts
01import { GerbilLLM } from "@tryhamster/gerbil/langchain";
02
03const llm = new GerbilLLM({
04 // Model configuration
05 model: "qwen3.5-0.8b",
06 device: "auto", // "auto" | "webgpu"
07 dtype: "q4", // quantization
08
09 // Generation options
10 maxTokens: 500,
11 temperature: 0.7,
12 topP: 0.9,
13 topK: 50,
14
15 // Thinking mode (Qwen3.5)
16 thinking: false,
17});

invoke()

invoke.ts
01// Simple invocation
02const result = await llm.invoke("Explain recursion");
03
04// With options
05const result = await llm.invoke("Write a poem", {
06 maxTokens: 200,
07 temperature: 0.9,
08});
09
10// With stop sequences
11const result = await llm.invoke("List 3 items:\n1.", {
12 stop: ["\n4."],
13});

Streaming

streaming.ts
01// Stream tokens
02const stream = llm.stream("Tell me a story");
03
04for await (const chunk of stream) {
05 process.stdout.write(chunk);
06}
07
08// With options
09for await (const chunk of llm.stream("Explain hooks", { maxTokens: 200 })) {
10 process.stdout.write(chunk);
11}

Vision

Use vision-capable models to analyze images:

vision.ts
01import { GerbilLLM } from "@tryhamster/gerbil/langchain";
02
03// Use a vision-capable model
04const llm = new GerbilLLM({ model: "qwen3.5-0.8b" });
05
06// Check if model supports vision
07const hasVision = await llm.supportsVision(); // true
08
09// Analyze an image
10const description = await llm.invokeWithImages(
11 "Describe this image in detail",
12 [{ source: "https://example.com/photo.jpg" }]
13);
14
15// Compare multiple images
16const diff = await llm.invokeWithImages(
17 "What changed between these two screenshots?",
18 [
19 { source: beforeScreenshot },
20 { source: afterScreenshot },
21 ]
22);
23
24// Use with local files (base64)
25import { readFileSync } from "fs";
26const imageData = readFileSync("photo.jpg").toString("base64");
27const result = await llm.invokeWithImages(
28 "What's in this photo?",
29 [{ source: `data:image/jpeg;base64,${imageData}` }]
30);

GerbilEmbeddings

embeddings.ts
01import { GerbilEmbeddings } from "@tryhamster/gerbil/langchain";
02
03// Vectors come from EmbeddingGemma-300M on the native engine.
04const embeddings = new GerbilEmbeddings();
05
06// Single query
07const vector = await embeddings.embedQuery("What is the meaning of life?");
08// Returns: number[] (768 dimensions, L2-normalized)
09
10// Multiple documents
11const vectors = await embeddings.embedDocuments([
12 "First document",
13 "Second document",
14 "Third document",
15]);
16// Returns: number[][] (array of vectors)

Speech & Audio

Speech runs on the native WebGPU engine rather than a LangChain wrapper. Text-to-speech uses Kani-TTS-2 via engine.speak(), and speech-to-text uses Moonshine via MoonshineSTT, both running on-device on WebGPU. See the Text-to-Speech and Speech-to-Text docs.

Chains

Use Gerbil with LangChain chains:

chains.ts
01import { GerbilLLM } from "@tryhamster/gerbil/langchain";
02import { PromptTemplate } from "@langchain/core/prompts";
03
04const llm = new GerbilLLM({ model: "qwen3.5-0.8b" });
05
06// Format a LangChain prompt template, then invoke the model.
07// GerbilLLM is a lightweight class (invoke / stream / batch /
08// invokeWithImages), not a Runnable, so call it directly:
09const prompt = PromptTemplate.fromTemplate(
10 "You are a helpful assistant. Answer this question: {question}"
11);
12
13const formatted = await prompt.format({
14 question: "What is the capital of France?",
15});
16const result = await llm.invoke(formatted);
17
18console.log(result); // "The capital of France is Paris."

Structured Output

structured.ts
01import { json } from "@tryhamster/gerbil";
02import { z } from "zod";
03
04// Define schema
05const personSchema = z.object({
06 name: z.string(),
07 age: z.number(),
08 city: z.string(),
09});
10
11// Validated structured output from the same local model
12const result = await json(
13 "Extract: John is 32 years old and lives in New York",
14 { schema: personSchema }
15);
16
17console.log(result);
18// { name: "John", age: 32, city: "New York" }

Vector Stores

vector-stores.ts
01import { GerbilEmbeddings } from "@tryhamster/gerbil/langchain";
02import { MemoryVectorStore } from "langchain/vectorstores/memory";
03import { Document } from "@langchain/core/documents";
04
05const embeddings = new GerbilEmbeddings();
06
07// Create documents
08const docs = [
09 new Document({ pageContent: "Gerbil is a local LLM library" }),
10 new Document({ pageContent: "It supports WebGPU acceleration" }),
11 new Document({ pageContent: "Works with the Vercel AI SDK" }),
12];
13
14// Create vector store
15const vectorStore = await MemoryVectorStore.fromDocuments(docs, embeddings);
16
17// Similarity search
18const results = await vectorStore.similaritySearch("What is Gerbil?", 2);
19console.log(results);

RAG Pipeline

Build a complete Retrieval-Augmented Generation pipeline:

rag.ts
01import { GerbilLLM, GerbilEmbeddings } from "@tryhamster/gerbil/langchain";
02import { MemoryVectorStore } from "langchain/vectorstores/memory";
03
04// Initialize
05const llm = new GerbilLLM({ model: "qwen3.5-0.8b" });
06const embeddings = new GerbilEmbeddings();
07
08// Create vector store from documents
09const vectorStore = await MemoryVectorStore.fromTexts(
10 [
11 "Gerbil runs LLMs locally in Node.js",
12 "It supports GPU acceleration via WebGPU",
13 "Models are cached on-device",
14 "Works offline after first download",
15 ],
16 [{}, {}, {}, {}],
17 embeddings
18);
19
20// Retrieve, then generate with the context
21const question = "Does Gerbil work offline?";
22const relevantDocs = await vectorStore.similaritySearch(question, 2);
23const context = relevantDocs.map((d) => d.pageContent).join("\n");
24
25const answer = await llm.invoke(`
26Answer the question based on the context below.
27
28Context: ${context}
29
30Question: ${question}
31
32Answer:
33`);
34
35console.log(answer);
36// "Yes, Gerbil works offline after the first download..."

Agents

agents.ts
01// LangChain's agent executors need a full Runnable LLM, which the
02// lightweight GerbilLLM shim is not. For tool-calling agents on the
03// same local model, use Gerbil's own agent loop instead:
04import { WebGPUEngine } from "@tryhamster/gerbil/gpu";
05
06const engine = await WebGPUEngine.create({
07 repo: "mlx-community/Qwen3.5-0.8B-4bit",
08});
09
10const { text, steps } = await engine.generateWithTools("What is 25 * 4 + 10?", {
11 tools: [calculatorTool], // defineTool(...), see the Tools docs
12});
13
14console.log(text);

See Tool Calling for the full agent-loop API.

Conversation Memory

conversation.ts
01import { GerbilLLM } from "@tryhamster/gerbil/langchain";
02
03const llm = new GerbilLLM({ model: "qwen3.5-0.8b" });
04
05// Keep the transcript yourself and replay it each turn:
06const history: string[] = [];
07
08async function chat(input: string) {
09 history.push(`User: ${input}`);
10 const reply = await llm.invoke(
11 `${history.join("\n")}\nAssistant:`
12 );
13 history.push(`Assistant: ${reply}`);
14 return reply;
15}
16
17await chat("My name is Alice");
18const result = await chat("What's my name?");
19console.log(result); // "Your name is Alice!"

Document Loaders

document-loaders.ts
01import { GerbilLLM, GerbilEmbeddings } from "@tryhamster/gerbil/langchain";
02import { TextLoader } from "langchain/document_loaders/fs/text";
03import { PDFLoader } from "langchain/document_loaders/fs/pdf";
04import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
05import { MemoryVectorStore } from "langchain/vectorstores/memory";
06
07// Load documents
08const textLoader = new TextLoader("./docs/readme.txt");
09const pdfLoader = new PDFLoader("./docs/manual.pdf");
10
11const textDocs = await textLoader.load();
12const pdfDocs = await pdfLoader.load();
13
14// Split into chunks
15const splitter = new RecursiveCharacterTextSplitter({
16 chunkSize: 500,
17 chunkOverlap: 50,
18});
19
20const splitDocs = await splitter.splitDocuments([...textDocs, ...pdfDocs]);
21
22// Create vector store
23const embeddings = new GerbilEmbeddings();
24const vectorStore = await MemoryVectorStore.fromDocuments(splitDocs, embeddings);
25
26// Query
27const results = await vectorStore.similaritySearch("How do I install?", 3);

Voice-Enabled Pipeline

Build a complete voice-to-voice agent with STT → LLM → TTS. The LangChain LLM handles text; speech is the native WebGPU engine (Moonshine for STT, Kani-TTS-2 for TTS):

voice-pipeline.ts
01import { GerbilLLM } from "@tryhamster/gerbil/langchain";
02import { MoonshineSTT, WebGPUEngine } from "@tryhamster/gerbil/gpu";
03
04const llm = new GerbilLLM({ model: "qwen3.5-0.8b" });
05const stt = await MoonshineSTT.create({ repo: "UsefulSensors/moonshine-base" });
06const tts = await WebGPUEngine.create({ repo: "nineninesix/kani-tts-450m-0.2-ft" });
07
08// Voice input → LLM → Voice output
09async function voiceChat(pcm16kMono: Float32Array) {
10 // 1. Transcribe user speech (raw 16 kHz mono PCM)
11 const { text: userMessage } = await stt.transcribe(pcm16kMono);
12 console.log("User said:", userMessage);
13
14 // 2. Generate response
15 const response = await llm.invoke(userMessage);
16 console.log("AI response:", response);
17
18 // 3. Speak response
19 const { pcm, sampleRate } = await tts.speak(response); // single built-in voice
20
21 return { pcm, sampleRate, text: response };
22}
23
24// Combine with RAG for voice-enabled knowledge base
25import { MemoryVectorStore } from "langchain/vectorstores/memory";
26import { GerbilEmbeddings } from "@tryhamster/gerbil/langchain";
27
28const embeddings = new GerbilEmbeddings();
29const vectorStore = await MemoryVectorStore.fromTexts(docs, metadata, embeddings);
30
31async function voiceRAG(pcm16kMono: Float32Array) {
32 // Transcribe question
33 const { text: question } = await stt.transcribe(pcm16kMono);
34
35 // Retrieve relevant documents
36 const relevantDocs = await vectorStore.similaritySearch(question, 3);
37 const context = relevantDocs.map(d => d.pageContent).join("\n");
38
39 // Generate answer with context
40 const answer = await llm.invoke(
41 `Context: ${context}\n\nQuestion: ${question}\n\nAnswer:`
42 );
43
44 // Speak the answer
45 const { pcm } = await tts.speak(answer);
46 return { pcm, answer };
47}