feat(deepseek): normalize model labels and unwrap redirect URLs

- Normalize provider_model so UI shows DeepSeek/DeepSeek-R1 instead of raw toggle text (联网搜索 etc.)
- Unwrap DeepSeek redirect URLs (chat.deepseek.com/api/redirect?url=...) to canonical sources
- Expand reasoning sections and separate reasoning search pages from answer-body citations
- Improve webpages-trigger click heuristics and auxiliary link filtering in answer rendering

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-24 23:37:16 +08:00
parent bc2e23cfcb
commit 4f3c39f5f5
3 changed files with 940 additions and 123 deletions
@@ -220,6 +220,39 @@ function statusLabel(status: string): string {
return t(`tracking.status.${status}`);
}
function resolveDeepSeekProviderModelLabel(value: string | null | undefined): string {
const normalized = String(value ?? "").trim();
if (!normalized || normalized === "--") {
return "DeepSeek";
}
const compact = normalized.replace(/\s+/g, "").toLowerCase();
if (/r1|reasoner|deepthink|深度思考/.test(compact)) {
return "DeepSeek";
}
const versionMatch =
normalized.match(/deepseek[-_\s]*(r1|v\d+(?:\.\d+)?)/i)
?? normalized.match(/\b(r1|v\d+(?:\.\d+)?)\b/i);
if (versionMatch?.[1]) {
return `DeepSeek-${versionMatch[1].toUpperCase()}`;
}
if (/search|web|联网|搜索/.test(compact)) {
return "DeepSeek";
}
return normalized;
}
function resolveAnswerProviderModel(platform: MonitoringQuestionDetailPlatform | null): string {
if (isDeepSeekInlineCitationPlatform(platform?.ai_platform_id)) {
return resolveDeepSeekProviderModelLabel(platform?.provider_model);
}
return String(platform?.provider_model ?? "").trim() || "--";
}
function resolveCitationTitle(citation: MonitoringQuestionDetailCitation): string {
return citation.cited_title || citation.article_title || citation.cited_url;
}
@@ -374,6 +407,78 @@ function buildCitationSourceLookup(platform: MonitoringQuestionDetailPlatform |
return lookup;
}
function buildDeepSeekAnswerLinkSourceLookup(
platform: MonitoringQuestionDetailPlatform | null,
): Map<string, { title: string | null; siteName: string | null }> {
const lookup = new Map<string, { title: string | null; siteName: string | null }>();
for (const item of platform?.inline_citations ?? []) {
const normalizedURL = normalizeInlineCitationUrl(item.normalized_url ?? item.url);
if (!normalizedURL || lookup.has(normalizedURL)) {
continue;
}
lookup.set(normalizedURL, {
title: item.title?.trim() || null,
siteName: item.site_name?.trim() || null,
});
}
for (const citation of platform?.citations ?? []) {
const normalizedURL = normalizeInlineCitationUrl(citation.cited_url);
if (!normalizedURL || lookup.has(normalizedURL)) {
continue;
}
lookup.set(normalizedURL, {
title: resolveCitationTitle(citation),
siteName: citation.site_name?.trim() || null,
});
}
return lookup;
}
function isHiddenDeepSeekAnswerNode(element: Element): boolean {
let current: Element | null = element;
for (let depth = 0; current && depth < 8; depth += 1) {
if (current.hasAttribute("hidden") || current.getAttribute("aria-hidden") === "true") {
return true;
}
const style = current.getAttribute("style") ?? "";
if (/(display\s*:\s*none|visibility\s*:\s*hidden|opacity\s*:\s*0)/i.test(style)) {
return true;
}
current = current.parentElement;
}
return false;
}
function isInsideDeepSeekAuxiliaryLinkContainer(anchor: HTMLAnchorElement): boolean {
let current = anchor.parentElement;
for (let depth = 0; current && depth < 8; depth += 1) {
const descriptor = `${current.className || ""} ${current.getAttribute("data-testid") || ""} ${current.getAttribute("role") || ""}`;
if (/(search-view-card|search-result|result-card|source-card|reference-card|citation-card|popover|tooltip|dropdown|floating|hover-card|preview-card)/i.test(descriptor)) {
return true;
}
current = current.parentElement;
}
return false;
}
function shouldCountDeepSeekAnswerLink(anchor: HTMLAnchorElement): boolean {
if (isHiddenDeepSeekAnswerNode(anchor) || isInsideDeepSeekAuxiliaryLinkContainer(anchor)) {
return false;
}
const text = String(anchor.textContent ?? "").trim();
if (normalizeDeepSeekCitationLabel(text)) {
return true;
}
if (shouldStripDeepSeekOrphanAnchor(anchor)) {
return false;
}
return /[A-Za-z0-9\u3400-\u9fff]/.test(text);
}
function normalizeDeepSeekCitationLabel(value: string | null | undefined): string | null {
const normalized = String(value ?? "").trim().replace(/\s+/g, " ");
if (!normalized) {
@@ -416,13 +521,16 @@ function pushUniqueDeepSeekCitationLabel(labels: string[], label: string): void
function shouldStripDeepSeekOrphanAnchor(anchor: HTMLAnchorElement): boolean {
const text = String(anchor.textContent ?? "").trim().replace(/\s+/g, "");
if (/^(link|source|citation|reference|open|原文|链接|来源|引用)$/i.test(text)) {
return true;
}
if (/[A-Za-z0-9\u3400-\u9fff]/.test(text)) {
return false;
}
if (anchor.querySelector("img, svg")) {
return true;
}
return text.length === 0 || /^[\[\](){}<>#.,:;!?+\-_/\\|~`'"“”‘’·•::,。!?;、🔗]+$/.test(text);
return text.length === 0 || /^[\[\](){}<>#.,:;!?+\-_/\\|~`'"“”‘’·•::,。!?;、🔗↗↘↙↖↪⤴]+$/.test(text);
}
function collectDeepSeekCitationReferenceLabels(
@@ -489,6 +597,17 @@ function resolveCitationIndexDisplayLabels(
return labels;
}
function shouldShowCitationSourceListIndex(
citationIndex: number,
platform: MonitoringQuestionDetailPlatform | null,
): boolean {
if (!isDeepSeekInlineCitationPlatform(platform?.ai_platform_id)) {
return true;
}
return resolveCitationIndexDisplayLabels(citationIndex, platform).length === 0;
}
function countHanCharacters(value: string): number {
return value.match(/[\u3400-\u9fff]/g)?.length ?? 0;
}
@@ -575,6 +694,8 @@ function renderDeepSeekAnswerContent(
if (!source && !citationSource) {
if (shouldStripDeepSeekOrphanAnchor(anchor)) {
anchor.remove();
} else {
anchor.replaceWith(document.createTextNode(anchor.textContent ?? ""));
}
continue;
}
@@ -657,28 +778,26 @@ function collectDeepSeekInlineCitationOccurrences(
return [];
}
const inlineCitationLookup = buildInlineCitationSourceLookup(platform);
if (!inlineCitationLookup.size) {
return [];
}
const sourceLookup = buildDeepSeekAnswerLinkSourceLookup(platform);
const counts = new Map<string, { label: string | null; siteName: string | null; citationCount: number }>();
for (const anchor of Array.from(root.querySelectorAll<HTMLAnchorElement>("a[href]"))) {
const normalizedURL = normalizeInlineCitationUrl(anchor.getAttribute("href") || anchor.href || "");
if (!normalizedURL || !inlineCitationLookup.has(normalizedURL)) {
if (!normalizedURL || !shouldCountDeepSeekAnswerLink(anchor)) {
continue;
}
const source = inlineCitationLookup.get(normalizedURL) ?? null;
const source = sourceLookup.get(normalizedURL) ?? null;
const existing = counts.get(normalizedURL);
if (existing) {
// One row per URL, but every visible answer occurrence contributes to the count.
existing.citationCount += 1;
continue;
}
counts.set(normalizedURL, {
label: source?.title?.trim() || null,
siteName: source?.site_name?.trim() || null,
label: source?.title || anchor.getAttribute("title")?.trim() || null,
siteName: source?.siteName || null,
citationCount: 1,
});
}
@@ -996,7 +1115,7 @@ const contentCitationColumns = computed(() => [
<div class="tracking-question-answer__meta">
<span>{{ activePlatform?.sampled_at ? formatDateTime(activePlatform.sampled_at) : "--" }}</span>
<span>{{ activePlatform?.provider_model ?? "--" }}</span>
<span>{{ resolveAnswerProviderModel(activePlatform) }}</span>
</div>
</div>
</section>
@@ -1031,6 +1150,7 @@ const contentCitationColumns = computed(() => [
{{ label }}
</span>
<span
v-if="shouldShowCitationSourceListIndex(index, activePlatform)"
class="tracking-question-source-card__index"
:class="{ 'tracking-question-source-card__index--secondary': resolveCitationIndexDisplayLabels(index, activePlatform).length > 0 }"
>