| |
| package app |
|
|
| import ( |
| "sort" |
| "sync" |
| "sync/atomic" |
| "time" |
|
|
| "ccLoad/internal/model" |
| "ccLoad/internal/util" |
| ) |
|
|
| |
| type ActiveRequest struct { |
| ID int64 `json:"id"` |
| Model string `json:"model"` |
| ClientIP string `json:"client_ip"` |
| StartTime int64 `json:"start_time"` |
| Streaming bool `json:"is_streaming"` |
| ChannelID int64 `json:"channel_id,omitempty"` |
| ChannelName string `json:"channel_name,omitempty"` |
| ChannelType string `json:"channel_type,omitempty"` |
| APIKeyUsed string `json:"api_key_used,omitempty"` |
| TokenID int64 `json:"token_id,omitempty"` |
| BaseURL string `json:"base_url,omitempty"` |
| BytesReceived int64 `json:"bytes_received,omitempty"` |
| ClientFirstByteTime float64 `json:"client_first_byte_time,omitempty"` |
| CostMultiplier float64 `json:"cost_multiplier"` |
| DebugLogAvailable bool `json:"debug_log_available,omitempty"` |
| } |
|
|
| type activeRequest struct { |
| ID int64 |
| Model string |
| ClientIP string |
| StartTime int64 |
| Streaming bool |
| ChannelID int64 |
| ChannelName string |
| ChannelType string |
| APIKeyUsed string |
| TokenID int64 |
| BaseURL string |
|
|
| CostMultiplier float64 |
| debugCapture *debugCapture |
|
|
| bytesCounter atomic.Int64 |
| clientFirstByteTimeUsec atomic.Int64 |
| } |
|
|
| |
| type activeRequestManager struct { |
| mu sync.RWMutex |
| requests map[int64]*activeRequest |
| nextID atomic.Int64 |
| } |
|
|
| func newActiveRequestManager() *activeRequestManager { |
| return &activeRequestManager{ |
| requests: make(map[int64]*activeRequest), |
| } |
| } |
|
|
| |
| func (m *activeRequestManager) Register(startTime time.Time, model, clientIP string, streaming bool) int64 { |
| id := m.nextID.Add(1) |
| req := &activeRequest{ |
| ID: id, |
| Model: model, |
| ClientIP: clientIP, |
| StartTime: startTime.UnixMilli(), |
| Streaming: streaming, |
| } |
| m.mu.Lock() |
| m.requests[id] = req |
| m.mu.Unlock() |
| return id |
| } |
|
|
| |
| |
| func (m *activeRequestManager) Update(id int64, channelID int64, channelName, channelType, apiKey string, tokenID int64, costMultiplier float64) { |
| m.mu.Lock() |
| if req, ok := m.requests[id]; ok { |
| req.ChannelID = channelID |
| req.ChannelName = channelName |
| req.ChannelType = channelType |
| req.APIKeyUsed = util.MaskAPIKey(apiKey) |
| req.TokenID = tokenID |
| req.CostMultiplier = costMultiplier |
| req.StartTime = time.Now().UnixMilli() |
| req.clientFirstByteTimeUsec.Store(0) |
| req.bytesCounter.Store(0) |
| } |
| m.mu.Unlock() |
| } |
|
|
| |
| func (m *activeRequestManager) SetBaseURL(id int64, baseURL string) { |
| m.mu.Lock() |
| if req, ok := m.requests[id]; ok { |
| req.BaseURL = baseURL |
| } |
| m.mu.Unlock() |
| } |
|
|
| |
| |
| func (m *activeRequestManager) SetDebugCapture(id int64, dc *debugCapture) { |
| m.mu.Lock() |
| if req, ok := m.requests[id]; ok { |
| req.debugCapture = dc |
| } |
| m.mu.Unlock() |
| } |
|
|
| |
| func (m *activeRequestManager) GetDebugLogSnapshot(id int64) (*model.DebugLogEntry, bool) { |
| m.mu.RLock() |
| req := m.requests[id] |
| var dc *debugCapture |
| if req != nil { |
| dc = req.debugCapture |
| } |
| m.mu.RUnlock() |
|
|
| if dc == nil { |
| return nil, false |
| } |
| return dc.buildEntry(nil), true |
| } |
|
|
| |
| func (m *activeRequestManager) Remove(id int64) { |
| m.mu.Lock() |
| delete(m.requests, id) |
| m.mu.Unlock() |
| } |
|
|
| |
| func (m *activeRequestManager) AddBytes(id int64, n int64) { |
| if n <= 0 { |
| return |
| } |
| m.mu.RLock() |
| req := m.requests[id] |
| m.mu.RUnlock() |
| if req != nil { |
| req.bytesCounter.Add(n) |
| } |
| } |
|
|
| |
| func (m *activeRequestManager) SetClientFirstByteTime(id int64, d time.Duration) { |
| if d <= 0 { |
| return |
| } |
| m.mu.RLock() |
| req := m.requests[id] |
| m.mu.RUnlock() |
| if req == nil { |
| return |
| } |
| usec := d.Microseconds() |
| if usec <= 0 { |
| return |
| } |
| req.clientFirstByteTimeUsec.CompareAndSwap(0, usec) |
| } |
|
|
| |
| func (m *activeRequestManager) List() []*ActiveRequest { |
| m.mu.RLock() |
| result := make([]*ActiveRequest, 0, len(m.requests)) |
| for _, req := range m.requests { |
| view := &ActiveRequest{ |
| ID: req.ID, |
| Model: req.Model, |
| ClientIP: req.ClientIP, |
| StartTime: req.StartTime, |
| Streaming: req.Streaming, |
| ChannelID: req.ChannelID, |
| ChannelName: req.ChannelName, |
| ChannelType: req.ChannelType, |
| APIKeyUsed: req.APIKeyUsed, |
| TokenID: req.TokenID, |
| BaseURL: req.BaseURL, |
| BytesReceived: req.bytesCounter.Load(), |
| CostMultiplier: req.CostMultiplier, |
| DebugLogAvailable: req.debugCapture != nil, |
| } |
| if usec := req.clientFirstByteTimeUsec.Load(); usec > 0 { |
| view.ClientFirstByteTime = float64(usec) / 1e6 |
| } |
| result = append(result, view) |
| } |
| m.mu.RUnlock() |
| |
| sort.Slice(result, func(i, j int) bool { |
| if result[i].StartTime != result[j].StartTime { |
| return result[i].StartTime > result[j].StartTime |
| } |
| return result[i].ID > result[j].ID |
| }) |
| return result |
| } |
|
|