> ## Documentation Index
> Fetch the complete documentation index at: https://docs.galileo.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Middleware

> Learn about using GalileoMiddleware for automatic logging of LangChain agents

{/*<!-- markdownlint-disable MD044 -->*/}

## Overview

`GalileoMiddleware` is a middleware component that integrates with LangGraph agents to provide comprehensive tracing and logging. Unlike the callback-based approach, middleware automatically intercepts agent execution at key points:

* **Agent lifecycle**: Tracks when an agent starts and completes
* **Model calls**: Logs all LLM invocations with prompts, responses, and metadata
* **Tool calls**: Captures tool invocations including function names, arguments, and outputs
* **Async support**: Full support for both synchronous and asynchronous agent execution

## Basic usage

To use `GalileoMiddleware`, simply add it to the `middleware` parameter when creating a LangChain agent:

<Note>
  In the TypeScript SDK, `GalileoMiddleware` is not available. Use `GalileoCallback` with the same constructor options to achieve equivalent functionality.
</Note>

The middleware automatically handles all logging internally. When the agent is invoked:

1. An agent node is created to track the overall execution
2. Each model call creates an LLM node with prompt and response details
3. Each tool call creates a tool node with function name, arguments, and output
4. All nodes are linked hierarchically under the agent node

## Configuration options

`GalileoMiddleware` accepts the following parameters:

* `galileo_logger` (optional): A custom `GalileoLogger` instance. If not provided, a default logger is created.
* `start_new_trace` (default: `True`): Whether to start a new trace on agent invocation. Set to `False` to add to an existing trace.
* `flush_on_chain_end` (default: `True`): Whether to flush logs to Galileo when the agent completes.
* `ingestion_hook` (optional): A callback function that receives `TracesIngestRequest` objects before they're sent to Galileo.

## Custom logger

You can provide a custom logger instance to integrate with existing logging infrastructure:

<CodeGroup>
  ```python Python theme={null}
  from galileo.logger import GalileoLogger

  # Create a custom logger
  logger = GalileoLogger(
      project_name="my-agent-project",
      console_output=True
  )

  # Use it with middleware
  agent = create_agent(
      model,
      tools=[get_weather, get_stock_price],
      middleware=[GalileoMiddleware(galileo_logger=logger)]
  )
  ```

  ```typescript TypeScript theme={null}
  import { GalileoCallback, GalileoLogger } from "galileo";

  // Create a custom logger
  const logger = new GalileoLogger({
    projectName: "my-agent-project",
    logStreamName: "agent-execution",
  });

  // In the TypeScript SDK, use GalileoCallback with a custom logger
  // (GalileoMiddleware is not available in TypeScript)
  const callback = new GalileoCallback(logger);

  // Pass the callback to your LangChain/LangGraph agent
  const result = await agent.invoke(
    { messages: [...] },
    { callbacks: [callback] }
  );
  ```
</CodeGroup>

## Trace management

By default, each agent invocation creates a new trace. You can control trace behavior:

### Add to existing trace

To add agent execution to an existing trace, use a shared logger with `start_new_trace` set to `False` (Python) or `false` (TypeScript):

<CodeGroup>
  ```python Python theme={null}
  # Create a logger and start a trace
  logger = GalileoLogger()
  session_id = logger.create_session()
  trace_id = logger.create_trace(session_id)

  # Create middleware that adds to existing trace
  middleware = GalileoMiddleware(
      galileo_logger=logger,
      start_new_trace=False
  )

  # The agent execution will be added to the existing trace
  agent = create_agent(model, tools=[...], middleware=[middleware])
  agent.invoke({"messages": [...]})
  ```

  ```typescript TypeScript theme={null}
  import { GalileoCallback, GalileoLogger } from "galileo";

  // Create a logger and start a trace
  const logger = new GalileoLogger({
    projectName: "my-project",
    logStreamName: "my-log-stream",
  });
  await logger.startSession({ name: "My session" });
  logger.startTrace({ input: "User query", name: "My pipeline" });

  // Create a callback that adds to the existing trace (startNewTrace = false)
  const callback = new GalileoCallback(
    logger,
    false // startNewTrace — append to the active trace
  );

  // The agent execution will be added to the existing trace
  const result = await agent.invoke(
    { messages: [...] },
    { callbacks: [callback] }
  );
  ```
</CodeGroup>

### Manual flush control

If you want to control when logs are flushed (e.g., for batch processing):

<CodeGroup>
  ```python Python theme={null}
  # Disable automatic flushing
  middleware = GalileoMiddleware(
      galileo_logger=logger,
      flush_on_chain_end=False
  )

  # Execute multiple agent calls
  agent.invoke({"messages": [...]})
  agent.invoke({"messages": [...]})

  # Manually flush when ready
  logger.flush()
  ```

  ```typescript TypeScript theme={null}
  import { GalileoCallback, GalileoLogger } from "galileo";

  const logger = new GalileoLogger({
    projectName: "my-project",
    logStreamName: "batch-execution",
  });

  // Disable automatic flushing (flushOnChainEnd = false)
  const callback = new GalileoCallback(
    logger,
    true,  // startNewTrace
    false  // flushOnChainEnd — we will flush manually
  );

  // Execute multiple agent calls — traces accumulate in memory
  await agent.invoke({ messages: [...] }, { callbacks: [callback] });
  await agent.invoke({ messages: [...] }, { callbacks: [callback] });

  // Manually flush when ready
  await logger.flush();
  ```
</CodeGroup>

## What gets logged

`GalileoMiddleware` captures the following information:

### Agent node

* Input state (messages)
* Output state (final messages)
* Execution time

### Model call nodes

* Model name and configuration (temperature, etc.)
* Input messages (including system message if present)
* Output response
* Tools available to the model
* Timing metrics (start time, time to first token if available)

### Tool call nodes

* Tool/function name
* Tool arguments (serialized)
* Tool output
* Execution time

## Comparison with GalileoCallback

`GalileoMiddleware` (Python) and `GalileoCallback` (Python and TypeScript) provide similar functionality but use different approaches:

| Feature               | GalileoMiddleware (Python only)           | GalileoCallback (Python and TypeScript)          |
| --------------------- | ----------------------------------------- | ------------------------------------------------ |
| **Integration point** | LangGraph agents via middleware parameter | LangChain components via callbacks parameter     |
| **Setup complexity**  | Simple - add to middleware list           | Manual - pass to each component                  |
| **Agent support**     | Native support for LangGraph agents       | Requires callback setup                          |
| **Flexibility**       | Automatic agent-level tracing             | Fine-grained control over individual components  |
| **Language support**  | Python only                               | Python and TypeScript                            |
| **Use case**          | LangGraph agents with minimal setup       | Complex LangChain applications with custom needs |

Use `GalileoMiddleware` when:

* You're building LangGraph agents **in Python**
* You want automatic, drop-in logging
* You prefer simpler setup

Use `GalileoCallback` when:

* You're using **TypeScript** (middleware is not available)
* You need fine-grained control over logging
* You're working with complex LangChain applications
* You want to log specific components selectively

## Async support

`GalileoMiddleware` (Python) fully supports asynchronous execution. The middleware automatically handles both sync and async contexts. In TypeScript, `GalileoCallback` handles async natively.

<CodeGroup>
  ```python Python theme={null}
  # Async agent usage
  async def main():
      agent = create_agent(
          model,
          tools=[get_weather, get_stock_price],
          middleware=[GalileoMiddleware()]
      )
      result = await agent.ainvoke({
          "messages": [HumanMessage(content="...")]
      })
      print(result)
  ```

  ```typescript TypeScript theme={null}
  import { GalileoCallback } from "galileo";
  import { ChatOpenAI } from "@langchain/openai";
  import { HumanMessage } from "@langchain/core/messages";
  import { createAgent } from "langchain";
  import { tool } from "@langchain/core/tools";
  import { z } from "zod";

  // Define tools
  const getWeather = tool(
    async ({ city }: { city: string }) => {
      return `The weather in ${city} is sunny, 72F.`;
    },
    {
      name: "get_weather",
      description: "Get the current weather for a city",
      schema: z.object({ city: z.string() }),
    }
  );

  // Create the Galileo callback
  const callback = new GalileoCallback();

  // Create the agent with tools
  const llm = new ChatOpenAI({ model: "gpt-4o" });
  const agent = createAgent({
    model: llm,
    tools: [getWeather],
  });

  // Invoke the agent — async is handled natively by GalileoCallback
  const result = await agent.invoke(
    { messages: [new HumanMessage("What's the weather in San Francisco?")] },
    { callbacks: [callback] }
  );

  console.log(result);
  ```
</CodeGroup>

In Python, the middleware uses the appropriate handler (`GalileoBaseHandler` or `GalileoAsyncBaseHandler`) based on the execution context. In TypeScript, `GalileoCallback` works with both sync and async invocations.

## Best practices

1. **Use middleware for LangGraph agents**: For LangGraph-based agents, middleware provides the simplest integration
2. **Add meaningful metadata**: Include relevant project and session information in your logger configuration
3. **Configure flush behavior**: For high-volume applications, consider disabling auto-flush and batch your logs
4. **Share loggers**: Use the same logger instance across middleware for unified trace management
5. **Monitor execution**: Review the hierarchical traces in Galileo to understand agent behavior

## Example

You can find a complete example of using `GalileoMiddleware` with a LangGraph agent in the [LangChain Middleware Example](https://github.com/rungalileo/sdk-examples/pull/156).

## Next steps

### Related documentation

<CardGroup cols={2}>
  <Card title="GalileoCallback" icon="code" horizontal href="/sdk-api/third-party-integrations/langchain">
    Use callbacks for fine-grained LangChain logging control.
  </Card>

  <Card title="Experiments" icon="flask" horizontal href="/sdk-api/third-party-integrations/langchain/experiments">
    Learn how to run and track experiments with LangChain.
  </Card>
</CardGroup>

### Cookbooks

<CardGroup cols={2}>
  <Card title="Monitor LangChain Agents with Galileo" icon="code" horizontal href="/cookbooks/use-cases/agent-langchain">
    Learn how to build and monitor a LangChain AI Agent using Galileo for tracing and observability.
  </Card>

  <Card title="Add evaluations to a multi-agent LangGraph application" icon="code" horizontal href="/cookbooks/use-cases/multi-agent-langgraph/multi-agent-langgraph">
    Learn how to add evaluations to a multi-agent LangGraph chat bot using Galileo
  </Card>
</CardGroup>
