channel_monitor_v2_error_taxonomy.go 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. package service
  2. import "strings"
  3. type ChannelMonitorV2ErrorInput struct {
  4. ErrorType string
  5. ErrorOwner string
  6. ErrorSource string
  7. StatusCode int
  8. UpstreamStatusCode int
  9. Message string
  10. }
  11. // ClassifyChannelMonitorV2Error is deliberately ordered. Once rows are stored
  12. // with a taxonomy version, changing this order requires a new version.
  13. func ClassifyChannelMonitorV2Error(input ChannelMonitorV2ErrorInput) string {
  14. errorType := strings.ToLower(strings.TrimSpace(input.ErrorType))
  15. owner := strings.ToLower(strings.TrimSpace(input.ErrorOwner))
  16. text := strings.ToLower(strings.Join([]string{input.ErrorType, input.ErrorSource, input.Message}, " "))
  17. if errorType == "cyber_policy" || channelMonitorV2ContainsAny(text, "content policy", "content_policy", "safety policy", "moderation", "blocked keyword") {
  18. return "content_policy"
  19. }
  20. if input.StatusCode == 401 || input.UpstreamStatusCode == 401 || channelMonitorV2ContainsAny(text, "unauthorized", "invalid api key", "invalid_api_key", "authentication", "api_key_disabled") {
  21. return "authentication"
  22. }
  23. if channelMonitorV2ContainsAny(text, "context window", "context length", "maximum prompt length", "too many tokens", "max_tokens") {
  24. return "context_limit"
  25. }
  26. if channelMonitorV2ContainsAny(text, "failed to deserialize", "missing required parameter", "invalid request", "invalid_request", "tool_choice") {
  27. return "invalid_request"
  28. }
  29. if channelMonitorV2ContainsAny(text, "does not support the requested model", "not supported by any configured account", "model not supported", "unsupported model") {
  30. return "model_unsupported"
  31. }
  32. if channelMonitorV2ContainsAny(text, "group not allowed", "group_not_allowed", "group access") {
  33. return "group_access"
  34. }
  35. if channelMonitorV2ContainsAny(text, "run out of credits", "insufficient balance", "insufficient quota", "subscription", "quota exceeded", "billing hard limit") {
  36. return "quota_or_balance"
  37. }
  38. if channelMonitorV2ContainsAny(text, "no available accounts", "no healthy account", "no healthy upstream account", "failover budget exhausted", "account pool") {
  39. return "account_pool_unavailable"
  40. }
  41. if input.StatusCode == 429 || input.UpstreamStatusCode == 429 || channelMonitorV2ContainsAny(text, "rate limit", "rate_limit", "high demand", "overloaded", "concurrency limit", "capacity") {
  42. return "rate_or_capacity"
  43. }
  44. if input.StatusCode == 408 || input.StatusCode == 504 || channelMonitorV2ContainsAny(text, "timeout", "deadline exceeded", "error code: 524", "gateway time-out", "gateway timeout") {
  45. return "timeout"
  46. }
  47. if channelMonitorV2ContainsAny(text, "transport", "stream_read_error", "connection reset", "connection refused", "tls", "http2", "missing terminal event", "unexpected eof") {
  48. return "transport_or_stream"
  49. }
  50. if input.StatusCode == 403 || input.UpstreamStatusCode == 403 {
  51. return "upstream_forbidden"
  52. }
  53. if input.StatusCode == 404 || input.UpstreamStatusCode == 404 {
  54. return "not_found"
  55. }
  56. if input.StatusCode == 499 || channelMonitorV2ContainsAny(text, "client cancelled", "client canceled", "context canceled") {
  57. return "client_cancelled"
  58. }
  59. if input.UpstreamStatusCode >= 500 || (owner == "provider" && input.StatusCode >= 500) {
  60. return "upstream_5xx"
  61. }
  62. if input.StatusCode >= 500 || errorType == "internal" || owner == "system" {
  63. return "internal"
  64. }
  65. return "other"
  66. }
  67. func channelMonitorV2ContainsAny(value string, needles ...string) bool {
  68. for _, needle := range needles {
  69. if strings.Contains(value, needle) {
  70. return true
  71. }
  72. }
  73. return false
  74. }