fix(ui): 修复图像模式问题并新增 JSON 结构化模板
- 支持 ModelScope API 新增的 PROCESSING 状态 - 修复 session 状态持久化的 P0 问题 - 新增 JSON 结构化图像提示词模板 - 增强图像模式模型选择的防御性
This commit is contained in:
1 parent
a4ab335c2f
commit
f64534b4cf
12 files changed
+469
-14
No files matched your search
@@ -238,11 +238,11 @@ export class ModelScopeImageAdapter extends AbstractImageProviderAdapter {
|
||||
// 任务失败或被取消,提取错误信息
|
||||
const errorMessage = data.error?.message || data.error || data.message || 'Unknown error'
|
||||
throw new Error(`Task ${status.toLowerCase()}: ${errorMessage}`)
|
||||
} else if (status !== 'PENDING' && status !== 'RUNNING') {
|
||||
} else if (status !== 'PENDING' && status !== 'RUNNING' && status !== 'PROCESSING') {
|
||||
// 未知的终态,视为失败
|
||||
throw new Error(`Unknown task status: ${status}`)
|
||||
}
|
||||
// task_status 为 PENDING 或 RUNNING,继续轮询
|
||||
// task_status 为 PENDING、RUNNING 或 PROCESSING,继续轮询
|
||||
}
|
||||
|
||||
throw new Error(`Task timeout after ${maxAttempts} attempts`)
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
import { Template, MessageTemplate } from '../../../types';
|
||||
|
||||
export const template: Template = {
|
||||
id: 'image2image-json-structured-optimize',
|
||||
name: 'JSON结构化提示词(图生图/通用)',
|
||||
content: [
|
||||
{
|
||||
role: 'system',
|
||||
content: `
|
||||
# Role: 图生图提示词结构化编排器(JSON 输出)
|
||||
|
||||
## Goal
|
||||
将用户原始描述改写为可直接用于图生图的“结构化 JSON 提示词”。
|
||||
|
||||
## Hard Rules (must)
|
||||
1. 只输出一个 JSON 对象(必须可被 JSON.parse 解析)
|
||||
2. 不要输出任何解释性文本、标题、前后缀、代码块、Markdown
|
||||
3. 不要输出数组包裹(顶层必须是 object)
|
||||
4. 严格 JSON:使用双引号、无注释、无尾随逗号
|
||||
|
||||
## Output Principles
|
||||
- JSON 结构要尽量通用:适用于人物、动物、物体、场景、抽象概念等
|
||||
- 字段名与字段值都使用中文(含键名);必要时可包含数字/单位/符号等
|
||||
- 结构可以自由发挥:可新增/删除/重命名字段,只要 JSON 合法且更贴合场景即可
|
||||
- 图生图场景:可在 constraints 中明确“保留/改变”的要点,但不要虚构输入图像不存在的细节
|
||||
|
||||
## Recommended (optional) Structure
|
||||
你可以参考以下结构,但不强制;可按需要增删扩展:
|
||||
{
|
||||
"场景": { },
|
||||
"参考图指导": {
|
||||
"使用输入图作为参考": true,
|
||||
"保留": [ "..." ],
|
||||
"改变": [ "..." ]
|
||||
},
|
||||
"约束": { "必须保留": [ "..." ], "避免": [ "..." ] },
|
||||
"负面提示词": [ "..." ]
|
||||
}
|
||||
|
||||
## Safety
|
||||
如原始描述包含不适当内容,进行合规替换与弱化,但仍保持画面意图可用。
|
||||
`
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: `请将以下“原始图生图描述”改写为“结构化 JSON 提示词”。
|
||||
|
||||
要求:
|
||||
- 仅输出 JSON(严格 JSON,禁止解释性文本/代码块)
|
||||
- JSON 可自由发挥扩展,但必须贴合原始描述并更具体可视
|
||||
- JSON 的键名与字段值都使用中文(含键名)
|
||||
|
||||
原始图生图描述:
|
||||
{{originalPrompt}}
|
||||
`
|
||||
}
|
||||
] as MessageTemplate[],
|
||||
metadata: {
|
||||
version: '1.0.0',
|
||||
lastModified: 1736208000000,
|
||||
author: 'System',
|
||||
description: '图生图的严格 JSON 输出模板;结构通用,允许自由扩展字段;可附带“保留/改变”指导',
|
||||
templateType: 'image2imageOptimize',
|
||||
language: 'zh'
|
||||
},
|
||||
isBuiltin: true
|
||||
};
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
import { Template, MessageTemplate } from '../../../types';
|
||||
|
||||
export const template: Template = {
|
||||
id: 'image2image-json-structured-optimize_en',
|
||||
name: 'JSON Structured Prompt (Img2Img / Generic)',
|
||||
content: [
|
||||
{
|
||||
role: 'system',
|
||||
content: `
|
||||
# Role: Img2Img Structured Prompt Composer (JSON Output)
|
||||
|
||||
## Goal
|
||||
Rewrite the user's input into a structured JSON prompt suitable for img2img.
|
||||
|
||||
## Hard Rules (must)
|
||||
1. Output exactly one JSON object (must be JSON.parse-able)
|
||||
2. No explanatory text, no headings, no wrappers, no Markdown, no code fences
|
||||
3. Top-level must be an object (not an array)
|
||||
4. Strict JSON: double quotes, no comments, no trailing commas
|
||||
|
||||
## Output Principles
|
||||
- Keep the JSON schema generic: works for people, animals, objects, scenes, abstract concepts
|
||||
- Prefer snake_case keys; values can be English or Chinese
|
||||
- The schema is flexible: add/remove/rename fields freely as long as JSON stays valid and fits the scene best
|
||||
- For img2img, you may specify what to preserve/change, but do not hallucinate details not implied by the input
|
||||
|
||||
## Recommended (optional) Structure
|
||||
You may use this as a reference (not mandatory):
|
||||
{
|
||||
"scene": { ... },
|
||||
"image_guidance": {
|
||||
"use_input_as_reference": true,
|
||||
"preserve": [ "..." ],
|
||||
"change": [ "..." ]
|
||||
},
|
||||
"constraints": { "must_keep": [ "..." ], "avoid": [ "..." ] },
|
||||
"negative_prompt": [ "..." ]
|
||||
}
|
||||
|
||||
## Safety
|
||||
If the input contains inappropriate content, replace/soften it to a compliant variant while keeping the intent usable.
|
||||
`
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: `Rewrite the following img2img description into a structured JSON prompt.
|
||||
|
||||
Requirements:
|
||||
- Output JSON only (strict JSON; no explanations / no code fences)
|
||||
- The JSON schema may be freely extended, but must remain faithful and more visually specific
|
||||
|
||||
Img2img description:
|
||||
{{originalPrompt}}
|
||||
`
|
||||
}
|
||||
] as MessageTemplate[],
|
||||
metadata: {
|
||||
version: '1.0.0',
|
||||
lastModified: 1736208000000,
|
||||
author: 'System',
|
||||
description: 'Strict JSON output template for img2img; generic and freely extensible schema with preserve/change guidance',
|
||||
templateType: 'image2imageOptimize',
|
||||
language: 'en'
|
||||
},
|
||||
isBuiltin: true
|
||||
};
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
import { Template, MessageTemplate } from '../../../types';
|
||||
|
||||
export const template: Template = {
|
||||
id: 'image-json-structured-optimize',
|
||||
name: 'JSON结构化提示词(通用)',
|
||||
content: [
|
||||
{
|
||||
role: 'system',
|
||||
content: `
|
||||
# Role: 图像提示词结构化编排器(JSON 输出)
|
||||
|
||||
## Goal
|
||||
将用户原始描述改写为可直接用于图像生成的“结构化 JSON 提示词”。
|
||||
|
||||
## Hard Rules (must)
|
||||
1. 只输出一个 JSON 对象(必须可被 JSON.parse 解析)
|
||||
2. 不要输出任何解释性文本、标题、前后缀、代码块、Markdown
|
||||
3. 不要输出数组包裹(顶层必须是 object)
|
||||
4. 严格 JSON:使用双引号、无注释、无尾随逗号
|
||||
|
||||
## Output Principles
|
||||
- JSON 结构要尽量通用:适用于人物、动物、物体、场景、抽象概念等
|
||||
- 字段名与字段值都使用中文(含键名);必要时可包含数字/单位/符号等
|
||||
- 结构可以自由发挥:可新增/删除/重命名字段,只要 JSON 合法且更贴合场景即可
|
||||
- 内容目标:更具体、更可视、更可控;避免空洞形容词堆砌
|
||||
|
||||
## Recommended (optional) Structure
|
||||
你可以参考以下结构,但不强制;可按需要增删扩展:
|
||||
{
|
||||
"场景": {
|
||||
"描述": "...",
|
||||
"主体": [
|
||||
{ "类型": "...", "描述": "...", "属性": { } }
|
||||
],
|
||||
"环境": { },
|
||||
"动作": { },
|
||||
"构图": { },
|
||||
"相机": { },
|
||||
"光照": { },
|
||||
"色彩": { },
|
||||
"风格": { },
|
||||
"细节": [ "..." ]
|
||||
},
|
||||
"约束": {
|
||||
"必须保留": [ "..." ],
|
||||
"避免": [ "..." ]
|
||||
},
|
||||
"负面提示词": [ "..." ]
|
||||
}
|
||||
|
||||
## Safety
|
||||
如原始描述包含不适当内容,进行合规替换与弱化,但仍保持画面意图可用。
|
||||
`
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: `请将以下“原始图像描述”改写为“结构化 JSON 提示词”。
|
||||
|
||||
要求:
|
||||
- 仅输出 JSON(严格 JSON,禁止解释性文本/代码块)
|
||||
- JSON 可自由发挥扩展,但必须贴合原始描述并更具体可视
|
||||
- JSON 的键名与字段值都使用中文(含键名)
|
||||
|
||||
原始图像描述:
|
||||
{{originalPrompt}}
|
||||
`
|
||||
}
|
||||
] as MessageTemplate[],
|
||||
metadata: {
|
||||
version: '1.0.0',
|
||||
lastModified: 1736208000000,
|
||||
author: 'System',
|
||||
description: '输出严格 JSON 的图像提示词优化模板;结构通用,允许自由扩展字段以适配任意场景',
|
||||
templateType: 'text2imageOptimize',
|
||||
language: 'zh'
|
||||
},
|
||||
isBuiltin: true
|
||||
};
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
import { Template, MessageTemplate } from '../../../types';
|
||||
|
||||
export const template: Template = {
|
||||
id: 'image-json-structured-optimize_en',
|
||||
name: 'JSON Structured Prompt (Generic)',
|
||||
content: [
|
||||
{
|
||||
role: 'system',
|
||||
content: `
|
||||
# Role: Structured Image Prompt Composer (JSON Output)
|
||||
|
||||
## Goal
|
||||
Rewrite the user's description into a structured JSON image prompt that can be used directly for image generation.
|
||||
|
||||
## Hard Rules (must)
|
||||
1. Output exactly one JSON object (must be JSON.parse-able)
|
||||
2. No explanatory text, no headings, no wrappers, no Markdown, no code fences
|
||||
3. Top-level must be an object (not an array)
|
||||
4. Strict JSON: double quotes, no comments, no trailing commas
|
||||
|
||||
## Output Principles
|
||||
- Keep the JSON schema generic: works for people, animals, objects, scenes, abstract concepts
|
||||
- Prefer snake_case keys; values can be English or Chinese
|
||||
- The schema is flexible: add/remove/rename fields freely as long as JSON stays valid and fits the scene best
|
||||
- Aim for concrete, visual, controllable details; avoid vague adjective piles
|
||||
|
||||
## Recommended (optional) Structure
|
||||
You may follow this as a reference (not mandatory):
|
||||
{
|
||||
"scene": {
|
||||
"description": "...",
|
||||
"entities": [
|
||||
{ "type": "...", "description": "...", "attributes": { } }
|
||||
],
|
||||
"environment": { },
|
||||
"action": { },
|
||||
"composition": { },
|
||||
"camera": { },
|
||||
"lighting": { },
|
||||
"color": { },
|
||||
"style": { },
|
||||
"details": [ "..." ]
|
||||
},
|
||||
"constraints": {
|
||||
"must_keep": [ "..." ],
|
||||
"avoid": [ "..." ]
|
||||
},
|
||||
"negative_prompt": [ "..." ]
|
||||
}
|
||||
|
||||
## Safety
|
||||
If the input contains inappropriate content, replace/soften it to a compliant variant while keeping the intent usable.
|
||||
`
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: `Rewrite the following image description into a structured JSON prompt.
|
||||
|
||||
Requirements:
|
||||
- Output JSON only (strict JSON; no explanations / no code fences)
|
||||
- The JSON schema may be freely extended, but must remain faithful and more visually specific
|
||||
|
||||
Image description:
|
||||
{{originalPrompt}}
|
||||
`
|
||||
}
|
||||
] as MessageTemplate[],
|
||||
metadata: {
|
||||
version: '1.0.0',
|
||||
lastModified: 1736208000000,
|
||||
author: 'System',
|
||||
description: 'Image prompt optimization template that outputs strict JSON; generic schema with flexible extensibility',
|
||||
templateType: 'text2imageOptimize',
|
||||
language: 'en'
|
||||
},
|
||||
isBuiltin: true
|
||||
};
|
||||
@@ -49,11 +49,15 @@ import { template as image_photography_optimize } from './image-optimize/text2im
|
||||
import { template as image_photography_optimize_en } from './image-optimize/text2image/photography-optimize_en';
|
||||
import { template as image_creative_text2image } from './image-optimize/text2image/creative-text2image';
|
||||
import { template as image_creative_text2image_en } from './image-optimize/text2image/creative-text2image_en';
|
||||
import { template as image_json_structured_optimize } from './image-optimize/text2image/json-structured-optimize';
|
||||
import { template as image_json_structured_optimize_en } from './image-optimize/text2image/json-structured-optimize_en';
|
||||
// 图生图
|
||||
import { template as image2image_optimize } from './image-optimize/image2image/image2image-optimize';
|
||||
import { template as image2image_optimize_en } from './image-optimize/image2image/image2image-optimize_en';
|
||||
import { template as image2image_design_text_edit_optimize } from './image-optimize/image2image/design-text-edit-optimize';
|
||||
import { template as image2image_design_text_edit_optimize_en } from './image-optimize/image2image/design-text-edit-optimize_en';
|
||||
import { template as image2image_json_structured_optimize } from './image-optimize/image2image/json-structured-optimize';
|
||||
import { template as image2image_json_structured_optimize_en } from './image-optimize/image2image/json-structured-optimize_en';
|
||||
// 图像迭代
|
||||
import { template as image_iterate_general } from './image-optimize/iterate/image-iterate-general';
|
||||
import { template as image_iterate_general_en } from './image-optimize/iterate/image-iterate-general_en';
|
||||
@@ -151,11 +155,15 @@ export const ALL_TEMPLATES = {
|
||||
image_photography_optimize_en,
|
||||
image_creative_text2image,
|
||||
image_creative_text2image_en,
|
||||
image_json_structured_optimize,
|
||||
image_json_structured_optimize_en,
|
||||
// 图生图模板
|
||||
image2image_optimize,
|
||||
image2image_optimize_en,
|
||||
image2image_design_text_edit_optimize,
|
||||
image2image_design_text_edit_optimize_en,
|
||||
image2image_json_structured_optimize,
|
||||
image2image_json_structured_optimize_en,
|
||||
// 图像迭代模板
|
||||
image_iterate_general,
|
||||
image_iterate_general_en,
|
||||
|
||||
@@ -790,6 +790,7 @@ type ContextWorkspaceExpose = {
|
||||
const systemWorkspaceRef = ref<ContextWorkspaceExpose | null>(null);
|
||||
type ContextUserWorkspaceExpose = ContextWorkspaceExpose & {
|
||||
// 提供最小可用 API,避免父组件依赖子组件内部实现细节
|
||||
contextUserOptimization?: import("../../composables/prompt/useContextUserOptimization").UseContextUserOptimization;
|
||||
setPrompt?: (prompt: string) => void;
|
||||
getPrompt?: () => string;
|
||||
getOptimizedPrompt?: () => string;
|
||||
@@ -1203,6 +1204,9 @@ const restoreBasicOrProVariableSession = () => {
|
||||
optimizer.currentChainId = savedState.chainId || '';
|
||||
optimizer.currentVersionId = savedState.versionId || '';
|
||||
|
||||
// 恢复测试内容(系统/用户模式都需要)
|
||||
testContent.value = savedState.testContent || '';
|
||||
|
||||
// 恢复模型选择
|
||||
if (savedState.selectedOptimizeModelKey) {
|
||||
modelManager.selectedOptimizeModel = savedState.selectedOptimizeModelKey;
|
||||
@@ -1235,6 +1239,57 @@ const restoreBasicOrProVariableSession = () => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 🔧 Pro-user(变量模式)会话恢复:恢复到 ContextUserWorkspace 的独立状态树
|
||||
*
|
||||
* 根因修复:
|
||||
* - Pro-user 实际使用 useContextUserOptimization/useContextUserTester 的内部 reactive 状态
|
||||
* - 若只恢复 optimizer 的字段,ContextUserWorkspace 仍保持空状态,导致切换/重挂载后“内容丢失”
|
||||
*/
|
||||
const restoreProVariableSessionToUserWorkspace = async () => {
|
||||
const savedState = proVariableSession.state;
|
||||
|
||||
// 这些字段是 App 层单例,仍需跟随 session 切换
|
||||
testContent.value = savedState.testContent || '';
|
||||
isCompareMode.value = savedState.isCompareMode;
|
||||
|
||||
// 等待 DOM 更新,确保 ContextUserWorkspace 已挂载并建立 ref
|
||||
await nextTick();
|
||||
|
||||
let contextUserOptimization = userWorkspaceRef.value?.contextUserOptimization;
|
||||
if (!contextUserOptimization) {
|
||||
// 防御性重试:部分切换路径下首次 nextTick 可能仍未建立 ref
|
||||
await nextTick();
|
||||
contextUserOptimization = userWorkspaceRef.value?.contextUserOptimization;
|
||||
if (!contextUserOptimization) return;
|
||||
}
|
||||
|
||||
// 恢复提示词与优化结果
|
||||
contextUserOptimization.prompt = savedState.prompt || '';
|
||||
contextUserOptimization.optimizedPrompt = savedState.optimizedPrompt || '';
|
||||
contextUserOptimization.optimizedReasoning = savedState.reasoning || '';
|
||||
|
||||
// 恢复链信息(versions 尝试通过 historyManager 重新拉取)
|
||||
contextUserOptimization.currentChainId = savedState.chainId || '';
|
||||
contextUserOptimization.currentVersionId = savedState.versionId || '';
|
||||
contextUserOptimization.currentVersions = [];
|
||||
|
||||
// 重置过程态(避免恢复后停留在 loading)
|
||||
contextUserOptimization.isOptimizing = false;
|
||||
contextUserOptimization.isIterating = false;
|
||||
|
||||
const historyManager = services.value?.historyManager;
|
||||
if (historyManager && savedState.chainId) {
|
||||
try {
|
||||
const chain = await historyManager.getChain(savedState.chainId);
|
||||
contextUserOptimization.currentVersions = chain.versions;
|
||||
contextUserOptimization.currentVersionId = savedState.versionId || chain.currentRecord.id;
|
||||
} catch (error) {
|
||||
console.warn('[PromptOptimizerApp] Pro-user 恢复链失败,使用 session 快照继续:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 🔧 Codex 修复:恢复 Pro-system 模式的 session 状态
|
||||
* Pro 多消息模式使用专用 session store,字段结构不同
|
||||
@@ -1317,9 +1372,12 @@ const restoreImageSession = () => {
|
||||
* 互斥控制由 useSessionRestoreCoordinator 处理
|
||||
*/
|
||||
const restoreSessionToUIInternal = async () => {
|
||||
if (functionMode.value === 'basic' || (functionMode.value === 'pro' && proSubMode.value === 'user')) {
|
||||
// Basic 模式或 Pro-variable 模式:使用通用恢复逻辑
|
||||
if (functionMode.value === 'basic') {
|
||||
// Basic 模式:使用通用恢复逻辑
|
||||
restoreBasicOrProVariableSession();
|
||||
} else if (functionMode.value === 'pro' && proSubMode.value === 'user') {
|
||||
// Pro-user(变量模式):恢复到 ContextUserWorkspace
|
||||
await restoreProVariableSessionToUserWorkspace();
|
||||
} else if (functionMode.value === 'pro' && proSubMode.value === 'system') {
|
||||
// Pro-system 模式:使用专用恢复逻辑(异步,等待 DOM 更新)
|
||||
await restoreProMultiMessageSession();
|
||||
@@ -1360,7 +1418,8 @@ watch(
|
||||
if (sessionManager.isSwitching) return;
|
||||
|
||||
// Pro-system 模式没有 prompt 字段,跳过同步
|
||||
if (functionMode.value === 'pro' && proSubMode.value === 'system') {
|
||||
// Pro-user 模式的 prompt 由 ContextUserWorkspace 内部管理,避免用 optimizer 覆盖 session
|
||||
if (functionMode.value === 'pro') {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1371,6 +1430,47 @@ watch(
|
||||
}
|
||||
);
|
||||
|
||||
// 同步 Pro-user(变量模式)工作区状态到 session store
|
||||
// 说明:Pro-user 使用 ContextUserWorkspace 内部的 useContextUserOptimization 状态树,不走 optimizer
|
||||
watch(
|
||||
() => {
|
||||
if (functionMode.value !== 'pro' || proSubMode.value !== 'user') return null;
|
||||
return userWorkspaceRef.value?.contextUserOptimization?.prompt ?? null;
|
||||
},
|
||||
(newPrompt) => {
|
||||
if (newPrompt === null) return;
|
||||
if (sessionManager.isSwitching) return;
|
||||
proVariableSession.updatePrompt(newPrompt || '');
|
||||
},
|
||||
{ flush: 'sync' }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => {
|
||||
if (functionMode.value !== 'pro' || proSubMode.value !== 'user') return null;
|
||||
const opt = userWorkspaceRef.value?.contextUserOptimization;
|
||||
if (!opt) return null;
|
||||
return [
|
||||
opt.optimizedPrompt,
|
||||
opt.optimizedReasoning,
|
||||
opt.currentChainId,
|
||||
opt.currentVersionId,
|
||||
] as const;
|
||||
},
|
||||
(payload) => {
|
||||
if (!payload) return;
|
||||
if (sessionManager.isSwitching) return;
|
||||
const [optimizedPrompt, reasoning, chainId, versionId] = payload;
|
||||
proVariableSession.updateOptimizedResult({
|
||||
optimizedPrompt: optimizedPrompt || '',
|
||||
reasoning: reasoning || '',
|
||||
chainId: chainId || '',
|
||||
versionId: versionId || '',
|
||||
});
|
||||
},
|
||||
{ flush: 'sync' }
|
||||
);
|
||||
|
||||
// 同步优化结果到 session store(包含 optimizedPrompt, reasoning, chainId, versionId)
|
||||
// ⚠️ Codex 要求:移除 truthy 检查,支持清空状态同步
|
||||
watch(
|
||||
@@ -1381,6 +1481,11 @@ watch(
|
||||
() => optimizer.currentVersionId,
|
||||
],
|
||||
([newOptimizedPrompt, newReasoning, newChainId, newVersionId]) => {
|
||||
// Pro-user 模式的优化结果由 ContextUserWorkspace 内部管理,避免用 optimizer 覆盖 session
|
||||
if (functionMode.value === 'pro' && proSubMode.value === 'user') {
|
||||
return;
|
||||
}
|
||||
|
||||
const session = getCurrentSession();
|
||||
if (session && !sessionManager.isSwitching) {
|
||||
session.updateOptimizedResult({
|
||||
@@ -1402,8 +1507,8 @@ watch(
|
||||
(newTestResults) => {
|
||||
if (sessionManager.isSwitching) return;
|
||||
|
||||
// Image 模式没有 updateTestResults 方法,跳过同步
|
||||
if (functionMode.value === 'image') {
|
||||
// 仅 Basic 模式使用 promptTester(其他模式有各自的测试器/工作区)
|
||||
if (functionMode.value !== 'basic') {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1434,6 +1539,10 @@ watch(
|
||||
|
||||
// Image 模式使用 updateTextModel
|
||||
if (functionMode.value === 'image') {
|
||||
// 避免模型选择初始化/短暂空值时覆盖 image session(导致下拉变成“未选择”)
|
||||
if (!modelManager.isModelSelectionReady || !newModel) {
|
||||
return;
|
||||
}
|
||||
if (typeof (session as any).updateTextModel === 'function') {
|
||||
(session as any).updateTextModel(newModel || '');
|
||||
}
|
||||
@@ -1513,6 +1622,24 @@ watch(
|
||||
}
|
||||
);
|
||||
|
||||
// 同步测试内容到 session store(用于刷新/切换后保留测试输入)
|
||||
watch(
|
||||
testContent,
|
||||
(newContent) => {
|
||||
if (sessionManager.isSwitching) return;
|
||||
|
||||
// Image 模式没有 testContent;Pro-system/ContextUser 模式也不使用此输入
|
||||
if (functionMode.value === 'image') return;
|
||||
if (functionMode.value === 'pro' && proSubMode.value === 'system') return;
|
||||
|
||||
const session = getCurrentSession();
|
||||
if (session && typeof (session as any).updateTestContent === 'function') {
|
||||
(session as any).updateTestContent(newContent || '');
|
||||
}
|
||||
},
|
||||
{ flush: 'sync' }
|
||||
);
|
||||
|
||||
// 同步对比模式到 session store
|
||||
watch(
|
||||
isCompareMode,
|
||||
|
||||
@@ -123,9 +123,7 @@
|
||||
"
|
||||
:rows="4"
|
||||
:autosize="{ minRows: 4, maxRows: 12 }"
|
||||
:maxlength="2000"
|
||||
clearable
|
||||
show-count
|
||||
:disabled="isOptimizing"
|
||||
/>
|
||||
|
||||
|
||||
@@ -286,7 +286,6 @@ export function useImageWorkspace(services: Ref<AppServices | null>, options: Us
|
||||
const refreshTextModels = async () => {
|
||||
if (!modelManager.value) {
|
||||
textModelOptions.value = []
|
||||
selectedTextModelKey.value = ''
|
||||
return
|
||||
}
|
||||
|
||||
@@ -303,6 +302,11 @@ export function useImageWorkspace(services: Ref<AppServices | null>, options: Us
|
||||
raw: m,
|
||||
}))
|
||||
|
||||
// 没有可用模型时:不要清空已选 key(避免服务/配置短暂不可用时把 session 覆盖成空)
|
||||
if (!textModels.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const currentKey = selectedTextModelKey.value
|
||||
const keys = new Set(textModels.map(m => m.id))
|
||||
const fallback = textModels[0]?.id || ''
|
||||
@@ -311,10 +315,6 @@ export function useImageWorkspace(services: Ref<AppServices | null>, options: Us
|
||||
if (needsFallback) {
|
||||
selectedTextModelKey.value = fallback
|
||||
}
|
||||
|
||||
if (!textModels.length) {
|
||||
selectedTextModelKey.value = ''
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[useImageWorkspace] Failed to refresh text models:', error)
|
||||
textModelOptions.value = []
|
||||
@@ -332,6 +332,11 @@ export function useImageWorkspace(services: Ref<AppServices | null>, options: Us
|
||||
raw: m,
|
||||
}))
|
||||
|
||||
// 没有可用模型时:不要清空已选 key(避免加载失败/初始化未就绪时覆盖 session)
|
||||
if (!imageModels.value.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const current = selectedImageModelKey.value
|
||||
const exists = imageModels.value.some(m => m.id === current)
|
||||
if (!exists) {
|
||||
|
||||
@@ -38,6 +38,9 @@ export interface BasicSystemSessionState {
|
||||
chainId: string
|
||||
versionId: string
|
||||
|
||||
// 测试区域内容(system 模式必填,用于测试区输入框)
|
||||
testContent: string
|
||||
|
||||
// 测试结果
|
||||
testResults: TestResults | null
|
||||
|
||||
@@ -63,6 +66,7 @@ const createDefaultState = (): BasicSystemSessionState => ({
|
||||
reasoning: '',
|
||||
chainId: '',
|
||||
versionId: '',
|
||||
testContent: '',
|
||||
testResults: null,
|
||||
selectedOptimizeModelKey: '',
|
||||
selectedTestModelKey: '',
|
||||
@@ -110,6 +114,14 @@ export const useBasicSystemSession = defineStore('basicSystemSession', () => {
|
||||
state.value.lastActiveAt = Date.now()
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新测试内容
|
||||
*/
|
||||
const updateTestContent = (content: string) => {
|
||||
state.value.testContent = content
|
||||
state.value.lastActiveAt = Date.now()
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新优化模型选择
|
||||
*/
|
||||
@@ -218,6 +230,7 @@ export const useBasicSystemSession = defineStore('basicSystemSession', () => {
|
||||
// 更新方法
|
||||
updatePrompt,
|
||||
updateOptimizedResult,
|
||||
updateTestContent,
|
||||
updateTestResults,
|
||||
updateOptimizeModel,
|
||||
updateTestModel,
|
||||
|
||||
@@ -20,6 +20,7 @@ export interface BasicUserSessionState {
|
||||
reasoning: string
|
||||
chainId: string
|
||||
versionId: string
|
||||
testContent: string
|
||||
testResults: TestResults | null
|
||||
selectedOptimizeModelKey: string
|
||||
selectedTestModelKey: string
|
||||
@@ -35,6 +36,7 @@ const createDefaultState = (): BasicUserSessionState => ({
|
||||
reasoning: '',
|
||||
chainId: '',
|
||||
versionId: '',
|
||||
testContent: '',
|
||||
testResults: null,
|
||||
selectedOptimizeModelKey: '',
|
||||
selectedTestModelKey: '',
|
||||
@@ -70,6 +72,11 @@ export const useBasicUserSession = defineStore('basicUserSession', () => {
|
||||
state.value.lastActiveAt = Date.now()
|
||||
}
|
||||
|
||||
const updateTestContent = (content: string) => {
|
||||
state.value.testContent = content
|
||||
state.value.lastActiveAt = Date.now()
|
||||
}
|
||||
|
||||
const updateOptimizeModel = (modelKey: string) => {
|
||||
state.value.selectedOptimizeModelKey = modelKey
|
||||
state.value.lastActiveAt = Date.now()
|
||||
@@ -149,6 +156,7 @@ export const useBasicUserSession = defineStore('basicUserSession', () => {
|
||||
state,
|
||||
updatePrompt,
|
||||
updateOptimizedResult,
|
||||
updateTestContent,
|
||||
updateTestResults,
|
||||
updateOptimizeModel,
|
||||
updateTestModel,
|
||||
|
||||
@@ -22,6 +22,7 @@ export interface ProVariableSessionState {
|
||||
reasoning: string
|
||||
chainId: string
|
||||
versionId: string
|
||||
testContent: string
|
||||
testResults: TestResults | null
|
||||
selectedOptimizeModelKey: string
|
||||
selectedTestModelKey: string
|
||||
@@ -37,6 +38,7 @@ const createDefaultState = (): ProVariableSessionState => ({
|
||||
reasoning: '',
|
||||
chainId: '',
|
||||
versionId: '',
|
||||
testContent: '',
|
||||
testResults: null,
|
||||
selectedOptimizeModelKey: '',
|
||||
selectedTestModelKey: '',
|
||||
@@ -72,6 +74,11 @@ export const useProVariableSession = defineStore('proVariableSession', () => {
|
||||
state.value.lastActiveAt = Date.now()
|
||||
}
|
||||
|
||||
const updateTestContent = (content: string) => {
|
||||
state.value.testContent = content
|
||||
state.value.lastActiveAt = Date.now()
|
||||
}
|
||||
|
||||
const updateOptimizeModel = (modelKey: string) => {
|
||||
state.value.selectedOptimizeModelKey = modelKey
|
||||
state.value.lastActiveAt = Date.now()
|
||||
@@ -151,6 +158,7 @@ export const useProVariableSession = defineStore('proVariableSession', () => {
|
||||
state,
|
||||
updatePrompt,
|
||||
updateOptimizedResult,
|
||||
updateTestContent,
|
||||
updateTestResults,
|
||||
updateOptimizeModel,
|
||||
updateTestModel,
|
||||
|
||||
Reference in new issue
Block a user