Commit d60ecae9 authored by edy's avatar edy

feat(settings): edit model endpoint configs

parent c591100b
......@@ -223,19 +223,19 @@ function mergeExpertModelConfig(
): ExpertModelConfig {
return {
image: {
baseUrl: FIXED_EXPERT_MODEL_ENDPOINTS.image.baseUrl,
baseUrl: typeof input?.image?.baseUrl === "string" ? input.image.baseUrl.trim() : current.image.baseUrl,
apiKeyConfigured: typeof input?.image?.apiKey === "string" ? Boolean(input.image.apiKey.trim()) : current.image.apiKeyConfigured,
modelId: FIXED_EXPERT_MODEL_ENDPOINTS.image.modelId
modelId: typeof input?.image?.modelId === "string" ? input.image.modelId.trim() : current.image.modelId
},
video: {
baseUrl: FIXED_EXPERT_MODEL_ENDPOINTS.video.baseUrl,
baseUrl: typeof input?.video?.baseUrl === "string" ? input.video.baseUrl.trim() : current.video.baseUrl,
apiKeyConfigured: typeof input?.video?.apiKey === "string" ? Boolean(input.video.apiKey.trim()) : current.video.apiKeyConfigured,
modelId: FIXED_EXPERT_MODEL_ENDPOINTS.video.modelId
modelId: typeof input?.video?.modelId === "string" ? input.video.modelId.trim() : current.video.modelId
},
copywriting: {
baseUrl: FIXED_EXPERT_MODEL_ENDPOINTS.copywriting.baseUrl,
baseUrl: typeof input?.copywriting?.baseUrl === "string" ? input.copywriting.baseUrl.trim() : current.copywriting.baseUrl,
apiKeyConfigured: typeof input?.copywriting?.apiKey === "string" ? Boolean(input.copywriting.apiKey.trim()) : current.copywriting.apiKeyConfigured,
modelId: FIXED_EXPERT_MODEL_ENDPOINTS.copywriting.modelId
modelId: typeof input?.copywriting?.modelId === "string" ? input.copywriting.modelId.trim() : current.copywriting.modelId
},
digitalHuman: {
...FIXED_DIGITAL_HUMAN_CONFIG,
......@@ -395,19 +395,25 @@ export class AppConfigService {
runtimeMode: normalizeRuntimeMode(config.runtimeMode ?? process.env.QJCLAW_RUNTIME_MODE),
expertModelConfig: {
image: {
baseUrl: defaultExpertModelConfig.image.baseUrl,
baseUrl: typeof config.expertModelConfig?.image?.baseUrl === "string"
? config.expertModelConfig.image.baseUrl
: defaultExpertModelConfig.image.baseUrl,
apiKeyConfigured: Boolean(config.expertModelConfig?.image?.apiKeyConfigured),
modelId: defaultExpertModelConfig.image.modelId
modelId: config.expertModelConfig?.image?.modelId ?? defaultExpertModelConfig.image.modelId
},
video: {
baseUrl: defaultExpertModelConfig.video.baseUrl,
baseUrl: typeof config.expertModelConfig?.video?.baseUrl === "string"
? config.expertModelConfig.video.baseUrl
: defaultExpertModelConfig.video.baseUrl,
apiKeyConfigured: Boolean(config.expertModelConfig?.video?.apiKeyConfigured),
modelId: defaultExpertModelConfig.video.modelId
modelId: config.expertModelConfig?.video?.modelId ?? defaultExpertModelConfig.video.modelId
},
copywriting: {
baseUrl: defaultExpertModelConfig.copywriting.baseUrl,
baseUrl: typeof config.expertModelConfig?.copywriting?.baseUrl === "string"
? config.expertModelConfig.copywriting.baseUrl
: defaultExpertModelConfig.copywriting.baseUrl,
apiKeyConfigured: Boolean(config.expertModelConfig?.copywriting?.apiKeyConfigured),
modelId: defaultExpertModelConfig.copywriting.modelId
modelId: config.expertModelConfig?.copywriting?.modelId ?? defaultExpertModelConfig.copywriting.modelId
},
digitalHuman: {
...FIXED_DIGITAL_HUMAN_CONFIG,
......
......@@ -366,10 +366,22 @@ export default function App() {
setWorkspacePathDraft,
imageModelApiKeyDraft,
setImageModelApiKeyDraft,
imageModelBaseUrlDraft,
setImageModelBaseUrlDraft,
imageModelModelIdDraft,
setImageModelModelIdDraft,
videoModelApiKeyDraft,
setVideoModelApiKeyDraft,
videoModelBaseUrlDraft,
setVideoModelBaseUrlDraft,
videoModelModelIdDraft,
setVideoModelModelIdDraft,
copywritingModelApiKeyDraft,
setCopywritingModelApiKeyDraft,
copywritingModelBaseUrlDraft,
setCopywritingModelBaseUrlDraft,
copywritingModelModelIdDraft,
setCopywritingModelModelIdDraft,
digitalHumanVolcAccessKeyDraft,
setDigitalHumanVolcAccessKeyDraft,
digitalHumanVolcSecretKeyDraft,
......@@ -436,8 +448,14 @@ export default function App() {
lobsterKey: lobsterKeyDraft,
workspacePath: workspacePathDraft,
imageModelApiKey: imageModelApiKeyDraft,
imageModelBaseUrl: imageModelBaseUrlDraft,
imageModelModelId: imageModelModelIdDraft,
videoModelApiKey: videoModelApiKeyDraft,
videoModelBaseUrl: videoModelBaseUrlDraft,
videoModelModelId: videoModelModelIdDraft,
copywritingModelApiKey: copywritingModelApiKeyDraft,
copywritingModelBaseUrl: copywritingModelBaseUrlDraft,
copywritingModelModelId: copywritingModelModelIdDraft,
digitalHumanVolcAccessKey: digitalHumanVolcAccessKeyDraft,
digitalHumanVolcSecretKey: digitalHumanVolcSecretKeyDraft,
digitalHumanQiniuAccessKey: digitalHumanQiniuAccessKeyDraft,
......@@ -464,14 +482,26 @@ export default function App() {
setWorkspacePathDraft,
setLobsterKeyDraft,
setImageModelApiKeyDraft,
setImageModelBaseUrlDraft,
setImageModelModelIdDraft,
setVideoModelApiKeyDraft,
setVideoModelBaseUrlDraft,
setVideoModelModelIdDraft,
setCopywritingModelApiKeyDraft,
setCopywritingModelBaseUrlDraft,
setCopywritingModelModelIdDraft,
setDigitalHumanVolcAccessKeyDraft,
setDigitalHumanVolcSecretKeyDraft,
setDigitalHumanQiniuAccessKeyDraft,
setDigitalHumanQiniuSecretKeyDraft,
setVideoAnalyzerBaseUrlDraft,
setVideoAnalyzerModelIdDraft,
setVideoAnalyzerApiKeyDraft,
setReplicationBriefBaseUrlDraft,
setReplicationBriefModelIdDraft,
setReplicationBriefApiKeyDraft,
setVectcutBaseUrlDraft,
setVectcutFileBaseUrlDraft,
setVectcutApiKeyDraft,
setXhsFeishuAppIdDraft,
setXhsFeishuAppSecretDraft,
......@@ -506,9 +536,21 @@ export default function App() {
const displayedWorkspacePath = workspacePathDraft.trim() || savedWorkspacePath || ui.none;
const hasPendingWorkspacePathChange = Boolean(config && workspacePathDraft.trim() && workspacePathDraft.trim() !== savedWorkspacePath);
const hasPendingBasicConfig = hasPendingLobsterKey || hasPendingWorkspacePathChange;
const hasPendingCopywritingConfig = Boolean(copywritingModelApiKeyDraft.trim());
const hasPendingImageConfig = Boolean(imageModelApiKeyDraft.trim());
const hasPendingVideoConfig = Boolean(videoModelApiKeyDraft.trim());
const hasPendingCopywritingConfig = Boolean(
copywritingModelBaseUrlDraft.trim() !== (config?.expertModelConfig.copywriting.baseUrl ?? "").trim()
|| copywritingModelModelIdDraft.trim() !== (config?.expertModelConfig.copywriting.modelId ?? "").trim()
|| copywritingModelApiKeyDraft.trim()
);
const hasPendingImageConfig = Boolean(
imageModelBaseUrlDraft.trim() !== (config?.expertModelConfig.image.baseUrl ?? "").trim()
|| imageModelModelIdDraft.trim() !== (config?.expertModelConfig.image.modelId ?? "").trim()
|| imageModelApiKeyDraft.trim()
);
const hasPendingVideoConfig = Boolean(
videoModelBaseUrlDraft.trim() !== (config?.expertModelConfig.video.baseUrl ?? "").trim()
|| videoModelModelIdDraft.trim() !== (config?.expertModelConfig.video.modelId ?? "").trim()
|| videoModelApiKeyDraft.trim()
);
const hasPendingDigitalHumanConfig = Boolean(
digitalHumanVolcAccessKeyDraft.trim()
|| digitalHumanVolcSecretKeyDraft.trim()
......@@ -544,14 +586,35 @@ export default function App() {
setXhsFeishuTableIdDraft(drafts.xhsFeishuTableId);
}, []);
const resetCopywritingSettingsDrafts = useCallback(() => {
setCopywritingModelApiKeyDraft(getResetCopywritingSettingsDrafts().copywritingModelApiKey);
}, []);
if (!config) {
return;
}
const drafts = getResetCopywritingSettingsDrafts(config);
setCopywritingModelBaseUrlDraft(drafts.copywritingModelBaseUrl);
setCopywritingModelModelIdDraft(drafts.copywritingModelModelId);
setCopywritingModelApiKeyDraft(drafts.copywritingModelApiKey);
}, [config]);
const resetImageSettingsDrafts = useCallback(() => {
setImageModelApiKeyDraft(getResetImageSettingsDrafts().imageModelApiKey);
}, []);
if (!config) {
return;
}
const drafts = getResetImageSettingsDrafts(config);
setImageModelBaseUrlDraft(drafts.imageModelBaseUrl);
setImageModelModelIdDraft(drafts.imageModelModelId);
setImageModelApiKeyDraft(drafts.imageModelApiKey);
}, [config]);
const resetVideoSettingsDrafts = useCallback(() => {
setVideoModelApiKeyDraft(getResetVideoSettingsDrafts().videoModelApiKey);
}, []);
if (!config) {
return;
}
const drafts = getResetVideoSettingsDrafts(config);
setVideoModelBaseUrlDraft(drafts.videoModelBaseUrl);
setVideoModelModelIdDraft(drafts.videoModelModelId);
setVideoModelApiKeyDraft(drafts.videoModelApiKey);
}, [config]);
const resetDigitalHumanSettingsDrafts = useCallback(() => {
const drafts = getResetDigitalHumanSettingsDrafts();
setDigitalHumanVolcAccessKeyDraft(drafts.digitalHumanVolcAccessKey);
......@@ -1419,8 +1482,14 @@ export default function App() {
xhsFeishuAppSecret: xhsFeishuAppSecretDraft,
xhsFeishuAppToken: xhsFeishuAppTokenDraft,
xhsFeishuTableId: xhsFeishuTableIdDraft,
copywritingModelBaseUrl: copywritingModelBaseUrlDraft,
copywritingModelModelId: copywritingModelModelIdDraft,
copywritingModelApiKey: copywritingModelApiKeyDraft,
imageModelBaseUrl: imageModelBaseUrlDraft,
imageModelModelId: imageModelModelIdDraft,
imageModelApiKey: imageModelApiKeyDraft,
videoModelBaseUrl: videoModelBaseUrlDraft,
videoModelModelId: videoModelModelIdDraft,
videoModelApiKey: videoModelApiKeyDraft,
digitalHumanVolcAccessKey: digitalHumanVolcAccessKeyDraft,
digitalHumanVolcSecretKey: digitalHumanVolcSecretKeyDraft,
......@@ -1442,8 +1511,14 @@ export default function App() {
setXhsFeishuAppSecret: setXhsFeishuAppSecretDraft,
setXhsFeishuAppToken: setXhsFeishuAppTokenDraft,
setXhsFeishuTableId: setXhsFeishuTableIdDraft,
setCopywritingModelBaseUrl: setCopywritingModelBaseUrlDraft,
setCopywritingModelModelId: setCopywritingModelModelIdDraft,
setCopywritingModelApiKey: setCopywritingModelApiKeyDraft,
setImageModelBaseUrl: setImageModelBaseUrlDraft,
setImageModelModelId: setImageModelModelIdDraft,
setImageModelApiKey: setImageModelApiKeyDraft,
setVideoModelBaseUrl: setVideoModelBaseUrlDraft,
setVideoModelModelId: setVideoModelModelIdDraft,
setVideoModelApiKey: setVideoModelApiKeyDraft,
setDigitalHumanVolcAccessKey: setDigitalHumanVolcAccessKeyDraft,
setDigitalHumanVolcSecretKey: setDigitalHumanVolcSecretKeyDraft,
......@@ -1459,18 +1534,18 @@ export default function App() {
setVectcutFileBaseUrl: setVectcutFileBaseUrlDraft,
setVectcutApiKey: setVectcutApiKeyDraft
},
onSaveLobsterKey: () => void saveLobsterKey(),
onSaveXhsFeishuConfig: () => void saveXhsFeishuConfig(),
onSaveLobsterKey: saveLobsterKey,
onSaveXhsFeishuConfig: saveXhsFeishuConfig,
onResetXhsFeishuConfig: resetXhsFeishuSettingsDrafts,
onSaveCopywritingConfig: () => void saveCopywritingConfig(),
onSaveCopywritingConfig: saveCopywritingConfig,
onResetCopywritingConfig: resetCopywritingSettingsDrafts,
onSaveImageConfig: () => void saveImageConfig(),
onSaveImageConfig: saveImageConfig,
onResetImageConfig: resetImageSettingsDrafts,
onSaveVideoConfig: () => void saveVideoConfig(),
onSaveVideoConfig: saveVideoConfig,
onResetVideoConfig: resetVideoSettingsDrafts,
onSaveDigitalHumanConfig: () => void saveDigitalHumanConfig(),
onSaveDigitalHumanConfig: saveDigitalHumanConfig,
onResetDigitalHumanConfig: resetDigitalHumanSettingsDrafts,
onSaveDouyinRuntimeConfig: () => void saveDouyinRuntimeConfig(),
onSaveDouyinRuntimeConfig: saveDouyinRuntimeConfig,
onResetDouyinRuntimeConfig: resetDouyinRuntimeSettingsDrafts,
onRevealSecret: revealConfigSecret,
pickWorkspaceDirectory,
......
......@@ -22,6 +22,24 @@ export interface DigitalHumanResetSettingsDrafts {
digitalHumanQiniuSecretKey: string
}
export interface CopywritingResetSettingsDrafts {
copywritingModelBaseUrl: string
copywritingModelModelId: string
copywritingModelApiKey: string
}
export interface ImageResetSettingsDrafts {
imageModelBaseUrl: string
imageModelModelId: string
imageModelApiKey: string
}
export interface VideoResetSettingsDrafts {
videoModelBaseUrl: string
videoModelModelId: string
videoModelApiKey: string
}
export interface DouyinRuntimeResetSettingsDrafts {
videoAnalyzerBaseUrl: string
videoAnalyzerModelId: string
......@@ -58,20 +76,26 @@ export function getResetBasicSettingsDrafts(config: AppConfig): BasicResetSettin
}
}
export function getResetCopywritingSettingsDrafts() {
export function getResetCopywritingSettingsDrafts(config: AppConfig): CopywritingResetSettingsDrafts {
return {
copywritingModelBaseUrl: config.expertModelConfig.copywriting.baseUrl,
copywritingModelModelId: config.expertModelConfig.copywriting.modelId ?? "",
copywritingModelApiKey: ""
}
}
export function getResetImageSettingsDrafts() {
export function getResetImageSettingsDrafts(config: AppConfig): ImageResetSettingsDrafts {
return {
imageModelBaseUrl: config.expertModelConfig.image.baseUrl,
imageModelModelId: config.expertModelConfig.image.modelId ?? "",
imageModelApiKey: ""
}
}
export function getResetVideoSettingsDrafts() {
export function getResetVideoSettingsDrafts(config: AppConfig): VideoResetSettingsDrafts {
return {
videoModelBaseUrl: config.expertModelConfig.video.baseUrl,
videoModelModelId: config.expertModelConfig.video.modelId ?? "",
videoModelApiKey: ""
}
}
......
......@@ -9,8 +9,14 @@ interface UseSaveSettingsOptions {
lobsterKey: string
workspacePath: string
imageModelApiKey: string
imageModelBaseUrl: string
imageModelModelId: string
videoModelApiKey: string
videoModelBaseUrl: string
videoModelModelId: string
copywritingModelApiKey: string
copywritingModelBaseUrl: string
copywritingModelModelId: string
digitalHumanVolcAccessKey: string
digitalHumanVolcSecretKey: string
digitalHumanQiniuAccessKey: string
......@@ -37,14 +43,26 @@ interface UseSaveSettingsOptions {
setWorkspacePathDraft(value: string): void
setLobsterKeyDraft(value: string): void
setImageModelApiKeyDraft(value: string): void
setImageModelBaseUrlDraft(value: string): void
setImageModelModelIdDraft(value: string): void
setVideoModelApiKeyDraft(value: string): void
setVideoModelBaseUrlDraft(value: string): void
setVideoModelModelIdDraft(value: string): void
setCopywritingModelApiKeyDraft(value: string): void
setCopywritingModelBaseUrlDraft(value: string): void
setCopywritingModelModelIdDraft(value: string): void
setDigitalHumanVolcAccessKeyDraft(value: string): void
setDigitalHumanVolcSecretKeyDraft(value: string): void
setDigitalHumanQiniuAccessKeyDraft(value: string): void
setDigitalHumanQiniuSecretKeyDraft(value: string): void
setVideoAnalyzerBaseUrlDraft(value: string): void
setVideoAnalyzerModelIdDraft(value: string): void
setVideoAnalyzerApiKeyDraft(value: string): void
setReplicationBriefBaseUrlDraft(value: string): void
setReplicationBriefModelIdDraft(value: string): void
setReplicationBriefApiKeyDraft(value: string): void
setVectcutBaseUrlDraft(value: string): void
setVectcutFileBaseUrlDraft(value: string): void
setVectcutApiKeyDraft(value: string): void
setXhsFeishuAppIdDraft(value: string): void
setXhsFeishuAppSecretDraft(value: string): void
......@@ -80,7 +98,7 @@ export function useSaveSettings({
resetDrafts?: (savedConfig: AppConfig) => void
}) => {
if (!config || saving) {
return
return false
}
const input: SaveConfigInput = {
......@@ -119,15 +137,17 @@ export function useSaveSettings({
options?.resetDrafts?.(savedConfig)
setters.setInfoText(options?.successMessage ?? (trimmedLobsterKey ? labels.saveSuccessPending : labels.saveSuccessApplied))
void refresh(false)
return true
} catch (error) {
setters.setErrorText(normalizeError(error))
return false
} finally {
setters.setSaving(false)
}
}, [config, desktopApi, labels.saveSuccessApplied, labels.saveSuccessPending, normalizeError, refresh, saving, setters])
const saveLobsterKey = useCallback(async () => {
await saveConfig({
return await saveConfig({
lobsterKey: drafts.lobsterKey,
resetDrafts: () => {
setters.setLobsterKeyDraft("")
......@@ -165,7 +185,7 @@ export function useSaveSettings({
}, [config, setters])
const saveWorkspaceDirectory = useCallback(async () => {
await saveConfig({
return await saveConfig({
workspacePath: drafts.workspacePath,
successMessage: labels.workspaceSaved,
resetDrafts: (savedConfig) => {
......@@ -175,7 +195,7 @@ export function useSaveSettings({
}, [drafts.workspacePath, labels.workspaceSaved, saveConfig, setters])
const saveXhsFeishuConfig = useCallback(async () => {
await saveConfig({
return await saveConfig({
xhsFeishuConfig: {
appId: drafts.xhsFeishuAppId.trim() || undefined,
appSecret: drafts.xhsFeishuAppSecret.trim() || undefined,
......@@ -192,46 +212,58 @@ export function useSaveSettings({
}, [drafts.xhsFeishuAppId, drafts.xhsFeishuAppSecret, drafts.xhsFeishuAppToken, drafts.xhsFeishuTableId, saveConfig, setters])
const saveCopywritingConfig = useCallback(async () => {
await saveConfig({
return await saveConfig({
expertModelConfig: {
copywriting: {
baseUrl: drafts.copywritingModelBaseUrl.trim() || undefined,
modelId: drafts.copywritingModelModelId.trim() || undefined,
apiKey: drafts.copywritingModelApiKey.trim() || undefined
}
},
resetDrafts: () => {
resetDrafts: (savedConfig) => {
setters.setCopywritingModelApiKeyDraft("")
setters.setCopywritingModelBaseUrlDraft(savedConfig.expertModelConfig.copywriting.baseUrl)
setters.setCopywritingModelModelIdDraft(savedConfig.expertModelConfig.copywriting.modelId ?? "")
}
})
}, [drafts.copywritingModelApiKey, saveConfig, setters])
}, [drafts.copywritingModelApiKey, drafts.copywritingModelBaseUrl, drafts.copywritingModelModelId, saveConfig, setters])
const saveImageConfig = useCallback(async () => {
await saveConfig({
return await saveConfig({
expertModelConfig: {
image: {
baseUrl: drafts.imageModelBaseUrl.trim() || undefined,
modelId: drafts.imageModelModelId.trim() || undefined,
apiKey: drafts.imageModelApiKey.trim() || undefined
}
},
resetDrafts: () => {
resetDrafts: (savedConfig) => {
setters.setImageModelApiKeyDraft("")
setters.setImageModelBaseUrlDraft(savedConfig.expertModelConfig.image.baseUrl)
setters.setImageModelModelIdDraft(savedConfig.expertModelConfig.image.modelId ?? "")
}
})
}, [drafts.imageModelApiKey, saveConfig, setters])
}, [drafts.imageModelApiKey, drafts.imageModelBaseUrl, drafts.imageModelModelId, saveConfig, setters])
const saveVideoConfig = useCallback(async () => {
await saveConfig({
return await saveConfig({
expertModelConfig: {
video: {
baseUrl: drafts.videoModelBaseUrl.trim() || undefined,
modelId: drafts.videoModelModelId.trim() || undefined,
apiKey: drafts.videoModelApiKey.trim() || undefined
}
},
resetDrafts: () => {
resetDrafts: (savedConfig) => {
setters.setVideoModelApiKeyDraft("")
setters.setVideoModelBaseUrlDraft(savedConfig.expertModelConfig.video.baseUrl)
setters.setVideoModelModelIdDraft(savedConfig.expertModelConfig.video.modelId ?? "")
}
})
}, [drafts.videoModelApiKey, saveConfig, setters])
}, [drafts.videoModelApiKey, drafts.videoModelBaseUrl, drafts.videoModelModelId, saveConfig, setters])
const saveDigitalHumanConfig = useCallback(async () => {
await saveConfig({
return await saveConfig({
expertModelConfig: {
digitalHuman: {
volcAccessKey: drafts.digitalHumanVolcAccessKey.trim() || undefined,
......@@ -250,7 +282,7 @@ export function useSaveSettings({
}, [drafts.digitalHumanQiniuAccessKey, drafts.digitalHumanQiniuSecretKey, drafts.digitalHumanVolcAccessKey, drafts.digitalHumanVolcSecretKey, saveConfig, setters])
const saveDouyinRuntimeConfig = useCallback(async () => {
await saveConfig({
return await saveConfig({
douyinRuntimeConfig: {
videoAnalyzer: {
baseUrl: drafts.videoAnalyzerBaseUrl.trim() || undefined,
......@@ -268,9 +300,15 @@ export function useSaveSettings({
apiKey: drafts.vectcutApiKey.trim() || undefined
}
},
resetDrafts: () => {
resetDrafts: (savedConfig) => {
setters.setVideoAnalyzerBaseUrlDraft(savedConfig.douyinRuntimeConfig.videoAnalyzer.baseUrl)
setters.setVideoAnalyzerModelIdDraft(savedConfig.douyinRuntimeConfig.videoAnalyzer.modelId ?? "")
setters.setVideoAnalyzerApiKeyDraft("")
setters.setReplicationBriefBaseUrlDraft(savedConfig.douyinRuntimeConfig.replicationBrief.baseUrl)
setters.setReplicationBriefModelIdDraft(savedConfig.douyinRuntimeConfig.replicationBrief.modelId ?? "")
setters.setReplicationBriefApiKeyDraft("")
setters.setVectcutBaseUrlDraft(savedConfig.douyinRuntimeConfig.vectcut.baseUrl)
setters.setVectcutFileBaseUrlDraft(savedConfig.douyinRuntimeConfig.vectcut.fileBaseUrl)
setters.setVectcutApiKeyDraft("")
}
})
......
......@@ -15,8 +15,14 @@ export function useSettingsState(config: AppConfig | null) {
const [lobsterKeyDraft, setLobsterKeyDraft] = useState("");
const [workspacePathDraft, setWorkspacePathDraft] = useState("");
const [imageModelApiKeyDraft, setImageModelApiKeyDraft] = useState("");
const [imageModelBaseUrlDraft, setImageModelBaseUrlDraft] = useState("");
const [imageModelModelIdDraft, setImageModelModelIdDraft] = useState("");
const [videoModelApiKeyDraft, setVideoModelApiKeyDraft] = useState("");
const [videoModelBaseUrlDraft, setVideoModelBaseUrlDraft] = useState("");
const [videoModelModelIdDraft, setVideoModelModelIdDraft] = useState("");
const [copywritingModelApiKeyDraft, setCopywritingModelApiKeyDraft] = useState("");
const [copywritingModelBaseUrlDraft, setCopywritingModelBaseUrlDraft] = useState("");
const [copywritingModelModelIdDraft, setCopywritingModelModelIdDraft] = useState("");
const [digitalHumanVolcAccessKeyDraft, setDigitalHumanVolcAccessKeyDraft] = useState("");
const [digitalHumanVolcSecretKeyDraft, setDigitalHumanVolcSecretKeyDraft] = useState("");
const [digitalHumanQiniuAccessKeyDraft, setDigitalHumanQiniuAccessKeyDraft] = useState("");
......@@ -45,8 +51,14 @@ export function useSettingsState(config: AppConfig | null) {
);
const hasPendingModelKeys = Boolean(
imageModelApiKeyDraft.trim()
|| imageModelBaseUrlDraft.trim() !== (config?.expertModelConfig.image.baseUrl ?? "").trim()
|| imageModelModelIdDraft.trim() !== (config?.expertModelConfig.image.modelId ?? "").trim()
|| videoModelApiKeyDraft.trim()
|| videoModelBaseUrlDraft.trim() !== (config?.expertModelConfig.video.baseUrl ?? "").trim()
|| videoModelModelIdDraft.trim() !== (config?.expertModelConfig.video.modelId ?? "").trim()
|| copywritingModelApiKeyDraft.trim()
|| copywritingModelBaseUrlDraft.trim() !== (config?.expertModelConfig.copywriting.baseUrl ?? "").trim()
|| copywritingModelModelIdDraft.trim() !== (config?.expertModelConfig.copywriting.modelId ?? "").trim()
|| digitalHumanVolcAccessKeyDraft.trim()
|| digitalHumanVolcSecretKeyDraft.trim()
|| digitalHumanQiniuAccessKeyDraft.trim()
......@@ -69,10 +81,22 @@ export function useSettingsState(config: AppConfig | null) {
setWorkspacePathDraft,
imageModelApiKeyDraft,
setImageModelApiKeyDraft,
imageModelBaseUrlDraft,
setImageModelBaseUrlDraft,
imageModelModelIdDraft,
setImageModelModelIdDraft,
videoModelApiKeyDraft,
setVideoModelApiKeyDraft,
videoModelBaseUrlDraft,
setVideoModelBaseUrlDraft,
videoModelModelIdDraft,
setVideoModelModelIdDraft,
copywritingModelApiKeyDraft,
setCopywritingModelApiKeyDraft,
copywritingModelBaseUrlDraft,
setCopywritingModelBaseUrlDraft,
copywritingModelModelIdDraft,
setCopywritingModelModelIdDraft,
digitalHumanVolcAccessKeyDraft,
setDigitalHumanVolcAccessKeyDraft,
digitalHumanVolcSecretKeyDraft,
......
......@@ -67,7 +67,7 @@ interface UseSmokeActionHandlersDeps {
douyinRuntimeConfig?: SaveConfigInput["douyinRuntimeConfig"]
xhsFeishuConfig?: SaveConfigInput["xhsFeishuConfig"]
successMessage?: string
}) => Promise<void>
}) => Promise<boolean>
waitForSmokeConfigPublish: (expected: AppConfig["expertModelConfig"]) => Promise<void>
resolveHomeIntentSuggestion: (prompt?: string) => Promise<{
visible: boolean
......
......@@ -306,15 +306,18 @@ export const mockDesktopApi = {
runtimeMode: input.runtimeMode,
expertModelConfig: {
image: {
...FIXED_EXPERT_MODEL_ENDPOINTS.image,
baseUrl: input.expertModelConfig?.image?.baseUrl?.trim() || FIXED_EXPERT_MODEL_ENDPOINTS.image.baseUrl,
modelId: input.expertModelConfig?.image?.modelId?.trim() || FIXED_EXPERT_MODEL_ENDPOINTS.image.modelId,
apiKeyConfigured: Boolean(input.expertModelConfig?.image?.apiKey?.trim()),
},
video: {
...FIXED_EXPERT_MODEL_ENDPOINTS.video,
baseUrl: input.expertModelConfig?.video?.baseUrl?.trim() || FIXED_EXPERT_MODEL_ENDPOINTS.video.baseUrl,
modelId: input.expertModelConfig?.video?.modelId?.trim() || FIXED_EXPERT_MODEL_ENDPOINTS.video.modelId,
apiKeyConfigured: Boolean(input.expertModelConfig?.video?.apiKey?.trim())
},
copywriting: {
...FIXED_EXPERT_MODEL_ENDPOINTS.copywriting,
baseUrl: input.expertModelConfig?.copywriting?.baseUrl?.trim() || FIXED_EXPERT_MODEL_ENDPOINTS.copywriting.baseUrl,
modelId: input.expertModelConfig?.copywriting?.modelId?.trim() || FIXED_EXPERT_MODEL_ENDPOINTS.copywriting.modelId,
apiKeyConfigured: Boolean(input.expertModelConfig?.copywriting?.apiKey?.trim()),
},
digitalHuman: {
......
......@@ -87,14 +87,20 @@ test("basic reset only affects lobster key and workspace path", () => {
})
})
test("single model resets only clear the current module key", () => {
assert.deepEqual(getResetCopywritingSettingsDrafts(), {
test("single model resets restore saved endpoints and clear the current module key", () => {
assert.deepEqual(getResetCopywritingSettingsDrafts(config), {
copywritingModelBaseUrl: "https://copy.example.test",
copywritingModelModelId: "copy-model",
copywritingModelApiKey: "",
})
assert.deepEqual(getResetImageSettingsDrafts(), {
assert.deepEqual(getResetImageSettingsDrafts(config), {
imageModelBaseUrl: "https://image.example.test",
imageModelModelId: "image-model",
imageModelApiKey: "",
})
assert.deepEqual(getResetVideoSettingsDrafts(), {
assert.deepEqual(getResetVideoSettingsDrafts(config), {
videoModelBaseUrl: "https://video.example.test",
videoModelModelId: "video-model",
videoModelApiKey: "",
})
})
......
......@@ -72,6 +72,8 @@ test("settings secret inputs default hidden and gate reveal buttons behind saved
assert.match(settingsPanelsSource, /const visible = configured && Boolean\(revealedSecrets\[secretId\]\)/)
assert.match(settingsPanelsSource, /const secretPlaceholder = configured && !value && !visible \? "••••••••••••" : placeholder/)
assert.match(settingsPanelsSource, /placeholder=\{secretPlaceholder\}/)
assert.match(settingsPanelsSource, /editable = true/)
assert.match(settingsPanelsSource, /readOnly=\{!editable\}/)
assert.match(settingsPanelsSource, /configured \? \(/)
assert.match(settingsPanelsSource, /secretId: "lobsterKey"/)
assert.match(settingsPanelsSource, /configured: workspaceApiKeyConfigured/)
......@@ -98,29 +100,37 @@ test("settings secret inputs default hidden and gate reveal buttons behind saved
assert.match(settingsStylesSource, /\.settings-secret-reveal-button svg\s*\{[\s\S]*?width:\s*22px;[\s\S]*?height:\s*22px;/m)
})
test("fixed expert model cards show base_url and model_id as read-only values", () => {
assert.match(settingsPanelsSource, /config\?\.expertModelConfig\.copywriting\.baseUrl/)
assert.match(settingsPanelsSource, /config\?\.expertModelConfig\.copywriting\.modelId/)
assert.match(settingsPanelsSource, /config\?\.expertModelConfig\.image\.baseUrl/)
assert.match(settingsPanelsSource, /config\?\.expertModelConfig\.image\.modelId/)
assert.match(settingsPanelsSource, /config\?\.expertModelConfig\.video\.baseUrl/)
assert.match(settingsPanelsSource, /config\?\.expertModelConfig\.video\.modelId/)
test("expert model cards become editable after clicking edit", () => {
assert.match(settingsPanelsSource, /const \[editingSettingsSections, setEditingSettingsSections\] = useState<EditingSettingsSections>\(\{\}\)/)
assert.match(settingsPanelsSource, /const renderEditableActions = \(/)
assert.match(settingsPanelsSource, /\{editing \? "取消" : "编辑"\}/)
assert.match(settingsPanelsSource, /disabled=\{saving \|\| !editing \|\| !hasPending\}/)
assert.match(settingsPanelsSource, /Promise\.resolve\(onSave\(\)\)\.then\(\(saved\) =>/)
assert.match(settingsPanelsSource, /if \(saved !== false\)/)
assert.match(settingsPanelsSource, /sectionId: "copywriting"/)
assert.match(settingsPanelsSource, /sectionId: "image"/)
assert.match(settingsPanelsSource, /sectionId: "video"/)
assert.match(settingsPanelsSource, /renderEditableConfigValue\("base_url", drafts\.copywritingModelBaseUrl, setters\.setCopywritingModelBaseUrl, config\?\.expertModelConfig\.copywriting\.baseUrl, editingCopywritingConfig/)
assert.match(settingsPanelsSource, /renderEditableConfigValue\("model_id", drafts\.copywritingModelModelId, setters\.setCopywritingModelModelId, config\?\.expertModelConfig\.copywriting\.modelId, editingCopywritingConfig/)
assert.match(settingsPanelsSource, /renderEditableConfigValue\("base_url", drafts\.imageModelBaseUrl, setters\.setImageModelBaseUrl, config\?\.expertModelConfig\.image\.baseUrl, editingImageConfig/)
assert.match(settingsPanelsSource, /renderEditableConfigValue\("model_id", drafts\.imageModelModelId, setters\.setImageModelModelId, config\?\.expertModelConfig\.image\.modelId, editingImageConfig/)
assert.match(settingsPanelsSource, /renderEditableConfigValue\("base_url", drafts\.videoModelBaseUrl, setters\.setVideoModelBaseUrl, config\?\.expertModelConfig\.video\.baseUrl, editingVideoConfig/)
assert.match(settingsPanelsSource, /renderEditableConfigValue\("model_id", drafts\.videoModelModelId, setters\.setVideoModelModelId, config\?\.expertModelConfig\.video\.modelId, editingVideoConfig/)
assert.match(settingsPanelsSource, /onSave: onSaveCopywritingConfig/)
assert.match(settingsPanelsSource, /onSave: onSaveImageConfig/)
assert.match(settingsPanelsSource, /onSave: onSaveVideoConfig/)
assert.match(settingsPanelsSource, /settings-readonly-config-value/)
})
test("douyin runtime base urls, model ids, and file base url use copyable read-only values", () => {
assert.match(settingsPanelsSource, /renderReadonlyConfigValue\("base_url", config\?\.douyinRuntimeConfig\.videoAnalyzer\.baseUrl\)/)
assert.match(settingsPanelsSource, /renderReadonlyConfigValue\("model_id", config\?\.douyinRuntimeConfig\.videoAnalyzer\.modelId \|\| "doubao-vision"\)/)
assert.match(settingsPanelsSource, /renderReadonlyConfigValue\("base_url", config\?\.douyinRuntimeConfig\.replicationBrief\.baseUrl\)/)
assert.match(settingsPanelsSource, /renderReadonlyConfigValue\("model_id", config\?\.douyinRuntimeConfig\.replicationBrief\.modelId \|\| "qwen-max"\)/)
assert.match(settingsPanelsSource, /renderReadonlyConfigValue\("base_url", config\?\.douyinRuntimeConfig\.vectcut\.baseUrl\)/)
assert.match(settingsPanelsSource, /renderReadonlyConfigValue\("file_base_url", config\?\.douyinRuntimeConfig\.vectcut\.fileBaseUrl\)/)
assert.doesNotMatch(settingsPanelsSource, /setVideoAnalyzerBaseUrl\(event\.target\.value\)/)
assert.doesNotMatch(settingsPanelsSource, /setVideoAnalyzerModelId\(event\.target\.value\)/)
assert.doesNotMatch(settingsPanelsSource, /setReplicationBriefBaseUrl\(event\.target\.value\)/)
assert.doesNotMatch(settingsPanelsSource, /setReplicationBriefModelId\(event\.target\.value\)/)
assert.doesNotMatch(settingsPanelsSource, /setVectcutBaseUrl\(event\.target\.value\)/)
assert.doesNotMatch(settingsPanelsSource, /setVectcutFileBaseUrl\(event\.target\.value\)/)
test("douyin runtime base urls, model ids, and file base url enter editable mode", () => {
assert.match(settingsPanelsSource, /sectionId: "douyinRuntime"/)
assert.match(settingsPanelsSource, /renderEditableConfigValue\("base_url", drafts\.videoAnalyzerBaseUrl, setters\.setVideoAnalyzerBaseUrl, config\?\.douyinRuntimeConfig\.videoAnalyzer\.baseUrl, editingDouyinRuntimeConfig/)
assert.match(settingsPanelsSource, /renderEditableConfigValue\("model_id", drafts\.videoAnalyzerModelId, setters\.setVideoAnalyzerModelId, config\?\.douyinRuntimeConfig\.videoAnalyzer\.modelId \|\| "doubao-vision", editingDouyinRuntimeConfig/)
assert.match(settingsPanelsSource, /renderEditableConfigValue\("base_url", drafts\.replicationBriefBaseUrl, setters\.setReplicationBriefBaseUrl, config\?\.douyinRuntimeConfig\.replicationBrief\.baseUrl, editingDouyinRuntimeConfig/)
assert.match(settingsPanelsSource, /renderEditableConfigValue\("model_id", drafts\.replicationBriefModelId, setters\.setReplicationBriefModelId, config\?\.douyinRuntimeConfig\.replicationBrief\.modelId \|\| "qwen-max", editingDouyinRuntimeConfig/)
assert.match(settingsPanelsSource, /renderEditableConfigValue\("base_url", drafts\.vectcutBaseUrl, setters\.setVectcutBaseUrl, config\?\.douyinRuntimeConfig\.vectcut\.baseUrl, editingDouyinRuntimeConfig/)
assert.match(settingsPanelsSource, /renderEditableConfigValue\("file_base_url", drafts\.vectcutFileBaseUrl, setters\.setVectcutFileBaseUrl, config\?\.douyinRuntimeConfig\.vectcut\.fileBaseUrl, editingDouyinRuntimeConfig/)
assert.match(settingsPanelsSource, /onSave: onSaveDouyinRuntimeConfig/)
})
test("read-only config values expose full values by title and copy on click or keyboard", () => {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment