| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337 |
- package service
- import (
- "bytes"
- "encoding/json"
- "strings"
- "github.com/tidwall/gjson"
- )
- // contentSessionSeedPrefix prevents collisions between content-derived seeds
- // and explicit session IDs (e.g. "sess-xxx" or "compat_cc_xxx").
- const contentSessionSeedPrefix = "compat_cs_"
- // contentStablePrefixSessionSeedPrefix distinguishes cache identities derived
- // only from request fields that remain stable across independent prompts.
- const contentStablePrefixSessionSeedPrefix = "compat_csp_"
- // deriveOpenAIContentSessionSeed builds a stable session seed from an
- // OpenAI-format request body. Only fields constant across conversation turns
- // are included: model, tools/functions definitions, the leading system/developer
- // prompt prefix in Chat messages, instructions (Responses API), and the first
- // user message.
- // Supports both Chat Completions (messages) and Responses API (input).
- func deriveOpenAIContentSessionSeed(body []byte) string {
- if len(body) == 0 {
- return ""
- }
- const (
- modelField = iota
- toolsField
- functionsField
- instructionsField
- messagesField
- inputField
- contentSessionSeedFieldCount
- allContentSessionSeedFields = 1<<contentSessionSeedFieldCount - 1
- )
- var fields [contentSessionSeedFieldCount]gjson.Result
- var seen uint8
- // Match gjson.GetBytes by starting at the first root container, even when
- // malformed input has a non-JSON prefix.
- root := body
- for i := 0; i < len(body); i++ {
- switch body[i] {
- case '{':
- root = body[i:]
- goto scanRoot
- case '[':
- return ""
- }
- }
- return ""
- scanRoot:
- nextKeyOffset := 1
- parseRawJSONView(root).ForEach(func(key, value gjson.Result) bool {
- if key.Index < nextKeyOffset || key.Index > len(root) {
- return false
- }
- // Result.ForEach can continue after the root '}' on malformed input.
- // The separator range excludes braces inside the preceding parsed value.
- if bytes.IndexByte(root[nextKeyOffset:key.Index], '}') >= 0 {
- return false
- }
- nextKeyOffset = value.Index + len(value.Raw)
- field := -1
- switch key.Str {
- case "model":
- field = modelField
- case "tools":
- field = toolsField
- case "functions":
- field = functionsField
- case "instructions":
- field = instructionsField
- case "messages":
- field = messagesField
- case "input":
- field = inputField
- }
- if field < 0 {
- return true
- }
- mask := uint8(1 << field)
- if seen&mask == 0 {
- fields[field] = value
- seen |= mask
- }
- return seen != allContentSessionSeedFields
- })
- var b strings.Builder
- if model := fields[modelField].String(); model != "" {
- _, _ = b.WriteString("model=")
- _, _ = b.WriteString(model)
- }
- if tools := fields[toolsField]; tools.Exists() && tools.IsArray() && tools.Raw != "[]" {
- _, _ = b.WriteString("|tools=")
- _, _ = b.WriteString(normalizeCompatSeedJSON(json.RawMessage(tools.Raw)))
- }
- if funcs := fields[functionsField]; funcs.Exists() && funcs.IsArray() && funcs.Raw != "[]" {
- _, _ = b.WriteString("|functions=")
- _, _ = b.WriteString(normalizeCompatSeedJSON(json.RawMessage(funcs.Raw)))
- }
- if instr := fields[instructionsField].String(); instr != "" {
- _, _ = b.WriteString("|instructions=")
- _, _ = b.WriteString(instr)
- }
- firstUserCaptured := false
- msgs := fields[messagesField]
- if msgs.Exists() && msgs.IsArray() {
- systemPrefixOpen := true
- msgs.ForEach(func(_, msg gjson.Result) bool {
- role := msg.Get("role").String()
- switch role {
- case "system", "developer":
- if systemPrefixOpen {
- _, _ = b.WriteString("|system=")
- if c := msg.Get("content"); c.Exists() {
- _, _ = b.WriteString(normalizeCompatSeedJSON(json.RawMessage(c.Raw)))
- }
- }
- case "user":
- systemPrefixOpen = false
- if !firstUserCaptured {
- _, _ = b.WriteString("|first_user=")
- if c := msg.Get("content"); c.Exists() {
- _, _ = b.WriteString(normalizeCompatSeedJSON(json.RawMessage(c.Raw)))
- }
- firstUserCaptured = true
- }
- default:
- systemPrefixOpen = false
- }
- return true
- })
- } else if inp := fields[inputField]; inp.Exists() {
- if inp.Type == gjson.String {
- _, _ = b.WriteString("|input=")
- _, _ = b.WriteString(inp.String())
- } else if inp.IsArray() {
- inp.ForEach(func(_, item gjson.Result) bool {
- role := item.Get("role").String()
- switch role {
- case "system", "developer":
- _, _ = b.WriteString("|system=")
- if c := item.Get("content"); c.Exists() {
- _, _ = b.WriteString(normalizeCompatSeedJSON(json.RawMessage(c.Raw)))
- }
- case "user":
- if !firstUserCaptured {
- _, _ = b.WriteString("|first_user=")
- if c := item.Get("content"); c.Exists() {
- _, _ = b.WriteString(normalizeCompatSeedJSON(json.RawMessage(c.Raw)))
- }
- firstUserCaptured = true
- }
- }
- if !firstUserCaptured && item.Get("type").String() == "input_text" {
- _, _ = b.WriteString("|first_user=")
- if text := item.Get("text").String(); text != "" {
- _, _ = b.WriteString(text)
- }
- firstUserCaptured = true
- }
- return true
- })
- }
- }
- if b.Len() == 0 {
- return ""
- }
- return contentSessionSeedPrefix + b.String()
- }
- // deriveOpenAIAnchoredContentSessionSeed returns the legacy content-derived
- // seed only when it contains a meaningful user/input anchor. This preserves
- // the existing session derivation while preventing model-only requests from
- // becoming a tenant-wide cache routing identity.
- func deriveOpenAIAnchoredContentSessionSeed(body []byte) string {
- if !hasOpenAIContentSessionUserAnchor(body) {
- return ""
- }
- return deriveOpenAIContentSessionSeed(body)
- }
- func hasOpenAIContentSessionUserAnchor(body []byte) bool {
- if len(body) == 0 {
- return false
- }
- if messages := gjson.GetBytes(body, "messages"); messages.Exists() && messages.IsArray() {
- anchored := false
- messages.ForEach(func(_, message gjson.Result) bool {
- if strings.TrimSpace(message.Get("role").String()) != "user" {
- return true
- }
- anchored = hasMeaningfulOpenAIContent(message.Get("content"))
- return false
- })
- return anchored
- }
- input := gjson.GetBytes(body, "input")
- if !input.Exists() {
- return false
- }
- if input.Type == gjson.String {
- return strings.TrimSpace(input.String()) != ""
- }
- if !input.IsArray() {
- return false
- }
- anchored := false
- input.ForEach(func(_, item gjson.Result) bool {
- if strings.TrimSpace(item.Get("role").String()) == "user" {
- anchored = hasMeaningfulOpenAIContent(item.Get("content"))
- return false
- }
- if strings.TrimSpace(item.Get("type").String()) == "input_text" {
- anchored = strings.TrimSpace(item.Get("text").String()) != ""
- return false
- }
- return true
- })
- return anchored
- }
- func hasMeaningfulOpenAIContent(content gjson.Result) bool {
- if !content.Exists() || content.Type == gjson.Null {
- return false
- }
- if content.Type == gjson.String {
- return strings.TrimSpace(content.String()) != ""
- }
- if !content.IsArray() {
- normalized, ok := normalizeNonEmptyCompatSeedJSON(content)
- return ok && strings.TrimSpace(normalized) != ""
- }
- meaningful := false
- content.ForEach(func(_, item gjson.Result) bool {
- if item.Type == gjson.String {
- meaningful = strings.TrimSpace(item.String()) != ""
- } else if text := item.Get("text"); text.Exists() {
- meaningful = strings.TrimSpace(text.String()) != ""
- } else {
- _, meaningful = normalizeNonEmptyCompatSeedJSON(item)
- }
- return !meaningful
- })
- return meaningful
- }
- // deriveOpenAIStablePrefixSessionSeed builds a seed from the reusable prefix
- // of an OpenAI-format request. User and assistant content are deliberately
- // excluded so independent prompts with the same system/tool prefix can share
- // an upstream prompt-cache routing identity.
- //
- // An empty result means the request has no meaningful stable prefix. Callers
- // must then use a narrower fallback instead of grouping all requests by tenant
- // and model alone.
- func deriveOpenAIStablePrefixSessionSeed(body []byte) string {
- if len(body) == 0 {
- return ""
- }
- var b strings.Builder
- hasStablePrefix := false
- appendJSON := func(label string, value gjson.Result) {
- normalized, ok := normalizeNonEmptyCompatSeedJSON(value)
- if !ok {
- return
- }
- _, _ = b.WriteString("|")
- _, _ = b.WriteString(label)
- _, _ = b.WriteString("=")
- _, _ = b.WriteString(normalized)
- hasStablePrefix = true
- }
- if tools := gjson.GetBytes(body, "tools"); tools.Exists() && tools.IsArray() {
- appendJSON("tools", tools)
- }
- if funcs := gjson.GetBytes(body, "functions"); funcs.Exists() && funcs.IsArray() {
- appendJSON("functions", funcs)
- }
- if instructions := gjson.GetBytes(body, "instructions"); strings.TrimSpace(instructions.String()) != "" {
- appendJSON("instructions", instructions)
- }
- appendSystemMessages := func(items gjson.Result) {
- items.ForEach(func(_, item gjson.Result) bool {
- role := strings.TrimSpace(item.Get("role").String())
- switch role {
- case "system", "developer":
- appendJSON(role, item.Get("content"))
- }
- return true
- })
- }
- if messages := gjson.GetBytes(body, "messages"); messages.Exists() && messages.IsArray() {
- appendSystemMessages(messages)
- } else if input := gjson.GetBytes(body, "input"); input.Exists() && input.IsArray() {
- appendSystemMessages(input)
- }
- if !hasStablePrefix {
- return ""
- }
- return contentStablePrefixSessionSeedPrefix + b.String()
- }
- func normalizeNonEmptyCompatSeedJSON(value gjson.Result) (string, bool) {
- if !value.Exists() || value.Type == gjson.Null {
- return "", false
- }
- normalized := normalizeCompatSeedJSON(json.RawMessage(value.Raw))
- switch normalized {
- case "", `""`, "[]", "{}", "null":
- return "", false
- default:
- return normalized, true
- }
- }
|