feat(prompts): add prompts loader and configuration management

- Implemented a new prompts loader in `loader.go` to manage prompt templates and configurations.
- Introduced caching mechanism for prompt configurations to optimize loading.
- Added functions to apply platform-specific prompt template overrides.
- Created unit tests in `loader_test.go` to validate prompt configuration loading and reloading behavior.
- Ensured that the last valid configuration is retained in case of errors during reload.
This commit is contained in:
2026-04-13 16:08:12 +08:00
parent 6066f43a7d
commit 1b01caac0f
21 changed files with 1630 additions and 392 deletions
@@ -142,6 +142,7 @@ const saveMutation = useMutation({
queryClient.invalidateQueries({ queryKey: ["workspace"] }),
queryClient.invalidateQueries({ queryKey: ["templates"] }),
queryClient.invalidateQueries({ queryKey: ["articles", "detail", articleId.value] }),
queryClient.invalidateQueries({ queryKey: ["articles", "detail", articleId.value, "publish-modal"] }),
]);
},
onError: (error) => {
@@ -29,6 +29,7 @@ const questionId = computed(() => parsePositiveNumber(route.params.questionId));
const questionHash = computed(() => normalizeQueryValue(route.query.question_hash));
const questionTitleFallback = computed(() => normalizeQueryValue(route.query.question_text));
const keywordId = computed(() => normalizeQueryValue(route.query.keyword_id));
const requestedPlatformId = computed(() => normalizeQueryValue(route.query.ai_platform_id));
const businessDate = computed(() => normalizeTrackingBusinessDate(route.query.business_date) || trackingToday);
const dateFrom = computed(() => {
return normalizeQueryValue(route.query.date_from) || businessDate.value;
@@ -44,6 +45,7 @@ const detailQuery = useQuery({
brandId.value,
questionId.value,
questionHash.value,
requestedPlatformId.value,
dateFrom.value,
dateTo.value,
]),
@@ -53,6 +55,7 @@ const detailQuery = useQuery({
date_from: dateFrom.value,
date_to: dateTo.value,
question_hash: questionHash.value || undefined,
ai_platform_id: requestedPlatformId.value || undefined,
}),
});
@@ -66,9 +69,8 @@ watch(
return;
}
const requestedPlatformId = normalizeQueryValue(route.query.ai_platform_id);
const requestedPlatform = requestedPlatformId
? platforms.find((item) => item.ai_platform_id === requestedPlatformId)
const requestedPlatform = requestedPlatformId.value
? platforms.find((item) => item.ai_platform_id === requestedPlatformId.value)
: null;
const sampledPlatform = platforms.find((item) => item.sample_status === "sampled");
const nextPlatform = requestedPlatform ?? sampledPlatform ?? platforms[0];
@@ -130,6 +132,7 @@ function goBack(): void {
query: {
brand_id: brandId.value ? String(brandId.value) : undefined,
keyword_id: keywordId.value || undefined,
ai_platform_id: requestedPlatformId.value || undefined,
business_date: businessDate.value,
},
});
+54 -2
View File
@@ -72,11 +72,26 @@ const platformAppearanceMap: Record<string, { color: string; surface: string; gl
kimi: { color: "#111827", surface: "#f3f4f6", glyph: "K" },
};
const trackingPlatformOptions = [
{ label: "全部", value: "all" },
{ label: "DeepSeek", value: "deepseek" },
{ label: "千问", value: "qwen" },
{ label: "豆包", value: "doubao" },
] as const;
const trackingPlatformIds = new Set<string>(
trackingPlatformOptions
.map((item) => item.value)
.filter((value) => value !== "all"),
);
const selectedBrandId = useStorage<number | null>("tracking_selected_brand", null);
const selectedKeywordId = useStorage<number | null>("tracking_selected_keyword", null);
const selectedPlatformId = useStorage<string>("tracking_selected_ai_platform_id", "all");
const selectedBusinessDate = useStorage<string>("tracking_selected_business_date", trackingToday);
const pluginPreparing = ref(false);
selectedPlatformId.value = normalizeTrackingPlatformId(selectedPlatformId.value);
selectedBusinessDate.value = normalizeTrackingBusinessDate(selectedBusinessDate.value) ?? trackingToday;
const brandsQuery = useQuery({
@@ -151,17 +166,28 @@ watch(
{ immediate: true },
);
watch([selectedBrandId, selectedKeywordId, selectedBusinessDate], ([brandId, keywordId, businessDate]) => {
watch(
() => route.query.ai_platform_id,
(value) => {
selectedPlatformId.value = normalizeTrackingPlatformId(value);
},
{ immediate: true },
);
watch([selectedBrandId, selectedKeywordId, selectedPlatformId, selectedBusinessDate], ([brandId, keywordId, platformId, businessDate]) => {
const nextBrandId = brandId ? String(brandId) : undefined;
const nextKeywordId = keywordId ? String(keywordId) : undefined;
const nextPlatformId = platformId !== "all" ? platformId : undefined;
const nextBusinessDate = normalizeTrackingBusinessDate(businessDate) ?? trackingToday;
const currentBrandId = normalizeQueryValue(route.query.brand_id) || undefined;
const currentKeywordId = normalizeQueryValue(route.query.keyword_id) || undefined;
const currentPlatformId = normalizeTrackingPlatformId(route.query.ai_platform_id);
const currentBusinessDate = normalizeTrackingBusinessDate(route.query.business_date) ?? trackingToday;
if (
currentBrandId === nextBrandId &&
currentKeywordId === nextKeywordId &&
currentPlatformId === (nextPlatformId ?? "all") &&
currentBusinessDate === nextBusinessDate
) {
return;
@@ -172,6 +198,7 @@ watch([selectedBrandId, selectedKeywordId, selectedBusinessDate], ([brandId, key
query: {
...(nextBrandId ? { brand_id: nextBrandId } : {}),
...(nextKeywordId ? { keyword_id: nextKeywordId } : {}),
...(nextPlatformId ? { ai_platform_id: nextPlatformId } : {}),
business_date: nextBusinessDate,
},
});
@@ -208,13 +235,21 @@ async function ensureMonitoringPluginReady(options?: { silent?: boolean }): Prom
}
const dashboardQuery = useQuery({
queryKey: computed(() => ["tracking", "dashboard", selectedBrandId.value, selectedKeywordId.value, selectedBusinessDate.value]),
queryKey: computed(() => [
"tracking",
"dashboard",
selectedBrandId.value,
selectedKeywordId.value,
selectedPlatformId.value,
selectedBusinessDate.value,
]),
enabled: computed(() => Boolean(selectedBrandId.value)),
queryFn: () =>
monitoringApi.dashboardComposite({
brand_id: selectedBrandId.value ?? undefined,
keyword_id: selectedKeywordId.value,
days: trackingMaxHistoryDays,
ai_platform_id: selectedPlatformId.value !== "all" ? selectedPlatformId.value : undefined,
business_date: selectedBusinessDate.value,
}),
});
@@ -451,6 +486,7 @@ function openQuestion(question: MonitoringHotQuestion): void {
business_date: selectedBusinessDate.value,
date_from: selectedBusinessDate.value,
date_to: selectedBusinessDate.value,
...(selectedPlatformId.value !== "all" ? { ai_platform_id: selectedPlatformId.value } : {}),
...(selectedKeywordId.value ? { keyword_id: String(selectedKeywordId.value) } : {}),
},
});
@@ -554,6 +590,14 @@ function normalizeQueryValue(value: unknown): string {
return String(Array.isArray(value) ? value[0] ?? "" : value ?? "").trim();
}
function normalizeTrackingPlatformId(value: unknown): string {
const normalized = normalizeQueryValue(value).toLowerCase();
if (!normalized) {
return "all";
}
return trackingPlatformIds.has(normalized) ? normalized : "all";
}
function normalizeTrackingBusinessDate(value: unknown): string | null {
const normalized = normalizeQueryValue(value);
if (!normalized) {
@@ -588,6 +632,14 @@ function disableTrackingDate(current: dayjs.Dayjs): boolean {
>
<template #actions>
<a-space wrap align="center" class="tracking-actions-wrap">
<div class="tracking-action-item">
<span class="tracking-action-label">平台</span>
<a-select
v-model:value="selectedPlatformId"
class="tracking-select"
:options="trackingPlatformOptions"
/>
</div>
<div class="tracking-action-item">
<span class="tracking-action-label">品牌</span>
<a-select