LangServe Documentation

repository·main·Indexed 25 days ago

https://github.com/langchain-ai/langserve

A framework for deploying LangChain runnables and chains as REST APIs using FastAPI and Pydantic. It provides standardized endpoints for invocation, batching, and streaming (/invoke, /batch, /stream, /stream_log), automatic schema inference, an interactive playground, and client SDKs for Python and TypeScript via RemoteRunnable.

Tokens
24.3K
Snippets
55
Records
118
Agent score
81%

What's inside LangServe

  1. LangGraph Compatibility and Limitations

    main

    LangGraph Compatibility

    LangServe is primarily designed for simple Runnables and standard langchain-core primitives. If you are deploying LangGraph applications, it is recommended to use LangGraph Cloud (beta) instead of LangServe.

    Limitations

    • Client Callbacks: Client-side callbacks are not currently supported for events originating on the server.
    • Pydantic V2 Compatibility: In LangServe versions <= 0.2.0, OpenAPI docs may not generate properly when using Pydantic V2 due to FastAPI limitations. To resolve this, either upgrade to langserve>=0.3.0 or downgrade to pydantic<2.0.
  2. Configure Playground Widgets

    main

    You can define custom UI widgets for the LangServe playground by adding an extra field to your Pydantic model's field definitions. A widget is identified by a type key.

    Available Manual Widgets:

    1. base64file: Creates a file upload input for base64 encoded strings.
    2. chat: Creates a chat interface. Requires input and output JSONPaths to map the request/response fields to the chat UI.

    Widget Schema Structure:

    type JsonPath = number | string | (number | string)[];
    type NameSpacedPath = { title: string; path: JsonPath };
    type OneOfPath = { oneOf: JsonPath[] };
    
    type Widget = {
      type: string; // e.g., 'base64file', 'chat'
      [key: string]: JsonPath | NameSpacedPath | OneOfPath;
    };
  3. Key features of LangServe

    main

    LangServe provides several automated and developer-friendly features for deploying LangChain objects:

    • Automatic Schema Inference: Input and output schemas are automatically inferred from your LangChain object and enforced on every API call with rich error messages.
    • Standardized Endpoints: Efficient /invoke, /batch, and /stream endpoints supporting high concurrency.
    • Streaming Support:
      • /stream_log: Streams all or some intermediate steps from your chain/agent.
      • /stream_events: (Available since v0.0.40) A simplified way to stream without parsing /stream_log output.
    • Interactive Playground: A /playground/ page with streaming output and intermediate steps.
    • API Documentation: Automatically generated API docs page with JSONSchema and Swagger.
    • Tracing: Built-in optional tracing to LangSmith via API key configuration.
    • Client SDK: A client SDK that allows you to call a LangServe server as if it were a local Runnable.
  4. Use the LangServe Playground

    main

    Every route added via add_routes includes a playground at /{path}/playground/. This UI allows you to configure and invoke your runnable with streaming output and view intermediate steps.

    For configurable runnables, the playground allows you to save and share a link containing your specific configuration.

  5. Handle File Uploads via Base64 Encoding

    main

    LangServe does not currently support multipart/form-data for file uploads. To upload files by value to a runnable, you must use base64 encoding.

    Alternatively, you can upload files by reference (e.g., an S3 URL) or use a dedicated FastAPI endpoint to handle multipart/form-data before passing the data to your LangChain logic.

    try:
        from pydantic.v1 import Field
    except ImportError:
        from pydantic import Field
    
    from langserve import CustomUserType
    
    class FileProcessingRequest(CustomUserType):
        """Request including a base64 encoded file."""
    
        # The extra field is used to specify a widget for the playground UI.
        file: str = Field(..., extra={"widget": {"type": "base64file"}})
        num_chars: int = 100
  6. Refactor LCEL chains into LangGraph nodes

    main

    For more advanced features (like persistence, memory, and better debugging), it is recommended to refactor complex LCEL (LangChain Expression Language) chains into discrete LangGraph nodes.

    Instead of a single long chain, break the logic into separate nodes for long-running or critical steps (e.g., a retriever_node and a generator_node). This allows you to monitor, debug, and manage the state of each step independently within the StateGraph.

    @dataclass
    class InputState:
        """Input question from the user."""
        question: str
       
    @dataclass 
    class OutputState:
        """The output from the graph."""
        answer: str
    
    @dataclass 
    class SharedState:
        question: str
        docs: List[str]
        response: str
       
    async def retriever_node(state: InputState) -> SharedState:
        """Rettrieve documents based on the user's question."""
        documents = await retriever.ainvoke({"context": state.question})
        return {
            "docs": documents
        }
    
    async def generator_node(state: SharedState) -> OutputState:
        """Generate an answer using an LLM based on the retrieved documents and question."""
        context = " -- DOCUMENT -- ".join(state.docs)
        prompt = [
            SystemMessage(
                content=(
                    "Answer the user's question based on the list of documents "
                    "that were retrieved. Here are the documents: \n\n"
                    f"{context}"
                )
            ),
            HumanMessage(content=state.question),
        ]
        ai_message = await llm.ainvoke(prompt)
        return {"answer": ai_message.content}
        
    # Define a new graph
    builder = StateGraph(
        SharedState, config_schema=Configuration, input=InputState, output=OutputState
    )
    builder.add_node("retriever", retriever_node)
    builder.add_node("generator", generator_node)
    builder.add_edge("__start__", "retriever")
    builder.add_edge("retriever", "generator")
    graph = builder.compile()
    graph.name = "RAG Graph"
  7. Configure and serve a LangServe application

    main

    After bootstrapping your app, follow these steps to define your logic and run the server. Note that this project uses poetry for dependency management.

    1. Define the runnable: Edit server.py and use add_routes to register your LangChain object:
    add_routes(app, your_runnable)
    1. Add dependencies: Use poetry to add any required third-party packages (e.g., langchain-openai):
    poetry add langchain-openai
    1. Set environment variables: Configure necessary keys, such as your provider API keys:
    export OPENAI_API_KEY="sk-..."
    1. Run the server: Start the application using the langchain serve command:
    poetry run langchain serve --port=8100
  8. Explore LangServe implementation examples

    main

    LangServe provides a variety of reference implementations in its examples directory to help you get started with different patterns. These include:

    • LLMs: Minimal examples using OpenAI and Anthropic with support for async, batching, and streaming.
    • Retrieval: Simple retriever servers, Conversational Retrievers, and Configurable Runnables (e.g., changing index names at runtime).
    • Agents: Implementations of agents with and without conversation history using OpenAI tools.
    • Persistence: Using RunnableWithMessageHistory to implement backend-persisted chat keyed by session_id or conversation_id and user_id.
    • Advanced Configuration: Using Configurable Runnable for runtime field and alternative configuration.
    • Custom API Patterns: Using APIHandler instead of add_routes for more flexible endpoint definitions and FastAPI integration.
    • Authentication: Various patterns including global dependencies, path dependencies, per-request config modifiers, and per-user logic (e.g., searching only within user-owned documents).
    • UI/Playground Widgets: Examples of custom widgets for the LangServe playground, such as chat interfaces and file upload widgets.
  9. Configure type-aware ESLint rules for production

    main

    When developing a production application with this template, it is recommended to enable type-aware lint rules to improve code quality.

    1. Update the parserOptions in your ESLint configuration to include project and tsconfigRootDir:

    2. Update the extends list in your ESLint configuration:

      • Replace plugin:@typescript-eslint/recommended with plugin:@typescript-eslint/recommended-type-checked or plugin:@typescript-eslint/strict-type-checked.
      • Optionally add plugin:@typescript-eslint/stylistic-type-checked.
      • Install eslint-plugin-react and add plugin:react/recommended and plugin:react/jsx-runtime to the extends list.
    parserOptions: {
      ecmaVersion: 'latest',
      sourceType: 'module',
      project: ['./tsconfig.json', './tsconfig.node.json'],
      tsconfigRootDir: __dirname,
    },
  10. Migrate from LangServe to LangGraph Platform (Quick Wrap)

    main

    If you have an existing LangServe application using add_routes, the fastest way to migrate to LangGraph Platform is to wrap your existing Runnable inside a LangGraph node.

    This approach involves:

    1. Defining InputState, OutputState, and SharedState using @dataclass to represent your original input/output schemas and the internal graph state.
    2. Creating an asynchronous node function that calls your existing runnable.ainvoke().
    3. Building a StateGraph using the defined states, adding the node, and setting the entrypoint.
    4. Compiling the graph.
    @dataclass
    class InputState:
        input: str
        foo: Optional[str] = None
    
    @dataclass
    class OutputState:
        output: Any
    
    @dataclass
    class SharedState:
        input: str
        foo: Optional[str] = None
        output: Any
        
    runnable = ... # Your existing Runnable
    
    async def my_node(state: InputState, config: RunnableConfig) -> OutputState:
        """Each node does work."""
        return await runnable.ainvoke({"input": state.input, "foo": state.foo})
    
    # Define a new graph
    builder = StateGraph(
        SharedState, config_schema=Configuration, input=InputState, output=OutputState
    )
    
    # Add the node to the graph
    builder.add_node("my_node", my_node)
    
    # Set the entrypoint
    builder.add_edge("__start__", "my_node")
    
    # Compile the workflow into an executable graph
    graph = builder.compile()
    graph.name = "New Graph"
  11. Install LangServe

    main

    You can install LangServe using pip. You can choose to install the full suite or specific components for client or server use.

    To install everything (client and server):

    pip install "langserve[all]"

    To install only the client components:

    pip install "langserve[client]"

    To install only the server components:

    pip install "langserve[server]"