Files
zyt/server/tests/FollowupAudioCitationDateSafetyTest.php
T

49 lines
3.8 KiB
PHP

<?php
declare(strict_types=1);
/** Production validateAnswer regression, synthetic input only; no DB, ASR or model transport. */
require dirname(__DIR__) . '/vendor/autoload.php';
use app\common\service\followupaudio\FollowupAudioPipeline as Pipeline;
use app\common\service\followupaudio\FollowupAudioPolicy as Policy;
use app\common\service\followupaudio\FollowupAudioTranscriptPrompt as Prompt;
if (($override = getenv('FOLLOWUP_AUDIO_POLICY_OVERRIDE')) !== false && $override !== '') { require $override; }
$rows = [];
$failed = [];
$run = static function (string $id, string $text, ?string $date, string $dateText,
?string $expectedDate, string $expectedDateText, bool $review, bool $v1 = false) use (&$rows, &$failed): void {
$segments = [['id' => 'asr_synthetic', 'start_ms' => 0, 'end_ms' => 120000, 'text' => $text]];
$item = ['kind' => 'blood', 'values' => ['systolic_pressure' => 120], 'record_date' => $date,
'record_time' => '07:45', 'date_text' => $dateText, 'time_text' => '七点四十五分',
'time_period' => null, 'time_estimated' => false, 'needs_review' => false];
if ($v1) { $item['evidence'] = [['segment_id' => $segments[0]['id'], 'text' => $text]]; }
else { $item['evidence_ids'] = [Prompt::citations($segments)[0]['id']]; }
$raw = ['schema_version' => $v1 ? 'followup-audio-transcript-v1' : 'followup-audio-transcript-v2',
'summary' => '合成日期安全测试', 'uncertainties' => [], 'items' => [$item]];
$actual = Pipeline::validateAnswer(json_encode($raw, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR),
$text, $segments, '2026-03-01 10:00:00', ['blood' => [['key' => 'systolic_pressure', 'type' => 'number']]]);
$observed = array_intersect_key($actual['items'][0], array_flip(['record_date', 'date_text', 'needs_review', 'time_estimated']));
$expected = ['record_date' => $expectedDate, 'date_text' => $expectedDateText, 'needs_review' => $review, 'time_estimated' => false];
$again = Policy::normalizeExtraction($actual, '2026-03-01 10:00:00');
$pass = Policy::canonical($observed) === Policy::canonical($expected) && $again === $actual;
$rows[] = ['id' => $id, 'input' => $raw, 'synthetic_source' => $text, 'expected' => $expected,
'actual' => $observed, 'renormalization_identical' => $again === $actual, 'passed' => $pass];
if (!$pass) { $failed[] = $id; }
};
$wide = '今天准备联系您。七点四十五分收缩压一百二十。';
$run('wide_window_other_sentence_does_not_fill_abstention', $wide, null, '', null, '', true);
$run('explicit_calendar_without_date_text_is_unbound', $wide, '2026-03-01', '', null, '', true);
$run('whitespace_date_text_is_unbound', $wide, '2026-03-01', ' ', null, '', true);
$run('explicit_null_even_with_label_stays_abstained', $wide, null, '今天', null, '今天', true);
$run('single_sentence_missing_date_still_preserves_model_abstention', '今天七点四十五分收缩压一百二十。', null, '', null, '', true);
$run('complete_explicit_date_still_checked', '今天七点四十五分收缩压一百二十。', '2026-03-01', '今天', '2026-03-01', '今天', false);
$run('conflicting_explicit_date_still_cleared', '昨天七点四十五分收缩压一百二十。', '2026-03-01', '昨天', null, '昨天', true);
$run('v1_response_in_strict_pipeline_also_preserves_abstention', $wide, null, '', null, '', true, true);
$run('unrelated_yesterday_sentence_not_assigned', '昨天已经联系完成。七点四十五分收缩压一百二十。', null, '', null, '', true);
echo json_encode(['suite' => 'followup-audio-citation-date-safety', 'synthetic_only' => true,
'database_calls' => 0, 'transport_calls' => 0, 'cases' => $rows, 'passed' => count($rows) - count($failed),
'total' => count($rows), 'failures' => $failed], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR) . PHP_EOL;
exit($failed === [] ? 0 : 1);