Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions config/config.tracing.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Local Tracing Configuration (Jaeger + Always-On Sampling)
# This config is used by tools/tracing/docker-compose.tracing.yaml via CONFIG_FILE.

bert_model:
model_id: models/all-MiniLM-L12-v2
threshold: 0.6
use_cpu: true

semantic_cache:
enabled: true
backend_type: "memory"
similarity_threshold: 0.8
max_entries: 1000
ttl_seconds: 3600
eviction_policy: "fifo"

tools:
enabled: true
top_k: 3
similarity_threshold: 0.2
tools_db_path: "config/tools_db.json"
fallback_to_empty: true

prompt_guard:
enabled: true
use_modernbert: true
model_id: "models/jailbreak_classifier_modernbert-base_model"
threshold: 0.7
use_cpu: true
jailbreak_mapping_path: "models/jailbreak_classifier_modernbert-base_model/jailbreak_type_mapping.json"

vllm_endpoints:
- name: "endpoint1"
address: "127.0.0.1"
port: 8000
weight: 1

model_config:
"openai/gpt-oss-20b":
reasoning_family: "gpt-oss"
preferred_endpoints: ["endpoint1"]
pii_policy:
allow_by_default: true

classifier:
category_model:
model_id: "models/category_classifier_modernbert-base_model"
use_modernbert: true
threshold: 0.6
use_cpu: true
category_mapping_path: "models/category_classifier_modernbert-base_model/category_mapping.json"
pii_model:
model_id: "models/pii_classifier_modernbert-base_presidio_token_model"
use_modernbert: true
threshold: 0.7
use_cpu: true
pii_mapping_path: "models/pii_classifier_modernbert-base_presidio_token_model/pii_type_mapping.json"

categories:
- name: math
system_prompt: "You are a mathematics expert. Provide step-by-step solutions."
model_scores:
- model: openai/gpt-oss-20b
score: 1.0
use_reasoning: true
- name: other
system_prompt: "You are a helpful assistant."
model_scores:
- model: openai/gpt-oss-20b
score: 0.7
use_reasoning: false

default_model: openai/gpt-oss-20b

reasoning_families:
gpt-oss:
type: "reasoning_effort"
parameter: "reasoning_effort"

default_reasoning_effort: high

api:
batch_classification:
max_batch_size: 100
concurrency_threshold: 5
max_concurrency: 8
metrics:
enabled: true

observability:
tracing:
enabled: true
provider: "opentelemetry"
exporter:
type: "otlp"
endpoint: "jaeger:4317" # Jaeger gRPC OTLP endpoint inside compose network
insecure: true
sampling:
type: "always_on" # Always sample in local/dev for easy debugging
rate: 1.0
resource:
service_name: "vllm-semantic-router"
service_version: "dev"
deployment_environment: "local"
6 changes: 3 additions & 3 deletions config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -185,11 +185,11 @@ api:
# Observability Configuration
observability:
tracing:
enabled: false # Enable distributed tracing (default: false)
enabled: true # Enable distributed tracing for docker-compose stack
provider: "opentelemetry" # Provider: opentelemetry, openinference, openllmetry
exporter:
type: "stdout" # Exporter: otlp, jaeger, zipkin, stdout
endpoint: "localhost:4317" # OTLP endpoint (when type: otlp)
type: "otlp" # Export spans to Jaeger (via OTLP gRPC)
endpoint: "jaeger:4317" # Jaeger collector inside compose network
insecure: true # Use insecure connection (no TLS)
sampling:
type: "always_on" # Sampling: always_on, always_off, probabilistic
Expand Down
45 changes: 44 additions & 1 deletion dashboard/backend/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ func main() {
routerAPI := flag.String("router_api", env("TARGET_ROUTER_API_URL", "http://localhost:8080"), "Router API base URL")
routerMetrics := flag.String("router_metrics", env("TARGET_ROUTER_METRICS_URL", "http://localhost:9190/metrics"), "Router metrics URL")
openwebuiURL := flag.String("openwebui", env("TARGET_OPENWEBUI_URL", ""), "Open WebUI base URL")
jaegerURL := flag.String("jaeger", env("TARGET_JAEGER_URL", ""), "Jaeger base URL")

flag.Parse()

Expand Down Expand Up @@ -340,13 +341,27 @@ func main() {
log.Printf("Warning: Grafana URL not configured")
}

// Smart /api/ router: route to Router API or Grafana API based on path
// Jaeger API proxy (needs to be set up early for the smart router below)
var jaegerAPIProxy *httputil.ReverseProxy
if *jaegerURL != "" {
// Create proxy for Jaeger API (no prefix stripping for /api/*)
jaegerAPIProxy, _ = newReverseProxy(*jaegerURL, "", false)
}

// Smart /api/ router: route to Router API, Jaeger API, or Grafana API based on path
mux.HandleFunc("/api/", func(w http.ResponseWriter, r *http.Request) {
// If path starts with /api/router/, use Router API proxy
if strings.HasPrefix(r.URL.Path, "/api/router/") && routerAPIProxy != nil {
routerAPIProxy.ServeHTTP(w, r)
return
}
// If path is Jaeger API (services, traces, operations, etc.), use Jaeger proxy
if jaegerAPIProxy != nil && (strings.HasPrefix(r.URL.Path, "/api/services") ||
strings.HasPrefix(r.URL.Path, "/api/traces") ||
strings.HasPrefix(r.URL.Path, "/api/operations")) {
jaegerAPIProxy.ServeHTTP(w, r)
return
}
// Otherwise, if Grafana is configured, proxy to Grafana API
if grafanaStaticProxy != nil {
grafanaStaticProxy.ServeHTTP(w, r)
Expand Down Expand Up @@ -382,6 +397,31 @@ func main() {
log.Printf("Warning: Prometheus URL not configured")
}

// Jaeger proxy (optional) - expose full UI under /embedded/jaeger and its static assets under /static/
if *jaegerURL != "" {
jp, err := newReverseProxy(*jaegerURL, "/embedded/jaeger", false)
if err != nil {
log.Fatalf("jaeger proxy error: %v", err)
}
// Jaeger UI (root UI under /embedded/jaeger)
mux.Handle("/embedded/jaeger", jp)
mux.Handle("/embedded/jaeger/", jp)

// Jaeger static assets are typically served under /static/* from the same origin
// Provide a passthrough proxy without prefix stripping
jStatic, _ := newReverseProxy(*jaegerURL, "", false)
mux.Handle("/static/", jStatic)

log.Printf("Jaeger proxy configured: %s; static assets proxied at /static/", *jaegerURL)
} else {
mux.HandleFunc("/embedded/jaeger/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusServiceUnavailable)
w.Write([]byte(`{"error":"Jaeger not configured","message":"TARGET_JAEGER_URL environment variable is not set"}`))
})
log.Printf("Info: Jaeger URL not configured (optional)")
}

// Open WebUI proxy (optional)
if *openwebuiURL != "" {
op, err := newReverseProxy(*openwebuiURL, "/embedded/openwebui", true)
Expand Down Expand Up @@ -409,6 +449,9 @@ func main() {
if *promURL != "" {
log.Printf("Prometheus: %s → /embedded/prometheus/", *promURL)
}
if *jaegerURL != "" {
log.Printf("Jaeger: %s → /embedded/jaeger/", *jaegerURL)
}
if *openwebuiURL != "" {
log.Printf("OpenWebUI: %s → /embedded/openwebui/", *openwebuiURL)
}
Expand Down
12 changes: 12 additions & 0 deletions dashboard/frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import MonitoringPage from './pages/MonitoringPage'
import ConfigPage from './pages/ConfigPage'
import PlaygroundPage from './pages/PlaygroundPage'
import TopologyPage from './pages/TopologyPage'
import TracingPage from './pages/TracingPage'
import { ConfigSection } from './components/ConfigNav'

const App: React.FC = () => {
Expand Down Expand Up @@ -117,6 +118,17 @@ const App: React.FC = () => {
</Layout>
}
/>
<Route
path="/tracing"
element={
<Layout
configSection={configSection}
onConfigSectionChange={(section) => setConfigSection(section as ConfigSection)}
>
<TracingPage />
</Layout>
}
/>
</Routes>
</BrowserRouter>
)
Expand Down
96 changes: 38 additions & 58 deletions dashboard/frontend/src/components/Layout.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -25,39 +25,11 @@
align-items: center;
gap: 0.5rem;
padding: 0 0.25rem;
flex-direction: column;
justify-content: center;
}

.sidebarCollapsed .brandContainer {
flex-direction: column;
gap: 0.75rem;
}

.collapseButton {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
padding: 6px;
background: transparent;
border: none;
border-radius: var(--radius-md);
cursor: pointer;
color: var(--color-text-secondary);
transition: all var(--transition-fast);
flex-shrink: 0;
}

.collapseButton:hover {
background-color: var(--color-bg-tertiary);
color: var(--color-text);
}

.collapseButton svg {
width: 20px;
height: 20px;
flex-shrink: 0;
}

.brand {
Expand Down Expand Up @@ -228,41 +200,36 @@
padding: 0 0.5rem;
display: flex;
align-items: center;
gap: 0.5rem;
}

.themeToggle {
padding: 0.5rem;
font-size: 1.25rem;
border-radius: var(--radius-md);
transition: background-color var(--transition-fast);
background: transparent;
border: none;
cursor: pointer;
color: var(--color-text);
}

.themeToggle:hover {
background-color: var(--color-bg-tertiary);
justify-content: center;
}

.iconButton {
.collapseButton {
display: flex;
align-items: center;
justify-content: center;
padding: 0.5rem;
width: 32px;
height: 32px;
padding: 6px;
background: transparent;
border: none;
border-radius: var(--radius-md);
transition: background-color var(--transition-fast);
color: var(--color-text-secondary);
text-decoration: none;
cursor: pointer;
color: var(--color-text-secondary);
transition: all var(--transition-fast);
flex-shrink: 0;
}

.iconButton:hover {
.collapseButton:hover {
background-color: var(--color-bg-tertiary);
color: var(--color-text);
}

.collapseButton svg {
width: 20px;
height: 20px;
flex-shrink: 0;
}

.main {
flex: 1;
display: flex;
Expand Down Expand Up @@ -292,22 +259,35 @@
flex: 1;
}

.headerBrand {
font-size: 1.125rem;
font-weight: 600;
color: var(--color-text);
}

.headerRight {
display: flex;
align-items: center;
gap: 1.5rem;
gap: 0.75rem;
}

.headerLink {
.headerIconButton {
display: flex;
align-items: center;
justify-content: center;
padding: 0.5rem;
font-size: 1.25rem;
border-radius: var(--radius-md);
transition: all var(--transition-fast);
background: transparent;
border: none;
cursor: pointer;
color: var(--color-text-secondary);
text-decoration: none;
font-size: 0.9375rem;
font-weight: 500;
transition: color var(--transition-fast);
white-space: nowrap;
}

.headerLink:hover {
.headerIconButton:hover {
background-color: var(--color-bg-tertiary);
color: var(--color-text);
}

Expand Down
Loading
Loading