Files
llmgw/internal/router/trace_test.go
T
Ilker ManapandClaude Opus 5.5 fd94265e08
CI / test (push) Failing after 7m17s
Pickles refused however they come; a string temperature passes again
From review. A mirror set already on the node is looked at again, not
trusted for having been fetched once; Pickled cuts a query or a fragment
from any source, hf: too; .pt2 is a pickle. The flag check reads a flag as
the engine does: underscores as dashes and any beginning of a refused flag
as that flag, since argparse takes --trust-remote for --trust-remote-code;
SGLang refuses --load-format, which would undo the agent's safetensors.

TripoSR's preset says allow_pickle: without it the preset could not be
added and the presets pass failed on it each time. A shared ComfyUI
environment's hidden entry takes its first member's allow_pickle, as it
takes its source.

The GenAI trace attributes made temperature and top_p typed fields of the
request's parse, so "0.7" as a string was a 400; they are read loosely,
as they are read only for the trace.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 22:19:06 +01:00

155 lines
5.9 KiB
Go

package router
import (
"strings"
"testing"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/propagation"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/sdk/trace/tracetest"
"go.opentelemetry.io/otel/trace/noop"
"gitea.mynodes.xyz/ilker/llmgw/internal/config"
)
func TestARequestIsTracedIntoTheEngine(t *testing.T) {
rec := tracetest.NewSpanRecorder()
prevTP, prevProp := otel.GetTracerProvider(), otel.GetTextMapPropagator()
otel.SetTracerProvider(sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(rec)))
otel.SetTextMapPropagator(propagation.TraceContext{})
t.Cleanup(func() { otel.SetTracerProvider(prevTP); otel.SetTextMapPropagator(prevProp) })
e := newFakeEngine(t, "m")
rt := newTestRouter(t, baseConfig(config.Endpoint{ID: "a", URL: e.srv.URL, Models: []string{"m"}}))
// The caller's own trace, continued rather than replaced.
const caller = "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
res := post(t, rt.Handler(), map[string]any{"model": "m", "messages": []any{}},
map[string]string{"traceparent": caller, "X-Request-Id": "req-1"})
if res.Code != 200 {
t.Fatalf("status %d: %s", res.Code, res.Body.String())
}
spans := map[string]sdktrace.ReadOnlySpan{}
for _, s := range rec.Ended() {
spans[s.Name()] = s
}
root, ok := spans["inference /v1/chat/completions"]
if !ok {
t.Fatalf("no inference span among %v", spans)
}
if got := root.SpanContext().TraceID().String(); got != "4bf92f3577b34da6a3ce929d0e0e4736" {
t.Errorf("the caller's trace was not continued: %s", got)
}
attrs := map[string]string{}
for _, a := range root.Attributes() {
attrs[string(a.Key)] = a.Value.Emit()
}
if attrs["llmgw.request_id"] != "req-1" || attrs["llmgw.model"] != "m" ||
attrs["http.response.status_code"] != "200" || attrs["llmgw.endpoint"] != "a" {
t.Errorf("inference span attributes %v", attrs)
}
for _, name := range []string{"admission", "schedule", "upstream"} {
s, ok := spans[name]
if !ok {
t.Errorf("no %s span", name)
continue
}
if s.Parent().SpanID() != root.SpanContext().SpanID() {
t.Errorf("%s is not under the request's span", name)
}
}
up := spans["upstream"]
tp, _ := e.traceparent.Load().(string)
if want := "00-" + up.SpanContext().TraceID().String() + "-" + up.SpanContext().SpanID().String() + "-01"; tp != want {
t.Errorf("the engine got traceparent %q, expected %q", tp, want)
}
}
func TestWithoutTracingTheEngineGetsNoTraceparent(t *testing.T) {
// What an unconfigured process has. Set explicitly, since the default
// global delegates for good to the first real provider another test set.
prevTP, prevProp := otel.GetTracerProvider(), otel.GetTextMapPropagator()
otel.SetTracerProvider(noop.NewTracerProvider())
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator())
t.Cleanup(func() { otel.SetTracerProvider(prevTP); otel.SetTextMapPropagator(prevProp) })
e := newFakeEngine(t, "m")
rt := newTestRouter(t, baseConfig(config.Endpoint{ID: "a", URL: e.srv.URL, Models: []string{"m"}}))
if res := post(t, rt.Handler(), map[string]any{"model": "m", "messages": []any{}}, nil); res.Code != 200 {
t.Fatalf("status %d", res.Code)
}
if tp, _ := e.traceparent.Load().(string); tp != "" {
t.Errorf("traceparent %q sent with tracing off", tp)
}
}
func TestTracesSpeakTheGenAIConventionsAndHoldNoText(t *testing.T) {
rec := tracetest.NewSpanRecorder()
prevTP := otel.GetTracerProvider()
otel.SetTracerProvider(sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(rec)))
t.Cleanup(func() { otel.SetTracerProvider(prevTP) })
e := newFakeEngine(t, "m")
rt := newTestRouter(t, baseConfig(config.Endpoint{ID: "a", URL: e.srv.URL, Models: []string{"m"}}))
res := post(t, rt.Handler(), map[string]any{"model": "m", "max_tokens": 64, "temperature": 0.2, "top_p": 0.9,
"messages": []any{map[string]any{"role": "user", "content": "the secret word is aubergine"}}}, nil)
if res.Code != 200 {
t.Fatalf("status %d: %s", res.Code, res.Body.String())
}
got := map[string]map[string]string{}
for _, s := range rec.Ended() {
attrs := map[string]string{}
for _, a := range s.Attributes() {
attrs[string(a.Key)] = a.Value.Emit()
// Neither the prompt nor the reply ("hi"), in any attribute.
if v := a.Value.Emit(); strings.Contains(v, "aubergine") || v == "hi" {
t.Errorf("span %s carries text in %s", s.Name(), a.Key)
}
}
got[s.Name()] = attrs
}
root := got["inference /v1/chat/completions"]
for k, want := range map[string]string{
"gen_ai.operation.name": "chat",
"gen_ai.request.model": "m",
"gen_ai.request.max_tokens": "64",
"gen_ai.request.temperature": "0.2",
"gen_ai.request.top_p": "0.9",
"gen_ai.response.model": "m",
"gen_ai.usage.input_tokens": "4",
"gen_ai.usage.output_tokens": "2",
} {
if root[k] != want {
t.Errorf("request span %s = %q, want %q", k, root[k], want)
}
}
up := got["upstream"]
if up["gen_ai.operation.name"] != "chat" || up["gen_ai.provider.name"] == "" || up["gen_ai.usage.output_tokens"] != "2" {
t.Errorf("upstream span %v", up)
}
}
func TestEveryInferencePathHasItsGenAIOperation(t *testing.T) {
for path, want := range map[string]string{
"/v1/chat/completions": "chat",
"/v1/completions": "text_completion",
"/v1/embeddings": "embeddings",
"/v1/rerank": "",
} {
if got := genAIOperation(path); got != want {
t.Errorf("%s: %q, want %q", path, got, want)
}
}
}
func TestATemperatureWrittenAsAStringIsTheEnginesToJudge(t *testing.T) {
// Read for the trace only: before it was, "0.7" reached the engine.
e := newFakeEngine(t, "m")
rt := newTestRouter(t, baseConfig(config.Endpoint{ID: "a", URL: e.srv.URL, Models: []string{"m"}}))
rec := post(t, rt.Handler(), map[string]any{"model": "m", "temperature": "0.7",
"messages": []any{map[string]any{"role": "user", "content": "hi"}}}, nil)
if rec.Code != 200 {
t.Errorf("status %d: %s", rec.Code, rec.Body.String())
}
}