anthropic_session.go 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. package service
  2. import (
  3. "encoding/json"
  4. "strings"
  5. "time"
  6. "github.com/tidwall/gjson"
  7. )
  8. // Anthropic 会话 Fallback 相关常量
  9. const (
  10. // anthropicSessionTTLSeconds Anthropic 会话缓存 TTL(5 分钟)
  11. anthropicSessionTTLSeconds = 300
  12. // anthropicDigestSessionKeyPrefix Anthropic 摘要 fallback 会话 key 前缀
  13. anthropicDigestSessionKeyPrefix = "anthropic:digest:"
  14. )
  15. // AnthropicSessionTTL 返回 Anthropic 会话缓存 TTL
  16. func AnthropicSessionTTL() time.Duration {
  17. return anthropicSessionTTLSeconds * time.Second
  18. }
  19. // BuildAnthropicDigestChain 根据 Anthropic 请求生成摘要链
  20. // 格式: s:<hash>-u:<hash>-a:<hash>-u:<hash>-...
  21. // s = system, u = user, a = assistant
  22. func BuildAnthropicDigestChain(parsed *ParsedRequest) string {
  23. if parsed == nil {
  24. return ""
  25. }
  26. var parts []string
  27. if systemRaw := parsed.SystemRaw(); len(systemRaw) > 0 && string(systemRaw) != "null" {
  28. parts = append(parts, "s:"+shortHash(canonicalAnthropicDigestJSON(systemRaw)))
  29. }
  30. messages := parsed.MessagesRaw()
  31. if len(messages) > 0 {
  32. gjson.ParseBytes(messages).ForEach(func(_, msg gjson.Result) bool {
  33. prefix := rolePrefix(msg.Get("role").String())
  34. content := msg.Get("content")
  35. parts = append(parts, prefix+":"+shortHash(canonicalAnthropicDigestJSON([]byte(content.Raw))))
  36. return true
  37. })
  38. }
  39. return strings.Join(parts, "-")
  40. }
  41. // canonicalAnthropicDigestJSON 保持 digest 对 JSON key 顺序和空白不敏感。
  42. func canonicalAnthropicDigestJSON(raw []byte) []byte {
  43. if len(raw) == 0 {
  44. return raw
  45. }
  46. var value any
  47. if err := json.Unmarshal(raw, &value); err != nil {
  48. return raw
  49. }
  50. canonical, err := json.Marshal(value)
  51. if err != nil {
  52. return raw
  53. }
  54. return canonical
  55. }
  56. // rolePrefix 将 Anthropic 的 role 映射为单字符前缀
  57. func rolePrefix(role string) string {
  58. switch role {
  59. case "assistant":
  60. return "a"
  61. default:
  62. return "u"
  63. }
  64. }
  65. // GenerateAnthropicDigestSessionKey 生成 Anthropic 摘要 fallback 的 sessionKey
  66. // 组合 prefixHash 前 8 位 + uuid 前 8 位,确保不同会话产生不同的 sessionKey
  67. func GenerateAnthropicDigestSessionKey(prefixHash, uuid string) string {
  68. prefix := prefixHash
  69. if len(prefixHash) >= 8 {
  70. prefix = prefixHash[:8]
  71. }
  72. uuidPart := uuid
  73. if len(uuid) >= 8 {
  74. uuidPart = uuid[:8]
  75. }
  76. return anthropicDigestSessionKeyPrefix + prefix + ":" + uuidPart
  77. }