fix: log resolved model name instead of group name in Recent Activity
When using model groups (e.g. 'deepseek-auto'), the dashboard logged the group name instead of the concrete resolved model (e.g. 'deepseek-reasoner'). Now: - logRequest passes the resolved modelID (concrete) + modelGroup (group name) - RequestLog struct has a new ModelGroup field (omitempty) - Dashboard displays resolved model (via group) when a group was used Files changed: internal/server/logging.go - add ModelGroup field internal/server/server.go - pass resolved modelID, capture modelGroup static/js/websocket.js - show group annotation in Recent Activity static/js/pages/overview.js - show group annotation in overview table static/js/pages/monitoring.js - show group annotation in stream
This commit is contained in:
@@ -12,6 +12,7 @@ type RequestLog struct {
|
|||||||
ClientID string `json:"client_id"`
|
ClientID string `json:"client_id"`
|
||||||
Provider string `json:"provider"`
|
Provider string `json:"provider"`
|
||||||
Model string `json:"model"`
|
Model string `json:"model"`
|
||||||
|
ModelGroup string `json:"model_group,omitempty"`
|
||||||
PromptTokens uint32 `json:"prompt_tokens"`
|
PromptTokens uint32 `json:"prompt_tokens"`
|
||||||
CompletionTokens uint32 `json:"completion_tokens"`
|
CompletionTokens uint32 `json:"completion_tokens"`
|
||||||
ReasoningTokens uint32 `json:"reasoning_tokens"`
|
ReasoningTokens uint32 `json:"reasoning_tokens"`
|
||||||
|
|||||||
+17
-14
@@ -362,7 +362,7 @@ func (s *Server) handleResponses(c *gin.Context) {
|
|||||||
if stream {
|
if stream {
|
||||||
ch, err := provider.ResponsesStream(c.Request.Context(), &req)
|
ch, err := provider.ResponsesStream(c.Request.Context(), &req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logRequest(startTime, clientID, providerName, req.Model, nil, err, false)
|
s.logRequest(startTime, clientID, providerName, req.Model, "", nil, err, false)
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -377,9 +377,9 @@ func (s *Server) handleResponses(c *gin.Context) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
fmt.Fprintf(w, "data: [DONE]\n\n")
|
fmt.Fprintf(w, "data: [DONE]\n\n")
|
||||||
if lastUsage != nil {
|
if lastUsage != nil {
|
||||||
s.logRequest(startTime, clientID, providerName, req.Model, lastUsage.ToUsage(), nil, false)
|
s.logRequest(startTime, clientID, providerName, req.Model, "", lastUsage.ToUsage(), nil, false)
|
||||||
} else {
|
} else {
|
||||||
s.logRequest(startTime, clientID, providerName, req.Model, nil, nil, false)
|
s.logRequest(startTime, clientID, providerName, req.Model, "", nil, nil, false)
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -399,15 +399,15 @@ func (s *Server) handleResponses(c *gin.Context) {
|
|||||||
|
|
||||||
resp, err := provider.Responses(c.Request.Context(), &req)
|
resp, err := provider.Responses(c.Request.Context(), &req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logRequest(startTime, clientID, providerName, req.Model, nil, err, false)
|
s.logRequest(startTime, clientID, providerName, req.Model, "", nil, err, false)
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.Usage != nil {
|
if resp.Usage != nil {
|
||||||
s.logRequest(startTime, clientID, providerName, req.Model, resp.Usage.ToUsage(), nil, false)
|
s.logRequest(startTime, clientID, providerName, req.Model, "", resp.Usage.ToUsage(), nil, false)
|
||||||
} else {
|
} else {
|
||||||
s.logRequest(startTime, clientID, providerName, req.Model, nil, nil, false)
|
s.logRequest(startTime, clientID, providerName, req.Model, "", nil, nil, false)
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, resp)
|
c.JSON(http.StatusOK, resp)
|
||||||
}
|
}
|
||||||
@@ -528,7 +528,9 @@ func (s *Server) handleChatCompletions(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if model is a group and route to a concrete model
|
// Check if model is a group and route to a concrete model
|
||||||
|
modelGroup := ""
|
||||||
if s.modelRouter != nil && s.modelRouter.IsGroup(modelID) {
|
if s.modelRouter != nil && s.modelRouter.IsGroup(modelID) {
|
||||||
|
modelGroup = modelID
|
||||||
userMessage := extractUserMessage(req.Messages)
|
userMessage := extractUserMessage(req.Messages)
|
||||||
decision, err := s.modelRouter.Route(c.Request.Context(), modelID, userMessage)
|
decision, err := s.modelRouter.Route(c.Request.Context(), modelID, userMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -536,7 +538,7 @@ func (s *Server) handleChatCompletions(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
modelID = decision.SelectedModel
|
modelID = decision.SelectedModel
|
||||||
log.Printf("[ROUTER] %s -> %s (%s: %s)", req.Model, modelID, decision.Strategy, decision.Reason)
|
log.Printf("[ROUTER] %s -> %s (%s: %s)", modelGroup, modelID, decision.Strategy, decision.Reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert ChatCompletionRequest to UnifiedRequest
|
// Convert ChatCompletionRequest to UnifiedRequest
|
||||||
@@ -657,7 +659,7 @@ if unifiedReq.MaxTokens == nil {
|
|||||||
if unifiedReq.Stream {
|
if unifiedReq.Stream {
|
||||||
ch, err := provider.ChatCompletionStream(c.Request.Context(), unifiedReq)
|
ch, err := provider.ChatCompletionStream(c.Request.Context(), unifiedReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logRequest(startTime, clientID, providerName, req.Model, nil, err, unifiedReq.HasImages)
|
s.logRequest(startTime, clientID, providerName, modelID, modelGroup, nil, err, unifiedReq.HasImages)
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -671,7 +673,7 @@ if unifiedReq.MaxTokens == nil {
|
|||||||
chunk, ok := <-ch
|
chunk, ok := <-ch
|
||||||
if !ok {
|
if !ok {
|
||||||
fmt.Fprintf(w, "data: [DONE]\n\n")
|
fmt.Fprintf(w, "data: [DONE]\n\n")
|
||||||
s.logRequest(startTime, clientID, providerName, req.Model, lastUsage, nil, unifiedReq.HasImages)
|
s.logRequest(startTime, clientID, providerName, modelID, modelGroup, lastUsage, nil, unifiedReq.HasImages)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if chunk.Usage != nil {
|
if chunk.Usage != nil {
|
||||||
@@ -689,12 +691,12 @@ if unifiedReq.MaxTokens == nil {
|
|||||||
|
|
||||||
resp, err := provider.ChatCompletion(c.Request.Context(), unifiedReq)
|
resp, err := provider.ChatCompletion(c.Request.Context(), unifiedReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logRequest(startTime, clientID, providerName, req.Model, nil, err, unifiedReq.HasImages)
|
s.logRequest(startTime, clientID, providerName, modelID, modelGroup, nil, err, unifiedReq.HasImages)
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logRequest(startTime, clientID, providerName, req.Model, resp.Usage, nil, unifiedReq.HasImages)
|
s.logRequest(startTime, clientID, providerName, modelID, modelGroup, resp.Usage, nil, unifiedReq.HasImages)
|
||||||
c.JSON(http.StatusOK, resp)
|
c.JSON(http.StatusOK, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -763,7 +765,7 @@ func (s *Server) handleImageGenerations(c *gin.Context) {
|
|||||||
|
|
||||||
resp, err := provider.ImageGeneration(c.Request.Context(), &req)
|
resp, err := provider.ImageGeneration(c.Request.Context(), &req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logRequest(startTime, clientID, providerName, req.Model, nil, err, false)
|
s.logRequest(startTime, clientID, providerName, req.Model, "", nil, err, false)
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -777,7 +779,7 @@ func (s *Server) handleImageGenerations(c *gin.Context) {
|
|||||||
// Calculate per-image cost (not per-token like chat)
|
// Calculate per-image cost (not per-token like chat)
|
||||||
cost := imageGenCost(providerName, req.Model, req.Size, uint32(len(resp.Data)))
|
cost := imageGenCost(providerName, req.Model, req.Size, uint32(len(resp.Data)))
|
||||||
|
|
||||||
s.logRequest(startTime, clientID, providerName, req.Model, &models.Usage{
|
s.logRequest(startTime, clientID, providerName, req.Model, "", &models.Usage{
|
||||||
PromptTokens: promptTokens,
|
PromptTokens: promptTokens,
|
||||||
CompletionTokens: uint32(len(resp.Data)),
|
CompletionTokens: uint32(len(resp.Data)),
|
||||||
TotalTokens: promptTokens + uint32(len(resp.Data)),
|
TotalTokens: promptTokens + uint32(len(resp.Data)),
|
||||||
@@ -819,12 +821,13 @@ func imageGenCost(provider, model string, size *string, n uint32) float64 {
|
|||||||
return perImage * float64(n)
|
return perImage * float64(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) logRequest(start time.Time, clientID, provider, model string, usage *models.Usage, err error, hasImages bool) {
|
func (s *Server) logRequest(start time.Time, clientID, provider, model, modelGroup string, usage *models.Usage, err error, hasImages bool) {
|
||||||
entry := RequestLog{
|
entry := RequestLog{
|
||||||
Timestamp: start,
|
Timestamp: start,
|
||||||
ClientID: clientID,
|
ClientID: clientID,
|
||||||
Provider: provider,
|
Provider: provider,
|
||||||
Model: model,
|
Model: model,
|
||||||
|
ModelGroup: modelGroup,
|
||||||
Status: "success",
|
Status: "success",
|
||||||
DurationMS: time.Since(start).Milliseconds(),
|
DurationMS: time.Since(start).Milliseconds(),
|
||||||
HasImages: hasImages,
|
HasImages: hasImages,
|
||||||
|
|||||||
@@ -392,7 +392,7 @@ class MonitoringPage {
|
|||||||
</div>
|
</div>
|
||||||
<div class="stream-entry-content">
|
<div class="stream-entry-content">
|
||||||
<strong>${request.client_id || 'Unknown'}</strong> →
|
<strong>${request.client_id || 'Unknown'}</strong> →
|
||||||
${request.provider || 'Unknown'} (${request.model || 'Unknown'})
|
${request.provider || 'Unknown'} (${request.model || 'Unknown'}${request.model_group ? ` via ${request.model_group}` : ''})
|
||||||
<div class="stream-entry-details">
|
<div class="stream-entry-details">
|
||||||
${request.total_tokens || request.tokens || 0} tokens • ${request.duration_ms || request.duration || 0}ms
|
${request.total_tokens || request.tokens || 0} tokens • ${request.duration_ms || request.duration || 0}ms
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ class OverviewPage {
|
|||||||
<td>${time}</td>
|
<td>${time}</td>
|
||||||
<td><span class="badge-client">${request.client_id}</span></td>
|
<td><span class="badge-client">${request.client_id}</span></td>
|
||||||
<td>${request.provider}</td>
|
<td>${request.provider}</td>
|
||||||
<td><code class="code-sm">${request.model}</code></td>
|
<td><code class="code-sm">${request.model}${request.model_group ? ` (via ${request.model_group})` : ''}</code></td>
|
||||||
<td>${request.tokens.toLocaleString()}</td>
|
<td>${request.tokens.toLocaleString()}</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="status-badge ${statusClass}">
|
<span class="status-badge ${statusClass}">
|
||||||
@@ -313,7 +313,7 @@ class OverviewPage {
|
|||||||
<td>${time}</td>
|
<td>${time}</td>
|
||||||
<td><span class="badge-client">${request.client_id}</span></td>
|
<td><span class="badge-client">${request.client_id}</span></td>
|
||||||
<td>${request.provider}</td>
|
<td>${request.provider}</td>
|
||||||
<td><code class="code-sm">${request.model}</code></td>
|
<td><code class="code-sm">${request.model}${request.model_group ? ` (via ${request.model_group})` : ''}</code></td>
|
||||||
<td>${(request.total_tokens || request.tokens || 0).toLocaleString()}</td>
|
<td>${(request.total_tokens || request.tokens || 0).toLocaleString()}</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="status-badge ${statusClass}">
|
<span class="status-badge ${statusClass}">
|
||||||
|
|||||||
@@ -309,7 +309,7 @@ class WebSocketManager {
|
|||||||
<td>${time}</td>
|
<td>${time}</td>
|
||||||
<td>${request.client_id || 'Unknown'}</td>
|
<td>${request.client_id || 'Unknown'}</td>
|
||||||
<td>${request.provider || 'Unknown'}</td>
|
<td>${request.provider || 'Unknown'}</td>
|
||||||
<td>${request.model || 'Unknown'}</td>
|
<td>${request.model || 'Unknown'}${request.model_group ? ` (via ${request.model_group})` : ''}</td>
|
||||||
<td>${(request.total_tokens || request.tokens || 0)}</td>
|
<td>${(request.total_tokens || request.tokens || 0)}</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="status-badge ${statusClass}">
|
<span class="status-badge ${statusClass}">
|
||||||
@@ -358,7 +358,7 @@ class WebSocketManager {
|
|||||||
</div>
|
</div>
|
||||||
<div class="stream-entry-content">
|
<div class="stream-entry-content">
|
||||||
<strong>${request.client_id || 'Unknown'}</strong> →
|
<strong>${request.client_id || 'Unknown'}</strong> →
|
||||||
${request.provider || 'Unknown'} (${request.model || 'Unknown'})
|
${request.provider || 'Unknown'} (${request.model || 'Unknown'}${request.model_group ? ` via ${request.model_group}` : ''})
|
||||||
<div class="stream-entry-details">
|
<div class="stream-entry-details">
|
||||||
${(request.total_tokens || request.tokens || 0)} tokens • ${(request.duration_ms || request.duration || 0)}ms
|
${(request.total_tokens || request.tokens || 0)} tokens • ${(request.duration_ms || request.duration || 0)}ms
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user