feat: add natural elder story companion flow

This commit is contained in:
Codex Local
2026-09-06 20:16:01 +08:00
parent 029a6097a2
commit 8c834c0aa7
20 changed files with 582 additions and 149 deletions
@@ -154,6 +154,8 @@ func TestServiceIdempotentlyCreatesConversationWithoutPersistingToken(t *testing
VALUES ('10000000-0000-0000-0000-000000000001',$1::uuid,$2::uuid,'prior-subject-1',1,'subject','2026-08-23T10:01:00Z','2026-08-23T10:01:05Z','我注册了一个个体工商户。','provider')`, []any{testSubjectID, testPriorConversationID}},
{`INSERT INTO conversation_summary (tenant_id,subject_id,conversation_id,summary_type,content,source_turn_ids,model_ref,prompt_version,generation_version,status,created_at)
VALUES ('10000000-0000-0000-0000-000000000001',$1::uuid,$2::uuid,'conversation_summary','上次聊到刚注册了一个个体工商户,准备开始新的工作。','["prior-subject-1"]'::jsonb,'mock-content-v1','prompt-v1',1,'READY','2026-08-23T10:21:00Z')`, []any{testSubjectID, testPriorConversationID}},
{`INSERT INTO conversation_summary (tenant_id,subject_id,conversation_id,summary_type,content,source_turn_ids,model_ref,prompt_version,generation_version,status,created_at)
VALUES ('10000000-0000-0000-0000-000000000001',$1::uuid,$2::uuid,'open_topic','如果长者愿意,可以聊聊开始新工作后最期待的事。','["prior-subject-1"]'::jsonb,'mock-content-v1','prompt-v1',1,'READY','2026-08-23T10:21:01Z')`, []any{testSubjectID, testPriorConversationID}},
{`INSERT INTO consent_record (tenant_id,subject_id,consent_type,policy_version,notice_digest,actor_user_id)
VALUES ('10000000-0000-0000-0000-000000000001',$1::uuid,'personalized_memory','orchestration-service-test',repeat('a',64),'10000000-0000-0000-0000-000000000101')`, []any{testSubjectID}},
{`INSERT INTO runtime_config_version (
@@ -220,7 +222,7 @@ func TestServiceIdempotentlyCreatesConversationWithoutPersistingToken(t *testing
if _, err = tx.Exec(ctx, `UPDATE conversation SET status=$2 WHERE id=$1::uuid`, testPriorConversationID, priorStatus); err != nil {
t.Fatal(err)
}
summaries, err := selectRecentConversationSummaries(ctx, tx, "10000000-0000-0000-0000-000000000001", testSubjectID, 3)
contexts, err := selectRecentConversationContexts(ctx, tx, "10000000-0000-0000-0000-000000000001", testSubjectID, 3)
if err != nil {
t.Fatal(err)
}
@@ -228,12 +230,15 @@ func TestServiceIdempotentlyCreatesConversationWithoutPersistingToken(t *testing
if priorStatus == "ACTIVE" {
want = 0
}
if len(summaries) != want {
t.Fatalf("status=%s summaries=%d want=%d", priorStatus, len(summaries), want)
if len(contexts) != want {
t.Fatalf("status=%s contexts=%d want=%d", priorStatus, len(contexts), want)
}
summaries, err = selectRecentConversationSummaries(ctx, tx, "10000000-0000-0000-0000-000000000001", "90000000-0000-0000-0000-000000009999", 3)
if err != nil || len(summaries) != 0 {
t.Fatalf("unrelated subject summaries=%d err=%v", len(summaries), err)
if len(contexts) == 1 && (contexts[0].Summary == "" || contexts[0].OpenTopic == "") {
t.Fatalf("summary and open topic were not selected from the same generation: %+v", contexts[0])
}
contexts, err = selectRecentConversationContexts(ctx, tx, "10000000-0000-0000-0000-000000000001", "90000000-0000-0000-0000-000000009999", 3)
if err != nil || len(contexts) != 0 {
t.Fatalf("unrelated subject contexts=%d err=%v", len(contexts), err)
}
})
}
@@ -394,7 +399,7 @@ func TestServiceIdempotentlyCreatesConversationWithoutPersistingToken(t *testing
if frozenBundleVersion != "cfg:"+bundle.ID || first.Session.EngineMode != "cascade" {
t.Fatalf("frozen bundle=%q engine=%q", frozenBundleVersion, first.Session.EngineMode)
}
for _, expected := range []string{"服务测试长者", "年轻时从事铁路工作", "老照片", "聊聊第一次参加工作的经历", "放慢语速", "工作经历", "交流边界", "收入", "最近第1次通话摘要", "个体工商户"} {
for _, expected := range []string{"服务测试长者", "年轻时从事铁路工作", "老照片", "聊聊第一次参加工作的经历", "放慢语速", "工作经历", "交流边界", "收入", "最近第1次通话摘要", "个体工商户", "可选续聊线索", "最期待的事"} {
if !strings.Contains(frozenContext, expected) {
t.Fatalf("frozen context %q does not contain %q", frozenContext, expected)
}