mirror of
https://github.com/netbirdio/netbird.git
synced 2026-07-16 19:49:56 +00:00
Request/response parsers and SSE/event-stream metering, the embedded pricing table, and the builtin middleware set: request parser, router, policy limit-check/record, cost meter, guardrail, identity inject, response parser. Includes the path-routed providers — Google Vertex AI (keyfile:: service-account OAuth minting) and AWS Bedrock (bearer auth, invoke/converse/streaming, optional /bedrock prefix) — plus the Models allowlist and unmeterable-publisher deny.
113 lines
4.3 KiB
Go
113 lines
4.3 KiB
Go
// Package llm provides the shared LLM request and response parsing
|
|
// library consumed by proxy middleware. It is runtime agnostic: the same
|
|
// package is used by the native built-in executor now and will be reused
|
|
// by the WASM adapter later.
|
|
package llm
|
|
|
|
// Provider identifies an LLM API provider.
|
|
type Provider int
|
|
|
|
const (
|
|
// ProviderUnknown signals that no parser matched the request.
|
|
ProviderUnknown Provider = 0
|
|
// ProviderOpenAI identifies the OpenAI API surface.
|
|
ProviderOpenAI Provider = 1
|
|
// ProviderAnthropic identifies the Anthropic Messages API surface.
|
|
ProviderAnthropic Provider = 2
|
|
// ProviderBedrock identifies the AWS Bedrock runtime surface.
|
|
ProviderBedrock Provider = 3
|
|
)
|
|
|
|
// RequestFacts captures the subset of the LLM request body that the
|
|
// middleware annotates as metadata (model, streaming flag). Additional
|
|
// fields are added as parsers grow.
|
|
type RequestFacts struct {
|
|
Model string
|
|
Stream bool
|
|
}
|
|
|
|
// Usage is the provider-agnostic token accounting emitted to metrics and
|
|
// access logs. Downstream consumers map InputTokens/OutputTokens to the
|
|
// plg.llm.* metadata allowlist entries.
|
|
//
|
|
// CachedInputTokens carries OpenAI's prompt_tokens_details.cached_tokens
|
|
// (a SUBSET of InputTokens) when the response is from OpenAI, or
|
|
// Anthropic's cache_read_input_tokens (ADDITIVE to InputTokens) when from
|
|
// Anthropic. The cost meter switches formula on KeyLLMProvider so the
|
|
// two shapes are billed correctly without double-counting.
|
|
//
|
|
// CacheCreationTokens carries Anthropic's cache_creation_input_tokens
|
|
// (ADDITIVE; not present in the OpenAI shape).
|
|
type Usage struct {
|
|
InputTokens int64
|
|
OutputTokens int64
|
|
TotalTokens int64
|
|
CachedInputTokens int64
|
|
CacheCreationTokens int64
|
|
}
|
|
|
|
// Parser is the per-provider interface implemented in this package. The
|
|
// dispatcher selects a parser by calling DetectFromURL against the incoming
|
|
// request path; ties break by registration order (see Parsers).
|
|
type Parser interface {
|
|
Provider() Provider
|
|
ProviderName() string
|
|
DetectFromURL(path string) bool
|
|
ParseRequest(body []byte) (RequestFacts, error)
|
|
ParseResponse(status int, contentType string, body []byte) (Usage, error)
|
|
// ExtractPrompt returns the user-facing prompt text from a request body.
|
|
// Different endpoint shapes (chat.completions, responses, messages) are
|
|
// handled by the per-provider implementation. Returns "" when no prompt
|
|
// can be extracted; never returns an error — extraction is best-effort
|
|
// because callers use the result for observability, not authorization.
|
|
ExtractPrompt(body []byte) string
|
|
// ExtractCompletion returns the assistant-facing completion text from a
|
|
// non-streaming response body. status and contentType match the
|
|
// ParseResponse arguments so implementations can fast-fail uniformly.
|
|
ExtractCompletion(status int, contentType string, body []byte) string
|
|
// ExtractSessionID returns a stable identifier that groups requests of
|
|
// the same conversation / coding session, read from the per-provider
|
|
// location clients populate (e.g. OpenAI Codex's client_metadata.session_id,
|
|
// Claude Code's metadata.user_id). Returns "" when the body carries no
|
|
// recognised session marker; extraction is best-effort and never errors.
|
|
ExtractSessionID(body []byte) string
|
|
}
|
|
|
|
// Parsers returns the built-in parser set in a stable order. The order is
|
|
// deterministic so that DetectFromURL ties produce consistent routing.
|
|
func Parsers() []Parser {
|
|
return []Parser{
|
|
OpenAIParser{},
|
|
AnthropicParser{},
|
|
BedrockParser{},
|
|
}
|
|
}
|
|
|
|
// DetectParser returns the first parser whose DetectFromURL matches the given
|
|
// request path. ok=false means no parser claimed the path.
|
|
func DetectParser(path string) (Parser, bool) {
|
|
for _, p := range Parsers() {
|
|
if p.DetectFromURL(path) {
|
|
return p, true
|
|
}
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
// ParserByName returns the parser whose ProviderName matches id. Used by
|
|
// callers that already know which provider surface a request will hit
|
|
// (e.g. the agent-network middleware chain configured per synthesised
|
|
// service) so they can skip URL sniffing. ok=false when no parser is
|
|
// registered under that name.
|
|
func ParserByName(id string) (Parser, bool) {
|
|
if id == "" {
|
|
return nil, false
|
|
}
|
|
for _, p := range Parsers() {
|
|
if p.ProviderName() == id {
|
|
return p, true
|
|
}
|
|
}
|
|
return nil, false
|
|
}
|