Launch Week 02 wrapped — explore all five launches

Handler Mode

Connect an AI app that has no HTTP endpoint by implementing a handler function the Confident Agent runs locally.

Included on the Enterprise plan. Book a demo, opens in a new tab. Not included on the Team plan. Not included on the Starter plan. Not included on the Free plan.

Handler Mode lets you connect an AI app that has no HTTP endpoint to Confident AI. Instead of pointing an AI Connection at a URL, you implement a small handler function that the Confident Agent runs inside your own environment. Confident AI sends inputs down the agent's existing outbound tunnel, your function produces the output locally, and the agent relays it back.

When to Use Handler Mode

Reach for Handler Mode when you want to evaluate or red team an app that isn't exposed as a callable service:

  • The pipeline is an internal script, notebook, or library with no API in front of it.
  • Standing up and hosting an endpoint (even internally) is more work than you want for an evaluation.
  • Security or compliance requires that inputs are processed in place, with nothing inbound and no code hosted by Confident AI.

If your app already has an HTTP endpoint, use a standard AI Connection — or, for a private endpoint behind a firewall, the Confident Agent in its default forwarding mode.

How It Works

The agent connects outbound over WebSocket Secure (WSS) exactly as in forwarding mode. The only difference is what happens when a request arrives: instead of forwarding it to an internal URL, the agent calls your handler function and relays whatever it returns.

sequenceDiagram
    participant P as Your Pipeline (imported)
    participant H as handler()
    participant A as Confident Agent
    participant C as Confident AI

    A->>C: Connect outbound (WSS/443)
    Note over A,C: WebSocket tunnel established

    C->>A: Send evaluation input
    A->>H: handler(request)
    H->>P: Call your app in-process
    P-->>H: Output
    H-->>A: return output
    A-->>C: Relay output back
    Note over C: Evaluation continues

Your handler is imported once when the agent starts, so expensive setup — loading a model, creating clients — happens a single time. The function is then called once per input, over the same long-lived connection.

Requirements

  • Outbound internet access on port 443 (WSS) from the machine running the agent
  • A runtime where your pipeline is importable (its dependencies installed)
  • No inbound ports need to be opened

Quick Start

A handler runs in the same runtime as the app it calls, so pick the tab for the language your pipeline is written in. Python and TypeScript load your handler from a file; Go, Java, and Rust register the handler in code as a library; any other language connects through a small local endpoint and the agent's forwarding mode.

  1. Install the agent

    Install the Confident Agent from PyPI into the same environment as your pipeline. It ships both the runner and the handler decorator.

    pip install confident-agent
  2. Write your handler

    Create a file — for example confident_handler.py — and decorate one function with @handler. It receives a Request and returns a Response containing your app's output. Import and call your existing pipeline directly; you only write the glue.

    from confident_agent import handler, Request, Response
    from my_company.pipeline import summarize  # your existing code
    
    @handler
    def summarize_handler(request: Request) -> Response:
        return Response(
            output=summarize(request.input, request.context),
        )

    The Response object always carries the output and can also include retrieval context, tool calls, or multi-turn state. Returning a plain string remains available as shorthand when output is the only field you need.

  3. Run the agent

    Start the agent pointing at your handler file, authenticated with a project API key. Because the handler imports your pipeline, run this in the same Python environment as your pipeline — the same virtualenv, conda env, or container your app already uses. This is the recommended way to run handler mode:

    CONFIDENT_API_KEY=<your-api-key> confident-agent --handler confident_handler.py

    That's the whole command — there is no URL or port to configure. The agent dials out to Confident AI, and when a test run needs your app's output, it calls your handler function directly.

    Running with Docker instead

    Only use Docker if your team deploys everything containerized. The stock confidentai/confident-agent image contains just the agent — not your pipeline's dependencies — so mounting your handler into it will fail on import (ModuleNotFoundError on your own packages). Instead, build your own image on top of ours, adding your code and dependencies:

    FROM confidentai/confident-agent
    USER root
    COPY . /app
    RUN pip install -r /app/requirements.txt
    USER confident

    Build it, then run it. In Docker, pass the same two settings as environment variables instead of CLI flags:

    docker build -t my-company/confident-handler .
    
    docker run -d \
      -e CONFIDENT_API_KEY=<your-api-key> \
      -e CONFIDENT_HANDLER=/app/confident_handler.py \
      my-company/confident-handler

    When the agent connects, the connection status flips to Agent connected in the AI Connection settings.

  4. Create the AI Connection

    In Project SettingsAI Connections, click New AI Connection, give it a name, and set the Target to Confident Agent (Handler). There is no endpoint URL to configure. Save, then test it.

The Handler

A handler is any function registered with the SDK — @handler in Python, handler(...) in TypeScript and Rust, confidentagent.Handler(...) in Go, Decorator.handler(...) in Java. The agent discovers it at startup; the function name is yours. Exactly one handler per agent process. (For languages that use the HTTP fallback, this section doesn't apply — your endpoint defines the contract instead.)

Request

Your handler receives one Request object. The contract carries the same data in every native SDK, but field names and types follow each language's conventions. Read only the fields you need; unused fields are safe to ignore.

FieldDescriptionType
request.inputThe input for a single-turn goldenstr
request.contextContext from the goldenlist[str]
request.retrieval_contextRetrieval context from the goldenlist[str]
request.expected_outputExpected output, when presentstr | None
request.turnsTurn history for multi-turn evaluationslist[Turn]
request.scenarioScenario for conversational goldensstr | None
request.stateMutable state carried across multi-turn simulationsAny
request.promptsPrompt versions attached to the connectiondict[str, Any] | None
request.hyperparametersHyperparameters attached to the connectiondict[str, Any] | None
request.test_case_idIdentifier for linking the test case to a tracestr | None
request.turn_idIdentifier for linking an individual turn to a tracestr | None

Response

Return a Response containing your app's output. The contract carries the same data in every native SDK, with language-specific field names and types. A plain string remains supported as shorthand for output-only handlers.

from confident_agent import handler, Request, Response

@handler
def rag_handler(request: Request) -> Response:
    result = pipeline(request.input)
    return Response(
        output=result.answer,
        retrieval_context=result.chunks,
        tools_called=result.tools,
    )
FieldDescriptionType
outputThe actual output of your app (required)str
retrieval_contextRetrieved chunks, for RAG metricslist[str] | None
tools_calledTools your app invoked, for tool metricslist[ToolCall] | None
stateUpdated state to carry into the next turnAny

Handling Errors

If your handler raises or returns an error, the agent reports the failure for that single input and the run continues — one bad input won't abort the whole evaluation. The error message is surfaced on the test case so you can debug it.

from confident_agent import handler, Request, Response

@handler
def summarize_handler(request: Request) -> Response:
    if not request.input:
        raise ValueError("empty input")
    return Response(
        output=summarize(request.input, request.context),
    )

Configuration

VariableDescriptionRequired
CONFIDENT_API_KEYYour project API keyYes
CONFIDENT_HANDLERPath to the file containing your @handler function (Python and TypeScript only)Yes, for handler mode
CONFIDENT_WS_BASE_URLWebSocket relay URLNo — defaults to wss://deepeval.confident-ai.com/ws/relay

In Python and TypeScript, the --handler CLI flag and the CONFIDENT_HANDLER environment variable are equivalent; use whichever fits how you run the agent. In Go, Java, and Rust the handler is registered in code, so neither applies.

Testing Your Handler

Click Ping on the connection. Confident AI sends one sample input through the tunnel, runs your handler, and shows what it returned — a green result confirms the handler is discovered, runs, and produces an output. If the handler isn't found, raises, or returns nothing, the error is shown so you can fix it before running a full evaluation or assessment.

✅ Done. Your AI app is connected with no endpoint — ready to run evaluations and red teaming.

Multi-Turn

For multi-turn evaluations, request.turns carries the conversation so far and request.state carries anything you returned from the previous turn. Return a Response with an updated state to thread information forward. See Multi-Turn State for the full model.

Next Steps

Setting this up for your organization?Set up the controls your team needs before a wider rolloutTalk to us

Last updated on

Built byConfident AI