Skip to content

Commit 26f8719

Browse files
authored
Merge branch 'main' into 1015-yuluo/chore-dev-config
2 parents 6ec731c + 260d165 commit 26f8719

File tree

11 files changed

+381
-127
lines changed

11 files changed

+381
-127
lines changed

config/config.tracing.yaml

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# Local Tracing Configuration (Jaeger + Always-On Sampling)
2+
# This config is used by tools/tracing/docker-compose.tracing.yaml via CONFIG_FILE.
3+
4+
bert_model:
5+
model_id: models/all-MiniLM-L12-v2
6+
threshold: 0.6
7+
use_cpu: true
8+
9+
semantic_cache:
10+
enabled: true
11+
backend_type: "memory"
12+
similarity_threshold: 0.8
13+
max_entries: 1000
14+
ttl_seconds: 3600
15+
eviction_policy: "fifo"
16+
17+
tools:
18+
enabled: true
19+
top_k: 3
20+
similarity_threshold: 0.2
21+
tools_db_path: "config/tools_db.json"
22+
fallback_to_empty: true
23+
24+
prompt_guard:
25+
enabled: true
26+
use_modernbert: true
27+
model_id: "models/jailbreak_classifier_modernbert-base_model"
28+
threshold: 0.7
29+
use_cpu: true
30+
jailbreak_mapping_path: "models/jailbreak_classifier_modernbert-base_model/jailbreak_type_mapping.json"
31+
32+
vllm_endpoints:
33+
- name: "endpoint1"
34+
address: "127.0.0.1"
35+
port: 8000
36+
weight: 1
37+
38+
model_config:
39+
"openai/gpt-oss-20b":
40+
reasoning_family: "gpt-oss"
41+
preferred_endpoints: ["endpoint1"]
42+
pii_policy:
43+
allow_by_default: true
44+
45+
classifier:
46+
category_model:
47+
model_id: "models/category_classifier_modernbert-base_model"
48+
use_modernbert: true
49+
threshold: 0.6
50+
use_cpu: true
51+
category_mapping_path: "models/category_classifier_modernbert-base_model/category_mapping.json"
52+
pii_model:
53+
model_id: "models/pii_classifier_modernbert-base_presidio_token_model"
54+
use_modernbert: true
55+
threshold: 0.7
56+
use_cpu: true
57+
pii_mapping_path: "models/pii_classifier_modernbert-base_presidio_token_model/pii_type_mapping.json"
58+
59+
categories:
60+
- name: math
61+
system_prompt: "You are a mathematics expert. Provide step-by-step solutions."
62+
model_scores:
63+
- model: openai/gpt-oss-20b
64+
score: 1.0
65+
use_reasoning: true
66+
- name: other
67+
system_prompt: "You are a helpful assistant."
68+
model_scores:
69+
- model: openai/gpt-oss-20b
70+
score: 0.7
71+
use_reasoning: false
72+
73+
default_model: openai/gpt-oss-20b
74+
75+
reasoning_families:
76+
gpt-oss:
77+
type: "reasoning_effort"
78+
parameter: "reasoning_effort"
79+
80+
default_reasoning_effort: high
81+
82+
api:
83+
batch_classification:
84+
max_batch_size: 100
85+
concurrency_threshold: 5
86+
max_concurrency: 8
87+
metrics:
88+
enabled: true
89+
90+
observability:
91+
tracing:
92+
enabled: true
93+
provider: "opentelemetry"
94+
exporter:
95+
type: "otlp"
96+
endpoint: "jaeger:4317" # Jaeger gRPC OTLP endpoint inside compose network
97+
insecure: true
98+
sampling:
99+
type: "always_on" # Always sample in local/dev for easy debugging
100+
rate: 1.0
101+
resource:
102+
service_name: "vllm-semantic-router"
103+
service_version: "dev"
104+
deployment_environment: "local"

config/config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,11 +185,11 @@ api:
185185
# Observability Configuration
186186
observability:
187187
tracing:
188-
enabled: false # Enable distributed tracing (default: false)
188+
enabled: true # Enable distributed tracing for docker-compose stack
189189
provider: "opentelemetry" # Provider: opentelemetry, openinference, openllmetry
190190
exporter:
191-
type: "stdout" # Exporter: otlp, jaeger, zipkin, stdout
192-
endpoint: "localhost:4317" # OTLP endpoint (when type: otlp)
191+
type: "otlp" # Export spans to Jaeger (via OTLP gRPC)
192+
endpoint: "jaeger:4317" # Jaeger collector inside compose network
193193
insecure: true # Use insecure connection (no TLS)
194194
sampling:
195195
type: "always_on" # Sampling: always_on, always_off, probabilistic

dashboard/backend/main.go

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ func main() {
271271
routerAPI := flag.String("router_api", env("TARGET_ROUTER_API_URL", "http://localhost:8080"), "Router API base URL")
272272
routerMetrics := flag.String("router_metrics", env("TARGET_ROUTER_METRICS_URL", "http://localhost:9190/metrics"), "Router metrics URL")
273273
openwebuiURL := flag.String("openwebui", env("TARGET_OPENWEBUI_URL", ""), "Open WebUI base URL")
274+
jaegerURL := flag.String("jaeger", env("TARGET_JAEGER_URL", ""), "Jaeger base URL")
274275

275276
flag.Parse()
276277

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

343-
// Smart /api/ router: route to Router API or Grafana API based on path
344+
// Jaeger API proxy (needs to be set up early for the smart router below)
345+
var jaegerAPIProxy *httputil.ReverseProxy
346+
if *jaegerURL != "" {
347+
// Create proxy for Jaeger API (no prefix stripping for /api/*)
348+
jaegerAPIProxy, _ = newReverseProxy(*jaegerURL, "", false)
349+
}
350+
351+
// Smart /api/ router: route to Router API, Jaeger API, or Grafana API based on path
344352
mux.HandleFunc("/api/", func(w http.ResponseWriter, r *http.Request) {
345353
// If path starts with /api/router/, use Router API proxy
346354
if strings.HasPrefix(r.URL.Path, "/api/router/") && routerAPIProxy != nil {
347355
routerAPIProxy.ServeHTTP(w, r)
348356
return
349357
}
358+
// If path is Jaeger API (services, traces, operations, etc.), use Jaeger proxy
359+
if jaegerAPIProxy != nil && (strings.HasPrefix(r.URL.Path, "/api/services") ||
360+
strings.HasPrefix(r.URL.Path, "/api/traces") ||
361+
strings.HasPrefix(r.URL.Path, "/api/operations")) {
362+
jaegerAPIProxy.ServeHTTP(w, r)
363+
return
364+
}
350365
// Otherwise, if Grafana is configured, proxy to Grafana API
351366
if grafanaStaticProxy != nil {
352367
grafanaStaticProxy.ServeHTTP(w, r)
@@ -382,6 +397,31 @@ func main() {
382397
log.Printf("Warning: Prometheus URL not configured")
383398
}
384399

400+
// Jaeger proxy (optional) - expose full UI under /embedded/jaeger and its static assets under /static/
401+
if *jaegerURL != "" {
402+
jp, err := newReverseProxy(*jaegerURL, "/embedded/jaeger", false)
403+
if err != nil {
404+
log.Fatalf("jaeger proxy error: %v", err)
405+
}
406+
// Jaeger UI (root UI under /embedded/jaeger)
407+
mux.Handle("/embedded/jaeger", jp)
408+
mux.Handle("/embedded/jaeger/", jp)
409+
410+
// Jaeger static assets are typically served under /static/* from the same origin
411+
// Provide a passthrough proxy without prefix stripping
412+
jStatic, _ := newReverseProxy(*jaegerURL, "", false)
413+
mux.Handle("/static/", jStatic)
414+
415+
log.Printf("Jaeger proxy configured: %s; static assets proxied at /static/", *jaegerURL)
416+
} else {
417+
mux.HandleFunc("/embedded/jaeger/", func(w http.ResponseWriter, r *http.Request) {
418+
w.Header().Set("Content-Type", "application/json")
419+
w.WriteHeader(http.StatusServiceUnavailable)
420+
w.Write([]byte(`{"error":"Jaeger not configured","message":"TARGET_JAEGER_URL environment variable is not set"}`))
421+
})
422+
log.Printf("Info: Jaeger URL not configured (optional)")
423+
}
424+
385425
// Open WebUI proxy (optional)
386426
if *openwebuiURL != "" {
387427
op, err := newReverseProxy(*openwebuiURL, "/embedded/openwebui", true)
@@ -409,6 +449,9 @@ func main() {
409449
if *promURL != "" {
410450
log.Printf("Prometheus: %s → /embedded/prometheus/", *promURL)
411451
}
452+
if *jaegerURL != "" {
453+
log.Printf("Jaeger: %s → /embedded/jaeger/", *jaegerURL)
454+
}
412455
if *openwebuiURL != "" {
413456
log.Printf("OpenWebUI: %s → /embedded/openwebui/", *openwebuiURL)
414457
}

dashboard/frontend/src/App.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import MonitoringPage from './pages/MonitoringPage'
66
import ConfigPage from './pages/ConfigPage'
77
import PlaygroundPage from './pages/PlaygroundPage'
88
import TopologyPage from './pages/TopologyPage'
9+
import TracingPage from './pages/TracingPage'
910
import { ConfigSection } from './components/ConfigNav'
1011

1112
const App: React.FC = () => {
@@ -117,6 +118,17 @@ const App: React.FC = () => {
117118
</Layout>
118119
}
119120
/>
121+
<Route
122+
path="/tracing"
123+
element={
124+
<Layout
125+
configSection={configSection}
126+
onConfigSectionChange={(section) => setConfigSection(section as ConfigSection)}
127+
>
128+
<TracingPage />
129+
</Layout>
130+
}
131+
/>
120132
</Routes>
121133
</BrowserRouter>
122134
)

dashboard/frontend/src/components/Layout.module.css

Lines changed: 38 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -25,39 +25,11 @@
2525
align-items: center;
2626
gap: 0.5rem;
2727
padding: 0 0.25rem;
28-
flex-direction: column;
28+
justify-content: center;
2929
}
3030

3131
.sidebarCollapsed .brandContainer {
32-
flex-direction: column;
33-
gap: 0.75rem;
34-
}
35-
36-
.collapseButton {
37-
display: flex;
38-
align-items: center;
3932
justify-content: center;
40-
width: 32px;
41-
height: 32px;
42-
padding: 6px;
43-
background: transparent;
44-
border: none;
45-
border-radius: var(--radius-md);
46-
cursor: pointer;
47-
color: var(--color-text-secondary);
48-
transition: all var(--transition-fast);
49-
flex-shrink: 0;
50-
}
51-
52-
.collapseButton:hover {
53-
background-color: var(--color-bg-tertiary);
54-
color: var(--color-text);
55-
}
56-
57-
.collapseButton svg {
58-
width: 20px;
59-
height: 20px;
60-
flex-shrink: 0;
6133
}
6234

6335
.brand {
@@ -228,41 +200,36 @@
228200
padding: 0 0.5rem;
229201
display: flex;
230202
align-items: center;
231-
gap: 0.5rem;
232-
}
233-
234-
.themeToggle {
235-
padding: 0.5rem;
236-
font-size: 1.25rem;
237-
border-radius: var(--radius-md);
238-
transition: background-color var(--transition-fast);
239-
background: transparent;
240-
border: none;
241-
cursor: pointer;
242-
color: var(--color-text);
243-
}
244-
245-
.themeToggle:hover {
246-
background-color: var(--color-bg-tertiary);
203+
justify-content: center;
247204
}
248205

249-
.iconButton {
206+
.collapseButton {
250207
display: flex;
251208
align-items: center;
252209
justify-content: center;
253-
padding: 0.5rem;
210+
width: 32px;
211+
height: 32px;
212+
padding: 6px;
213+
background: transparent;
214+
border: none;
254215
border-radius: var(--radius-md);
255-
transition: background-color var(--transition-fast);
256-
color: var(--color-text-secondary);
257-
text-decoration: none;
258216
cursor: pointer;
217+
color: var(--color-text-secondary);
218+
transition: all var(--transition-fast);
219+
flex-shrink: 0;
259220
}
260221

261-
.iconButton:hover {
222+
.collapseButton:hover {
262223
background-color: var(--color-bg-tertiary);
263224
color: var(--color-text);
264225
}
265226

227+
.collapseButton svg {
228+
width: 20px;
229+
height: 20px;
230+
flex-shrink: 0;
231+
}
232+
266233
.main {
267234
flex: 1;
268235
display: flex;
@@ -292,22 +259,35 @@
292259
flex: 1;
293260
}
294261

262+
.headerBrand {
263+
font-size: 1.125rem;
264+
font-weight: 600;
265+
color: var(--color-text);
266+
}
267+
295268
.headerRight {
296269
display: flex;
297270
align-items: center;
298-
gap: 1.5rem;
271+
gap: 0.75rem;
299272
}
300273

301-
.headerLink {
274+
.headerIconButton {
275+
display: flex;
276+
align-items: center;
277+
justify-content: center;
278+
padding: 0.5rem;
279+
font-size: 1.25rem;
280+
border-radius: var(--radius-md);
281+
transition: all var(--transition-fast);
282+
background: transparent;
283+
border: none;
284+
cursor: pointer;
302285
color: var(--color-text-secondary);
303286
text-decoration: none;
304-
font-size: 0.9375rem;
305-
font-weight: 500;
306-
transition: color var(--transition-fast);
307-
white-space: nowrap;
308287
}
309288

310-
.headerLink:hover {
289+
.headerIconButton:hover {
290+
background-color: var(--color-bg-tertiary);
311291
color: var(--color-text);
312292
}
313293

0 commit comments

Comments
 (0)