← All Go packages
github.com/anulum/director-ai/gateway/internal/scoring
package scoring // import "github.com/anulum/director-ai/gateway/internal/scoring"
Package scoring wraps the generated “director.v1.CoherenceScoring“ gRPC client
so the rest of the gateway stays transport-agnostic. A caller constructs one
“Client“ per process and passes it into middleware; the implementation handles
dial, retries, and channel lifecycle.
TYPES
type Client struct {
// Has unexported fields.
}
Client is a thin wrapper around the generated stub with explicit lifecycle.
The zero value is not usable; construct with Dial.
func Dial(addr string, timeout time.Duration) (*Client, error)
Dial opens an insecure channel to “addr“ (e.g. “localhost:50052“) and
returns a Client. Production deployments supply a secure channel by calling
grpc.NewClient directly and passing the result to WithConn.
func WithConn(conn *grpc.ClientConn) *Client
WithConn builds a Client around a pre-dialled connection. The caller retains
ownership of the connection — Close must not be called on connections passed
here.
func (c *Client) Close() error
Close releases the underlying channel. Safe to call on a Client that was
built via Dial; a no-op on clients from WithConn when their connection is
externally owned.
func (c *Client) ScoreClaim(
ctx context.Context,
claim string,
documents []string,
tenantID, requestID string,
threshold float32,
) (*directorv1.CoherenceVerdict, int64, error)
ScoreClaim is a one-shot scoring call. “claim“ is the candidate answer,
“documents“ are the retrieved context snippets, and “threshold“ overrides
the server default when positive.
func (c *Client) StartStream(ctx context.Context) (*StreamHandle, error)
StartStream opens a bidirectional ScoreStream with the given context.
The caller MUST call Close when done.
type Middleware struct {
Scorer Scorer
Timeout time.Duration
// ThresholdHeader, when set, lets clients tune the threshold per
// request via an HTTP header (e.g. "X-Coherence-Threshold"). An
// empty string keeps the server default.
ThresholdHeader string
}
Middleware runs ScoreClaim against the assistant response once the upstream
handler returns. The verdict is attached to the response headers; requests
with “Accept: text/event-stream“ bypass scoring because SSE output is not
buffered here (streaming mode is a future-phase concern).
func (m *Middleware) Enabled() bool
Enabled reports whether the middleware is wired to a real scorer.
func (m *Middleware) Handler(next http.Handler) http.Handler
Handler wraps next with post-response scoring. The middleware buffers
non-streaming responses so it can read the assistant message, calls
ScoreClaim with it, and either:
- adds “X-Coherence-Score“ and “X-Coherence-Halted“ headers and forwards
the body unchanged (halted=false), or
- rewrites the response as 422 JSON (halted=true, default), signalling a
hallucination.
A zero-value Middleware returns next unchanged. Use “Enabled“ to check that
a scorer was supplied before wiring.
type Scorer interface {
ScoreClaim(
ctx context.Context,
claim string,
documents []string,
tenantID, requestID string,
threshold float32,
) (*directorv1.CoherenceVerdict, int64, error)
}
Scorer is the subset of Client methods the middleware depends on. Extracted
so tests can inject a stub without wiring gRPC.
type StreamHandle struct {
// Has unexported fields.
}
StreamHandle is the active side of a ScoreStream RPC. Send emits a token;
Recv blocks for the next verdict; Close ends the stream cleanly (half-close
then wait for the server's trailing metadata).
func (h *StreamHandle) Close() error
Close signals end-of-stream to the server and waits for the RPC to finish.
func (h *StreamHandle) Recv() (*directorv1.CoherenceVerdict, error)
Recv blocks for the next verdict. Returns (verdict, io.EOF) when the server
has closed the stream normally (e.g. after a halt).
func (h *StreamHandle) Send(req *directorv1.ScoreTokenRequest) error
Send emits a single token request.