explain

package
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 31, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package explain turns captured LLM API traffic into compact digests.

The proxy stores raw request and response bodies. LLM responses are very often server-sent event streams made of hundreds of tiny events, which an agent should never have to read. This package:

  • detects LLM traffic from the host, path, request body and response content type (Detect);
  • reassembles a streamed response into the JSON object the same call would have returned without streaming (Reassemble);
  • renders a short, deterministic text digest of the exchange together with normalised token usage and the stop reason (Explain).

Supported providers are the Anthropic Messages API, OpenAI Chat Completions (and the many compatible APIs that share its wire format), the OpenAI Responses API, and, best effort, the Gemini generateContent API.

ParseSSE is a small, tolerant event-stream parser that the reassemblers share; it is exported because other parts of pano render raw streams too.

Index

Constants

View Source
const (
	// Anthropic is the Messages API: POST /v1/messages, streamed or not.
	Anthropic = "anthropic"
	// OpenAIChat is Chat Completions: POST /v1/chat/completions, including the
	// compatible APIs exposed by Azure, OpenRouter, Groq, Together, DeepSeek,
	// xAI, Mistral and others.
	OpenAIChat = "openai-chat"
	// OpenAIResponses is the Responses API: POST /v1/responses.
	OpenAIResponses = "openai-responses"
	// Gemini is generativelanguage.googleapis.com :generateContent and
	// :streamGenerateContent (best effort).
	Gemini = "gemini"
)

Provider identifiers.

View Source
const (
	IncludeFinal    = "final"    // reassembled response content blocks
	IncludeUsage    = "usage"    // token usage line
	IncludeTools    = "tools"    // tool names in the request line and tool-call inputs
	IncludeStop     = "stop"     // stop/finish reason
	IncludeSystem   = "system"   // the system prompt (truncated)
	IncludeMessages = "messages" // one line per request message
	IncludeThinking = "thinking" // thinking / reasoning text instead of a placeholder
	IncludeErrors   = "errors"   // errors line
	IncludeRequest  = "request"  // the raw request JSON, pretty-printed (truncated)
)

Include names accepted in Options.Include.

View Source
const DefaultMaxChars = 4000

DefaultMaxChars is the digest budget used when Options.MaxChars is zero.

Variables

DefaultInclude is the include set used when Options.Include is empty.

View Source
var ErrNotLLM = errors.New("explain: not recognised LLM traffic")

ErrNotLLM is returned by Explain when the exchange is not recognised LLM traffic and no provider was forced.

Providers lists every provider identifier.

Functions

func Detect

func Detect(host, path string, reqBody []byte, respMIME string) (provider string, ok bool)

Detect identifies the provider from the host, request path, request body shape and response content type. ok is false for traffic that is not a recognised LLM completion call.

Path suffixes are checked first so that gateways and proxies hosted on any domain are recognised; the request body shape is the fallback for providers reached through non-standard paths (Bedrock, Vertex, self-hosted gateways).

func Reassemble

func Reassemble(provider string, sse []byte) (final []byte, partial bool, err error)

Reassemble turns a provider's streamed response body (SSE, or a JSON array of chunks for Gemini) into the equivalent non-streaming JSON object. partial is true when the stream ended before its terminal event; the object returned then reflects everything received so far.

Types

type Event

type Event struct {
	// Name is the value of the "event:" field, or "" when the event has none
	// (OpenAI streams, for example, only send "data:" lines).
	Name string
	// Data is the payload: every "data:" line of the event joined with "\n".
	Data string
	// ID is the value of the "id:" field, if any.
	ID string
}

Event is one server-sent event as split out of a text/event-stream body.

func ParseSSE

func ParseSSE(b []byte) []Event

ParseSSE splits an event-stream body into events. It follows the WHATWG event-stream grammar loosely: lines end in CRLF, LF or CR; a line starting with ':' is a comment; multiple "data:" lines are joined with a newline; the single optional space after the field colon is dropped; a blank line dispatches the pending event. A pending event at end of input is dispatched too, so a stream that was cut off without its trailing blank line still yields its last event. Events without any field are dropped.

type Options

type Options struct {
	// Include selects digest sections; entries may also be comma-separated.
	// Defaults to DefaultInclude.
	Include []string
	// MaxChars caps the digest text. Defaults to DefaultMaxChars.
	MaxChars int
	// Provider forces a provider instead of detecting one.
	Provider string
}

Options controls what Explain renders.

type Result

type Result struct {
	Provider string
	Model    string
	Stream   bool
	Status   int
	// Text is the rendered digest.
	Text string
	// Final is the response as the non-streaming API would have returned it:
	// the reassembled object for streams, the body itself otherwise. nil when
	// not applicable (errors, empty or non-JSON bodies).
	Final []byte
	// Usage is normalised token usage: input_tokens (all prompt tokens,
	// cached ones included), output_tokens, cache_read_input_tokens,
	// cache_creation_input_tokens, total_tokens, reasoning_tokens (each only
	// when the provider reports it) and the provider's own object under "raw".
	Usage      map[string]any
	StopReason string
	// Partial is true when a stream ended before its terminal event.
	Partial bool
	Errors  []string
}

Result is a digest of one LLM exchange.

func Explain

func Explain(host, path string, status int, reqHeaders http.Header, reqBody []byte, respHeaders http.Header, respBody []byte, opts Options) (*Result, error)

Explain builds the digest of one exchange. reqBody and respBody are decoded bytes (any Content-Encoding already removed). status is the HTTP status of the response; bodies of error responses (≥ 400) are summarised into Result.Errors rather than reassembled.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL