| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615 |
- package service
- import (
- "encoding/json"
- "strings"
- "testing"
- "github.com/stretchr/testify/require"
- "github.com/tidwall/gjson"
- )
- func TestDeriveOpenAIContentSessionSeed_EmptyInputs(t *testing.T) {
- require.Empty(t, deriveOpenAIContentSessionSeed(nil))
- require.Empty(t, deriveOpenAIContentSessionSeed([]byte{}))
- require.Empty(t, deriveOpenAIContentSessionSeed([]byte(`{}`)))
- }
- func TestDeriveOpenAIContentSessionSeed_ModelOnly(t *testing.T) {
- seed := deriveOpenAIContentSessionSeed([]byte(`{"model":"gpt-5.4"}`))
- require.Contains(t, seed, contentSessionSeedPrefix)
- require.Contains(t, seed, "model=gpt-5.4")
- }
- func TestDeriveOpenAIContentSessionSeed_ChatCompletions_StableAcrossTurns(t *testing.T) {
- turn1 := []byte(`{
- "model": "gpt-5.4",
- "messages": [
- {"role": "system", "content": "You are helpful."},
- {"role": "user", "content": "Hello"}
- ]
- }`)
- turn2 := []byte(`{
- "model": "gpt-5.4",
- "messages": [
- {"role": "system", "content": "You are helpful."},
- {"role": "user", "content": "Hello"},
- {"role": "assistant", "content": "Hi there!"},
- {"role": "user", "content": "How are you?"}
- ]
- }`)
- s1 := deriveOpenAIContentSessionSeed(turn1)
- s2 := deriveOpenAIContentSessionSeed(turn2)
- require.Equal(t, s1, s2, "seed should be stable across later turns")
- require.NotEmpty(t, s1)
- }
- func TestDeriveOpenAIContentSessionSeed_ChatCompletions_IgnoresLaterSystemMessages(t *testing.T) {
- turn1 := []byte(`{
- "model": "gpt-5.4",
- "messages": [
- {"role": "system", "content": "You are helpful."},
- {"role": "user", "content": "Hello"},
- {"role": "assistant", "content": "Hi there!"},
- {"role": "user", "content": "How are you?"}
- ]
- }`)
- turn2 := []byte(`{
- "model": "gpt-5.4",
- "messages": [
- {"role": "system", "content": "You are helpful."},
- {"role": "user", "content": "Hello"},
- {"role": "assistant", "content": "Hi there!"},
- {"role": "system", "content": "Return JSON for this turn."},
- {"role": "user", "content": "How are you?"}
- ]
- }`)
- require.Equal(t, deriveOpenAIContentSessionSeed(turn1), deriveOpenAIContentSessionSeed(turn2))
- }
- func TestDeriveOpenAIContentSessionSeed_ChatCompletions_UsesLeadingSystemDeveloperPrefix(t *testing.T) {
- firstSystem := []byte(`{
- "model": "gpt-5.4",
- "messages": [
- {"role": "system", "content": "System A"},
- {"role": "developer", "content": "Developer B"},
- {"role": "user", "content": "Hello"}
- ]
- }`)
- changedLaterSystem := []byte(`{
- "model": "gpt-5.4",
- "messages": [
- {"role": "system", "content": "System A"},
- {"role": "developer", "content": "Developer C"},
- {"role": "user", "content": "Hello"}
- ]
- }`)
- seed := deriveOpenAIContentSessionSeed(firstSystem)
- require.Contains(t, seed, "System A")
- require.Contains(t, seed, "Developer B")
- require.NotEqual(t, seed, deriveOpenAIContentSessionSeed(changedLaterSystem))
- withLaterSystem := []byte(`{
- "model": "gpt-5.4",
- "messages": [
- {"role": "system", "content": "System A"},
- {"role": "developer", "content": "Developer B"},
- {"role": "user", "content": "Hello"},
- {"role": "system", "content": "Dynamic system"}
- ]
- }`)
- require.Equal(t, seed, deriveOpenAIContentSessionSeed(withLaterSystem))
- }
- func TestDeriveOpenAIContentSessionSeed_ChatCompletions_DifferentFirstUserDiffers(t *testing.T) {
- req1 := []byte(`{"model":"gpt-5.4","messages":[{"role":"user","content":"Question A"}]}`)
- req2 := []byte(`{"model":"gpt-5.4","messages":[{"role":"user","content":"Question B"}]}`)
- s1 := deriveOpenAIContentSessionSeed(req1)
- s2 := deriveOpenAIContentSessionSeed(req2)
- require.NotEqual(t, s1, s2)
- }
- func TestDeriveOpenAIContentSessionSeed_ChatCompletions_DifferentSystemDiffers(t *testing.T) {
- req1 := []byte(`{"model":"gpt-5.4","messages":[{"role":"system","content":"A"},{"role":"user","content":"Hi"}]}`)
- req2 := []byte(`{"model":"gpt-5.4","messages":[{"role":"system","content":"B"},{"role":"user","content":"Hi"}]}`)
- s1 := deriveOpenAIContentSessionSeed(req1)
- s2 := deriveOpenAIContentSessionSeed(req2)
- require.NotEqual(t, s1, s2)
- }
- func TestDeriveOpenAIContentSessionSeed_ChatCompletions_DifferentModelDiffers(t *testing.T) {
- req1 := []byte(`{"model":"gpt-5.4","messages":[{"role":"user","content":"Hi"}]}`)
- req2 := []byte(`{"model":"gpt-4o","messages":[{"role":"user","content":"Hi"}]}`)
- s1 := deriveOpenAIContentSessionSeed(req1)
- s2 := deriveOpenAIContentSessionSeed(req2)
- require.NotEqual(t, s1, s2)
- }
- func TestDeriveOpenAIContentSessionSeed_ChatCompletions_WithTools(t *testing.T) {
- withTools := []byte(`{
- "model": "gpt-5.4",
- "tools": [{"type":"function","function":{"name":"get_weather"}}],
- "messages": [{"role": "user", "content": "Hello"}]
- }`)
- withoutTools := []byte(`{
- "model": "gpt-5.4",
- "messages": [{"role": "user", "content": "Hello"}]
- }`)
- s1 := deriveOpenAIContentSessionSeed(withTools)
- s2 := deriveOpenAIContentSessionSeed(withoutTools)
- require.NotEqual(t, s1, s2, "tools should affect the seed")
- require.Contains(t, s1, "|tools=")
- }
- func TestDeriveOpenAIContentSessionSeed_ChatCompletions_WithFunctions(t *testing.T) {
- body := []byte(`{
- "model": "gpt-5.4",
- "functions": [{"name":"get_weather","parameters":{}}],
- "messages": [{"role": "user", "content": "Hello"}]
- }`)
- seed := deriveOpenAIContentSessionSeed(body)
- require.Contains(t, seed, "|functions=")
- }
- func TestDeriveOpenAIContentSessionSeed_ChatCompletions_DeveloperRole(t *testing.T) {
- body := []byte(`{
- "model": "gpt-5.4",
- "messages": [
- {"role": "developer", "content": "You are helpful."},
- {"role": "user", "content": "Hello"}
- ]
- }`)
- seed := deriveOpenAIContentSessionSeed(body)
- require.Contains(t, seed, "|system=")
- require.Contains(t, seed, "|first_user=")
- }
- func TestDeriveOpenAIContentSessionSeed_ChatCompletions_StructuredContent(t *testing.T) {
- body := []byte(`{
- "model": "gpt-5.4",
- "messages": [
- {"role": "user", "content": [{"type":"text","text":"Hello"}]}
- ]
- }`)
- seed := deriveOpenAIContentSessionSeed(body)
- require.NotEmpty(t, seed)
- require.Contains(t, seed, "|first_user=")
- }
- func TestDeriveOpenAIContentSessionSeed_ResponsesAPI_InputString(t *testing.T) {
- body := []byte(`{"model":"gpt-5.4","input":"Hello, how are you?"}`)
- seed := deriveOpenAIContentSessionSeed(body)
- require.Contains(t, seed, "|input=Hello, how are you?")
- }
- func TestDeriveOpenAIContentSessionSeed_ResponsesAPI_InputArray(t *testing.T) {
- body := []byte(`{
- "model": "gpt-5.4",
- "input": [
- {"role": "system", "content": "You are helpful."},
- {"role": "user", "content": "Hello"}
- ]
- }`)
- seed := deriveOpenAIContentSessionSeed(body)
- require.Contains(t, seed, "|system=")
- require.Contains(t, seed, "|first_user=")
- }
- func TestDeriveOpenAIContentSessionSeed_ResponsesAPI_WithInstructions(t *testing.T) {
- body := []byte(`{
- "model": "gpt-5.4",
- "instructions": "You are a coding assistant.",
- "input": "Write a hello world"
- }`)
- seed := deriveOpenAIContentSessionSeed(body)
- require.Contains(t, seed, "|instructions=You are a coding assistant.")
- require.Contains(t, seed, "|input=Write a hello world")
- }
- func TestDeriveOpenAIContentSessionSeed_Deterministic(t *testing.T) {
- body := []byte(`{
- "model": "gpt-5.4",
- "messages": [
- {"role": "system", "content": "You are helpful."},
- {"role": "user", "content": "Hello"}
- ]
- }`)
- s1 := deriveOpenAIContentSessionSeed(body)
- s2 := deriveOpenAIContentSessionSeed(body)
- require.Equal(t, s1, s2, "seed must be deterministic")
- }
- func TestDeriveOpenAIContentSessionSeed_PrefixPresent(t *testing.T) {
- body := []byte(`{"model":"gpt-5.4","messages":[{"role":"user","content":"Hi"}]}`)
- seed := deriveOpenAIContentSessionSeed(body)
- require.True(t, len(seed) > len(contentSessionSeedPrefix))
- require.Equal(t, contentSessionSeedPrefix, seed[:len(contentSessionSeedPrefix)])
- }
- func TestDeriveOpenAIContentSessionSeed_EmptyToolsIgnored(t *testing.T) {
- body := []byte(`{"model":"gpt-5.4","tools":[],"messages":[{"role":"user","content":"Hi"}]}`)
- seed := deriveOpenAIContentSessionSeed(body)
- require.NotContains(t, seed, "|tools=")
- }
- func TestDeriveOpenAIContentSessionSeed_MessagesPreferredOverInput(t *testing.T) {
- body := []byte(`{
- "model": "gpt-5.4",
- "messages": [{"role": "user", "content": "from messages"}],
- "input": "from input"
- }`)
- seed := deriveOpenAIContentSessionSeed(body)
- require.Contains(t, seed, "|first_user=")
- require.NotContains(t, seed, "|input=")
- }
- func TestDeriveOpenAIContentSessionSeed_JSONCanonicalisation(t *testing.T) {
- compact := []byte(`{"model":"gpt-5.4","tools":[{"type":"function","function":{"name":"get_weather","description":"Get weather"}}],"messages":[{"role":"user","content":"Hi"}]}`)
- spaced := []byte(`{
- "model": "gpt-5.4",
- "tools": [
- { "type" : "function", "function": { "description": "Get weather", "name": "get_weather" } }
- ],
- "messages": [ { "role": "user", "content": "Hi" } ]
- }`)
- s1 := deriveOpenAIContentSessionSeed(compact)
- s2 := deriveOpenAIContentSessionSeed(spaced)
- require.Equal(t, s1, s2, "different formatting of identical JSON should produce the same seed")
- }
- func TestDeriveOpenAIContentSessionSeed_SingleScanMatchesLegacyBytes(t *testing.T) {
- largeValue := strings.Repeat("payload", 1<<17)
- tests := []struct {
- name string
- body []byte
- }{
- {
- name: "large chat completions",
- body: []byte(`{"metadata":"` + largeValue + `","model":"gpt-5.4","tools":[{"type":"function","function":{"name":"lookup"}}],"functions":[{"name":"legacy_lookup"}],"messages":[{"role":"system","content":"System prompt"},{"role":"developer","content":[{"type":"text","text":"Developer prompt"}]},{"role":"user","content":"Hello"}]}`),
- },
- {
- name: "large responses",
- body: []byte(`{"metadata":"` + largeValue + `","model":"gpt-5.4","instructions":"Be concise.","tools":[{"type":"function","name":"lookup"}],"input":[{"role":"system","content":"System prompt"},{"role":"user","content":[{"type":"input_text","text":"Hello"}]}]}`),
- },
- {
- name: "fields in reverse order",
- body: []byte(`{"input":"fallback input","messages":[{"role":"user","content":"chat wins"}],"instructions":"Be concise.","functions":[{"name":"lookup"}],"tools":[{"type":"function","name":"lookup"}],"model":"gpt-5.4"}`),
- },
- {
- name: "missing and wrong type fields",
- body: []byte(`{"tools":[],"functions":null,"instructions":0,"messages":{},"input":[{"type":"input_text","text":"fallback"}]}`),
- },
- {
- name: "duplicate fields keep first value",
- body: []byte(`{"model":"first","model":"second","tools":[{"name":"first"}],"tools":[{"name":"second"}],"functions":[],"functions":[{"name":"second"}],"instructions":"first","instructions":"second","messages":null,"messages":[{"role":"user","content":"second"}],"input":"first input","input":"second input"}`),
- },
- {
- name: "escaped field names",
- body: []byte(`{"mo\u0064el":"gpt-5.4","mess\u0061ges":[{"role":"user","content":"Hello"}]}`),
- },
- {
- name: "trailing object fields are outside the root",
- body: []byte(`{"foo":1}{"model":"trailing","input":"trailing input"}`),
- },
- {
- name: "trailing quoted fields are outside the root",
- body: []byte(`{"model":"root"}"input":"trailing input"`),
- },
- {
- name: "leading garbage before the root",
- body: []byte(`garbage{"model":"gpt-5.4","input":"Hello"}`),
- },
- {
- name: "escaped braces remain inside string values",
- body: []byte(`{"metadata":"escaped } and [ and \" quote","model":"root","input":"Hello"}{"model":"trailing"}`),
- },
- {
- name: "nested braces do not end the root",
- body: []byte(`{"metadata":{"nested":"} ]"},"model":"root","input":"Hello"}{"model":"trailing"}`),
- },
- {
- name: "root array does not expose nested or trailing object fields",
- body: []byte(`[{"model":"nested"}]{"model":"trailing","input":"trailing input"}`),
- },
- {
- name: "trailing messages do not override root input",
- body: []byte(`{"input":"root input"}{"messages":[{"role":"user","content":"trailing"}]}`),
- },
- {
- name: "truncated string containing a closing brace",
- body: []byte(`{"model":"root","metadata":"still } inside`),
- },
- {
- name: "lenient truncated body",
- body: []byte(`{"model":"gpt-5.4","messages":[{"role":"user","content":"Hello"}]`),
- },
- }
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- require.Equal(t, referenceDeriveOpenAIContentSessionSeed(test.body), deriveOpenAIContentSessionSeed(test.body))
- })
- }
- }
- func TestDeriveOpenAIContentSessionSeed_AllTruncationOffsetsMatchLegacyBytes(t *testing.T) {
- bodies := []string{
- `{"model":"gpt-5.4","tools":[{"type":"function","function":{"name":"lookup"}}],"functions":[{"name":"legacy"}],"instructions":"escaped \" } text","messages":[{"role":"system","content":"System"},{"role":"user","content":[{"type":"text","text":"Hello"}]}],"input":"fallback"}`,
- `{"model":"gpt-5.4","instructions":"Be concise.","tools":[{"type":"function","name":"lookup"}],"input":[{"role":"system","content":"System"},{"role":"user","content":[{"type":"input_text","text":"Hello"}]}]}`,
- }
- for bodyIndex, body := range bodies {
- for end := 1; end < len(body); end++ {
- truncated := []byte(body[:end])
- require.Equalf(t, referenceDeriveOpenAIContentSessionSeed(truncated), deriveOpenAIContentSessionSeed(truncated), "body %d truncated at byte %d", bodyIndex, end)
- }
- }
- }
- func referenceDeriveOpenAIContentSessionSeed(body []byte) string {
- if len(body) == 0 {
- return ""
- }
- var b strings.Builder
- if model := gjson.GetBytes(body, "model").String(); model != "" {
- _, _ = b.WriteString("model=")
- _, _ = b.WriteString(model)
- }
- if tools := gjson.GetBytes(body, "tools"); tools.Exists() && tools.IsArray() && tools.Raw != "[]" {
- _, _ = b.WriteString("|tools=")
- _, _ = b.WriteString(normalizeCompatSeedJSON(json.RawMessage(tools.Raw)))
- }
- if funcs := gjson.GetBytes(body, "functions"); funcs.Exists() && funcs.IsArray() && funcs.Raw != "[]" {
- _, _ = b.WriteString("|functions=")
- _, _ = b.WriteString(normalizeCompatSeedJSON(json.RawMessage(funcs.Raw)))
- }
- if instr := gjson.GetBytes(body, "instructions").String(); instr != "" {
- _, _ = b.WriteString("|instructions=")
- _, _ = b.WriteString(instr)
- }
- firstUserCaptured := false
- msgs := gjson.GetBytes(body, "messages")
- 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 := gjson.GetBytes(body, "input"); 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()
- }
- func TestDeriveOpenAIContentSessionSeed_ResponsesAPI_InputTextTypedItem(t *testing.T) {
- body := []byte(`{
- "model": "gpt-5.4",
- "input": [{"type": "input_text", "text": "Hello world"}]
- }`)
- seed := deriveOpenAIContentSessionSeed(body)
- require.Contains(t, seed, "|first_user=")
- require.Contains(t, seed, "Hello world")
- }
- func TestDeriveOpenAIContentSessionSeed_ResponsesAPI_TypedMessageItem(t *testing.T) {
- body := []byte(`{
- "model": "gpt-5.4",
- "input": [{"type": "message", "role": "user", "content": "Hello from typed message"}]
- }`)
- seed := deriveOpenAIContentSessionSeed(body)
- require.Contains(t, seed, "|first_user=")
- require.Contains(t, seed, "Hello from typed message")
- }
- func TestDeriveOpenAIStablePrefixSessionSeed_IgnoresUserContent(t *testing.T) {
- first := []byte(`{
- "model": "grok",
- "instructions": "Be concise.",
- "tools": [{"type":"function","name":"lookup","parameters":{"type":"object"}}],
- "input": [{"role":"user","content":"Question A"}]
- }`)
- second := []byte(`{
- "model": "grok",
- "instructions": "Be concise.",
- "tools": [{"parameters":{"type":"object"},"name":"lookup","type":"function"}],
- "input": [{"role":"user","content":"Question B"}]
- }`)
- firstSeed := deriveOpenAIStablePrefixSessionSeed(first)
- secondSeed := deriveOpenAIStablePrefixSessionSeed(second)
- require.NotEmpty(t, firstSeed)
- require.Equal(t, firstSeed, secondSeed)
- require.NotContains(t, firstSeed, "Question A")
- require.NotContains(t, firstSeed, "first_user")
- }
- func TestDeriveOpenAIStablePrefixSessionSeed_IsolatesStablePrefixFields(t *testing.T) {
- base := []byte(`{
- "instructions":"Be concise.",
- "tools":[{"type":"function","name":"lookup"}],
- "input":[{"role":"system","content":"System A"},{"role":"user","content":"Question"}]
- }`)
- differentInstructions := []byte(`{
- "instructions":"Be detailed.",
- "tools":[{"type":"function","name":"lookup"}],
- "input":[{"role":"system","content":"System A"},{"role":"user","content":"Question"}]
- }`)
- differentTools := []byte(`{
- "instructions":"Be concise.",
- "tools":[{"type":"function","name":"search"}],
- "input":[{"role":"system","content":"System A"},{"role":"user","content":"Question"}]
- }`)
- differentSystem := []byte(`{
- "instructions":"Be concise.",
- "tools":[{"type":"function","name":"lookup"}],
- "input":[{"role":"system","content":"System B"},{"role":"user","content":"Question"}]
- }`)
- baseSeed := deriveOpenAIStablePrefixSessionSeed(base)
- require.NotEqual(t, baseSeed, deriveOpenAIStablePrefixSessionSeed(differentInstructions))
- require.NotEqual(t, baseSeed, deriveOpenAIStablePrefixSessionSeed(differentTools))
- require.NotEqual(t, baseSeed, deriveOpenAIStablePrefixSessionSeed(differentSystem))
- }
- func TestDeriveOpenAIStablePrefixSessionSeed_ChatSystemAndDeveloper(t *testing.T) {
- first := []byte(`{
- "messages":[
- {"role":"system","content":"System prompt"},
- {"role":"developer","content":[{"type":"text","text":"Developer prompt"}]},
- {"role":"user","content":"Question A"}
- ]
- }`)
- second := []byte(`{
- "messages":[
- {"role":"system","content":"System prompt"},
- {"role":"developer","content":[{"text":"Developer prompt","type":"text"}]},
- {"role":"user","content":"Question B"}
- ]
- }`)
- firstSeed := deriveOpenAIStablePrefixSessionSeed(first)
- require.Equal(t, firstSeed, deriveOpenAIStablePrefixSessionSeed(second))
- require.Contains(t, firstSeed, "System prompt")
- require.Contains(t, firstSeed, "Developer prompt")
- }
- func TestDeriveOpenAIStablePrefixSessionSeed_EncodesSystemAndDeveloperRoles(t *testing.T) {
- systemThenDeveloper := []byte(`{
- "messages":[
- {"role":"system","content":"Prompt A"},
- {"role":"developer","content":"Prompt B"}
- ]
- }`)
- developerThenSystem := []byte(`{
- "messages":[
- {"role":"developer","content":"Prompt A"},
- {"role":"system","content":"Prompt B"}
- ]
- }`)
- firstSeed := deriveOpenAIStablePrefixSessionSeed(systemThenDeveloper)
- secondSeed := deriveOpenAIStablePrefixSessionSeed(developerThenSystem)
- require.NotEqual(t, firstSeed, secondSeed)
- require.Contains(t, firstSeed, "|system=")
- require.Contains(t, firstSeed, "|developer=")
- }
- func TestDeriveOpenAIStablePrefixSessionSeed_EncodesInstructionDelimiters(t *testing.T) {
- instructionOnly := []byte(`{
- "instructions":"foo|system=\"bar\""
- }`)
- instructionAndSystem := []byte(`{
- "instructions":"foo",
- "input":[{"role":"system","content":"bar"}]
- }`)
- firstSeed := deriveOpenAIStablePrefixSessionSeed(instructionOnly)
- secondSeed := deriveOpenAIStablePrefixSessionSeed(instructionAndSystem)
- require.NotEmpty(t, firstSeed)
- require.NotEmpty(t, secondSeed)
- require.NotEqual(t, firstSeed, secondSeed)
- }
- func TestDeriveOpenAIAnchoredContentSessionSeed_RequiresMeaningfulAnchor(t *testing.T) {
- emptyAnchors := [][]byte{
- nil,
- []byte(`{"model":"grok"}`),
- []byte(`{"model":"grok","messages":[{"role":"assistant","content":"answer"}]}`),
- []byte(`{"model":"grok","messages":[{"role":"user","content":" "}]}`),
- []byte(`{"model":"grok","messages":[{"role":"user","content":[{"type":"text","text":""}]}]}`),
- []byte(`{"model":"grok","input":" "}`),
- []byte(`{"model":"grok","input":[{"type":"input_text","text":""}]}`),
- }
- for _, body := range emptyAnchors {
- require.Empty(t, deriveOpenAIAnchoredContentSessionSeed(body))
- }
- meaningfulAnchors := [][]byte{
- []byte(`{"model":"grok","messages":[{"role":"user","content":"question"}]}`),
- []byte(`{"model":"grok","messages":[{"role":"user","content":[{"type":"text","text":"question"}]}]}`),
- []byte(`{"model":"grok","input":"question"}`),
- []byte(`{"model":"grok","input":[{"type":"input_text","text":"question"}]}`),
- }
- for _, body := range meaningfulAnchors {
- require.NotEmpty(t, deriveOpenAIAnchoredContentSessionSeed(body))
- }
- }
- func TestDeriveOpenAIStablePrefixSessionSeed_RequiresMeaningfulPrefix(t *testing.T) {
- tests := [][]byte{
- nil,
- []byte(`{}`),
- []byte(`{"model":"grok","input":"Question A"}`),
- []byte(`{"model":"grok","tools":[],"input":"Question A"}`),
- []byte(`{"model":"grok","functions":[],"instructions":" ","messages":[{"role":"system","content":""},{"role":"user","content":"Question A"}]}`),
- }
- for _, body := range tests {
- require.Empty(t, deriveOpenAIStablePrefixSessionSeed(body))
- }
- }
|