Class LlmProviderSniffer

java.lang.Object
org.mockserver.llm.client.LlmProviderSniffer

public final class LlmProviderSniffer extends Object
Maps a forwarded request's target host/URL to an LLM Provider, enabling GenAI observability on the proxy/forward path. Pure function of its inputs (request host/path, configuration) — no shared mutable state.

Detection order:

  1. Well-known provider hosts (exact or wildcard match), including the OpenAI Codex backend (chatgpt.com) used by coding CLIs such as opencode, which serves the Responses API at /backend-api/codex/responses
  2. Configured mockserver.llmBaseUrl Ollama host match
  3. Fallback to configured mockserver.llmProvider — only when the request path looks like an LLM endpoint (contains any of /chat/completions, /messages, /completions, /responses, /embeddings, /v1/, :generatecontent, /api/generate, /api/chat)
Returns Optional.empty() when the request is not LLM traffic.
  • Method Details

    • sniff

      public static Optional<Provider> sniff(HttpRequest forwardedRequest)
      Sniff the LLM provider from a forwarded request's target host.
      Parameters:
      forwardedRequest - the request that was forwarded to the upstream
      Returns:
      the detected provider, or empty if this is not LLM traffic
    • detectForAnalysis

      public static Optional<Provider> detectForAnalysis(HttpRequest request)
      Detect the LLM provider for OFFLINE analysis of already-captured traffic (e.g. the optimisation report). Recognises LLM traffic that sniff(org.mockserver.model.HttpRequest) cannot — most importantly MOCKED traffic served by MockServer itself on localhost, where there is no upstream provider host. Tries the host-based sniff(org.mockserver.model.HttpRequest) first, then falls back to sniffByPath(org.mockserver.model.HttpRequest), which mirrors the dashboard's client-side path detection so the SAME traffic appears in both the Sessions view and the optimisation report.

      Intended only for read-only analysis. The live forward/span path must keep using host-gated sniff(org.mockserver.model.HttpRequest) so forwarded non-LLM traffic is never mis-classified.

    • detectForAnalysis

      public static Optional<Provider> detectForAnalysis(HttpRequest request, HttpResponse response)
      Detect the LLM provider for OFFLINE analysis, using the response body as an additional, more resilient signal. Detection order, cheapest/most-specific first:
      1. well-known host (sniff(org.mockserver.model.HttpRequest))
      2. recognised URL path shape (sniffByPath(org.mockserver.model.HttpRequest))
      3. request/response body shape (sniffByBodyShape(org.mockserver.model.HttpRequest, org.mockserver.model.HttpResponse)) — the resilient fallback that recognises LLM traffic from the wire format itself, so a coding CLI that routes through an unknown host or a non-standard path (e.g. a future endpoint rename, a private gateway, a new tool) is still classified without a code change.
      The body shape is the slowest-moving signal — it is the provider's API contract — so keying on it (rather than only on host/path, the dimension that varies most between tools and versions) is what keeps capture working as the LLM APIs and CLI harnesses evolve.
    • sniffByBodyShape

      public static Optional<Provider> sniffByBodyShape(HttpRequest request, HttpResponse response)
      Provider detection from the request/response body shape alone — no host or path required. This is the resilient fallback for detectForAnalysis(org.mockserver.model.HttpRequest): the wire format is the provider's API contract and moves far more slowly than the host/path a given CLI happens to use, so recognising LLM traffic by its body keeps the Traffic / LLM Traces / LLM Optimise views working when a tool changes endpoints or a new tool appears.

      Keys on the most stable, provider-distinctive markers and stays conservative (returns empty rather than guess) so non-LLM traffic is not mis-classified. Read-only analysis use only — never the live forward path.

    • sniffByPath

      public static Optional<Provider> sniffByPath(HttpRequest request)
      Path-shape based provider detection, mirroring the dashboard's llmTraffic.ts ordering (Anthropic, Azure, Bedrock, OpenAI Responses, OpenAI, Gemini, Ollama). Used to analyse captured traffic whose host is not a known provider (e.g. mocked LLM responses on localhost).
    • sniffByHost

      public static Optional<Provider> sniffByHost(String host)
      Sniff the LLM provider from an explicit host string (for unit testing or callers that already have the host extracted). Equivalent to sniffByHostAndPath(host, null) — the configured-provider fallback is only applied when the path looks like an LLM endpoint, so with a null path the fallback is skipped.
    • sniffByHostAndPath

      public static Optional<Provider> sniffByHostAndPath(String host, String path)
      Sniff the LLM provider from an explicit host and path.
      Parameters:
      host - the target host (may be null)
      path - the request path (may be null) — used only for the configured-provider fallback gate
    • extractModelFromResponse

      public static String extractModelFromResponse(HttpResponse response)
      Extract the model name from the forwarded response body. Providers typically include a "model" field in their JSON response (OpenAI, Anthropic, Gemini use this pattern). Returns null if extraction fails.
    • extractModelFromRequest

      public static String extractModelFromRequest(HttpRequest request)
      Extract the model name from the forwarded request body. Providers typically include a "model" field in their JSON request body. Returns null if extraction fails.