Add configurable evidence guard for resume tailoring
This commit is contained in:
@@ -113,6 +113,93 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="space-y-3 rounded-lg border p-3">
|
||||
<div>
|
||||
<div class="font-medium">Tailoring configuration</div>
|
||||
<p class="mt-1 text-xs text-muted-foreground">
|
||||
Set the app-wide evidence guard, then choose the mode for this rewrite.
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
class="flex w-full items-center gap-3 rounded-md border p-3 text-left"
|
||||
:class="
|
||||
globalEvidenceGuard
|
||||
? 'border-emerald-600 bg-emerald-50'
|
||||
: 'border-orange-500 bg-orange-50'
|
||||
"
|
||||
type="button"
|
||||
:aria-pressed="globalEvidenceGuard"
|
||||
@click="toggleGlobalEvidenceGuard"
|
||||
>
|
||||
<span
|
||||
class="relative h-5 w-9 shrink-0 rounded-full transition-colors"
|
||||
:class="globalEvidenceGuard ? 'bg-emerald-600' : 'bg-orange-500'"
|
||||
>
|
||||
<span
|
||||
class="absolute left-0.5 top-0.5 size-4 rounded-full bg-white shadow transition-transform"
|
||||
:class="globalEvidenceGuard ? 'translate-x-4' : ''"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
<strong class="block text-xs">
|
||||
{{
|
||||
globalEvidenceGuard
|
||||
? "App-wide evidence guard on"
|
||||
: "App-wide evidence guard off"
|
||||
}}
|
||||
</strong>
|
||||
<small class="mt-0.5 block text-xs text-muted-foreground">
|
||||
{{
|
||||
globalEvidenceGuard
|
||||
? "Claim-level evidence rules are available across Signal."
|
||||
: "All tailoring and revision uses profile-based rules."
|
||||
}}
|
||||
</small>
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="flex w-full items-center gap-3 rounded-md border bg-muted/40 p-3 text-left"
|
||||
:class="evidenceMode === 'profile' ? 'border-orange-500 bg-orange-50' : ''"
|
||||
type="button"
|
||||
:disabled="!globalEvidenceGuard"
|
||||
:aria-pressed="evidenceMode === 'profile'"
|
||||
:aria-disabled="!globalEvidenceGuard"
|
||||
@click="evidenceMode = evidenceMode === 'strict' ? 'profile' : 'strict'"
|
||||
>
|
||||
<span
|
||||
class="relative h-5 w-9 shrink-0 rounded-full transition-colors"
|
||||
:class="evidenceMode === 'profile' ? 'bg-orange-500' : 'bg-gray-400'"
|
||||
>
|
||||
<span
|
||||
class="absolute left-0.5 top-0.5 size-4 rounded-full bg-white shadow transition-transform"
|
||||
:class="evidenceMode === 'profile' ? 'translate-x-4' : ''"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
<strong class="block text-xs">
|
||||
{{
|
||||
evidenceMode === "profile"
|
||||
? "Flexible profile-based rewrite"
|
||||
: "Strict source evidence"
|
||||
}}
|
||||
</strong>
|
||||
<small class="mt-0.5 block text-xs text-muted-foreground">
|
||||
{{
|
||||
evidenceMode === "profile"
|
||||
? globalEvidenceGuard
|
||||
? "Uses the full resume profile; claim-level IDs are optional."
|
||||
: "Forced by the app-wide setting; claim-level IDs are optional."
|
||||
: "Every claim must cite a supporting profile fact."
|
||||
}}
|
||||
</small>
|
||||
</span>
|
||||
</button>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
Flexible mode still excludes employers, skills, dates, metrics, and
|
||||
achievements that are absent from this resume or your added context.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<UiAlert v-if="errorMessage" variant="destructive">
|
||||
<UiAlertTitle>Signal could not complete the request</UiAlertTitle>
|
||||
<UiAlertDescription>{{ errorMessage }}</UiAlertDescription>
|
||||
@@ -185,6 +272,8 @@ const jobURL = ref("");
|
||||
const jobText = ref("");
|
||||
const about = ref("");
|
||||
const tailoringStrength = ref(50);
|
||||
const evidenceMode = ref<"strict" | "profile">("strict");
|
||||
const globalEvidenceGuard = ref(true);
|
||||
const busy = ref(false);
|
||||
const errorMessage = ref("");
|
||||
const successMessage = ref("");
|
||||
@@ -209,10 +298,50 @@ const strengthDescription = computed(() => {
|
||||
return "Fuller supported detail and stronger job-aligned wording without invention.";
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
onMounted(async () => {
|
||||
hasBackup.value = Boolean(localStorage.getItem(backupKey.value));
|
||||
try {
|
||||
const response = await fetch(`${apiBase.value}/api/settings`);
|
||||
if (!response.ok) return;
|
||||
const settings = (await response.json()) as { evidence_guard: boolean };
|
||||
globalEvidenceGuard.value = settings.evidence_guard;
|
||||
if (!settings.evidence_guard) evidenceMode.value = "profile";
|
||||
} catch {
|
||||
// The API error will be surfaced if the user starts a tailoring request.
|
||||
}
|
||||
});
|
||||
|
||||
const toggleGlobalEvidenceGuard = async () => {
|
||||
const previous = globalEvidenceGuard.value;
|
||||
const next = !previous;
|
||||
globalEvidenceGuard.value = next;
|
||||
if (!next) evidenceMode.value = "profile";
|
||||
errorMessage.value = "";
|
||||
|
||||
try {
|
||||
const response = await fetch(`${apiBase.value}/api/settings`, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ evidence_guard: next })
|
||||
});
|
||||
const settings = (await response.json()) as {
|
||||
evidence_guard?: boolean;
|
||||
detail?: string;
|
||||
};
|
||||
if (!response.ok) {
|
||||
throw new Error(settings.detail || "Could not update the Signal settings.");
|
||||
}
|
||||
globalEvidenceGuard.value = Boolean(settings.evidence_guard);
|
||||
if (!globalEvidenceGuard.value) evidenceMode.value = "profile";
|
||||
successMessage.value = globalEvidenceGuard.value
|
||||
? "Evidence guard enabled across Signal."
|
||||
: "Evidence guard disabled across Signal; profile truth rules remain active.";
|
||||
} catch (error) {
|
||||
globalEvidenceGuard.value = previous;
|
||||
errorMessage.value = error instanceof Error ? error.message : "Unknown error.";
|
||||
}
|
||||
};
|
||||
|
||||
async function post<T>(path: string, payload: Record<string, unknown>): Promise<T> {
|
||||
const response = await fetch(`${apiBase.value}${path}`, {
|
||||
method: "POST",
|
||||
@@ -300,6 +429,7 @@ const tailorResume = async () => {
|
||||
markdown: data.markdown,
|
||||
about: about.value,
|
||||
tailoring_strength: tailoringStrength.value,
|
||||
evidence_mode: evidenceMode.value,
|
||||
...jobPayload()
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user