feat: enable explicitly authorized human-reviewed clinical adoption
This commit is contained in:
@@ -23,11 +23,13 @@ final class FollowupAudioLogic
|
||||
$ready = Store::ready();
|
||||
$enabled = Store::enabled() && $scope;
|
||||
$verified = Store::verified();
|
||||
$manualReady = Gate::manualReviewReady();
|
||||
$daily = Access::canDaily($actor, $info);
|
||||
return [
|
||||
'enabled' => $enabled, 'audio_verified' => $verified,
|
||||
'manual_review_enabled' => Gate::manualReviewEnabled(), 'manual_review_ready' => $manualReady,
|
||||
'preview_only' => $preview, 'preview_ready' => $preview && $ready, 'test_scope_allowed' => $scope,
|
||||
'can_upload' => $enabled && $ready, 'can_review' => $enabled && $ready, 'can_apply' => $enabled && $verified && !$preview,
|
||||
'can_upload' => $enabled && $ready, 'can_review' => $enabled && $ready, 'can_apply' => $enabled && ($verified || $manualReady) && !$preview,
|
||||
'can_daily' => $daily, 'limits' => Upload::limits(),
|
||||
'models' => $enabled ? ProviderConfig::readyModels() : [],
|
||||
// No migration/dictionary dependency is introduced when the feature is OFF.
|
||||
|
||||
@@ -42,6 +42,23 @@ final class FollowupAudioGate
|
||||
if (!self::scopeAllowed($diagnosisId, $actor, $settings)) { throw new DomainException('FOLLOWUP_AUDIO_PREVIEW_SCOPE_DENIED'); }
|
||||
}
|
||||
|
||||
/** Explicit human-adoption operation is separate from model accuracy certification. */
|
||||
public static function manualReviewEnabled(?array $settings = null): bool
|
||||
{
|
||||
return (($settings ?? (array) config('followup_audio', []))['manual_review_enabled'] ?? false) === true;
|
||||
}
|
||||
|
||||
public static function manualReviewReady(?string $profile = null, ?array $settings = null, ?array $legacy = null): bool
|
||||
{
|
||||
$settings = $settings ?? (array) config('followup_audio', []); $legacy = $legacy ?? (array) config('prescription_ai', []);
|
||||
if (self::previewOnly($settings) || !self::manualReviewEnabled($settings)) { return false; }
|
||||
if ($profile === null) {
|
||||
foreach (['qwen', 'openai'] as $slot) { if (self::manualReviewReady($slot, $settings, $legacy)) { return true; } }
|
||||
return false;
|
||||
}
|
||||
return FollowupAudioProviderConfig::previewVerified($profile, $settings, $legacy);
|
||||
}
|
||||
|
||||
public static function ready(?string $profile = null, ?array $settings = null, ?array $legacy = null): bool
|
||||
{
|
||||
$settings = $settings ?? (array) config('followup_audio', []); $legacy = $legacy ?? (array) config('prescription_ai', []);
|
||||
@@ -50,7 +67,7 @@ final class FollowupAudioGate
|
||||
return false;
|
||||
}
|
||||
return self::previewOnly($settings) ? FollowupAudioProviderConfig::previewVerified($profile, $settings, $legacy)
|
||||
: FollowupAudioProviderConfig::verified($profile, $settings, $legacy);
|
||||
: FollowupAudioProviderConfig::verified($profile, $settings, $legacy) || self::manualReviewReady($profile, $settings, $legacy);
|
||||
}
|
||||
|
||||
/** Persisted preview-origin tasks never become eligible for adoption after a configuration change. */
|
||||
|
||||
@@ -113,6 +113,9 @@ final class FollowupAudioStore
|
||||
private static function reuse(array $task, string $recordedAt): array
|
||||
{
|
||||
$message = '同一诊单、录音内容及模型已有任务,已复用原任务,不会再次调用模型。';
|
||||
if (!FollowupAudioGate::previewOnly() && FollowupAudioGate::taskPreview($task)) {
|
||||
$message .= '这是历史测试预览任务,保持测试用途;本次未创建业务任务,原任务不提供确认入单。';
|
||||
}
|
||||
if (!self::providerMatches($task)) {
|
||||
$message .= '原任务的服务配置已变更或缺少配置快照,不能自动改用新服务重跑。';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user