Files
deerflow-code/frontend-web/src/core/system-settings/api.ts
T

1612 lines
48 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { apiFetch } from "@/core/api";
import { getBackendBaseURL } from "@/core/config";
import { withKnowledgeRequestTimeout } from "@/core/llmwiki/request-timeout";
import { OptType, writeYLog } from "@/core/log/y-log";
export type WordHeadingNumberingStyle =
| "hybrid_decimal"
| "official_chinese"
| "decimal"
| "chapter_section"
| "none";
export interface PromptPrefixSettings {
image_generation_unavailable_hint_enabled: boolean;
python_dependency_delete_protection_enabled: boolean;
enabled: boolean;
switch_label: string;
prompt_prefix: string;
ordinary_qa_markdown_format_enabled: boolean;
word_heading_numbering_style: WordHeadingNumberingStyle;
}
export type KnowledgeBaseProductVersion = "v1" | "v2";
export type OrdinaryKnowledgeLanding = "wiki" | "weknora";
export interface StrategyProjectSettings {
taiwan_strait_demo_enabled: boolean;
}
export interface StrategyProjectSettingsUpdate {
taiwan_strait_demo_enabled?: boolean;
}
export interface GeneralQaPageDescription {
welcome_title: string;
welcome_description: string;
input_placeholder: string;
}
export interface GeneralQaPageDescriptions {
full: GeneralQaPageDescription;
compact: GeneralQaPageDescription;
}
export interface PageHeaderDescription {
title: string;
description: string;
}
export interface AIWritingGuideCard {
title: string;
description: string;
}
export interface AIWritingPageDescription extends PageHeaderDescription {
guide_cards: AIWritingGuideCard[];
}
export interface PageDescriptionsSettings {
general_qa: GeneralQaPageDescriptions;
knowledge_base: PageHeaderDescription;
ai_writing: AIWritingPageDescription;
strategy_projects: PageHeaderDescription;
deep_research: PageHeaderDescription;
gongwen: PageHeaderDescription;
light_app_management: PageHeaderDescription;
light_app_list: PageHeaderDescription;
agents: PageHeaderDescription;
business_chains: PageHeaderDescription;
skills: PageHeaderDescription;
knowledge_management: PageHeaderDescription;
}
export type PageDescriptionsUpdate = PageDescriptionsSettings;
export async function loadPageDescriptionsSettings(): Promise<PageDescriptionsSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/page-descriptions`,
);
if (!response.ok) {
throw new Error(`无法读取页面描述配置(HTTP ${response.status})`);
}
return (await response.json()) as PageDescriptionsSettings;
}
export async function updatePageDescriptionsSettings(
body: PageDescriptionsUpdate,
): Promise<PageDescriptionsSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/page-descriptions`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
},
);
if (!response.ok) {
const detail = await response
.json()
.catch(() => ({ detail: "保存页面描述配置失败" }));
throw new Error(detail.detail ?? "保存页面描述配置失败");
}
const data = (await response.json()) as PageDescriptionsSettings;
writeYLog({
optType: OptType.edit,
optDetail: "CM助手-全量-系统设置-页面描述配置编辑",
url: "/api/system-settings/page-descriptions",
reqParam: body,
returnParam: data,
});
return data;
}
export async function loadStrategyProjectSettings(): Promise<StrategyProjectSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/strategy-projects`,
);
if (!response.ok) {
throw new Error(`无法读取策略项目设置(HTTP ${response.status})`);
}
return (await response.json()) as StrategyProjectSettings;
}
export async function updateStrategyProjectSettings(
body: StrategyProjectSettingsUpdate,
): Promise<StrategyProjectSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/strategy-projects`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
},
);
if (!response.ok) {
const detail = await response
.json()
.catch(() => ({ detail: "保存策略项目设置失败" }));
throw new Error(detail.detail ?? "保存策略项目设置失败");
}
return (await response.json()) as StrategyProjectSettings;
}
export async function loadOrdinaryKnowledgeLanding(signal?: AbortSignal): Promise<OrdinaryKnowledgeLanding> {
return withKnowledgeRequestTimeout(async (requestSignal) => {
const response = await apiFetch(`${getBackendBaseURL()}/api/system-settings/ordinary-knowledge-landing`, { signal: requestSignal });
if (!response.ok) throw new Error("无法读取普通知识库默认页面设置");
return ((await response.json()) as { mode: OrdinaryKnowledgeLanding }).mode;
}, { signal, timeoutMs: 10_000 });
}
export async function saveOrdinaryKnowledgeLanding(mode: OrdinaryKnowledgeLanding): Promise<OrdinaryKnowledgeLanding> {
const response = await apiFetch(`${getBackendBaseURL()}/api/system-settings/ordinary-knowledge-landing`, {
method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ mode }),
});
if (!response.ok) throw new Error("保存失败,仅管理员可配置普通知识库默认页面");
return ((await response.json()) as { mode: OrdinaryKnowledgeLanding }).mode;
}
export async function loadKnowledgeBaseProductVersion(signal?: AbortSignal): Promise<KnowledgeBaseProductVersion> {
return withKnowledgeRequestTimeout(async (requestSignal) => {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/knowledge-base-product-version`,
{ signal: requestSignal },
);
if (!response.ok) throw new Error(`无法读取助手知识库访问设置(HTTP ${response.status})`);
return ((await response.json()) as { version: KnowledgeBaseProductVersion }).version;
}, { signal });
}
export async function updateKnowledgeBaseProductVersion(
version: KnowledgeBaseProductVersion,
): Promise<KnowledgeBaseProductVersion> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/knowledge-base-product-version`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ version }),
},
);
if (!response.ok) throw new Error(`Failed to update knowledge base product version: ${response.status}`);
return ((await response.json()) as { version: KnowledgeBaseProductVersion }).version;
}
/**
* Whether assistant knowledge bases are released to ordinary users.
*
* The persisted v1/v2 product switch remains the source of truth: v1 keeps
* assistant knowledge admin-only, while v2 exposes it in day-to-day surfaces.
*/
export async function loadAssistantKnowledgeVisibility(signal?: AbortSignal): Promise<boolean> {
return (await loadKnowledgeBaseProductVersion(signal)) === "v2";
}
export async function saveAssistantKnowledgeVisibility(
visible: boolean,
): Promise<boolean> {
return (await updateKnowledgeBaseProductVersion(visible ? "v2" : "v1")) === "v2";
}
export interface PluginDomainAgentMappingDto {
id: string;
domain: string;
agent_id: string;
enabled: boolean;
}
export interface PluginManagementSettings {
domainAgentMappings: Array<{
id: string;
domain: string;
agentId: string;
enabled: boolean;
}>;
}
export interface PluginManagementUpdate {
domainAgentMappings?: Array<{
id: string;
domain: string;
agentId: string;
enabled: boolean;
}>;
}
export interface PromptPrefixUpdate {
image_generation_unavailable_hint_enabled?: boolean;
python_dependency_delete_protection_enabled?: boolean;
enabled?: boolean;
switch_label?: string;
prompt_prefix?: string;
ordinary_qa_markdown_format_enabled?: boolean;
word_heading_numbering_style?: WordHeadingNumberingStyle;
}
function fromPluginManagementDto(data: {
domain_agent_mappings: PluginDomainAgentMappingDto[];
}): PluginManagementSettings {
return {
domainAgentMappings: (data.domain_agent_mappings ?? []).map((item) => ({
id: item.id,
domain: item.domain,
agentId: item.agent_id,
enabled: item.enabled,
})),
};
}
function toPluginManagementDto(body: PluginManagementUpdate) {
return {
domain_agent_mappings: (body.domainAgentMappings ?? []).map((item) => ({
id: item.id,
domain: item.domain,
agent_id: item.agentId,
enabled: item.enabled,
})),
};
}
export async function loadPluginManagementSettings(): Promise<PluginManagementSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/plugin-management`,
);
if (!response.ok) {
throw new Error(
`Failed to load plugin management settings: ${response.status}`,
);
}
const data = (await response.json()) as {
domain_agent_mappings: PluginDomainAgentMappingDto[];
};
const mapped = fromPluginManagementDto(data);
writeYLog({
optType: OptType.view,
optDetail: "CM助手-全量-系统设置-插件管理查看",
url: "/api/system-settings/plugin-management",
returnParam: mapped,
});
return mapped;
}
export async function updatePluginManagementSettings(
body: PluginManagementUpdate,
): Promise<PluginManagementSettings> {
const payload = toPluginManagementDto(body);
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/plugin-management`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
},
);
if (!response.ok) {
const detail = await response
.json()
.catch(() => ({ detail: "Failed to update plugin management settings" }));
throw new Error(detail.detail ?? "Failed to update plugin management settings");
}
const data = (await response.json()) as {
domain_agent_mappings: PluginDomainAgentMappingDto[];
};
const mapped = fromPluginManagementDto(data);
writeYLog({
optType: OptType.edit,
optDetail: "CM助手-全量-系统设置-插件管理编辑",
url: "/api/system-settings/plugin-management",
reqParam: payload,
returnParam: mapped,
});
return mapped;
}
export async function loadPromptPrefixSettings(): Promise<PromptPrefixSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/prompt-prefix`,
);
if (!response.ok) {
throw new Error(`Failed to load prompt prefix settings: ${response.status}`);
}
const data = (await response.json()) as PromptPrefixSettings;
writeYLog({
optType: OptType.view,
optDetail: "CM助手-全量-系统设置-提示词前缀查看",
url: "/api/system-settings/prompt-prefix",
returnParam: data,
});
return data;
}
export async function updatePromptPrefixSettings(
body: PromptPrefixUpdate,
): Promise<PromptPrefixSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/prompt-prefix`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
},
);
if (!response.ok) {
const detail = await response
.json()
.catch(() => ({ detail: "Failed to update prompt prefix" }));
throw new Error(detail.detail ?? "Failed to update prompt prefix");
}
const data = (await response.json()) as PromptPrefixSettings;
writeYLog({
optType: OptType.edit,
optDetail: "CM助手-全量-系统设置-提示词前缀编辑",
url: "/api/system-settings/prompt-prefix",
reqParam: body,
returnParam: data,
});
return data;
}
export interface PrivateSquareStatus {
enabled: boolean;
password_set: boolean;
}
export interface PrivateSquareUpdate {
enabled?: boolean;
password?: string;
}
export interface CitationDisplaySettings {
retrieved_files_enabled: boolean;
reference_mode_enabled: boolean;
cleanup_words: string[];
}
export interface CitationDisplayUpdate {
retrieved_files_enabled?: boolean;
reference_mode_enabled?: boolean;
cleanup_words?: string[];
}
export type HtmlMapEngine = "echarts" | "leaflet";
export interface HtmlMapSettings {
engine: HtmlMapEngine;
}
export interface HtmlRuntimeSettings {
base_url: string;
map_tile_url: string;
}
export type AppIdentityHeaderTitleFontMode = "adaptive" | "fixed";
export interface AppIdentitySettings {
system_name: string;
header_title_font_mode: AppIdentityHeaderTitleFontMode;
header_title_font_size: number;
logo_url: string | null;
logo_version: string | null;
has_logo: boolean;
light_red_header_background_url: string | null;
light_red_header_background_version: string | null;
has_light_red_header_background: boolean;
light_red_nav_active_url: string | null;
light_red_nav_active_version: string | null;
has_light_red_nav_active: boolean;
}
export interface AppIdentityUpdate {
systemName: string;
headerTitleFontMode: AppIdentityHeaderTitleFontMode;
headerTitleFontSize: number;
logo?: File | null;
removeLogo?: boolean;
lightRedHeaderBackground?: File | null;
removeLightRedHeaderBackground?: boolean;
lightRedNavActive?: File | null;
removeLightRedNavActive?: boolean;
}
export interface LeaderboardSettings {
exclude_user_ids: string[];
include_scheduled: boolean;
include_admins: boolean;
include_failed: boolean;
cleanup_system_questions: boolean;
}
export interface LeaderboardSettingsUpdate {
exclude_user_ids?: string[];
include_scheduled?: boolean;
include_admins?: boolean;
include_failed?: boolean;
cleanup_system_questions?: boolean;
}
export async function loadPrivateSquareStatus(): Promise<PrivateSquareStatus> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/private-square`,
);
if (!response.ok) {
throw new Error(
`Failed to load private square settings: ${response.status}`,
);
}
const data = (await response.json()) as PrivateSquareStatus;
writeYLog({
optType: OptType.view,
optDetail: "CM助手-全量-系统设置-私密广场查看",
url: "/api/system-settings/private-square",
returnParam: data,
});
return data;
}
export interface PublishSquare {
id: string;
name: string;
description: string;
}
export interface PublishSquaresSettings {
squares: PublishSquare[];
default_square_id: string;
}
export interface PublishSquaresUpdate {
squares?: PublishSquare[];
default_square_id?: string;
}
/** Agent Gallery category selected for the wx route's common-agent panel. */
export interface WxCommonAgentsSettings {
/** `null` keeps the original common-agent recommendation logic. */
agent_type: string | null;
}
export async function loadWxCommonAgents(): Promise<WxCommonAgentsSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/wx-common-agents`,
);
if (!response.ok) {
throw new Error(`Failed to load wx common agents: ${response.status}`);
}
return (await response.json()) as WxCommonAgentsSettings;
}
export async function updateWxCommonAgents(
agentType: string | null,
): Promise<WxCommonAgentsSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/wx-common-agents`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ agent_type: agentType }),
},
);
if (!response.ok) {
const detail = await response
.json()
.catch(() => ({ detail: "Failed to update wx common agents" }));
throw new Error(detail.detail ?? "Failed to update wx common agents");
}
return (await response.json()) as WxCommonAgentsSettings;
}
export async function loadPublishSquares(): Promise<PublishSquaresSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/publish-squares`,
);
if (!response.ok) {
throw new Error(`Failed to load publish squares: ${response.status}`);
}
const data = (await response.json()) as PublishSquaresSettings;
writeYLog({
optType: OptType.view,
optDetail: "CM助手-全量-系统设置-发布广场查看",
url: "/api/system-settings/publish-squares",
returnParam: data,
});
return data;
}
export async function updatePublishSquares(
body: PublishSquaresUpdate,
): Promise<PublishSquaresSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/publish-squares`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
},
);
if (!response.ok) {
const detail = await response
.json()
.catch(() => ({ detail: "Failed to update publish squares" }));
throw new Error(detail.detail ?? "Failed to update publish squares");
}
const data = (await response.json()) as PublishSquaresSettings;
writeYLog({
optType: OptType.edit,
optDetail: "CM助手-全量-系统设置-发布广场编辑",
url: "/api/system-settings/publish-squares",
reqParam: body,
returnParam: data,
});
return data;
}
export const BUILTIN_THEME_IDS = [
"light",
"dark",
"lightblue",
"lightgreen",
"iceblue",
"lightyellow",
] as const;
export type BuiltinThemeId = (typeof BUILTIN_THEME_IDS)[number];
export type ThemeMode = BuiltinThemeId | `custom-theme-${string}`;
export type CustomThemeBaseMode = "light" | "dark";
export interface CustomThemeColorOverrides {
button_hover?: string | null;
navigation_gradient_start?: string | null;
navigation_gradient_end?: string | null;
selected_background?: string | null;
border?: string | null;
muted?: string | null;
muted_text?: string | null;
destructive?: string | null;
brand_title?: string | null;
primary_foreground?: string | null;
}
export interface CustomThemePreset {
id: `custom-theme-${string}`;
name: string;
primary: string;
text_color?: string | null;
mode: CustomThemeBaseMode;
overrides?: CustomThemeColorOverrides;
enabled: boolean;
sort_order: number;
}
export type ViewMode = "card" | "list";
export type LayoutMode = "full" | "compact";
export type CompactShortcutParamType = "login" | "theme" | "path";
export interface CompactShortcutRouteParam {
key: string;
paramType: CompactShortcutParamType;
source?: string | null;
customValue?: string | null;
themeValue?: string | null;
}
export interface CompactShortcutButton {
id: string;
label: string;
url: string;
route_params: CompactShortcutRouteParam[];
enabled: boolean;
sort_order: number;
}
export interface AppearanceSettings {
default_theme: ThemeMode;
default_skill_view: ViewMode;
default_agent_view: ViewMode;
default_layout_mode: LayoutMode;
default_composer_width_full?: number;
default_composer_width_compact?: number;
default_composer_height_full?: number;
default_composer_height_compact?: number;
default_agent_composer_width?: number;
default_agent_composer_height?: number;
compact_shortcut_buttons: CompactShortcutButton[];
custom_themes: CustomThemePreset[];
}
export interface AppearanceUpdate {
default_theme?: ThemeMode;
default_skill_view?: ViewMode;
default_agent_view?: ViewMode;
default_layout_mode?: LayoutMode;
default_composer_width_full?: number;
default_composer_width_compact?: number;
default_composer_height_full?: number;
default_composer_height_compact?: number;
default_agent_composer_width?: number;
default_agent_composer_height?: number;
compact_shortcut_buttons?: CompactShortcutButton[];
custom_themes?: CustomThemePreset[];
}
export async function loadAppearanceSettings(): Promise<AppearanceSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/appearance`,
);
if (!response.ok) {
throw new Error(
`Failed to load appearance settings: ${response.status}`,
);
}
const data = (await response.json()) as AppearanceSettings;
writeYLog({
optType: OptType.view,
optDetail: "CM助手-全量-系统设置-外观查看",
url: "/api/system-settings/appearance",
returnParam: data,
});
return data;
}
export async function updateAppearanceSettings(
body: AppearanceUpdate,
): Promise<AppearanceSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/appearance`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
},
);
if (!response.ok) {
const detail = await response
.json()
.catch(() => ({ detail: "Failed to update appearance" }));
throw new Error(detail.detail ?? "Failed to update appearance");
}
const data = (await response.json()) as AppearanceSettings;
writeYLog({
optType: OptType.edit,
optDetail: "CM助手-全量-系统设置-外观编辑",
url: "/api/system-settings/appearance",
reqParam: body,
returnParam: data,
});
return data;
}
export async function loadCitationDisplaySettings(): Promise<CitationDisplaySettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/citation-display`,
);
if (!response.ok) {
throw new Error(
`Failed to load citation display settings: ${response.status}`,
);
}
const data = (await response.json()) as CitationDisplaySettings;
writeYLog({
optType: OptType.view,
optDetail: "CM助手-全量-系统设置-引用展示查看",
url: "/api/system-settings/citation-display",
returnParam: data,
});
return data;
}
export async function loadLeaderboardSettings(): Promise<LeaderboardSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/leaderboard`,
);
if (!response.ok) {
throw new Error(`Failed to load leaderboard settings: ${response.status}`);
}
const data = (await response.json()) as LeaderboardSettings;
writeYLog({
optType: OptType.view,
optDetail: "CM助手-全量-系统设置-排行榜查看",
url: "/api/system-settings/leaderboard",
returnParam: data,
});
return data;
}
export async function updateLeaderboardSettings(
body: LeaderboardSettingsUpdate,
): Promise<LeaderboardSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/leaderboard`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
},
);
if (!response.ok) {
const detail = await response
.json()
.catch(() => ({ detail: "Failed to update leaderboard settings" }));
throw new Error(detail.detail ?? "Failed to update leaderboard settings");
}
const data = (await response.json()) as LeaderboardSettings;
writeYLog({
optType: OptType.edit,
optDetail: "CM助手-全量-系统设置-排行榜编辑",
url: "/api/system-settings/leaderboard",
reqParam: body,
returnParam: data,
});
return data;
}
export async function loadHtmlMapSettings(): Promise<HtmlMapSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/html-map`,
);
if (!response.ok) {
throw new Error(`Failed to load HTML map settings: ${response.status}`);
}
const data = (await response.json()) as HtmlMapSettings;
writeYLog({
optType: OptType.view,
optDetail: "CM助手-全量-系统设置-HTML地图查看",
url: "/api/system-settings/html-map",
returnParam: data,
});
return data;
}
export async function updateHtmlMapSettings(
body: HtmlMapSettings,
): Promise<HtmlMapSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/html-map`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
},
);
if (!response.ok) {
const detail = await response
.json()
.catch(() => ({ detail: "Failed to update HTML map settings" }));
throw new Error(detail.detail ?? "Failed to update HTML map settings");
}
const data = (await response.json()) as HtmlMapSettings;
writeYLog({
optType: OptType.edit,
optDetail: "CM助手-全量-系统设置-HTML地图编辑",
url: "/api/system-settings/html-map",
reqParam: body,
returnParam: data,
});
return data;
}
export async function loadAppIdentitySettings(): Promise<AppIdentitySettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/app-identity`,
);
if (!response.ok) {
throw new Error(`Failed to load app identity: ${response.status}`);
}
const data = (await response.json()) as AppIdentitySettings;
writeYLog({
optType: OptType.view,
optDetail: "CM助手-全量-系统设置-系统名称查看",
url: "/api/system-settings/app-identity",
returnParam: data,
});
return data;
}
export async function updateAppIdentitySettings(
update: AppIdentityUpdate,
): Promise<AppIdentitySettings> {
const body = new FormData();
body.set("system_name", update.systemName);
body.set("header_title_font_mode", update.headerTitleFontMode);
body.set("header_title_font_size", String(update.headerTitleFontSize));
if (update.logo) body.set("logo", update.logo);
if (update.removeLogo) body.set("remove_logo", "true");
if (update.lightRedHeaderBackground) {
body.set("light_red_header_background", update.lightRedHeaderBackground);
}
if (update.removeLightRedHeaderBackground) {
body.set("remove_light_red_header_background", "true");
}
if (update.lightRedNavActive) {
body.set("light_red_nav_active", update.lightRedNavActive);
}
if (update.removeLightRedNavActive) {
body.set("remove_light_red_nav_active", "true");
}
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/app-identity`,
{ method: "PUT", body },
);
if (!response.ok) {
const detail = await response
.json()
.catch(() => ({ detail: "Failed to update app identity" }));
throw new Error(detail.detail ?? "Failed to update app identity");
}
const data = (await response.json()) as AppIdentitySettings;
writeYLog({
optType: OptType.edit,
optDetail: "CM助手-全量-系统设置-系统名称编辑",
url: "/api/system-settings/app-identity",
reqParam: {
system_name: update.systemName,
header_title_font_mode: update.headerTitleFontMode,
header_title_font_size: update.headerTitleFontSize,
logo_name: update.logo?.name,
remove_logo: Boolean(update.removeLogo),
light_red_header_background_name:
update.lightRedHeaderBackground?.name,
remove_light_red_header_background: Boolean(
update.removeLightRedHeaderBackground,
),
light_red_nav_active_name: update.lightRedNavActive?.name,
remove_light_red_nav_active: Boolean(update.removeLightRedNavActive),
},
returnParam: data,
});
return data;
}
export async function deleteAppIdentitySettings(): Promise<AppIdentitySettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/app-identity`,
{ method: "DELETE" },
);
if (!response.ok) {
const detail = await response
.json()
.catch(() => ({ detail: "Failed to reset app identity" }));
throw new Error(detail.detail ?? "Failed to reset app identity");
}
const data = (await response.json()) as AppIdentitySettings;
writeYLog({
optType: OptType.delete,
optDetail: "CM助手-全量-系统设置-系统名称清除配置",
url: "/api/system-settings/app-identity",
returnParam: data,
});
return data;
}
export function resolveAppIdentityLogoUrl(
logoUrl: string | null | undefined,
): string | undefined {
return resolveAppIdentityAssetUrl(logoUrl);
}
export function resolveAppIdentityAssetUrl(
assetUrl: string | null | undefined,
): string | undefined {
if (!assetUrl) return undefined;
if (/^https?:\/\//i.test(assetUrl)) return assetUrl;
const base = getBackendBaseURL();
return `${base}${assetUrl.startsWith("/") ? "" : "/"}${assetUrl}`;
}
export async function loadHtmlRuntimeSettings(): Promise<HtmlRuntimeSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/html-runtime`,
);
if (!response.ok) {
throw new Error(`Failed to load HTML runtime settings: ${response.status}`);
}
const data = (await response.json()) as HtmlRuntimeSettings;
writeYLog({
optType: OptType.view,
optDetail: "CM助手-全量-系统设置-HTML运行地址查看",
url: "/api/system-settings/html-runtime",
returnParam: data,
});
return data;
}
export async function updateHtmlRuntimeSettings(
body: HtmlRuntimeSettings,
): Promise<HtmlRuntimeSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/html-runtime`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
},
);
if (!response.ok) {
const detail = await response
.json()
.catch(() => ({ detail: "Failed to update HTML runtime settings" }));
const message =
typeof detail.detail === "string"
? detail.detail
: "Failed to update HTML runtime settings";
throw new Error(message);
}
const data = (await response.json()) as HtmlRuntimeSettings;
writeYLog({
optType: OptType.edit,
optDetail: "CM助手-全量-系统设置-HTML运行地址编辑",
url: "/api/system-settings/html-runtime",
reqParam: body,
returnParam: data,
});
return data;
}
export async function updateCitationDisplaySettings(
body: CitationDisplayUpdate,
): Promise<CitationDisplaySettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/citation-display`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
},
);
if (!response.ok) {
const detail = await response
.json()
.catch(() => ({ detail: "Failed to update citation display" }));
throw new Error(detail.detail ?? "Failed to update citation display");
}
const data = (await response.json()) as CitationDisplaySettings;
writeYLog({
optType: OptType.edit,
optDetail: "CM助手-全量-系统设置-引用展示编辑",
url: "/api/system-settings/citation-display",
reqParam: body,
returnParam: data,
});
return data;
}
export interface SkillCompressionSettings {
enabled: boolean;
search_top_n: number;
keep_index_in_prompt: boolean;
}
export interface SkillCompressionUpdate {
enabled?: boolean;
search_top_n?: number;
keep_index_in_prompt?: boolean;
}
export interface SkillVersioningSettings {
enabled: boolean;
}
export interface ModelConcurrencySettings {
enabled: boolean;
default_limit: number;
model_limits: Record<string, number>;
user_default_limit: number;
user_model_limits: Record<string, number>;
}
export interface ModelConcurrencyUpdate {
enabled?: boolean;
default_limit?: number;
model_limits?: Record<string, number>;
user_default_limit?: number;
user_model_limits?: Record<string, number>;
}
export interface MemoryRuntimeSettings {
enabled: boolean;
}
export interface ConversationContextSettings {
max_run_steps: number;
recent_turn_window_enabled: boolean;
recent_turns: number;
max_conversation_rounds: number;
use_question_as_title: boolean;
}
export interface ConversationContextUpdate {
max_run_steps?: number;
recent_turn_window_enabled?: boolean;
max_conversation_rounds?: number;
use_question_as_title?: boolean;
}
export interface RoundtableRoomLimitSettings {
enabled: boolean;
max_rooms: number;
max_hold_minutes: number;
lease_minutes: number;
max_cycles: number;
write_file_compatibility_enabled: boolean;
}
export interface RoundtableRoomLimitUpdate {
enabled?: boolean;
max_rooms?: number;
max_hold_minutes?: number;
lease_minutes?: number;
max_cycles?: number;
write_file_compatibility_enabled?: boolean;
}
export interface OrdinaryKnowledgeRetrievalSettings {
smart_reasoning_enabled: boolean;
advanced_management_enabled: boolean;
}
export interface OrdinaryKnowledgeRetrievalUpdate {
smart_reasoning_enabled?: boolean;
advanced_management_enabled?: boolean;
}
export async function loadMemoryRuntimeSettings(): Promise<MemoryRuntimeSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/memory`,
);
if (!response.ok) {
throw new Error(`Failed to load memory runtime setting: ${response.status}`);
}
return (await response.json()) as MemoryRuntimeSettings;
}
export async function updateMemoryRuntimeSettings(
body: MemoryRuntimeSettings,
): Promise<MemoryRuntimeSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/memory`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
},
);
if (!response.ok) {
const detail = await response
.json()
.catch(() => ({ detail: "Failed to update memory runtime setting" }));
throw new Error(detail.detail ?? "Failed to update memory runtime setting");
}
const data = (await response.json()) as MemoryRuntimeSettings;
writeYLog({
optType: OptType.edit,
optDetail: "CM助手-全量-系统设置-问答记忆开关",
url: "/api/system-settings/memory",
reqParam: body,
returnParam: data,
});
return data;
}
export interface WriteFileCompatibilitySettings {
enabled: boolean;
}
export async function loadWriteFileCompatibilitySettings(): Promise<WriteFileCompatibilitySettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/write-file-compatibility`,
);
if (!response.ok) {
throw new Error(
`Failed to load write_file compatibility setting: ${response.status}`,
);
}
return (await response.json()) as WriteFileCompatibilitySettings;
}
export async function updateWriteFileCompatibilitySettings(
body: WriteFileCompatibilitySettings,
): Promise<WriteFileCompatibilitySettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/write-file-compatibility`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
},
);
if (!response.ok) {
const detail = await response.json().catch(() => ({
detail: "Failed to update write_file compatibility setting",
}));
throw new Error(
detail.detail ?? "Failed to update write_file compatibility setting",
);
}
const data = (await response.json()) as WriteFileCompatibilitySettings;
writeYLog({
optType: OptType.edit,
optDetail: "CM助手-全量-系统设置-文件流式兼容开关",
url: "/api/system-settings/write-file-compatibility",
reqParam: body,
returnParam: data,
});
return data;
}
export interface CallMetricsSettings {
enabled: boolean;
}
export async function loadCallMetricsSettings(): Promise<CallMetricsSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/call-metrics`,
);
if (!response.ok) {
throw new Error(`无法读取调用统计开关(HTTP ${response.status})`);
}
const data = (await response.json()) as CallMetricsSettings;
writeYLog({
optType: OptType.view,
optDetail: "CM助手-全量-系统设置-调用统计查看",
url: "/api/system-settings/call-metrics",
returnParam: data,
});
return data;
}
export async function updateCallMetricsSettings(
body: CallMetricsSettings,
): Promise<CallMetricsSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/call-metrics`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
},
);
if (!response.ok) {
const detail = await response.json().catch(() => ({
detail: "保存调用统计开关失败",
}));
throw new Error(detail.detail ?? "保存调用统计开关失败");
}
const data = (await response.json()) as CallMetricsSettings;
writeYLog({
optType: OptType.edit,
optDetail: "CM助手-全量-系统设置-调用统计开关",
url: "/api/system-settings/call-metrics",
reqParam: body,
returnParam: data,
});
return data;
}
export async function loadConversationContextSettings(): Promise<ConversationContextSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/conversation-context`,
);
if (!response.ok) {
throw new Error(
`Failed to load conversation context setting: ${response.status}`,
);
}
return (await response.json()) as ConversationContextSettings;
}
export async function updateConversationContextSettings(
body: ConversationContextUpdate,
): Promise<ConversationContextSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/conversation-context`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
},
);
if (!response.ok) {
const detail = await response
.json()
.catch(() => ({ detail: "Failed to update conversation context setting" }));
throw new Error(
detail.detail ?? "Failed to update conversation context setting",
);
}
const data = (await response.json()) as ConversationContextSettings;
writeYLog({
optType: OptType.edit,
optDetail: "CM助手-全量-系统设置-上下文管理",
url: "/api/system-settings/conversation-context",
reqParam: body,
returnParam: data,
});
return data;
}
export async function loadRoundtableRoomLimitSettings(): Promise<RoundtableRoomLimitSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/roundtable-room-limit`,
);
if (!response.ok) {
throw new Error(`无法读取会商运行设置(HTTP ${response.status})`);
}
return (await response.json()) as RoundtableRoomLimitSettings;
}
export async function updateRoundtableRoomLimitSettings(
body: RoundtableRoomLimitUpdate,
): Promise<RoundtableRoomLimitSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/roundtable-room-limit`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
},
);
if (!response.ok) {
const detail = await response
.json()
.catch(() => ({ detail: "保存会商运行设置失败" }));
const message =
typeof detail.detail === "string" ? detail.detail : "保存会商运行设置失败";
throw new Error(message);
}
const data = (await response.json()) as RoundtableRoomLimitSettings;
writeYLog({
optType: OptType.edit,
optDetail: "CM助手-全量-系统设置-会商运行限制",
url: "/api/system-settings/roundtable-room-limit",
reqParam: body,
returnParam: data,
});
return data;
}
export async function loadOrdinaryKnowledgeRetrievalSettings(): Promise<OrdinaryKnowledgeRetrievalSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/ordinary-knowledge-retrieval`,
);
if (!response.ok) {
throw new Error(
`Failed to load ordinary knowledge retrieval setting: ${response.status}`,
);
}
return (await response.json()) as OrdinaryKnowledgeRetrievalSettings;
}
export async function updateOrdinaryKnowledgeRetrievalSettings(
body: OrdinaryKnowledgeRetrievalUpdate,
): Promise<OrdinaryKnowledgeRetrievalSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/ordinary-knowledge-retrieval`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
},
);
if (!response.ok) {
const detail = await response
.json()
.catch(() => ({ detail: "Failed to update knowledge retrieval setting" }));
throw new Error(
detail.detail ?? "Failed to update knowledge retrieval setting",
);
}
const data = (await response.json()) as OrdinaryKnowledgeRetrievalSettings;
writeYLog({
optType: OptType.edit,
optDetail: "CM助手-全量-系统设置-普通知识库检索与管理设置",
url: "/api/system-settings/ordinary-knowledge-retrieval",
reqParam: body,
returnParam: data,
});
return data;
}
export async function loadSkillCompressionSettings(): Promise<SkillCompressionSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/skill-compression`,
);
if (!response.ok) {
throw new Error(
`Failed to load skill compression settings: ${response.status}`,
);
}
const data = (await response.json()) as SkillCompressionSettings;
writeYLog({
optType: OptType.view,
optDetail: "CM助手-全量-系统设置-技能压缩查看",
url: "/api/system-settings/skill-compression",
returnParam: data,
});
return data;
}
export async function loadSkillVersioningSettings(): Promise<SkillVersioningSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/skill-versioning`,
);
if (!response.ok) {
throw new Error(
`Failed to load skill versioning settings: ${response.status}`,
);
}
const data = (await response.json()) as SkillVersioningSettings;
writeYLog({
optType: OptType.view,
optDetail: "CM助手-全量-系统设置-技能迭代查看",
url: "/api/system-settings/skill-versioning",
returnParam: data,
});
return data;
}
export async function loadModelConcurrencySettings(): Promise<ModelConcurrencySettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/model-concurrency`,
);
if (!response.ok) {
throw new Error(
`Failed to load model concurrency settings: ${response.status}`,
);
}
const data = (await response.json()) as ModelConcurrencySettings;
writeYLog({
optType: OptType.view,
optDetail: "CM助手-全量-系统设置-模型并发限制查看",
url: "/api/system-settings/model-concurrency",
returnParam: data,
});
return data;
}
export async function updateModelConcurrencySettings(
body: ModelConcurrencyUpdate,
): Promise<ModelConcurrencySettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/model-concurrency`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
},
);
if (!response.ok) {
const detail = await response
.json()
.catch(() => ({ detail: "Failed to update model concurrency settings" }));
throw new Error(detail.detail ?? "Failed to update model concurrency settings");
}
const data = (await response.json()) as ModelConcurrencySettings;
writeYLog({
optType: OptType.edit,
optDetail: "CM助手-全量-系统设置-模型并发限制编辑",
url: "/api/system-settings/model-concurrency",
reqParam: body,
returnParam: data,
});
return data;
}
export async function updateSkillCompressionSettings(
body: SkillCompressionUpdate,
): Promise<SkillCompressionSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/skill-compression`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
},
);
if (!response.ok) {
const detail = await response
.json()
.catch(() => ({ detail: "Failed to update skill compression" }));
throw new Error(detail.detail ?? "Failed to update skill compression");
}
const data = (await response.json()) as SkillCompressionSettings;
writeYLog({
optType: OptType.edit,
optDetail: "CM助手-全量-系统设置-技能压缩编辑",
url: "/api/system-settings/skill-compression",
reqParam: body,
returnParam: data,
});
return data;
}
export async function updateSkillVersioningSettings(
body: SkillVersioningSettings,
): Promise<SkillVersioningSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/skill-versioning`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
},
);
if (!response.ok) {
const detail = await response
.json()
.catch(() => ({ detail: "Failed to update skill versioning" }));
throw new Error(detail.detail ?? "Failed to update skill versioning");
}
const data = (await response.json()) as SkillVersioningSettings;
writeYLog({
optType: OptType.edit,
optDetail: "CM助手-全量-系统设置-技能迭代编辑",
url: "/api/system-settings/skill-versioning",
reqParam: body,
returnParam: data,
});
return data;
}
/** 「按类型聚合」模块(zzJC / zzXD)选中的智能体标签配置(需求 0616 #3/#4)。 */
export interface AgentModuleConfig {
key: string;
tag_ids: string[];
}
export async function loadAgentModuleConfig(
key: string,
): Promise<AgentModuleConfig> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/agent-modules/${encodeURIComponent(key)}`,
);
if (!response.ok) {
throw new Error(`Failed to load agent module config: ${response.status}`);
}
const data = (await response.json()) as AgentModuleConfig;
writeYLog({
optType: OptType.view,
optDetail: "CM助手-全量-系统设置-智能体模块查看",
url: `/api/system-settings/agent-modules/${key}`,
reqParam: { key },
returnParam: data,
});
return data;
}
export async function updateAgentModuleConfig(
key: string,
tagIds: string[],
): Promise<AgentModuleConfig> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/agent-modules/${encodeURIComponent(key)}`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ tag_ids: tagIds }),
},
);
if (!response.ok) {
const detail = await response
.json()
.catch(() => ({ detail: "Failed to update agent module config" }));
throw new Error(detail.detail ?? "Failed to update agent module config");
}
const data = (await response.json()) as AgentModuleConfig;
writeYLog({
optType: OptType.edit,
optDetail: "CM助手-全量-系统设置-智能体模块编辑",
url: `/api/system-settings/agent-modules/${key}`,
reqParam: { key, tag_ids: tagIds },
returnParam: data,
});
return data;
}
export interface LogSystemSettings {
enabled: boolean;
url: string;
label: string;
}
export async function loadLogSystemSettings(): Promise<LogSystemSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/log-system`,
);
if (!response.ok) {
throw new Error(`Failed to load log system settings: ${response.status}`);
}
const data = (await response.json()) as LogSystemSettings;
writeYLog({
optType: OptType.view,
optDetail: "CM助手-全量-系统设置-日志系统查看",
url: "/api/system-settings/log-system",
returnParam: data,
});
return data;
}
export async function updatePrivateSquareSettings(
body: PrivateSquareUpdate,
): Promise<PrivateSquareStatus> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/private-square`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
},
);
if (!response.ok) {
const detail = await response
.json()
.catch(() => ({ detail: "Failed to update private square" }));
throw new Error(detail.detail ?? "Failed to update private square");
}
const data = (await response.json()) as PrivateSquareStatus;
writeYLog({
optType: OptType.edit,
optDetail: "CM助手-全量-系统设置-私密广场编辑",
url: "/api/system-settings/private-square",
// omit `password` (secret) from the logged request params
reqParam: { enabled: body.enabled, password_set: body.password != null },
returnParam: data,
});
return data;
}
export interface RetryKeepPartialSettings {
enabled: boolean;
}
export async function loadRetryKeepPartialSettings(): Promise<RetryKeepPartialSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/retry-keep-partial`,
);
if (!response.ok) {
throw new Error(
`Failed to load retry-keep-partial setting: ${response.status}`,
);
}
return (await response.json()) as RetryKeepPartialSettings;
}
export async function updateRetryKeepPartialSettings(
body: RetryKeepPartialSettings,
): Promise<RetryKeepPartialSettings> {
const response = await apiFetch(
`${getBackendBaseURL()}/api/system-settings/retry-keep-partial`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
},
);
if (!response.ok) {
const detail = await response.json().catch(() => ({
detail: "Failed to update retry-keep-partial setting",
}));
throw new Error(
detail.detail ?? "Failed to update retry-keep-partial setting",
);
}
const data = (await response.json()) as RetryKeepPartialSettings;
writeYLog({
optType: OptType.edit,
optDetail: "CM助手-全量-系统设置-重试保留正文开关",
url: "/api/system-settings/retry-keep-partial",
reqParam: body,
returnParam: data,
});
return data;
}