Compose appended request messages
This commit is contained in:
@@ -1110,14 +1110,18 @@ func TestDeriveStructuredSchemaName(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashRenderedPromptIncludesCacheControlWhenPresent(t *testing.T) {
|
||||
uncached := domain.RenderedPrompt{Messages: []domain.RenderedMessage{
|
||||
{Role: "system", Content: "sys"},
|
||||
{Role: "user", Content: "usr"},
|
||||
func TestHashRenderedPromptIncludesEveryContractedField(t *testing.T) {
|
||||
base := domain.RenderedPrompt{Messages: []domain.RenderedMessage{
|
||||
{Role: domain.RoleSystem, Content: "sys"},
|
||||
{Role: domain.RoleUser, Content: "usr"},
|
||||
}}
|
||||
wantLegacyHash := hashString("system\nsys\n---\nuser\nusr\n---\n")
|
||||
if got := hashRenderedPrompt(uncached); got != wantLegacyHash {
|
||||
t.Fatalf("expected no-cache hash to preserve legacy input, got %q want %q", got, wantLegacyHash)
|
||||
identical := domain.RenderedPrompt{Messages: []domain.RenderedMessage{
|
||||
{Role: domain.RoleSystem, Content: "sys"},
|
||||
{Role: domain.RoleUser, Content: "usr"},
|
||||
}}
|
||||
baseHash := hashRenderedPrompt(base)
|
||||
if baseHash != hashRenderedPrompt(identical) {
|
||||
t.Fatal("identical rendered prompts must have the same hash")
|
||||
}
|
||||
|
||||
withCache := domain.RenderedPrompt{Messages: []domain.RenderedMessage{
|
||||
@@ -1129,7 +1133,7 @@ func TestHashRenderedPromptIncludesCacheControlWhenPresent(t *testing.T) {
|
||||
TTL: "1h",
|
||||
},
|
||||
},
|
||||
{Role: "user", Content: "usr"},
|
||||
{Role: domain.RoleUser, Content: "usr"},
|
||||
}}
|
||||
alsoWithCache := domain.RenderedPrompt{Messages: []domain.RenderedMessage{
|
||||
{
|
||||
@@ -1140,7 +1144,7 @@ func TestHashRenderedPromptIncludesCacheControlWhenPresent(t *testing.T) {
|
||||
TTL: "1h",
|
||||
},
|
||||
},
|
||||
{Role: "user", Content: "usr"},
|
||||
{Role: domain.RoleUser, Content: "usr"},
|
||||
}}
|
||||
withoutTTL := domain.RenderedPrompt{Messages: []domain.RenderedMessage{
|
||||
{
|
||||
@@ -1150,11 +1154,11 @@ func TestHashRenderedPromptIncludesCacheControlWhenPresent(t *testing.T) {
|
||||
Type: domain.CacheControlEphemeral,
|
||||
},
|
||||
},
|
||||
{Role: "user", Content: "usr"},
|
||||
{Role: domain.RoleUser, Content: "usr"},
|
||||
}}
|
||||
|
||||
cachedHash := hashRenderedPrompt(withCache)
|
||||
if cachedHash == hashRenderedPrompt(uncached) {
|
||||
if cachedHash == baseHash {
|
||||
t.Fatal("expected cache control to change rendered prompt hash")
|
||||
}
|
||||
if cachedHash != hashRenderedPrompt(alsoWithCache) {
|
||||
@@ -1163,6 +1167,29 @@ func TestHashRenderedPromptIncludesCacheControlWhenPresent(t *testing.T) {
|
||||
if cachedHash == hashRenderedPrompt(withoutTTL) {
|
||||
t.Fatal("expected ttl changes to affect rendered prompt hash")
|
||||
}
|
||||
|
||||
variants := []struct {
|
||||
name string
|
||||
prompt domain.RenderedPrompt
|
||||
}{
|
||||
{name: "role", prompt: domain.RenderedPrompt{Messages: []domain.RenderedMessage{{Role: domain.RoleDeveloper, Content: "sys"}, {Role: domain.RoleUser, Content: "usr"}}}},
|
||||
{name: "content", prompt: domain.RenderedPrompt{Messages: []domain.RenderedMessage{{Role: domain.RoleSystem, Content: "changed"}, {Role: domain.RoleUser, Content: "usr"}}}},
|
||||
{name: "order", prompt: domain.RenderedPrompt{Messages: []domain.RenderedMessage{{Role: domain.RoleUser, Content: "usr"}, {Role: domain.RoleSystem, Content: "sys"}}}},
|
||||
{name: "session", prompt: domain.RenderedPrompt{SessionID: "session", Messages: base.Messages}},
|
||||
}
|
||||
for _, variant := range variants {
|
||||
t.Run(variant.name, func(t *testing.T) {
|
||||
if hashRenderedPrompt(variant.prompt) == baseHash {
|
||||
t.Fatalf("%s did not change the rendered-prompt hash", variant.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
oneMessage := domain.RenderedPrompt{Messages: []domain.RenderedMessage{{Role: domain.RoleUser, Content: "x\n---\nassistant\ny"}}}
|
||||
twoMessages := domain.RenderedPrompt{Messages: []domain.RenderedMessage{{Role: domain.RoleUser, Content: "x"}, {Role: domain.RoleAssistant, Content: "y"}}}
|
||||
if hashRenderedPrompt(oneMessage) == hashRenderedPrompt(twoMessages) {
|
||||
t.Fatal("length-framed hashing did not distinguish legacy separator collision")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashRenderedPromptIncludesSessionIDWhenPresent(t *testing.T) {
|
||||
@@ -1204,6 +1231,75 @@ func TestHashRenderedPromptIncludesSessionIDWhenPresent(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunnerComposesAppendedMessagesIntoEveryRenderedPrompt(t *testing.T) {
|
||||
renderer := &fakeRenderer{rendered: &domain.RenderedPrompt{
|
||||
SessionID: "rendered-session",
|
||||
Messages: []domain.RenderedMessage{
|
||||
{Role: domain.RoleSystem, Content: "ordinary prefix", CacheControl: &domain.CacheControl{Type: domain.CacheControlEphemeral, TTL: "1h"}},
|
||||
{Role: domain.RoleUser, Content: "ordinary request"},
|
||||
},
|
||||
}}
|
||||
client := &fakeLLM{resp: &domain.GenerateResponse{Content: "output"}}
|
||||
runner := NewRunner(
|
||||
&fakePromptRepo{def: promptDef(domain.FormatText, domain.ValidationNone, 0)},
|
||||
&fakeExecutionProfileRepo{profiles: map[string]*domain.ExecutionProfile{"exec": defaultExecutionProfile()}},
|
||||
nil,
|
||||
defaultArtifactReader(),
|
||||
renderer,
|
||||
client,
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
appended := []domain.RenderedMessage{
|
||||
{Role: domain.RoleAssistant, Content: "consumer response"},
|
||||
{Role: domain.RoleUser, Content: "consumer correction", CacheControl: &domain.CacheControl{Type: domain.CacheControlEphemeral}},
|
||||
}
|
||||
request := domain.RunRequest{PromptID: "p", ProfileID: "exec", Inputs: singleInputRef(), AppendedMessages: appended}
|
||||
|
||||
plain, err := runner.Prepare(context.Background(), domain.RunRequest{PromptID: "p", ProfileID: "exec", Inputs: singleInputRef()})
|
||||
if err != nil {
|
||||
t.Fatalf("prepare without appended messages: %v", err)
|
||||
}
|
||||
empty, err := runner.Prepare(context.Background(), domain.RunRequest{PromptID: "p", ProfileID: "exec", Inputs: singleInputRef(), AppendedMessages: []domain.RenderedMessage{}})
|
||||
if err != nil {
|
||||
t.Fatalf("prepare with empty appended messages: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(empty.Messages, plain.Messages) || empty.RenderedPromptHash != plain.RenderedPromptHash {
|
||||
t.Fatalf("empty appended messages changed preparation: empty=%#v plain=%#v", empty, plain)
|
||||
}
|
||||
prepared, err := runner.Prepare(context.Background(), request)
|
||||
if err != nil {
|
||||
t.Fatalf("prepare with appended messages: %v", err)
|
||||
}
|
||||
if prepared.PromptHash != plain.PromptHash || prepared.RenderedPromptHash == plain.RenderedPromptHash {
|
||||
t.Fatalf("hashes with appended messages = (%q, %q), plain = (%q, %q)", prepared.PromptHash, prepared.RenderedPromptHash, plain.PromptHash, plain.RenderedPromptHash)
|
||||
}
|
||||
|
||||
expected := []domain.RenderedMessage{
|
||||
{Role: domain.RoleSystem, Content: "ordinary prefix", CacheControl: &domain.CacheControl{Type: domain.CacheControlEphemeral, TTL: "1h"}},
|
||||
{Role: domain.RoleUser, Content: "ordinary request"},
|
||||
{Role: domain.RoleAssistant, Content: "consumer response"},
|
||||
{Role: domain.RoleUser, Content: "consumer correction", CacheControl: &domain.CacheControl{Type: domain.CacheControlEphemeral}},
|
||||
}
|
||||
if !reflect.DeepEqual(prepared.Messages, expected) {
|
||||
t.Fatalf("prepared messages = %#v, want %#v", prepared.Messages, expected)
|
||||
}
|
||||
|
||||
result, err := runner.Run(context.Background(), request)
|
||||
if err != nil {
|
||||
t.Fatalf("run with appended messages: %v", err)
|
||||
}
|
||||
if result.RenderedPromptHash != prepared.RenderedPromptHash || !reflect.DeepEqual(client.lastReq.Prompt.Messages, expected) {
|
||||
t.Fatalf("run did not use the prepared effective prompt: result=%#v request=%#v prepared=%#v", result, client.lastReq.Prompt.Messages, prepared)
|
||||
}
|
||||
|
||||
renderer.rendered.Messages[0].Content = "changed source"
|
||||
appended[1].CacheControl.Type = "changed caller value"
|
||||
if !reflect.DeepEqual(prepared.Messages, expected) {
|
||||
t.Fatalf("prepared messages changed after source or caller mutation: %#v", prepared.Messages)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunnerRunSuccessful(t *testing.T) {
|
||||
promptRepo := &fakePromptRepo{def: promptDef(domain.FormatMarkdown, domain.ValidationBasic, 0)}
|
||||
execRepo := &fakeExecutionProfileRepo{profiles: map[string]*domain.ExecutionProfile{"exec": defaultExecutionProfile()}}
|
||||
|
||||
Reference in New Issue
Block a user