feat: record task attempt chains
This commit is contained in:
@@ -40,7 +40,8 @@ func buildSuccessRecord(task store.GatewayTask, user *auth.User, body map[string
|
||||
}
|
||||
|
||||
func taskMetrics(task store.GatewayTask, user *auth.User, body map[string]any, candidate store.RuntimeModelCandidate, response clients.Response, simulated bool) map[string]any {
|
||||
metrics := map[string]any{
|
||||
metrics := attemptMetrics(candidate, 0, simulated)
|
||||
for key, value := range map[string]any{
|
||||
"kind": task.Kind,
|
||||
"runMode": task.RunMode,
|
||||
"requestedModel": task.Model,
|
||||
@@ -57,6 +58,8 @@ func taskMetrics(task store.GatewayTask, user *auth.User, body map[string]any, c
|
||||
"queueKey": candidate.QueueKey,
|
||||
"requestId": response.RequestID,
|
||||
"simulated": simulated,
|
||||
} {
|
||||
metrics[key] = value
|
||||
}
|
||||
if user != nil {
|
||||
metrics["apiKeyId"] = user.APIKeyID
|
||||
@@ -94,6 +97,29 @@ func taskMetrics(task store.GatewayTask, user *auth.User, body map[string]any, c
|
||||
return metrics
|
||||
}
|
||||
|
||||
func attemptMetrics(candidate store.RuntimeModelCandidate, attemptNo int, simulated bool) map[string]any {
|
||||
metrics := map[string]any{
|
||||
"resolvedModel": candidate.ModelName,
|
||||
"modelName": candidate.ModelName,
|
||||
"modelAlias": candidate.ModelAlias,
|
||||
"providerModel": candidate.ProviderModelName,
|
||||
"canonicalModel": candidate.CanonicalModelKey,
|
||||
"modelType": candidate.ModelType,
|
||||
"provider": candidate.Provider,
|
||||
"platformId": candidate.PlatformID,
|
||||
"platformKey": candidate.PlatformKey,
|
||||
"platformName": candidate.PlatformName,
|
||||
"platformModelId": candidate.PlatformModelID,
|
||||
"clientId": candidate.ClientID,
|
||||
"queueKey": candidate.QueueKey,
|
||||
"simulated": simulated,
|
||||
}
|
||||
if attemptNo > 0 {
|
||||
metrics["attempt"] = attemptNo
|
||||
}
|
||||
return metrics
|
||||
}
|
||||
|
||||
func usageToMap(usage clients.Usage) map[string]any {
|
||||
out := map[string]any{}
|
||||
if usage.InputTokens > 0 {
|
||||
@@ -172,6 +198,46 @@ func failureMetrics(err error, simulated bool) (string, map[string]any, time.Tim
|
||||
return meta.RequestID, metrics, meta.ResponseStartedAt, meta.ResponseFinishedAt, meta.ResponseDurationMS
|
||||
}
|
||||
|
||||
func mergeMetrics(values ...map[string]any) map[string]any {
|
||||
out := map[string]any{}
|
||||
for _, value := range values {
|
||||
for key, item := range value {
|
||||
out[key] = item
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func summarizeAttempts(attempts []store.TaskAttempt) []map[string]any {
|
||||
items := make([]map[string]any, 0, len(attempts))
|
||||
for _, attempt := range attempts {
|
||||
item := map[string]any{
|
||||
"attempt": attempt.AttemptNo,
|
||||
"status": attempt.Status,
|
||||
"platformId": attempt.PlatformID,
|
||||
"platformName": attempt.PlatformName,
|
||||
"provider": attempt.Provider,
|
||||
"platformModelId": attempt.PlatformModelID,
|
||||
"modelName": attempt.ModelName,
|
||||
"providerModelName": attempt.ProviderModelName,
|
||||
"modelAlias": attempt.ModelAlias,
|
||||
"modelType": attempt.ModelType,
|
||||
"clientId": attempt.ClientID,
|
||||
"queueKey": attempt.QueueKey,
|
||||
"requestId": attempt.RequestID,
|
||||
"retryable": attempt.Retryable,
|
||||
"simulated": attempt.Simulated,
|
||||
"errorCode": attempt.ErrorCode,
|
||||
"errorMessage": attempt.ErrorMessage,
|
||||
"responseDurationMs": attempt.ResponseDurationMS,
|
||||
"startedAt": attempt.StartedAt,
|
||||
"finishedAt": attempt.FinishedAt,
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
func messageCount(body map[string]any) int {
|
||||
messages, _ := body["messages"].([]any)
|
||||
return len(messages)
|
||||
|
||||
@@ -85,6 +85,7 @@ func (s *Service) execute(ctx context.Context, task store.GatewayTask, user *aut
|
||||
if err == nil {
|
||||
billings := s.billings(ctx, user, task.Kind, body, candidate, response, isSimulation(task, candidate))
|
||||
record := buildSuccessRecord(task, user, body, candidate, response, billings, isSimulation(task, candidate))
|
||||
record.Metrics = s.withAttemptHistory(ctx, task.ID, record.Metrics)
|
||||
finished, finishErr := s.store.FinishTaskSuccess(ctx, store.FinishTaskSuccessInput{
|
||||
TaskID: task.ID,
|
||||
Result: response.Result,
|
||||
@@ -161,6 +162,7 @@ func (s *Service) runCandidate(ctx context.Context, task store.GatewayTask, user
|
||||
Status: "running",
|
||||
Simulated: simulated,
|
||||
RequestSnapshot: body,
|
||||
Metrics: attemptMetrics(candidate, attemptNo, simulated),
|
||||
})
|
||||
if err != nil {
|
||||
return clients.Response{}, err
|
||||
@@ -172,7 +174,7 @@ func (s *Service) runCandidate(ctx context.Context, task store.GatewayTask, user
|
||||
AttemptID: attemptID,
|
||||
Status: "failed",
|
||||
Retryable: false,
|
||||
Metrics: map[string]any{"error": err.Error(), "candidateModel": candidate.ModelName, "clientId": candidate.ClientID},
|
||||
Metrics: mergeMetrics(attemptMetrics(candidate, attemptNo, simulated), map[string]any{"error": err.Error(), "retryable": false}),
|
||||
ErrorCode: "rate_limit",
|
||||
ErrorMessage: err.Error(),
|
||||
})
|
||||
@@ -224,6 +226,7 @@ func (s *Service) runCandidate(ctx context.Context, task store.GatewayTask, user
|
||||
responseDurationMS = 0
|
||||
}
|
||||
}
|
||||
metrics = mergeMetrics(attemptMetrics(candidate, attemptNo, simulated), metrics)
|
||||
_ = s.store.FinishTaskAttempt(ctx, store.FinishTaskAttemptInput{
|
||||
AttemptID: attemptID,
|
||||
Status: "failed",
|
||||
@@ -242,9 +245,10 @@ func (s *Service) runCandidate(ctx context.Context, task store.GatewayTask, user
|
||||
}
|
||||
uploadedResult, err := s.uploadGeneratedAssets(ctx, response.Result)
|
||||
if err != nil {
|
||||
metrics := taskMetrics(task, user, body, candidate, response, simulated)
|
||||
metrics["error"] = err.Error()
|
||||
metrics["retryable"] = clients.IsRetryable(err)
|
||||
metrics := mergeMetrics(taskMetrics(task, user, body, candidate, response, simulated), map[string]any{
|
||||
"error": err.Error(),
|
||||
"retryable": clients.IsRetryable(err),
|
||||
})
|
||||
_ = s.store.FinishTaskAttempt(ctx, store.FinishTaskAttemptInput{
|
||||
AttemptID: attemptID,
|
||||
Status: "failed",
|
||||
@@ -299,6 +303,7 @@ func (s *Service) clientFor(candidate store.RuntimeModelCandidate, simulated boo
|
||||
|
||||
func (s *Service) failTask(ctx context.Context, taskID string, code string, message string, simulated bool, cause error) (store.GatewayTask, error) {
|
||||
requestID, metrics, responseStartedAt, responseFinishedAt, responseDurationMS := failureMetrics(cause, simulated)
|
||||
metrics = s.withAttemptHistory(ctx, taskID, metrics)
|
||||
failed, err := s.store.FinishTaskFailure(ctx, store.FinishTaskFailureInput{
|
||||
TaskID: taskID,
|
||||
Code: code,
|
||||
@@ -318,6 +323,21 @@ func (s *Service) failTask(ctx context.Context, taskID string, code string, mess
|
||||
return failed, nil
|
||||
}
|
||||
|
||||
func (s *Service) withAttemptHistory(ctx context.Context, taskID string, metrics map[string]any) map[string]any {
|
||||
attempts, err := s.store.ListTaskAttempts(ctx, taskID)
|
||||
if err != nil {
|
||||
s.logger.Warn("list task attempts for metrics failed", "taskID", taskID, "error", err)
|
||||
return metrics
|
||||
}
|
||||
if len(attempts) == 0 {
|
||||
return metrics
|
||||
}
|
||||
metrics = mergeMetrics(metrics)
|
||||
metrics["attemptCount"] = len(attempts)
|
||||
metrics["attempts"] = summarizeAttempts(attempts)
|
||||
return metrics
|
||||
}
|
||||
|
||||
func (s *Service) emit(ctx context.Context, taskID string, eventType string, status string, phase string, progress float64, message string, payload map[string]any, simulated bool) error {
|
||||
event, err := s.store.AddTaskEvent(ctx, taskID, eventType, status, phase, progress, message, payload, simulated)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user