'测试古籍', 'chapter' => '消渴篇', 'path' => '123-测试古籍.txt', 'excerpt' => '原文中谈到消渴的历史记载。', 'line' => 42, 'commit' => TcmAncientBooksReference::COMMIT, 'url' => 'https://github.com/xiaopangxia/TCM-Ancient-Books/blob/' . TcmAncientBooksReference::COMMIT . '/123-%E6%B5%8B%E8%AF%95%E5%8F%A4%E7%B1%8D.txt#L42', ]; $localizedSource = $source; $localizedSource['url'] = ''; $localized = TcmAncientBooksReference::localizeSources([$source]); provenanceExpect($localized === [$localizedSource], 'saved citation drops external URL without losing original text or location'); $json = json_encode([$source], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); $diagnosisFormat = new ReflectionMethod(DiagnosisAiLogic::class, 'formatReportRow'); $diagnosis = $diagnosisFormat->invoke(null, [ 'report_content' => '{}', 'prompt_version' => 'patient-context-case-explain-nihaixia-ancient-v4', 'ancient_book_sources_json' => $json, ], ''); provenanceExpect($diagnosis['ancient_book_sources'] === [$localizedSource], 'diagnosis report localizes saved book and line'); $oldDiagnosis = $diagnosisFormat->invoke(null, ['report_content' => '{}', 'prompt_version' => 'legacy-v2'], ''); provenanceExpect($oldDiagnosis['ancient_book_sources'] === [], 'old diagnosis report gets no ancient citation'); $patientFormat = new ReflectionMethod(PatientAiReportLogic::class, 'formatReportRow'); $patient = $patientFormat->invoke(null, [ 'report_json' => '{}', 'prompt_version' => 'patient-longitudinal-report-nihaixia-ancient-v4', 'ancient_book_sources_json' => $json, ]); provenanceExpect($patient['ancient_book_sources'] === [$localizedSource], 'patient report localizes saved book and line'); $oldPatient = $patientFormat->invoke(null, ['report_json' => '{}', 'prompt_version' => 'legacy-v2']); provenanceExpect($oldPatient['ancient_book_sources'] === [], 'old patient report gets no ancient citation'); $anotherSource = $source; $anotherSource['path'] = '124-测试古籍二.txt'; $anotherSource['line'] = 88; $anotherSource['url'] = 'https://github.com/xiaopangxia/TCM-Ancient-Books/blob/' . TcmAncientBooksReference::COMMIT . '/124-%E6%B5%8B%E8%AF%95%E5%8F%A4%E7%B1%8D%E4%BA%8C.txt#L88'; $mergeSources = new ReflectionMethod(PatientAiReportLogic::class, 'mergeAncientBookSources'); $merged = $mergeSources->invoke(null, [$localizedSource], ['ancient_book_sources' => [$source, $anotherSource]]); $localizedAnother = $anotherSource; $localizedAnother['url'] = ''; provenanceExpect($merged === [$localizedSource, $localizedAnother], 'different local file lines remain distinct while duplicates collapse'); $prescriptionBody = [ 'report' => ['diagnosis' => '保留原有报告'], 'knowledge_source' => NihaixiaClinicalSkill::knowledgeSource(), 'ancient_book_sources' => [$source], ]; $prescriptionBody['knowledge_source']['url'] = 'https://github.com/coinweth/nihaixia-skill/blob/' . NihaixiaClinicalSkill::COMMIT . '/SKILL.md'; $localizePrescription = new ReflectionMethod(PrescriptionAiLogic::class, 'localizeSourceMetadata'); $prescription = $localizePrescription->invoke(null, $prescriptionBody); provenanceExpect($prescription['report'] === $prescriptionBody['report'], 'prescription report content is preserved'); provenanceExpect($prescription['knowledge_source']['url'] === '' && $prescription['knowledge_source']['path'] === 'SKILL.md', 'saved prescription skill source is local'); provenanceExpect($prescription['ancient_book_sources'] === [$localizedSource], 'saved prescription book citation is local'); echo "TCM ancient-books report provenance contract passed.\n";