feat: one-click and batch execution for suggested evidence queries
This commit is contained in:
parent
63bbf0d755
commit
4abb837aaa
3 changed files with 92 additions and 30 deletions
|
|
@ -788,3 +788,18 @@ def test_visual_assets_are_referenced_for_review_images():
|
|||
"match-web.svg",
|
||||
]:
|
||||
assert not (assets_dir / orphan_asset).exists(), f"{orphan_asset} is unused; do not reintroduce it"
|
||||
|
||||
|
||||
def test_suggested_queries_support_one_click_and_batch_execution():
|
||||
script = _read(APP_JS)
|
||||
styles = _read(STYLES)
|
||||
|
||||
assert "executeSuggestedQueries" in script
|
||||
assert "data-run-suggested-query" in script
|
||||
assert 'id="run-all-suggested-queries"' in script
|
||||
assert "모두 실행" in script
|
||||
assert "바로 실행" in script
|
||||
assert "suggested-query-run-status" in script
|
||||
# 기존 "입력칸 채우기" 버튼은 수정용으로 유지된다.
|
||||
assert "data-suggested-query" in script
|
||||
assert ".suggested-query-item" in styles
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ const knowledgeEntries = [];
|
|||
|
||||
const searchCoverage = {};
|
||||
|
||||
let suggestedQueryRunSummary = null; // { caseId, message } — 현재 케이스의 마지막 추천 쿼리 실행 결과
|
||||
|
||||
const DEFAULT_COVERAGE_THRESHOLDS = Object.freeze({
|
||||
|
||||
coverageGoodRate: 70,
|
||||
|
|
@ -888,59 +890,47 @@ function suggestedEvidenceQueries(submission) {
|
|||
|
||||
|
||||
function renderEvidenceNextActions(submission) {
|
||||
const summary =
|
||||
suggestedQueryRunSummary && suggestedQueryRunSummary.caseId === submission.id
|
||||
? `<div class="inline-status" id="suggested-query-run-result">${escapeHtml(suggestedQueryRunSummary.message)}</div>`
|
||||
: "";
|
||||
|
||||
if (!evidenceNeedsFollowup(submission)) return "";
|
||||
if (!evidenceNeedsFollowup(submission)) return summary;
|
||||
|
||||
const queries = suggestedEvidenceQueries(submission);
|
||||
|
||||
const reasons = evidenceFollowupReasons(submission);
|
||||
|
||||
if (!queries.length) return "";
|
||||
if (!queries.length) return summary;
|
||||
|
||||
return `
|
||||
|
||||
<section class="evidence-next-action-panel" aria-label="근거 보강 추천">
|
||||
|
||||
<div>
|
||||
|
||||
<strong>근거 보강 추천</strong>
|
||||
|
||||
<span>현재 근거가 부족합니다. 추천 쿼리를 선택하면 수동 검색 입력칸에만 채워집니다.</span>
|
||||
|
||||
<span>추천 쿼리를 바로 실행하거나, 쿼리 본문을 눌러 수동 검색 입력칸에서 수정할 수 있습니다.</span>
|
||||
</div>
|
||||
|
||||
<ul class="evidence-followup-reasons">
|
||||
|
||||
${reasons.map((reason) => `<li>${escapeHtml(reason)}</li>`).join("")}
|
||||
|
||||
</ul>
|
||||
|
||||
<div class="suggested-query-list">
|
||||
|
||||
${queries
|
||||
|
||||
.map(
|
||||
|
||||
(query) => `
|
||||
|
||||
<button class="row-action" type="button" data-suggested-query="${escapeHtml(query)}" data-suggested-provider="naver">
|
||||
|
||||
${escapeHtml(query)}
|
||||
|
||||
</button>
|
||||
|
||||
<span class="suggested-query-item">
|
||||
<button class="row-action" type="button" data-suggested-query="${escapeHtml(query)}" data-suggested-provider="naver">
|
||||
${escapeHtml(query)}
|
||||
</button>
|
||||
<button class="row-action" type="button" data-run-suggested-query="${escapeHtml(query)}">바로 실행</button>
|
||||
</span>
|
||||
`,
|
||||
|
||||
)
|
||||
|
||||
.join("")}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="suggested-query-actions">
|
||||
<button class="row-action" type="button" id="run-all-suggested-queries">모두 실행</button>
|
||||
</div>
|
||||
<div id="suggested-query-run-status" class="inline-status" aria-live="polite"></div>
|
||||
${summary}
|
||||
</section>
|
||||
|
||||
`;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -968,6 +958,39 @@ function applySuggestedQuery(button) {
|
|||
}
|
||||
|
||||
|
||||
async function executeSuggestedQueries(queries) {
|
||||
const submission = getSelectedCase();
|
||||
if (!submission || !queries.length) return;
|
||||
|
||||
const statusTarget = document.getElementById("suggested-query-run-status");
|
||||
const naver = providers.find((provider) => provider.id === "naver");
|
||||
if (!naver || !naver.enabled) {
|
||||
if (statusTarget) statusTarget.textContent = "네이버 외부 검색 tool이 비활성입니다.";
|
||||
return;
|
||||
}
|
||||
|
||||
const results = [];
|
||||
for (const [index, query] of queries.entries()) {
|
||||
if (statusTarget) statusTarget.textContent = `"${query}" 실행 중… (${index + 1}/${queries.length})`;
|
||||
try {
|
||||
await apiJson("/api/search/manual", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ submission_id: submission.id, provider: "naver", query }),
|
||||
});
|
||||
results.push(`"${query}" 완료`);
|
||||
} catch (errorValue) {
|
||||
results.push(`"${query}" 실패: ${errorValue.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
suggestedQueryRunSummary = {
|
||||
caseId: submission.id,
|
||||
message: `추천 쿼리 실행 — ${results.join(" · ")}`,
|
||||
};
|
||||
await refreshFromApi();
|
||||
}
|
||||
|
||||
|
||||
|
||||
function filteredSubmissions() {
|
||||
const query = state.filters.query.trim().toLowerCase();
|
||||
|
|
@ -2129,6 +2152,8 @@ function selectCase(caseId) {
|
|||
|
||||
state.selectedCaseId = caseId;
|
||||
|
||||
suggestedQueryRunSummary = null;
|
||||
|
||||
switchView("workbench");
|
||||
|
||||
switchWorkbenchTab("evidence");
|
||||
|
|
@ -3547,6 +3572,18 @@ function bindEvents() {
|
|||
|
||||
|
||||
|
||||
const runSuggestedButton = event.target.closest("[data-run-suggested-query]");
|
||||
if (runSuggestedButton) {
|
||||
void executeSuggestedQueries([runSuggestedButton.dataset.runSuggestedQuery]);
|
||||
return;
|
||||
}
|
||||
|
||||
const runAllSuggestedButton = event.target.closest("#run-all-suggested-queries");
|
||||
if (runAllSuggestedButton) {
|
||||
void executeSuggestedQueries(suggestedEvidenceQueries(getSelectedCase()));
|
||||
return;
|
||||
}
|
||||
|
||||
const rerunQueryButton = event.target.closest("[data-rerun-query]");
|
||||
|
||||
if (rerunQueryButton) {
|
||||
|
|
|
|||
|
|
@ -2758,3 +2758,13 @@ tbody tr.selected-row,
|
|||
grid-column: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.suggested-query-item {
|
||||
display: inline-flex;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.suggested-query-actions {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue