LangChain
LLMOps integrates with LangChain in two ways:
- LLM Proxy — Route all LangChain LLM calls through LLMOps for cost tracking, guardrails, and provider routing.
- Tracing — Send LangChain execution traces to LLMOps for full observability of chains, tools, and retrievers.
LLM Proxy
LLMOps exposes an OpenAI-compatible API. Point LangChain's ChatOpenAI at your LLMOps instance to route all LLM calls through it.
Installation
npm install @langchain/openai langchainFor Python:
pip install langchain-openaiSetup
There are two ways to connect LangChain to LLMOps depending on your architecture:
Using provider() (embedded)
If LLMOps is running inside your application (e.g., as Express middleware), use llmopsClient.provider(). This routes requests in-process without an extra HTTP hop:
import { ChatOpenAI } from '@langchain/openai';
import llmopsClient from './llmops';
const { baseURL, apiKey, fetch } = llmopsClient.provider();
const llm = new ChatOpenAI({
configuration: { baseURL, fetch },
apiKey,
model: '@openai/gpt-4o-mini',
});Since provider() bypasses the HTTP gateway, traces are not automatically created. Use langchainTracer() or an OTLP exporter to send traces to LLMOps.
Using explicit baseURL (proxy)
If LLMOps is running as a standalone proxy, or you want the gateway to automatically create traces, point LangChain at the gateway URL directly:
import { ChatOpenAI } from '@langchain/openai';
const llm = new ChatOpenAI({
configuration: {
baseURL: 'http://localhost:3000/llmops/api/genai/v1',
},
apiKey: process.env.LLMOPS_ENV_SECRET,
model: '@openai/gpt-4o-mini',
});This makes real HTTP requests to the gateway, which automatically creates traces for every call.
Environment variables
LLMOPS_ENV_SECRET=your-environment-secretProvider Routing
The @slug/model format routes requests through a specific provider configured in your LLMOps dashboard. The @openai/ prefix routes through the provider with slug openai. You can use any provider slug configured in your environment (e.g., @anthropic/claude-sonnet-4-20250514, @openrouter/google/gemini-2.0-flash).
Config-based Routing
Instead of @slug/model, you can route via a config ID. Configs let you define model, provider, and guardrails in the LLMOps dashboard:
const llm = new ChatOpenAI({
configuration: {
baseURL: 'http://localhost:3000/llmops/api/genai/v1',
defaultHeaders: {
'x-llmops-config': 'your-config-id',
},
},
apiKey: process.env.LLMOPS_ENV_SECRET,
model: 'gpt-4o-mini',
});Chat Completions
import { ChatOpenAI } from '@langchain/openai';
const llm = new ChatOpenAI({
configuration: {
baseURL: 'http://localhost:3000/llmops/api/genai/v1',
},
apiKey: process.env.LLMOPS_ENV_SECRET,
model: '@openai/gpt-4o-mini',
});
const response = await llm.invoke('What is LLMOps?');
console.log(response.content);Streaming
const stream = await llm.stream('Tell me a joke');
for await (const chunk of stream) {
process.stdout.write(chunk.content as string);
}Embeddings
import { OpenAIEmbeddings } from '@langchain/openai';
const embeddings = new OpenAIEmbeddings({
configuration: {
baseURL: 'http://localhost:3000/llmops/api/genai/v1',
},
apiKey: process.env.LLMOPS_ENV_SECRET,
model: '@openai/text-embedding-3-small',
});
const vectors = await embeddings.embedQuery('Hello world');
console.log(vectors.length);Tool Calling
LangChain tool calling works through LLMOps with no changes:
import { tool } from '@langchain/core/tools';
import { z } from 'zod';
const weatherTool = tool(
async ({ city }) => `The weather in ${city} is sunny, 72°F.`,
{
name: 'get_weather',
description: 'Get the weather for a city',
schema: z.object({ city: z.string() }),
}
);
const llmWithTools = llm.bindTools([weatherTool]);
const response = await llmWithTools.invoke('What is the weather in San Francisco?');Tracing
LLMOps captures the full LangChain execution hierarchy — agents, chains, LLM calls, tool invocations, and retrievers — as nested spans in a single trace.
TypeScript (SDK)
The recommended approach for TypeScript. Uses LangChain's built-in LangChainTracer with an LLMOps-compatible client — no environment variables, no extra dependencies.
Install dependencies
npm install @langchain/core @langchain/openai @langchain/langgraphCreate the tracer
Use llmopsClient.langchainTracer() to create a client, then pass it to LangChain's LangChainTracer:
import { ChatOpenAI } from '@langchain/openai';
import { LangChainTracer } from '@langchain/core/tracers/tracer_langchain';
import { createReactAgent } from '@langchain/langgraph/prebuilt';
import llmopsClient from './llmops';
const tracer = new LangChainTracer({
client: llmopsClient.langchainTracer(),
});Use provider() for the LLM
Since the tracer captures all runs, use provider() for the LLM to avoid double-tracing:
const { baseURL, apiKey, fetch } = llmopsClient.provider();
const llm = new ChatOpenAI({
configuration: { baseURL, fetch },
apiKey,
model: '@openai/gpt-4o-mini',
});Pass the tracer to your agent or chain
Add the tracer to the callbacks array when invoking:
const agent = createReactAgent({ llm, tools });
const result = await agent.invoke(
{ messages: [{ role: 'user', content: 'What is the weather in Tokyo?' }] },
{ callbacks: [tracer] }
);Every step — the agent loop, each LLM call, each tool invocation — appears as a nested span under a single trace.
Python (Environment Variables)
LLMOps exposes a LangSmith-compatible endpoint. Set the standard LangChain tracing environment variables to point at your LLMOps instance:
LANGCHAIN_TRACING_V2=true
LANGCHAIN_ENDPOINT=http://localhost:3000/llmops/api/langsmith
LANGCHAIN_API_KEY=your-environment-secretThen use LangChain as usual — all runs are automatically traced:
import os
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
base_url="http://localhost:3000/llmops/api/genai/v1",
api_key=os.environ["LLMOPS_ENV_SECRET"],
model="@openai/gpt-4o-mini",
)
# This call is traced end-to-end in LLMOps
response = llm.invoke("What is LLMOps?")OpenTelemetry
LangChain also supports native OpenTelemetry export. Use the OTLP endpoint if you prefer OTel-based tracing:
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
exporter = OTLPSpanExporter(
endpoint="http://localhost:3000/llmops/api/otlp/v1/traces",
headers={
"Authorization": f"Bearer {os.environ['LLMOPS_ENV_SECRET']}",
},
)
provider = TracerProvider()
provider.add_span_processor(BatchSpanProcessor(exporter))
from opentelemetry import trace
trace.set_tracer_provider(provider)Python (Full Example)
A complete Python setup using LLMOps as a proxy for both LLM routing and tracing:
import os
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
llm = ChatOpenAI(
base_url="http://localhost:3000/llmops/api/genai/v1",
api_key=os.environ["LLMOPS_ENV_SECRET"],
model="@openai/gpt-4o-mini",
)
# Chat
response = llm.invoke("What is LLMOps?")
print(response.content)
# Streaming
for chunk in llm.stream("Tell me a joke"):
print(chunk.content, end="")
# Embeddings
embeddings = OpenAIEmbeddings(
base_url="http://localhost:3000/llmops/api/genai/v1",
api_key=os.environ["LLMOPS_ENV_SECRET"],
model="@openai/text-embedding-3-small",
)
vectors = embeddings.embed_query("Hello world")