refactor(ops): 重构ops核心服务层代码
This commit is contained in:
@@ -22,14 +22,13 @@ type OpsErrorLog struct {
|
|||||||
Platform string `json:"platform"`
|
Platform string `json:"platform"`
|
||||||
Model string `json:"model"`
|
Model string `json:"model"`
|
||||||
|
|
||||||
LatencyMs *int `json:"latency_ms"`
|
|
||||||
|
|
||||||
IsRetryable bool `json:"is_retryable"`
|
IsRetryable bool `json:"is_retryable"`
|
||||||
RetryCount int `json:"retry_count"`
|
RetryCount int `json:"retry_count"`
|
||||||
|
|
||||||
Resolved bool `json:"resolved"`
|
Resolved bool `json:"resolved"`
|
||||||
ResolvedAt *time.Time `json:"resolved_at"`
|
ResolvedAt *time.Time `json:"resolved_at"`
|
||||||
ResolvedByUserID *int64 `json:"resolved_by_user_id"`
|
ResolvedByUserID *int64 `json:"resolved_by_user_id"`
|
||||||
|
ResolvedByUserName string `json:"resolved_by_user_name"`
|
||||||
ResolvedRetryID *int64 `json:"resolved_retry_id"`
|
ResolvedRetryID *int64 `json:"resolved_retry_id"`
|
||||||
ResolvedStatusRaw string `json:"-"`
|
ResolvedStatusRaw string `json:"-"`
|
||||||
|
|
||||||
@@ -37,10 +36,13 @@ type OpsErrorLog struct {
|
|||||||
RequestID string `json:"request_id"`
|
RequestID string `json:"request_id"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
|
|
||||||
UserID *int64 `json:"user_id"`
|
UserID *int64 `json:"user_id"`
|
||||||
APIKeyID *int64 `json:"api_key_id"`
|
UserEmail string `json:"user_email"`
|
||||||
AccountID *int64 `json:"account_id"`
|
APIKeyID *int64 `json:"api_key_id"`
|
||||||
GroupID *int64 `json:"group_id"`
|
AccountID *int64 `json:"account_id"`
|
||||||
|
AccountName string `json:"account_name"`
|
||||||
|
GroupID *int64 `json:"group_id"`
|
||||||
|
GroupName string `json:"group_name"`
|
||||||
|
|
||||||
ClientIP *string `json:"client_ip"`
|
ClientIP *string `json:"client_ip"`
|
||||||
RequestPath string `json:"request_path"`
|
RequestPath string `json:"request_path"`
|
||||||
@@ -110,6 +112,7 @@ type OpsRetryAttempt struct {
|
|||||||
SourceErrorID int64 `json:"source_error_id"`
|
SourceErrorID int64 `json:"source_error_id"`
|
||||||
Mode string `json:"mode"`
|
Mode string `json:"mode"`
|
||||||
PinnedAccountID *int64 `json:"pinned_account_id"`
|
PinnedAccountID *int64 `json:"pinned_account_id"`
|
||||||
|
PinnedAccountName string `json:"pinned_account_name"`
|
||||||
|
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
StartedAt *time.Time `json:"started_at"`
|
StartedAt *time.Time `json:"started_at"`
|
||||||
@@ -121,6 +124,7 @@ type OpsRetryAttempt struct {
|
|||||||
HTTPStatusCode *int `json:"http_status_code"`
|
HTTPStatusCode *int `json:"http_status_code"`
|
||||||
UpstreamRequestID *string `json:"upstream_request_id"`
|
UpstreamRequestID *string `json:"upstream_request_id"`
|
||||||
UsedAccountID *int64 `json:"used_account_id"`
|
UsedAccountID *int64 `json:"used_account_id"`
|
||||||
|
UsedAccountName string `json:"used_account_name"`
|
||||||
ResponsePreview *string `json:"response_preview"`
|
ResponsePreview *string `json:"response_preview"`
|
||||||
ResponseTruncated *bool `json:"response_truncated"`
|
ResponseTruncated *bool `json:"response_truncated"`
|
||||||
|
|
||||||
|
|||||||
@@ -98,7 +98,6 @@ type OpsInsertErrorLogInput struct {
|
|||||||
// It is set by OpsService.RecordError before persisting.
|
// It is set by OpsService.RecordError before persisting.
|
||||||
UpstreamErrorsJSON *string
|
UpstreamErrorsJSON *string
|
||||||
|
|
||||||
DurationMs *int
|
|
||||||
TimeToFirstTokenMs *int64
|
TimeToFirstTokenMs *int64
|
||||||
|
|
||||||
RequestBodyJSON *string // sanitized json string (not raw bytes)
|
RequestBodyJSON *string // sanitized json string (not raw bytes)
|
||||||
|
|||||||
@@ -236,7 +236,68 @@ func (s *OpsService) GetErrorLogs(ctx context.Context, filter *OpsErrorLogFilter
|
|||||||
if s.opsRepo == nil {
|
if s.opsRepo == nil {
|
||||||
return &OpsErrorLogList{Errors: []*OpsErrorLog{}, Total: 0, Page: 1, PageSize: 20}, nil
|
return &OpsErrorLogList{Errors: []*OpsErrorLog{}, Total: 0, Page: 1, PageSize: 20}, nil
|
||||||
}
|
}
|
||||||
return s.opsRepo.ListErrorLogs(ctx, filter)
|
result, err := s.opsRepo.ListErrorLogs(ctx, filter)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[Ops] GetErrorLogs failed: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply error filtering based on settings (for historical data)
|
||||||
|
result = s.filterErrorLogsBySettings(ctx, result)
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// filterErrorLogsBySettings filters error logs based on advanced settings.
|
||||||
|
// This ensures that historical errors are also filtered when viewing the dashboard.
|
||||||
|
func (s *OpsService) filterErrorLogsBySettings(ctx context.Context, result *OpsErrorLogList) *OpsErrorLogList {
|
||||||
|
if result == nil || len(result.Errors) == 0 {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
settings, err := s.GetOpsAdvancedSettings(ctx)
|
||||||
|
if err != nil || settings == nil {
|
||||||
|
// If we can't get settings, return unfiltered (fail open)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
filtered := make([]*OpsErrorLog, 0, len(result.Errors))
|
||||||
|
for _, errLog := range result.Errors {
|
||||||
|
if shouldFilterErrorLog(settings, errLog) {
|
||||||
|
continue // Skip this error
|
||||||
|
}
|
||||||
|
filtered = append(filtered, errLog)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update total count to reflect filtered results
|
||||||
|
result.Errors = filtered
|
||||||
|
result.Total = len(filtered)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// shouldFilterErrorLog determines if an error log should be filtered based on settings.
|
||||||
|
func shouldFilterErrorLog(settings *OpsAdvancedSettings, errLog *OpsErrorLog) bool {
|
||||||
|
if settings == nil || errLog == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
msgLower := strings.ToLower(errLog.Message)
|
||||||
|
|
||||||
|
// Check if count_tokens errors should be ignored
|
||||||
|
if settings.IgnoreCountTokensErrors && strings.Contains(errLog.RequestPath, "/count_tokens") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if context canceled errors should be ignored
|
||||||
|
if settings.IgnoreContextCanceled && strings.Contains(msgLower, "context canceled") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if "no available accounts" errors should be ignored
|
||||||
|
if settings.IgnoreNoAvailableAccounts && strings.Contains(msgLower, "no available accounts") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *OpsService) GetErrorLogByID(ctx context.Context, id int64) (*OpsErrorLogDetail, error) {
|
func (s *OpsService) GetErrorLogByID(ctx context.Context, id int64) (*OpsErrorLogDetail, error) {
|
||||||
|
|||||||
@@ -368,9 +368,11 @@ func defaultOpsAdvancedSettings() *OpsAdvancedSettings {
|
|||||||
Aggregation: OpsAggregationSettings{
|
Aggregation: OpsAggregationSettings{
|
||||||
AggregationEnabled: false,
|
AggregationEnabled: false,
|
||||||
},
|
},
|
||||||
IgnoreCountTokensErrors: false,
|
IgnoreCountTokensErrors: false,
|
||||||
AutoRefreshEnabled: false,
|
IgnoreContextCanceled: true, // Default to true - client disconnects are not errors
|
||||||
AutoRefreshIntervalSec: 30,
|
IgnoreNoAvailableAccounts: false, // Default to false - this is a real routing issue
|
||||||
|
AutoRefreshEnabled: false,
|
||||||
|
AutoRefreshIntervalSec: 30,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,6 +81,8 @@ type OpsAdvancedSettings struct {
|
|||||||
DataRetention OpsDataRetentionSettings `json:"data_retention"`
|
DataRetention OpsDataRetentionSettings `json:"data_retention"`
|
||||||
Aggregation OpsAggregationSettings `json:"aggregation"`
|
Aggregation OpsAggregationSettings `json:"aggregation"`
|
||||||
IgnoreCountTokensErrors bool `json:"ignore_count_tokens_errors"`
|
IgnoreCountTokensErrors bool `json:"ignore_count_tokens_errors"`
|
||||||
|
IgnoreContextCanceled bool `json:"ignore_context_canceled"`
|
||||||
|
IgnoreNoAvailableAccounts bool `json:"ignore_no_available_accounts"`
|
||||||
AutoRefreshEnabled bool `json:"auto_refresh_enabled"`
|
AutoRefreshEnabled bool `json:"auto_refresh_enabled"`
|
||||||
AutoRefreshIntervalSec int `json:"auto_refresh_interval_seconds"`
|
AutoRefreshIntervalSec int `json:"auto_refresh_interval_seconds"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,8 +38,9 @@ type OpsUpstreamErrorEvent struct {
|
|||||||
AtUnixMs int64 `json:"at_unix_ms,omitempty"`
|
AtUnixMs int64 `json:"at_unix_ms,omitempty"`
|
||||||
|
|
||||||
// Context
|
// Context
|
||||||
Platform string `json:"platform,omitempty"`
|
Platform string `json:"platform,omitempty"`
|
||||||
AccountID int64 `json:"account_id,omitempty"`
|
AccountID int64 `json:"account_id,omitempty"`
|
||||||
|
AccountName string `json:"account_name,omitempty"`
|
||||||
|
|
||||||
// Outcome
|
// Outcome
|
||||||
UpstreamStatusCode int `json:"upstream_status_code,omitempty"`
|
UpstreamStatusCode int `json:"upstream_status_code,omitempty"`
|
||||||
|
|||||||
Reference in New Issue
Block a user