feat(vllm): refine monitor snapshots and historical request counts (#14109)

This commit is contained in:
ssongliu
2026-10-16 13:36:29 +08:00
committed by GitHub
parent f226fed8cd
commit 690cb48280
19 changed files with 157 additions and 31 deletions
+6
View File
@@ -1,6 +1,7 @@
package dto
import (
"encoding/json"
"time"
"github.com/1Panel-dev/1Panel/agent/app/model"
@@ -144,6 +145,11 @@ type MonitorVLLMCurrent struct {
AppInstallID uint `json:"appInstallID" validate:"required"`
}
type MonitorVLLMCurrentData struct {
model.MonitorVLLM
Metrics map[string]json.RawMessage `json:"metrics,omitempty"`
}
type MonitorVLLMClean struct {
AppInstallID uint `json:"appInstallID" validate:"required"`
}
+2
View File
@@ -86,7 +86,9 @@ type MonitorVLLM struct {
RawMetrics string `json:"-"`
HistogramDeltas string `json:"-"`
IntervalSeconds float64 `json:"intervalSeconds" gorm:"-"`
SampleIntervalSeconds int `json:"-"`
RequestDeltas string `json:"-"`
CompletedRequests *float64 `json:"completedRequests" gorm:"-"`
QueueTime *float64 `json:"queueTime"`
AverageGenerationTokens *float64 `json:"averageGenerationTokens"`
PrefixCacheHitRate *float64 `json:"prefixCacheHitRate"`
+10 -2
View File
@@ -16,7 +16,8 @@ type VLLMMonitorRepo struct{}
type VLLMHistoryPoint struct {
model.MonitorVLLM
Bucket int64
Bucket int64
CompletedRequests *float64
}
func (r *VLLMMonitorRepo) Create(point *model.MonitorVLLM) error {
@@ -77,12 +78,19 @@ func (r *VLLMMonitorRepo) History(ctx context.Context, id uint, start, end time.
}
db = db.Group("bucket").Order("bucket ASC")
} else {
columns = append([]string{"id", "created_at", "app_install_id", "status"}, metrics...)
columns = append([]string{"id", "created_at", "app_install_id", "status", "sample_interval_seconds"}, metrics...)
if group == "" || group == "requests" {
columns = append(columns, "histogram_deltas", "request_deltas")
}
db = db.Order("created_at ASC, id ASC")
}
if group == "" || group == "overview" {
count := "CASE WHEN json_valid(request_deltas) THEN CASE WHEN json_extract(request_deltas, '$.seconds') > 0 THEN ROUND(request_throughput * json_extract(request_deltas, '$.seconds')) END END"
if seconds > 0 {
count = "SUM(" + count + ")"
}
columns = append(columns, count+" AS completed_requests")
}
var points []VLLMHistoryPoint
err := db.Select(strings.Join(columns, ", ")).Scan(&points).Error
return points, err
+1 -1
View File
@@ -55,7 +55,7 @@ type IMonitorService interface {
LoadIOOptions() []string
LoadNetworkOptions() []string
LoadVLLMMonitorData(ctx context.Context, req dto.MonitorVLLMSearch) (dto.MonitorVLLMData, error)
LoadVLLMCurrent(ctx context.Context, req dto.MonitorVLLMCurrent) (model.MonitorVLLM, error)
LoadVLLMCurrent(ctx context.Context, req dto.MonitorVLLMCurrent) (dto.MonitorVLLMCurrentData, error)
CleanVLLMMonitor(req dto.MonitorVLLMClean) error
LoadGPUOptions() dto.MonitorGPUOptions
LoadGPUMonitorData(req dto.MonitorGPUSearch) (dto.MonitorGPUData, error)
+34 -28
View File
@@ -60,6 +60,9 @@ func (m *MonitorService) LoadVLLMMonitorData(ctx context.Context, req dto.Monito
if err != nil {
return data, err
}
for i := range points {
points[i].MonitorVLLM.CompletedRequests = points[i].CompletedRequests
}
if data.BucketSeconds > 0 {
aggregates := map[int64]*vllm.HistoryAccumulator{}
err = vllmMonitorRepo.WalkDeltas(ctx, req.AppInstallID, req.StartTime, req.EndTime, data.BucketSeconds, req.Aggregation, req.Group, func(bucket int64, histograms, requests string) error {
@@ -91,15 +94,6 @@ func (m *MonitorService) LoadVLLMMonitorData(ctx context.Context, req dto.Monito
data.Points = append(data.Points, point)
}
} else {
setting, err := settingRepo.GetValueByKey("VLLMMonitorInterval")
if err != nil {
return data, err
}
intervalSeconds, err := strconv.Atoi(setting)
if err != nil || intervalSeconds <= 0 {
return data, fmt.Errorf("invalid vLLM monitoring interval: %s", setting)
}
interval := time.Duration(intervalSeconds) * time.Second
for i, point := range points {
if err := ctx.Err(); err != nil {
return data, err
@@ -109,8 +103,20 @@ func (m *MonitorService) LoadVLLMMonitorData(ctx context.Context, req dto.Monito
return data, err
}
aggregate.Apply(&point.MonitorVLLM, "max")
if i > 0 && point.CreatedAt.Sub(points[i-1].CreatedAt) > 2*interval {
data.Points = append(data.Points, model.MonitorVLLM{CreatedAt: points[i-1].CreatedAt.Add(interval), AppInstallID: req.AppInstallID})
if i > 0 {
previous := points[i-1]
interval := time.Duration(max(previous.SampleIntervalSeconds, point.SampleIntervalSeconds)) * time.Second
if interval == 0 {
if i > 1 {
interval = previous.CreatedAt.Sub(points[i-2].CreatedAt)
}
if i+1 < len(points) {
interval = max(interval, points[i+1].CreatedAt.Sub(point.CreatedAt))
}
}
if interval > 0 && point.CreatedAt.Sub(previous.CreatedAt) > 2*interval {
data.Points = append(data.Points, model.MonitorVLLM{CreatedAt: previous.CreatedAt.Add(interval), AppInstallID: req.AppInstallID})
}
}
data.Points = append(data.Points, point.MonitorVLLM)
}
@@ -118,31 +124,30 @@ func (m *MonitorService) LoadVLLMMonitorData(ctx context.Context, req dto.Monito
return data, nil
}
func (m *MonitorService) LoadVLLMCurrent(ctx context.Context, req dto.MonitorVLLMCurrent) (model.MonitorVLLM, error) {
func (m *MonitorService) LoadVLLMCurrent(ctx context.Context, req dto.MonitorVLLMCurrent) (dto.MonitorVLLMCurrentData, error) {
install, err := appInstallRepo.GetFirst(repo.WithByID(req.AppInstallID))
if err != nil {
return model.MonitorVLLM{}, err
return dto.MonitorVLLMCurrentData{}, err
}
if install.App.Key != "vllm" {
return model.MonitorVLLM{}, fmt.Errorf("not a vLLM installation")
return dto.MonitorVLLMCurrentData{}, fmt.Errorf("not a vLLM installation")
}
previous, err := collectVLLMMetrics(ctx, install, model.MonitorVLLM{})
point, err := collectVLLMMetrics(ctx, install, model.MonitorVLLM{})
data := dto.MonitorVLLMCurrentData{MonitorVLLM: point}
if err != nil {
global.LOG.Debugf("Collect vLLM metrics on port %d failed: %v", install.HttpPort, err)
return previous, nil
return data, nil
}
timer := time.NewTimer(time.Second)
defer timer.Stop()
select {
case <-ctx.Done():
return model.MonitorVLLM{}, ctx.Err()
case <-timer.C:
if err := json.Unmarshal([]byte(point.RawMetrics), &data.Metrics); err != nil {
return data, err
}
point, err := collectVLLMMetrics(ctx, install, previous)
if err != nil {
global.LOG.Debugf("Collect vLLM metrics on port %d failed: %v", install.HttpPort, err)
for name := range data.Metrics {
if name == "request_prompt_tokens_sum" || name == "request_prompt_tokens_count" ||
(!strings.HasSuffix(name, "_total") && !strings.HasSuffix(name, "_sum") && !strings.HasSuffix(name, "_count")) {
delete(data.Metrics, name)
}
}
return point, nil
return data, nil
}
func (m *MonitorService) CleanVLLMMonitor(req dto.MonitorVLLMClean) error {
@@ -164,7 +169,7 @@ func StartVLLMMonitor(interval string) error {
return fmt.Errorf("invalid vLLM monitoring interval: %s", interval)
}
service := &MonitorService{}
job := cron.NewChain(cron.Recover(cron.DefaultLogger)).Then(cron.FuncJob(service.saveVLLMData))
job := cron.NewChain(cron.Recover(cron.DefaultLogger)).Then(cron.FuncJob(func() { service.saveVLLMData(seconds) }))
id, err := global.Cron.AddFunc(fmt.Sprintf("@every %ds", seconds), func() { go job.Run() })
if err != nil {
return err
@@ -176,7 +181,7 @@ func StartVLLMMonitor(interval string) error {
return nil
}
func (m *MonitorService) saveVLLMData() {
func (m *MonitorService) saveVLLMData(intervalSeconds int) {
if !vllmMonitorMutex.TryLock() {
return
}
@@ -228,6 +233,7 @@ func (m *MonitorService) saveVLLMData() {
return err
}
point, err := collectVLLMMetrics(context.Background(), install, previous)
point.SampleIntervalSeconds = intervalSeconds
if err != nil {
global.LOG.Debugf("Collect vLLM metrics for %s failed: %v", install.Name, err)
}
+1
View File
@@ -114,6 +114,7 @@ func agentDBMigrations() []*gormigrate.Migration {
migrations.AddVLLMMonitor,
migrations.AddMonitorSettings,
migrations.AddVLLMRequestMetrics,
migrations.AddVLLMSampleInterval,
}
}
+7
View File
@@ -1881,3 +1881,10 @@ var AddVLLMRequestMetrics = &gormigrate.Migration{
return global.VLLMMonitorDB.AutoMigrate(&model.MonitorVLLM{})
},
}
var AddVLLMSampleInterval = &gormigrate.Migration{
ID: "20261016-vllm-sample-interval",
Migrate: func(tx *gorm.DB) error {
return global.VLLMMonitorDB.AutoMigrate(&model.MonitorVLLM{})
},
}
+8
View File
@@ -1517,6 +1517,14 @@ const message = {
extraHeaders: 'Extra Headers',
},
vllmMonitor: {
concurrency: 'Current concurrency',
sampleMoment: 'At sampling time',
sampleWindow: 'Sample interval',
requestUnit: 'requests',
processing: 'Processing',
queued: 'Queued',
requestTiming: 'Request timing',
completedRequests: 'Requests completed in interval',
tokensPerSecond: 'tokens/s',
requestsPerSecond: 'Requests/s',
finishReasonLabels: {
+8
View File
@@ -1526,6 +1526,14 @@ const message = {
extraHeaders: 'Encabezados adicionales',
},
vllmMonitor: {
concurrency: 'Concurrencia actual',
sampleMoment: 'Al tomar la muestra',
sampleWindow: 'Intervalo de muestreo',
requestUnit: 'solicitudes',
processing: 'En proceso',
queued: 'En cola',
requestTiming: 'Tiempo de las solicitudes',
completedRequests: 'Solicitudes completadas en el intervalo',
tokensPerSecond: 'tokens/s',
requestsPerSecond: 'Solicitudes/s',
finishReasonLabels: {
+8
View File
@@ -1502,6 +1502,14 @@ const message = {
extraHeaders: 'هدرهای اضافی',
},
vllmMonitor: {
concurrency: 'درخواست‌های هم‌زمان فعلی',
sampleMoment: 'در لحظه نمونه‌برداری',
sampleWindow: 'بازه نمونه‌برداری',
requestUnit: 'درخواست',
processing: 'در حال پردازش',
queued: 'در صف',
requestTiming: 'زمان پردازش درخواست',
completedRequests: 'تعداد درخواست‌های تکمیل‌شده در بازه',
tokensPerSecond: 'tokens/ثانیه',
requestsPerSecond: 'درخواست/ثانیه',
finishReasonLabels: {
+8
View File
@@ -1505,6 +1505,14 @@ const message = {
extraHeaders: '追加ヘッダー',
},
vllmMonitor: {
concurrency: '現在の同時リクエスト',
sampleMoment: 'サンプリング時点',
sampleWindow: '集計区間',
requestUnit: '件',
processing: '処理中',
queued: '待機中',
requestTiming: 'リクエスト処理時間',
completedRequests: '区間内の完了リクエスト数',
tokensPerSecond: 'tokens/秒',
requestsPerSecond: 'リクエスト/秒',
finishReasonLabels: {
+8
View File
@@ -1490,6 +1490,14 @@ const message = {
extraHeaders: '추가 헤더',
},
vllmMonitor: {
concurrency: '현재 동시 요청',
sampleMoment: '샘플링 시점',
sampleWindow: '집계 구간',
requestUnit: '개 요청',
processing: '처리 중',
queued: '대기 중',
requestTiming: '요청 소요 시간',
completedRequests: '구간 내 완료 요청 수',
tokensPerSecond: 'tokens/초',
requestsPerSecond: '요청/초',
finishReasonLabels: {
+8
View File
@@ -1484,6 +1484,14 @@ const message = {
extraHeaders: 'Headers ເພີ່ມເຕີມ',
},
vllmMonitor: {
concurrency: 'ຄຳຮ້ອງຂໍພ້ອມກັນໃນປັດຈຸບັນ',
sampleMoment: 'ໃນເວລາເກັບຕົວຢ່າງ',
sampleWindow: 'ຊ່ວງເວລາເກັບຕົວຢ່າງ',
requestUnit: 'ຄຳຮ້ອງຂໍ',
processing: 'ກຳລັງປະມວນຜົນ',
queued: 'ກຳລັງລໍຖ້າ',
requestTiming: 'ເວລາປະມວນຜົນຄຳຮ້ອງຂໍ',
completedRequests: 'ຈຳນວນຄຳຮ້ອງຂໍທີ່ສຳເລັດໃນຊ່ວງເວລາ',
tokensPerSecond: 'tokens/ວິນາທີ',
requestsPerSecond: 'ຄຳຮ້ອງຂໍ/ວິນາທີ',
finishReasonLabels: {
+8
View File
@@ -1531,6 +1531,14 @@ const message = {
extraHeaders: 'Pengepala Tambahan',
},
vllmMonitor: {
concurrency: 'Permintaan serentak semasa',
sampleMoment: 'Pada masa pensampelan',
sampleWindow: 'Selang pensampelan',
requestUnit: 'permintaan',
processing: 'Sedang diproses',
queued: 'Dalam baris gilir',
requestTiming: 'Tempoh permintaan',
completedRequests: 'Permintaan selesai dalam selang masa',
tokensPerSecond: 'tokens/s',
requestsPerSecond: 'Permintaan/s',
finishReasonLabels: {
+8
View File
@@ -1527,6 +1527,14 @@ const message = {
extraHeaders: 'Cabeçalhos extras',
},
vllmMonitor: {
concurrency: 'Concorrência atual',
sampleMoment: 'No momento da coleta',
sampleWindow: 'Intervalo de coleta',
requestUnit: 'solicitações',
processing: 'Em processamento',
queued: 'Na fila',
requestTiming: 'Tempo das solicitações',
completedRequests: 'Solicitações concluídas no intervalo',
tokensPerSecond: 'tokens/s',
requestsPerSecond: 'Solicitações/s',
finishReasonLabels: {
+8
View File
@@ -1520,6 +1520,14 @@ const message = {
extraHeaders: 'Дополнительные заголовки',
},
vllmMonitor: {
concurrency: 'Текущие параллельные запросы',
sampleMoment: 'На момент замера',
sampleWindow: 'Интервал замера',
requestUnit: 'запросов',
processing: 'В обработке',
queued: 'В очереди',
requestTiming: 'Время обработки запросов',
completedRequests: 'Завершённые запросы за интервал',
tokensPerSecond: 'tokens/с',
requestsPerSecond: 'Запросов/с',
finishReasonLabels: {
+8
View File
@@ -1527,6 +1527,14 @@ const message = {
extraHeaders: 'Ek Başlıklar',
},
vllmMonitor: {
concurrency: 'Mevcut eşzamanlı istekler',
sampleMoment: 'Örnekleme anında',
sampleWindow: 'Örnekleme aralığı',
requestUnit: 'istek',
processing: 'İşleniyor',
queued: 'Kuyrukta',
requestTiming: 'İstek süreleri',
completedRequests: 'Aralıkta tamamlanan istek sayısı',
tokensPerSecond: 'tokens/sn',
requestsPerSecond: 'İstek/sn',
finishReasonLabels: {
+8
View File
@@ -1437,6 +1437,14 @@ const message = {
extraHeaders: '額外請求標頭',
},
vllmMonitor: {
concurrency: '目前並行請求',
sampleMoment: '取樣時刻',
sampleWindow: '統計區間',
requestUnit: '個請求',
processing: '處理中',
queued: '排隊中',
requestTiming: '請求耗時',
completedRequests: '區間完成請求數',
tokensPerSecond: 'tokens/秒',
requestsPerSecond: '請求/秒',
finishReasonLabels: {
+8
View File
@@ -1450,6 +1450,14 @@ const message = {
extraHeaders: '额外请求头',
},
vllmMonitor: {
concurrency: '当前并发',
sampleMoment: '采样时刻',
sampleWindow: '统计区间',
requestUnit: '个请求',
processing: '处理中',
queued: '排队中',
requestTiming: '请求耗时',
completedRequests: '区间完成请求数',
tokensPerSecond: 'tokens/秒',
requestsPerSecond: '请求/秒',
finishReasonLabels: {