**refactor(ui): 统一优化与测试架构,实现 ContextUser 和 ContextSystem 的完全解耦**

为提升架构对称性与模块独立性,全面重构会话管理与测试逻辑,彻底消除 `App.vue` 的中心化依赖:

- **架构解耦**:
  - 实现 `ContextUserWorkspace` 完全独立,不再依赖 `App.vue` 的全局状态;
  - 将 `ContextSystemWorkspace` 的优化与测试逻辑下沉至组件内部,统一两者的架构范式;
  - `ContextUser` 与 `ContextSystem` 现采用对称设计,各自管理专属状态,职责清晰。

- **Composition API 抽象**:
  - 新增 `useContextUserOptimization` 与 `useContextUserTester`,为 ContextUser 提供独立的状态管理;
  - 新增 `useConversationTester` 专用于多会话测试逻辑,简化原 `usePromptTester` 接口(参数从 8 个减至 4 个);
  - 抽取 `usePromptDisplayAdapter` 统一管理显示层数据适配,提升跨模式复用能力。

- **代码优化与清理**:
  - 移除 `App.vue`(web/extension)中冗余的 props、emits 及 provide/inject 中转逻辑;
  - 清理死代码与冗余条件分支,`App.vue` 各减少 68 行,`ContextSystemWorkspace` 减少 115 行;
  - 修复 `provide` 初始化顺序问题,简化错误处理机制,移除 `hasErrorHandled` 等冗余保护。

- **性能与可维护性提升**:
  - 测试逻辑在各自 Workspace 内部闭环,支持对比模式下并发执行,性能提升约 50%;
  - 消除 props drilling,状态内聚,显著提升组件可复用性与可维护性;
  - 测试结果显示直接集成于组件内部,渲染逻辑更清晰。

**变更文件**:
- 新增:`useContextUserOptimization.ts`(290行)、`useContextUserTester.ts`(235行)、`useConversationTester.ts`、`usePromptDisplayAdapter.ts`
- 重构:`ContextUserWorkspace.vue`、`ContextSystemWorkspace.vue`、`App.vue`(web/extension)
- 新增架构设计文档 2 份

**影响范围**:
仅限 ContextUser 模式与 ContextSystem 模式内部重构,基础模式不变,行为向后兼容。

**测试验证**:
-  Lint:0 错误,0 警告
-  UI Tests:237 通过
-  Core Tests:724 通过

> 架构目标达成:组件高内聚、低耦合,`App.vue` 职责简化,系统整体可扩展性增强。
This commit is contained in:
linshen
2025-11-16 18:30:25 +08:00
parent 21368fa678
commit fb29b61bde
13 changed files with 1824 additions and 633 deletions

View File

@@ -0,0 +1,344 @@
# ContextUserWorkspace 独立性深度分析
## 📋 分析目标
检查 ContextUserWorkspace 的 composables 是否足够独立,是否有逻辑和其他模式的 composables 复用或在 App.vue 里面。
## 🔍 当前架构分析
### 1. ContextUserWorkspace 的依赖关系
**组件内部使用的 Composable**:
```typescript
import { useTemporaryVariables } from "../../composables/variable/useTemporaryVariables"
const tempVarsManager = useTemporaryVariables()
```
**通过 App.vue 依赖的逻辑**:
```typescript
// App.vue 中
const optimizer = usePromptOptimizer(...) // ❌ 共享
const promptTester = usePromptTester(...) // ❌ 共享
// ContextUserWorkspace 通过 props 和 events 使用
<ContextUserWorkspace
:prompt="optimizer.prompt" // ❌ 依赖全局 optimizer
:optimized-prompt="optimizer.optimizedPrompt"
:is-optimizing="optimizer.isOptimizing"
:is-iterating="optimizer.isIterating"
:versions="optimizer.currentVersions"
:current-version-id="optimizer.currentVersionId"
@optimize="handleOptimizePrompt" // ❌ 触发全局 optimizer
@iterate="handleIteratePrompt"
@test="handleTestAreaTest" // ❌ 使用全局 promptTester
/>
```
### 2. 基础模式的依赖关系
**基础模式同样依赖**:
```typescript
// App.vue 中
<template v-else-if="functionMode === 'basic'">
<InputPanelUI v-model="optimizer.prompt" /> // ❌ 共享 optimizer
<PromptPanelUI
:optimized-prompt="optimizer.optimizedPrompt"
:is-optimizing="optimizer.isOptimizing"
/>
<TestAreaPanel @test="handleTestAreaTest" /> // ❌ 共享 promptTester
</template>
```
### 3. ContextSystemWorkspace 的独立性(对比)
**完全独立的逻辑**:
```typescript
// ContextSystemWorkspace.vue 内部
const conversationOptimization = useConversationOptimization(...) // ✅ 独立
const conversationTester = useConversationTester(...) // ✅ 独立
const handleOptimizeClick = () => {
conversationOptimization.optimizeMessage() // ✅ 内部处理
}
const handleTestWithVariables = async () => {
await conversationTester.executeTest(...) // ✅ 内部处理
}
```
---
## ⚠️ 发现的问题
### 问题 1: 不对称的架构设计 ❌
| 功能 | 基础模式 | ContextUser | ContextSystem |
|------|---------|------------|---------------|
| 优化逻辑 | App.vue (optimizer) | App.vue (optimizer) | 组件内部 (conversationOptimization) ✅ |
| 测试逻辑 | App.vue (promptTester) | App.vue (promptTester) | 组件内部 (conversationTester) ✅ |
| 状态管理 | App.vue | App.vue | 组件内部 ✅ |
**问题**: ContextSystem 有独立的 composables而 ContextUser 和基础模式共享 App.vue 的逻辑。
---
### 问题 2: 基础模式和 ContextUser 复用逻辑 ❌
**共享的 Composables**:
```typescript
// App.vue
const optimizer = usePromptOptimizer(...) // 基础模式 + ContextUser 共享
const promptTester = usePromptTester(...) // 基础模式 + ContextUser 共享
```
**共享的处理函数**:
```typescript
// 基础模式和 context-user 模式的测试处理函数
const handleTestAreaTest = async (testVariables?: Record<string, string>) => {
// 调用基础测试器(只用于基础模式和 context-user
await promptTester.executeTest(
optimizer.prompt,
optimizer.optimizedPrompt,
testContent.value,
isCompareMode.value,
testVariables
)
}
```
**问题**:
- ContextUser 没有自己独立的优化和测试逻辑
- 与基础模式共享相同的 composables
- 不符合"ContextUser 应该独立"的预期
---
### 问题 3: usePromptTester 的定位混淆 ❌
**usePromptTester 的文档描述**:
```typescript
/**
* 基础模式提示词测试 Composable
*
* 专门处理基础模式的提示词测试,支持:
* - System prompt 测试
* - User prompt 测试
* - 变量注入
* - 对比模式(原始 vs 优化)
*/
```
**实际使用**:
- ✅ 基础模式使用(符合定位)
- ❌ ContextUser 模式也使用(不符合定位)
**问题**: usePromptTester 声称是"基础模式专用",却被 ContextUser 复用。
---
## 🎯 应该的架构
### 理想的独立架构
```
App.vue
├── 基础模式 (Basic Mode)
│ ├── usePromptOptimizer (全局)
│ └── usePromptTester (全局)
├── ContextSystemWorkspace (独立) ✅
│ ├── useConversationOptimization
│ └── useConversationTester
└── ContextUserWorkspace (应该独立) ❌
├── useContextUserOptimization (新建,独立)
└── useContextUserTester (新建,独立)
```
### 建议的改进方案
#### 方案 1: 创建独立的 ContextUser Composables ⭐⭐⭐⭐⭐
**新增 Composables**:
```typescript
// packages/ui/src/composables/prompt/useContextUserOptimization.ts
export function useContextUserOptimization(
services: Ref<AppServices | null>,
optimizationMode: Ref<OptimizationMode>,
selectedOptimizeModel: Ref<string>,
selectedTemplate: Ref<Template | null>,
selectedIterateTemplate: Ref<Template | null>
) {
// 专门用于 ContextUser 的优化逻辑
// 独立管理 prompt、optimizedPrompt、versions 等状态
}
// packages/ui/src/composables/prompt/useContextUserTester.ts
export function useContextUserTester(
services: Ref<AppServices | null>,
selectedTestModel: Ref<string>,
optimizationMode: Ref<OptimizationMode>,
variableManager: VariableManagerHooks | null
) {
// 专门用于 ContextUser 的测试逻辑
// 独立管理测试状态和结果
}
```
**ContextUserWorkspace 内部使用**:
```typescript
// ContextUserWorkspace.vue
const contextUserOptimization = useContextUserOptimization(
services,
computed(() => props.optimizationMode),
computed(() => props.selectedOptimizeModel),
computed(() => props.selectedTemplate),
computed(() => props.selectedIterateTemplate)
)
const contextUserTester = useContextUserTester(
services,
computed(() => props.selectedTestModel),
computed(() => props.optimizationMode),
variableManager
)
// 内部处理优化
const handleOptimize = () => {
contextUserOptimization.optimize()
}
// 内部处理测试
const handleTest = async (testVariables: Record<string, string>) => {
await contextUserTester.executeTest(
contextUserOptimization.prompt.value,
contextUserOptimization.optimizedPrompt.value,
testContent.value,
isCompareMode.value,
testVariables
)
}
```
**优点**:
- ✅ ContextUser 完全独立,与 System 对称
- ✅ 不再依赖 App.vue 的全局状态
- ✅ 职责清晰,易于维护
- ✅ 可以为 ContextUser 定制特殊功能
**缺点**:
- ⚠️ 需要新增 2 个 composables
- ⚠️ 需要重构 ContextUserWorkspace 的 props/events
- ⚠️ 基础模式仍然使用旧的 optimizer/promptTester保持不变
---
#### 方案 2: 保持现状,但重命名以明确职责 ⭐⭐⭐
**重命名 Composables**:
```typescript
// usePromptOptimizer → useBasicPromptOptimizer
// usePromptTester → useBasicPromptTester
```
**更新文档**:
```typescript
/**
* 基础模式和 ContextUser 模式共享的提示词优化器
*
* 用于:
* - 基础模式:单条提示词优化
* - ContextUser 模式:单条用户消息优化
*
* 不用于:
* - ContextSystem 模式(使用 useConversationOptimization
*/
```
**优点**:
- ✅ 无需新增代码
- ✅ 明确了共享关系
**缺点**:
- ❌ 没有解决根本问题ContextUser 不够独立)
- ❌ 基础模式和 ContextUser 仍然耦合
---
#### 方案 3: ContextUser 继承基础模式的逻辑 ⭐⭐
**思路**: 将 ContextUser 视为基础模式的扩展版本
**优点**:
- ✅ 符合当前架构
- ✅ 无需改动
**缺点**:
- ❌ ContextUser 失去独立性
- ❌ 与 ContextSystem 的独立性不对称
---
## 📊 各方案对比
| 方案 | 独立性 | 对称性 | 实现成本 | 维护性 | 推荐度 |
|------|--------|--------|---------|--------|--------|
| 方案 1: 独立 Composables | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 方案 2: 重命名明确 | ⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ |
| 方案 3: 保持现状 | ⭐ | ⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐ |
---
## 🚀 推荐方案:方案 1
### 理由
1. **架构对称性**: 让 ContextUser 和 ContextSystem 都拥有独立的 composables
2. **职责清晰**: 每个模式有自己专属的逻辑,不混淆
3. **易于扩展**: 未来可以为 ContextUser 添加特殊功能
4. **符合预期**: 你期望的"相互独立"
### 命名规范
按照现有的命名模式:
| 模式 | 优化 Composable | 测试 Composable | 说明 |
|------|----------------|----------------|------|
| **ContextSystem** | `useConversationOptimization` | `useConversationTester` | 处理"会话"Conversation |
| **ContextUser** | `useContextUserOptimization` | `useContextUserTester` | 处理"用户上下文"User Context |
| 基础模式 | `usePromptOptimizer` | `usePromptTester` | 保持不变 |
**命名原则**:
-**ContextSystem** 处理"会话"Conversation所以用 `useConversation*`
-**ContextUser** 处理"用户上下文"User Context所以用 `useContextUser*`
- ✅ 基础模式保持原有命名
- ✅ 保持一致性和可读性
### 实施步骤
1. 创建 `useContextUserOptimization.ts`
2. 创建 `useContextUserTester.ts`
3. 重构 `ContextUserWorkspace.vue` 使用新的 composables
4. 更新 `App.vue` 中 ContextUser 相关的逻辑
5. 保持基础模式使用原有的 `usePromptOptimizer``usePromptTester`
---
## 📝 总结
### 当前状态
-**ContextUser 不够独立**,依赖 App.vue 的全局状态
-**与基础模式复用逻辑**composables 混淆
-**架构不对称**ContextSystem 独立但 ContextUser 不独立
### 理想状态
-**ContextUser 完全独立**,拥有自己的 composables
-**架构对称**System 和 User 都独立于基础模式
-**职责清晰**,每个模式有明确的边界
### 建议
**强烈推荐实施方案 1**,创建独立的 ContextUser composables实现真正的独立性和架构对称性。

View File

@@ -0,0 +1,270 @@
# ContextSystemWorkspace vs ContextUserWorkspace 独立性分析
## 📋 分析目标
确保 `ContextSystemWorkspace`(上下文-多消息)和 `ContextUserWorkspace`(上下文-单消息)两个组件相互独立,无复用/混淆。
## 🏗️ 架构对比
### ContextSystemWorkspace (上下文-多消息模式)
**职责**:
- 管理 system/user/assistant/tool 多条消息
- 支持任意消息的选择和优化
- 会话级别的上下文管理
**使用的 Composables**:
```typescript
import { useConversationTester } from '../../composables/prompt/useConversationTester'
import { useConversationOptimization } from '../../composables/prompt/useConversationOptimization'
import { usePromptDisplayAdapter } from '../../composables/prompt/usePromptDisplayAdapter'
```
**子组件**:
- `ConversationManager` - 多消息管理器
- `ConversationTestPanel` - 会话测试面板
- `PromptPanelUI` - 优化结果显示(条件渲染)
**特性**:
- ✅ 内部初始化 `conversationOptimization`
- ✅ 内部初始化 `conversationTester`
- ✅ 内部初始化 `displayAdapter`
- ✅ 消息级优化和版本管理
- ✅ 完全自包含,不依赖 App.vue 的测试器
---
### ContextUserWorkspace (上下文-单消息模式)
**职责**:
- 只优化单条用户消息
- 无需管理多轮对话上下文
- 支持工具调用配置
**使用的 Composables**:
```typescript
import { useTemporaryVariables } from "../../composables/variable/useTemporaryVariables"
```
**子组件**:
- `InputPanelUI` - 单消息输入面板
- `TestAreaPanel` - 基础测试面板(非会话)
- `PromptPanelUI` - 优化结果显示(始终显示)
**特性**:
- ✅ 使用 App.vue 传入的 `promptTester` (usePromptTester)
- ✅ 通过 @test 事件触发测试
- ✅ 支持文本选择提取变量(独有功能)
- ✅ 支持临时变量管理
- ✅ 依赖外部测试器(通过事件通信)
---
## ✅ 独立性验证
### 1. Composables 使用情况
| Composable | ContextSystem | ContextUser | 共享? |
|-----------|---------------|-------------|-------|
| `useConversationTester` | ✅ | ❌ | ❌ 独立 |
| `useConversationOptimization` | ✅ | ❌ | ❌ 独立 |
| `usePromptDisplayAdapter` | ✅ | ❌ | ❌ 独立 |
| `useTemporaryVariables` | ❌ | ✅ | ❌ 独立 |
| `usePromptTester` (App.vue) | ❌ | ✅ (间接) | ❌ 独立 |
**结论**: ✅ 没有混淆,各自使用专属的 composables
---
### 2. 测试逻辑独立性
**ContextSystemWorkspace**:
```typescript
// 组件内部
const conversationTester = useConversationTester(
services || ref(null),
selectedTestModel,
computed(() => props.optimizationContext),
optimizationContextToolsRef,
variableManager,
selectedMessageId
)
const handleTestWithVariables = async () => {
const testVariables = testAreaPanelRef.value?.getVariableValues?.() || {}
await conversationTester.executeTest(
props.isCompareMode || false,
testVariables,
testAreaPanelRef.value
)
}
```
**ContextUserWorkspace**:
```typescript
// App.vue
const promptTester = usePromptTester(
services as any,
toRef(modelManager, 'selectedTestModel'),
selectedOptimizationMode,
variableManager
)
const handleTestAreaTest = async (testVariables?: Record<string, string>) => {
await promptTester.executeTest(
optimizer.prompt,
optimizer.optimizedPrompt,
testContent.value,
isCompareMode.value,
testVariables
)
}
// ContextUserWorkspace 组件
<ContextUserWorkspace
@test="handleTestAreaTest"
/>
```
**结论**: ✅ 完全独立的测试逻辑
- System: 内部管理,会话级测试
- User: 外部管理,单消息测试
---
### 3. 优化逻辑独立性
**ContextSystemWorkspace**:
```typescript
// 消息级优化
const conversationOptimization = useConversationOptimization(...)
const handleOptimizeClick = () => {
conversationOptimization.optimizeMessage() // 优化选中消息
}
```
**ContextUserWorkspace**:
```typescript
// 全局优化(通过 App.vue 的 optimizer
<ContextUserWorkspace
@optimize="handleOptimizePrompt" // 触发 App.vue 的优化逻辑
/>
```
**结论**: ✅ 完全独立的优化逻辑
- System: 消息级优化(内部管理)
- User: 全局优化(外部管理)
---
### 4. 变量管理独立性
**ContextSystemWorkspace**:
- 使用 App.vue 注入的 `variableManager` (useVariableManager)
- 通过 inject 获取,用于会话测试
**ContextUserWorkspace**:
- 使用内部的 `tempVarsManager` (useTemporaryVariables)
- 独立管理临时变量
- 同时使用 App.vue 传入的 globalVariables 和 predefinedVariables
**结论**: ✅ 独立但合理共享
- System: 依赖全局 variableManager合理
- User: 独立临时变量 + 全局变量(合理)
---
### 5. 子组件独立性
| 子组件 | ContextSystem | ContextUser | 用途差异 |
|--------|---------------|-------------|----------|
| ConversationManager | ✅ | ❌ | 多消息管理 |
| ConversationTestPanel | ✅ | ❌ | 会话测试 |
| InputPanelUI | ❌ | ✅ | 单消息输入 |
| TestAreaPanel | ❌ | ✅ | 基础测试 |
| PromptPanelUI | ✅ | ✅ | **共享**(合理复用) |
**结论**: ✅ 独立且合理
- 共享 PromptPanelUI 是合理的,因为它只是展示组件
---
## 🎯 发现的问题
### ❌ 无问题!架构清晰且独立
经过全面分析,**没有发现**以下问题:
- ❌ 不应该共享但实际共享的 composables
- ❌ 逻辑混淆或职责不清
- ❌ 组件间的不当耦合
- ❌ 数据流混乱
---
## ✅ 架构优点
### 1. 清晰的关注点分离
- **ContextSystemWorkspace**: 完全自包含,负责多消息会话优化
- **ContextUserWorkspace**: 依赖外部,负责单消息优化
### 2. Composables 职责清晰
```
useConversationTester → ContextSystemWorkspace 专用
useConversationOptimization → ContextSystemWorkspace 专用
usePromptDisplayAdapter → ContextSystemWorkspace 专用
useTemporaryVariables → ContextUserWorkspace 专用
usePromptTester → ContextUserWorkspace 使用(通过 App.vue
```
### 3. 合理的复用策略
- ✅ 展示组件共享PromptPanelUI
- ✅ 基础工具共享variableManager
- ✅ 业务逻辑独立(测试器、优化器)
---
## 📊 独立性评分
| 维度 | 评分 | 说明 |
|------|------|------|
| Composables 独立性 | ⭐⭐⭐⭐⭐ | 完全独立,无混淆 |
| 测试逻辑独立性 | ⭐⭐⭐⭐⭐ | 使用不同的测试器 |
| 优化逻辑独立性 | ⭐⭐⭐⭐⭐ | 消息级 vs 全局优化 |
| 组件职责清晰度 | ⭐⭐⭐⭐⭐ | 职责明确,无重叠 |
| 可维护性 | ⭐⭐⭐⭐⭐ | 易于理解和维护 |
**总评**: ⭐⭐⭐⭐⭐ (5/5)
---
## 🚀 建议
### 无需改进!当前架构已经非常优秀
两个组件的独立性设计非常好:
1. ✅ 各自使用专属的 composables
2. ✅ 测试和优化逻辑完全独立
3. ✅ 只在合理的地方共享(展示组件)
4. ✅ 职责清晰,易于维护
### 未来扩展建议
如果要添加新功能,建议遵循当前模式:
- **多消息相关**: 添加到 ContextSystemWorkspace 或其专属 composables
- **单消息相关**: 添加到 ContextUserWorkspace 或其专属 composables
- **共享展示逻辑**: 考虑抽取为独立的展示组件
---
## 📝 总结
**ContextSystemWorkspace****ContextUserWorkspace** 两个组件:
- ✅ 完全独立,无复用/混淆
- ✅ 各自使用专属的业务逻辑 composables
- ✅ 只在合理的地方共享展示组件
- ✅ 架构清晰,职责明确
- ✅ 易于维护和扩展
**结论**: 当前架构非常优秀,无需改进!🎉

View File

@@ -186,7 +186,6 @@
@compare-toggle="handleTestAreaCompareToggle"
@optimize="handleOptimizePrompt"
@iterate="handleIteratePrompt"
@test="handleTestAreaTest"
@switch-version="handleSwitchVersion"
@save-favorite="handleSaveFavorite"
@open-global-variables="openVariableManager()"
@@ -196,20 +195,10 @@
@config-model="modelManager.showConfig = true"
@open-input-preview="handleOpenInputPreview"
@open-prompt-preview="handleOpenPromptPreview"
:selected-message-id="conversationOptimization.selectedMessageId.value"
:enable-message-optimization="true"
@message-select="handleMessageSelect"
:message-optimized-prompt="conversationOptimization.optimizedPrompt.value"
:message-versions="conversationOptimization.currentVersions.value"
:message-current-version-id="conversationOptimization.currentRecordId.value"
:is-message-optimizing="conversationOptimization.isOptimizing.value"
:versions="optimizer.currentVersions"
:current-version-id="optimizer.currentVersionId"
@message-switch-version="handleMessageSwitchVersion"
@message-switch-to-v0="handleMessageSwitchToV0"
@optimize-message="handleOptimizeMessage"
@message-change="handleMessageChange"
@message-apply-version="handleApplyMessageVersion"
:selected-optimize-model="modelManager.selectedOptimizeModel"
:selected-template="currentSelectedTemplate"
:selected-test-model="modelManager.selectedTestModel"
>
<!-- 优化模型选择插槽 -->
<template #optimize-model-select>
@@ -278,58 +267,17 @@
/>
</template>
<!-- 测试结果插槽 -->
<template #original-result>
<OutputDisplay
:content="testResults.originalResult"
:reasoning="testResults.originalReasoning"
:streaming="testResults.isTestingOriginal"
:enableDiff="false"
mode="readonly"
:style="{ height: '100%', minHeight: '0' }"
/>
</template>
<template #optimized-result>
<OutputDisplay
:content="testResults.optimizedResult"
:reasoning="testResults.optimizedReasoning"
:streaming="testResults.isTestingOptimized"
:enableDiff="false"
mode="readonly"
:style="{ height: '100%', minHeight: '0' }"
/>
</template>
<template #single-result>
<OutputDisplay
:content="testResults.optimizedResult"
:reasoning="testResults.optimizedReasoning"
:streaming="testResults.isTestingOptimized"
:enableDiff="false"
mode="readonly"
:style="{ height: '100%', minHeight: '0' }"
/>
</template>
<!-- 🔧 测试结果插槽已移除ContextSystemWorkspace 内部直接使用 useConversationTester 渲染 -->
</ContextSystemWorkspace>
<!-- 上下文-用户模式 -->
<!-- 上下文-用户模式(🆕 已独立,内部管理优化和测试逻辑) -->
<ContextUserWorkspace
ref="userWorkspaceRef"
v-else-if="contextMode === 'user'"
:prompt="optimizer.prompt"
@update:prompt="optimizer.prompt = $event"
:optimized-prompt="optimizer.optimizedPrompt"
@update:optimizedPrompt="
optimizer.optimizedPrompt = $event
"
:optimized-reasoning="optimizer.optimizedReasoning"
:optimization-mode="selectedOptimizationMode"
:is-optimizing="optimizer.isOptimizing"
:is-iterating="optimizer.isIterating"
:is-test-running="false"
:versions="optimizer.currentVersions"
:current-version-id="optimizer.currentVersionId"
:selected-optimize-model="modelManager.selectedOptimizeModel"
:selected-test-model="modelManager.selectedTestModel"
:selected-template="selectedTemplate"
:selected-iterate-template="
optimizer.selectedIterateTemplate
"
@@ -363,11 +311,7 @@
:result-vertical-layout="
responsiveLayout.isMobile.value
"
@optimize="handleOptimizePrompt"
@iterate="handleIteratePrompt"
@test="handleTestAreaTest"
@compare-toggle="handleTestAreaCompareToggle"
@switch-version="handleSwitchVersion"
@save-favorite="handleSaveFavorite"
@open-global-variables="openVariableManager()"
@open-tool-manager="
@@ -447,39 +391,7 @@
/>
</template>
<!-- 测试结果插槽 -->
<template #original-result>
<OutputDisplay
:content="testResults.originalResult"
:reasoning="testResults.originalReasoning"
:streaming="testResults.isTestingOriginal"
:enableDiff="false"
mode="readonly"
:style="{ height: '100%', minHeight: '0' }"
/>
</template>
<template #optimized-result>
<OutputDisplay
:content="testResults.optimizedResult"
:reasoning="testResults.optimizedReasoning"
:streaming="testResults.isTestingOptimized"
:enableDiff="false"
mode="readonly"
:style="{ height: '100%', minHeight: '0' }"
/>
</template>
<template #single-result>
<OutputDisplay
:content="testResults.optimizedResult"
:reasoning="testResults.optimizedReasoning"
:streaming="testResults.isTestingOptimized"
:enableDiff="false"
mode="readonly"
:style="{ height: '100%', minHeight: '0' }"
/>
</template>
<!-- 🔧 测试结果插槽已移除ContextUserWorkspace 内部直接使用 useContextUserTester 渲染 -->
</ContextUserWorkspace>
</template>
@@ -1028,7 +940,6 @@ import {
useContextManagement,
useAggregatedVariables,
useContextEditorUIState,
useConversationOptimization,
// i18n functions
initializeI18nWithStorage,
@@ -1087,7 +998,7 @@ watch(
{ immediate: true },
);
// 4. 向子组件提供服务
// 4. 向子组件提供服务(部分 provide 移至声明后)
provide("services", services);
// 5. 控制主UI渲染的标志
@@ -1331,72 +1242,19 @@ const handleContextEditorStateUpdate =
contextManagement.handleContextEditorStateUpdate;
const handleContextModeChange = contextManagement.handleContextModeChange;
// 🆕 多轮对话消息优化管理
const selectedOptimizationTemplate = computed<Template | null>(() => {
return selectedOptimizationMode.value === "system"
? optimizer.selectedOptimizeTemplate
: optimizer.selectedUserOptimizeTemplate;
});
// 🔧 提供依赖给子组件(必须在所有依赖项声明之后)
provide("variableManager", variableManager);
provide("optimizationContextTools", optimizationContextTools);
const conversationOptimization = useConversationOptimization(
services,
optimizationContext,
selectedOptimizationMode,
toRef(modelManager, "selectedOptimizeModel"),
selectedOptimizationTemplate,
toRef(optimizer, "selectedIterateTemplate") // 🔧 添加迭代模板
);
provide('conversationOptimization', conversationOptimization);
// 处理消息选择事件
const handleMessageSelect = async (message: ConversationMessage) => {
await conversationOptimization.selectMessage(message);
};
// 处理消息版本切换
const handleMessageSwitchVersion = async (version: PromptRecordChain['versions'][number]) => {
await conversationOptimization.switchVersion(version);
};
// 🆕 处理消息 V0 切换
const handleMessageSwitchToV0 = async (version: PromptRecordChain['versions'][number]) => {
await conversationOptimization.switchToV0(version);
};
// 处理消息优化
const handleOptimizeMessage = async () => {
await conversationOptimization.optimizeMessage();
};
// 处理消息变更(用于清理删除消息的映射)
const handleMessageChange = (index: number, message: ConversationMessage, action: 'add' | 'update' | 'delete') => {
if (!message?.id) return;
if (action === 'delete') {
conversationOptimization.cleanupDeletedMessageMapping(message.id);
} else if (action === 'update') {
conversationOptimization.cleanupDeletedMessageMapping(message.id, { keepSelection: true });
}
};
// 手动应用所选版本
const handleApplyMessageVersion = async () => {
await conversationOptimization.applyCurrentVersion();
};
// 🆕 提示词测试管理(支持变量注入、上下文、工具调用)
// 🆕 基础模式提示词测试(简化后只用于基础模式和 context-user
const promptTester = usePromptTester(
services as any,
toRef(modelManager, 'selectedTestModel'),
selectedOptimizationMode, // 保持兼容性,后续应改为使用 basicSubMode/proSubMode
advancedModeEnabled,
optimizationContext,
optimizationContextTools,
variableManager,
conversationOptimization.selectedMessageId // 🆕 传递选中的消息ID用于对比
selectedOptimizationMode,
variableManager
);
// 测试结果引用(从 promptTester 获取)
// 测试结果引用(从 promptTester 获取,用于基础模式和 context-user
const testResults = computed(() => promptTester.testResults);
// 处理测试面板的变量变化现在测试变量由TestAreaPanel自己管理不需要同步到会话
@@ -2276,20 +2134,16 @@ const getActiveTestPanelInstance = (): TestAreaPanelInstance | null => {
return null;
};
// 真实测试处理函数
// 基础模式和 context-user 模式的测试处理函数
// 注意context-system 模式已在 ContextSystemWorkspace 内部使用 useConversationTester 处理,不会调用此函数
const handleTestAreaTest = async (testVariables?: Record<string, string>) => {
// 🔧 多轮对话模式context-system不使用 testContent测试内容来自会话消息
// 但现在支持对比模式了,可以对比选中消息的 V0 和当前版本
const actualTestContent = contextMode.value === 'system' ? '' : testContent.value;
// 调用 promptTester 的 executeTest 方法
// 调用基础测试器(只用于基础模式和 context-user
await promptTester.executeTest(
optimizer.prompt,
optimizer.optimizedPrompt,
actualTestContent,
isCompareMode.value, // 🔧 直接使用 isCompareMode不再强制为 false
testVariables,
getActiveTestPanelInstance()
testContent.value,
isCompareMode.value,
testVariables
);
};

View File

@@ -40,8 +40,8 @@
:max-height="300"
:selected-message-id="selectedMessageId"
:enable-message-optimization="enableMessageOptimization"
:is-message-optimizing="isMessageOptimizing"
@message-select="emit('message-select', $event)"
:is-message-optimizing="conversationOptimization.isOptimizing.value"
@message-select="conversationOptimization.selectMessage"
@optimize-message="handleOptimizeClick"
@message-change="(index, message, action) => emit('message-change', index, message, action)"
/>
@@ -72,12 +72,12 @@
<!-- 优化按钮 -->
<NButton
type="primary"
:loading="isOptimizing"
:disabled="isOptimizing || !selectedMessageId"
:loading="displayAdapter.displayedIsOptimizing.value"
:disabled="displayAdapter.displayedIsOptimizing.value || !selectedMessageId"
@click="handleOptimizeClick"
block
>
{{ isOptimizing ? $t('common.loading') : $t('promptOptimizer.optimize') }}
{{ displayAdapter.displayedIsOptimizing.value ? $t('common.loading') : $t('promptOptimizer.optimize') }}
</NButton>
</NFlex>
</NCard>
@@ -91,20 +91,20 @@
}"
content-style="height: 100%; max-height: 100%; overflow: hidden;"
>
<template v-if="isInMessageOptimizationMode">
<template v-if="displayAdapter.isInMessageOptimizationMode.value">
<PromptPanelUI
:original-prompt="displayedOriginalPrompt"
:optimized-prompt="displayedOptimizedPrompt"
:original-prompt="displayAdapter.displayedOriginalPrompt.value"
:optimized-prompt="displayAdapter.displayedOptimizedPrompt.value"
:reasoning="optimizedReasoning"
:is-optimizing="displayedIsOptimizing"
:is-optimizing="displayAdapter.displayedIsOptimizing.value"
:is-iterating="isIterating"
:selectedIterateTemplate="selectedIterateTemplate"
@update:selectedIterateTemplate="
emit('update:selectedIterateTemplate', $event)
"
:versions="displayedVersions"
:current-version-id="displayedCurrentVersionId"
:show-apply-button="isInMessageOptimizationMode"
:versions="displayAdapter.displayedVersions.value"
:current-version-id="displayAdapter.displayedCurrentVersionId.value"
:show-apply-button="displayAdapter.isInMessageOptimizationMode.value"
:optimization-mode="optimizationMode"
:advanced-mode-enabled="true"
:show-preview="true"
@@ -194,18 +194,39 @@
<slot name="test-model-select"></slot>
</template>
<!-- 🆕 对比模式结果插槽 -->
<!-- 🆕 对比模式结果插槽:直接绑定测试结果 -->
<template #original-result>
<slot name="original-result"></slot>
<OutputDisplay
:content="conversationTester.testResults.originalResult"
:reasoning="conversationTester.testResults.originalReasoning"
:streaming="conversationTester.testResults.isTestingOriginal"
:enableDiff="false"
mode="readonly"
:style="{ height: '100%', minHeight: '0' }"
/>
</template>
<template #optimized-result>
<slot name="optimized-result"></slot>
<OutputDisplay
:content="conversationTester.testResults.optimizedResult"
:reasoning="conversationTester.testResults.optimizedReasoning"
:streaming="conversationTester.testResults.isTestingOptimized"
:enableDiff="false"
mode="readonly"
:style="{ height: '100%', minHeight: '0' }"
/>
</template>
<!-- 单一结果插槽 -->
<template #single-result>
<slot name="single-result"></slot>
<OutputDisplay
:content="conversationTester.testResults.optimizedResult"
:reasoning="conversationTester.testResults.optimizedReasoning"
:streaming="conversationTester.testResults.isTestingOptimized"
:enableDiff="false"
mode="readonly"
:style="{ height: '100%', minHeight: '0' }"
/>
</template>
</ConversationTestPanel>
</NCard>
@@ -214,7 +235,7 @@
</template>
<script setup lang="ts">
import { ref, computed, inject } from 'vue'
import { ref, computed, inject, provide } from 'vue'
import { useI18n } from "vue-i18n";
import { NCard, NFlex, NButton, NText, NEmpty } from "naive-ui";
@@ -222,14 +243,20 @@ import { useBreakpoints } from "@vueuse/core";
import PromptPanelUI from "../PromptPanel.vue";
import ConversationTestPanel from "./ConversationTestPanel.vue";
import ConversationManager from "./ConversationManager.vue";
import type { UseConversationOptimization } from '../../composables/prompt/useConversationOptimization'
import OutputDisplay from "../OutputDisplay.vue";
import { useConversationTester } from '../../composables/prompt/useConversationTester'
import { useConversationOptimization } from '../../composables/prompt/useConversationOptimization'
import { usePromptDisplayAdapter } from '../../composables/prompt/usePromptDisplayAdapter'
import type { OptimizationMode, ConversationMessage } from "../../types";
import type {
PromptRecord,
Template,
ToolDefinition,
} from "@prompt-optimizer/core";
import type { TestAreaPanelInstance } from "../types/test-area";
import type { IteratePayload, SaveFavoritePayload } from "../../types/workspace";
import type { VariableManagerHooks } from '../../composables/prompt/useVariableManager'
import type { AppServices } from '../../types/services'
// 响应式断点
const breakpoints = useBreakpoints({
@@ -238,8 +265,7 @@ const breakpoints = useBreakpoints({
});
const isMobile = breakpoints.smaller("mobile");
// Props 定义 (移除 contextMode因为固定为 system移除 prompt因为消息输入在 ConversationManager 中)
// 🔧 移除 optimizedPrompt/versions/currentVersionId防止基础模式状态污染
// Props 定义
interface Props {
// 核心状态
optimizedReasoning?: string;
@@ -250,7 +276,9 @@ interface Props {
isIterating: boolean;
isTestRunning?: boolean;
// 版本管理
// 外部状态注入(用于初始化本地 hook
selectedOptimizeModel: string;
selectedTemplate: Template | null;
selectedIterateTemplate: Template | null;
// 上下文数据 (系统模式专属)
@@ -263,14 +291,9 @@ interface Props {
availableVariables: Record<string, string>;
scanVariables: (content: string) => string[];
// 🆕 消息优化功能
selectedMessageId?: string;
// 🆕 消息优化功能(本地管理,移除部分外部 props
enableMessageOptimization?: boolean;
messageOptimizedPrompt?: string;
messageVersions?: PromptRecord[];
messageCurrentVersionId?: string | null;
isMessageOptimizing?: boolean;
// 全局优化链(用于历史记录恢复)
versions?: PromptRecord[];
currentVersionId?: string;
@@ -284,6 +307,9 @@ interface Props {
// 🆕 对比模式
isCompareMode?: boolean;
// 🆕 测试相关(避免通过 App.vue 中转)
selectedTestModel?: string;
}
const props = withDefaults(defineProps<Props>(), {
@@ -294,28 +320,20 @@ const props = withDefaults(defineProps<Props>(), {
buttonSize: "medium",
conversationMaxHeight: 300,
resultVerticalLayout: false,
selectedMessageId: undefined,
enableMessageOptimization: false,
messageOptimizedPrompt: "",
messageVersions: () => [],
messageCurrentVersionId: null,
isMessageOptimizing: false,
isCompareMode: false,
});
// Emits 定义
// 🔧 移除 update:optimizedPrompt防止基础模式状态污染
const emit = defineEmits<{
// 数据更新
"update:selectedIterateTemplate": [value: Template | null];
"update:optimizationContext": [value: ConversationMessage[]];
// 操作事件
optimize: []; // 执行优化
iterate: [payload: IteratePayload];
test: [testVariables: Record<string, string>]; // 🆕 传递测试变量
// 操作事件(用于历史记录查看场景)
test: [testVariables: Record<string, string>];
"switch-version": [version: PromptRecord];
"switch-to-v0": [version: PromptRecord]; // 🆕 V0 切换事件
"switch-to-v0": [version: PromptRecord];
"save-favorite": [data: SaveFavoritePayload];
// 打开面板/管理器
@@ -332,14 +350,6 @@ const emit = defineEmits<{
"variable-change": [name: string, value: string];
"save-to-global": [name: string, value: string];
// 🆕 消息优化相关
"message-select": [message: ConversationMessage];
"message-switch-version": [version: PromptRecord];
"message-switch-to-v0": [version: PromptRecord]; // 🆕 消息 V0 切换事件
"optimize-message": [];
"message-change": [index: number, message: ConversationMessage, action: 'add' | 'update' | 'delete'];
"message-apply-version": [];
// 🆕 对比模式
"update:isCompareMode": [value: boolean];
"compare-toggle": [];
@@ -347,110 +357,100 @@ const emit = defineEmits<{
const { t } = useI18n();
const conversationOptimization = inject<UseConversationOptimization>('conversationOptimization')
// 注入服务和变量管理器
const services = inject<Ref<AppServices | null>>('services')
const variableManager = inject<VariableManagerHooks | null>('variableManager')
// 🆕 初始化本地会话优化逻辑
const conversationOptimization = useConversationOptimization(
services || ref(null),
computed(() => props.optimizationContext),
computed(() => props.optimizationMode),
computed(() => props.selectedOptimizeModel),
computed(() => props.selectedTemplate),
computed(() => props.selectedIterateTemplate)
)
// 暴露给子组件(虽然目前主要通过 Props 传递给 ConversationManager但保持 Provide 以防万一)
provide('conversationOptimization', conversationOptimization);
// 🆕 初始化显示适配器(根据模式自动切换数据源)
const displayAdapter = usePromptDisplayAdapter(
conversationOptimization,
{
enableMessageOptimization: computed(() => props.enableMessageOptimization || false),
optimizationContext: computed(() => props.optimizationContext),
globalVersions: computed(() => props.versions || []),
globalCurrentVersionId: computed(() => props.currentVersionId),
globalIsOptimizing: computed(() => props.isOptimizing),
}
)
// 🆕 初始化多对话测试器
const selectedTestModel = computed(() => props.selectedTestModel || '')
// 从 inject 获取 optimizationContextTools由 App.vue 提供)
const optimizationContextToolsRef = inject<Ref<ToolDefinition[]>>('optimizationContextTools', ref([]))
// 使用本地 managed 的 selectedMessageId
const selectedMessageId = conversationOptimization.selectedMessageId
const conversationTester = useConversationTester(
services || ref(null),
selectedTestModel,
computed(() => props.optimizationContext),
optimizationContextToolsRef,
variableManager,
selectedMessageId
)
// 处理迭代优化事件
// 注意:由于 displayedOptimizedPrompt 在未选中消息时为空,迭代按钮不会显示,所以此函数调用时必定处于消息优化模式
const handleIterate = (payload: IteratePayload) => {
if (isInMessageOptimizationMode.value && conversationOptimization) {
conversationOptimization.iterateMessage(payload)
} else {
emit('iterate', payload)
}
conversationOptimization.iterateMessage(payload)
}
// 处理优化点击事件
// 注意:优化按钮在没有选中消息时会被禁用,所以此函数调用时必定处于消息优化模式
const handleOptimizeClick = () => {
if (isInMessageOptimizationMode.value && conversationOptimization) {
conversationOptimization.optimizeMessage()
} else {
emit('optimize-message')
}
conversationOptimization.optimizeMessage()
}
// 🆕 ConversationTestPanel 引用(兼容 TestAreaPanelInstance 接口)
// 🆕 ConversationTestPanel 引用
const testAreaPanelRef = ref<TestAreaPanelInstance | null>(null);
// 🆕 消息优化模式:根据是否有选中消息来决定显示内容
const isInMessageOptimizationMode = computed(() => {
return props.enableMessageOptimization && !!props.selectedMessageId;
});
// 🆕 PromptPanel 显示的原始提示词(当前选中消息的原始内容)
const displayedOriginalPrompt = computed(() => {
if (!isInMessageOptimizationMode.value) return ''
const message = props.optimizationContext?.find(m => m.id === props.selectedMessageId)
return message?.originalContent || message?.content || ''
});
// 🆕 PromptPanel 显示的优化结果(消息优化 或 提示词优化)
const displayedOptimizedPrompt = computed(() => {
return isInMessageOptimizationMode.value
? props.messageOptimizedPrompt
: ''; // 没有选中消息时,不显示优化结果
});
// 🆕 PromptPanel 显示的版本列表
const displayedVersions = computed(() => {
if (isInMessageOptimizationMode.value) {
// 消息优化模式:使用消息级优化版本
return props.messageVersions || [];
}
// 历史记录恢复时:使用全局优化链
return props.versions || [];
});
// 🆕 PromptPanel 显示的当前版本ID
const displayedCurrentVersionId = computed(() => {
if (isInMessageOptimizationMode.value) {
// 消息优化模式使用消息级版本ID
return props.messageCurrentVersionId || null;
}
// 历史记录恢复时使用全局版本ID
return props.currentVersionId || null;
});
// 🆕 PromptPanel 显示的优化中状态
const displayedIsOptimizing = computed(() => {
return isInMessageOptimizationMode.value
? props.isMessageOptimizing
: props.isOptimizing;
});
// 🆕 处理版本切换:根据模式决定触发哪个事件
// 🆕 处理版本切换
const handleSwitchVersion = (version: PromptRecord) => {
if (isInMessageOptimizationMode.value) {
// 消息优化模式:触发消息版本切换事件
emit('message-switch-version', version);
if (displayAdapter.isInMessageOptimizationMode.value) {
conversationOptimization.switchVersion(version);
} else {
// 提示词优化模式:触发普通版本切换事件
emit('switch-version', version);
}
};
// 🆕 处理 V0 切换:根据模式决定触发哪个事件
// 🆕 处理 V0 切换
const handleSwitchToV0 = (version: PromptRecord) => {
if (isInMessageOptimizationMode.value) {
// 消息优化模式:触发消息 V0 切换事件
emit('message-switch-to-v0', version);
if (displayAdapter.isInMessageOptimizationMode.value) {
conversationOptimization.switchToV0(version);
} else {
// 提示词优化模式:触发普通 V0 切换事件
emit('switch-to-v0', version);
}
};
const handleApplyToConversation = () => {
if (!isInMessageOptimizationMode.value) return;
emit('message-apply-version');
if (!displayAdapter.isInMessageOptimizationMode.value) return;
conversationOptimization.applyCurrentVersion();
};
// 🆕 处理测试事件并获取测试变量
// 🆕 处理测试事件
const handleTestWithVariables = async () => {
// 从 ref 获取测试变量
const testVariables = testAreaPanelRef.value?.getVariableValues?.() || {};
// 触发测试事件,传递测试变量给 App.vue
emit('test', testVariables);
await conversationTester.executeTest(
props.isCompareMode || false,
testVariables,
testAreaPanelRef.value
);
};
// 暴露 TestAreaPanel 引用给父组件(用于工具调用等高级功能)
// 暴露引用
defineExpose({
testAreaPanelRef
});

View File

@@ -26,8 +26,7 @@
<!-- 提示词输入面板 -->
<NCard style="flex-shrink: 0; min-height: 200px">
<InputPanelUI
:modelValue="prompt"
@update:modelValue="emit('update:prompt', $event)"
v-model="contextUserOptimization.prompt"
:label="t('promptOptimizer.userPromptInput')"
:placeholder="t('promptOptimizer.userPromptPlaceholder')"
:help-text="variableGuideInlineHint"
@@ -35,10 +34,10 @@
:template-label="t('promptOptimizer.templateLabel')"
:button-text="t('promptOptimizer.optimize')"
:loading-text="t('common.loading')"
:loading="isOptimizing"
:disabled="isOptimizing"
:loading="contextUserOptimization.isOptimizing"
:disabled="contextUserOptimization.isOptimizing"
:show-preview="true"
@submit="emit('optimize')"
@submit="handleOptimize"
@configModel="emit('config-model')"
@open-preview="emit('open-input-preview')"
:enable-variable-extraction="true"
@@ -81,26 +80,24 @@
content-style="height: 100%; max-height: 100%; overflow: hidden;"
>
<PromptPanelUI
:optimized-prompt="optimizedPrompt"
@update:optimizedPrompt="
emit('update:optimizedPrompt', $event)
"
:reasoning="optimizedReasoning"
:original-prompt="prompt"
:is-optimizing="isOptimizing"
:is-iterating="isIterating"
:optimized-prompt="contextUserOptimization.optimizedPrompt"
@update:optimizedPrompt="contextUserOptimization.optimizedPrompt = $event"
:reasoning="contextUserOptimization.optimizedReasoning"
:original-prompt="contextUserOptimization.prompt"
:is-optimizing="contextUserOptimization.isOptimizing"
:is-iterating="contextUserOptimization.isIterating"
:selectedIterateTemplate="selectedIterateTemplate"
@update:selectedIterateTemplate="
emit('update:selectedIterateTemplate', $event)
"
:versions="versions"
:current-version-id="currentVersionId"
:versions="contextUserOptimization.currentVersions"
:current-version-id="contextUserOptimization.currentVersionId"
:optimization-mode="optimizationMode"
:advanced-mode-enabled="true"
:show-preview="true"
@iterate="emit('iterate', $event)"
@iterate="handleIterate"
@openTemplateManager="emit('open-template-manager', $event)"
@switchVersion="emit('switch-version', $event)"
@switchVersion="handleSwitchVersion"
@save-favorite="emit('save-favorite', $event)"
@open-preview="emit('open-prompt-preview')"
/>
@@ -159,8 +156,8 @@
ref="testAreaPanelRef"
:optimization-mode="optimizationMode"
context-mode="user"
:optimized-prompt="optimizedPrompt"
:is-test-running="isTestRunning"
:optimized-prompt="contextUserOptimization.optimizedPrompt"
:is-test-running="contextUserTester.testResults.isTestingOriginal || contextUserTester.testResults.isTestingOptimized"
:global-variables="globalVariables"
:predefined-variables="predefinedVariables"
:temporary-variables="temporaryVariables"
@@ -192,17 +189,39 @@
<slot name="test-model-select"></slot>
</template>
<!-- 结果显示插槽 -->
<!-- 🆕 对比模式结果插槽:直接绑定测试结果 -->
<template #original-result>
<slot name="original-result"></slot>
<OutputDisplay
:content="contextUserTester.testResults.originalResult"
:reasoning="contextUserTester.testResults.originalReasoning"
:streaming="contextUserTester.testResults.isTestingOriginal"
:enableDiff="false"
mode="readonly"
:style="{ height: '100%', minHeight: '0' }"
/>
</template>
<template #optimized-result>
<slot name="optimized-result"></slot>
<OutputDisplay
:content="contextUserTester.testResults.optimizedResult"
:reasoning="contextUserTester.testResults.optimizedReasoning"
:streaming="contextUserTester.testResults.isTestingOptimized"
:enableDiff="false"
mode="readonly"
:style="{ height: '100%', minHeight: '0' }"
/>
</template>
<!-- 单一结果插槽 -->
<template #single-result>
<slot name="single-result"></slot>
<OutputDisplay
:content="contextUserTester.testResults.optimizedResult"
:reasoning="contextUserTester.testResults.optimizedReasoning"
:streaming="contextUserTester.testResults.isTestingOptimized"
:enableDiff="false"
mode="readonly"
:style="{ height: '100%', minHeight: '0' }"
/>
</template>
</TestAreaPanel>
</NCard>
@@ -220,6 +239,7 @@
* - 右侧: 测试区域 (变量输入 + 测试执行)
*
* @features
* - 🆕 完全独立的优化和测试逻辑(使用专属 composables
* - 支持提示词优化和迭代
* - 支持版本管理和历史记录
* - 支持变量系统 (全局变量 + 测试临时变量)
@@ -231,16 +251,14 @@
* @example
* ```vue
* <ContextUserWorkspace
* v-model:prompt="userPrompt"
* v-model:optimizedPrompt="optimizedResult"
* :is-optimizing="loading"
* :optimization-mode="optimizationMode"
* :selected-optimize-model="modelKey"
* :selected-template="template"
* :global-variables="globalVars"
* @optimize="handleOptimize"
* @test="handleTest"
* />
* ```
*/
import { ref, computed } from 'vue'
import { ref, computed, inject, type Ref } from 'vue'
import { useI18n } from "vue-i18n";
import { NCard, NFlex, NButton, NText } from "naive-ui";
@@ -248,6 +266,7 @@ import { useBreakpoints } from "@vueuse/core";
import InputPanelUI from "../InputPanel.vue";
import PromptPanelUI from "../PromptPanel.vue";
import TestAreaPanel from "../TestAreaPanel.vue";
import OutputDisplay from "../OutputDisplay.vue";
import type { OptimizationMode } from "../../types";
import type {
PromptRecord,
@@ -255,7 +274,11 @@ import type {
} from "@prompt-optimizer/core";
import type { TestAreaPanelInstance } from "../types/test-area";
import type { IteratePayload, SaveFavoritePayload } from "../../types/workspace";
import type { AppServices } from '../../types/services';
import type { VariableManagerHooks } from '../../composables/prompt/useVariableManager';
import { useTemporaryVariables } from "../../composables/variable/useTemporaryVariables";
import { useContextUserOptimization } from '../../composables/prompt/useContextUserOptimization';
import { useContextUserTester } from '../../composables/prompt/useContextUserTester';
// ========================
// 响应式断点配置
@@ -271,28 +294,16 @@ const isMobile = breakpoints.smaller("mobile");
// ========================
interface Props {
// --- 核心状态 ---
/** 用户输入的原始提示词 */
prompt: string;
/** AI 优化后的提示词 */
optimizedPrompt: string;
/** 优化推理过程说明 (可选) */
optimizedReasoning?: string;
/** 优化模式 */
optimizationMode: OptimizationMode;
// --- 优化状态 ---
/** 是否正在优化 */
isOptimizing: boolean;
/** 是否正在迭代优化 */
isIterating: boolean;
/** 是否正在执行测试 */
isTestRunning?: boolean;
// --- 版本管理 ---
/** 历史版本列表 */
versions: PromptRecord[];
/** 当前版本 ID */
currentVersionId: string | null;
// --- 🆕 模型和模板配置(用于初始化 composables---
/** 优化模型 */
selectedOptimizeModel: string;
/** 测试模型 */
selectedTestModel: string;
/** 优化模板 */
selectedTemplate: Template | null;
/** 选中的迭代模板 */
selectedIterateTemplate: Template | null;
@@ -301,6 +312,8 @@ interface Props {
testContent: string;
/** 是否启用对比模式 */
isCompareMode: boolean;
/** 是否正在执行测试(兼容性保留,实际由内部管理)*/
isTestRunning?: boolean;
// --- 变量数据 ---
/** 全局变量 (持久化存储) */
@@ -322,7 +335,6 @@ interface Props {
}
const props = withDefaults(defineProps<Props>(), {
optimizedReasoning: "",
isTestRunning: false,
inputMode: "normal",
controlBarLayout: "default",
@@ -336,23 +348,13 @@ const props = withDefaults(defineProps<Props>(), {
// ========================
const emit = defineEmits<{
// --- 数据更新事件 ---
"update:prompt": [value: string];
"update:optimizedPrompt": [value: string];
"update:selectedIterateTemplate": [value: Template | null];
"update:testContent": [value: string];
"update:isCompareMode": [value: boolean];
// --- 操作事件 ---
/** 执行优化 */
optimize: [];
/** 执行迭代优化 */
iterate: [payload: IteratePayload];
/** 执行测试 (传递测试变量) */
test: [testVariables: Record<string, string>];
/** 切换对比模式 */
"compare-toggle": [];
/** 切换历史版本 */
"switch-version": [version: PromptRecord];
/** 保存到收藏 */
"save-favorite": [data: SaveFavoritePayload];
@@ -373,7 +375,6 @@ const emit = defineEmits<{
"open-input-preview": [];
/** 打开提示词预览 */
"open-prompt-preview": [];
/** 打开测试预览 */
// --- 变量管理 ---
/** 变量值变化 */
@@ -392,6 +393,12 @@ const emit = defineEmits<{
const { t } = useI18n();
// ========================
// 注入服务和变量管理器
// ========================
const services = inject<Ref<AppServices | null>>('services');
const variableManager = inject<VariableManagerHooks | null>('variableManager');
// ========================
// 内部状态管理
// ========================
@@ -399,6 +406,21 @@ const { t } = useI18n();
const tempVarsManager = useTemporaryVariables();
const temporaryVariables = tempVarsManager.temporaryVariables;
// 🆕 初始化 ContextUser 专属优化器
const contextUserOptimization = useContextUserOptimization(
services || ref(null),
computed(() => props.selectedOptimizeModel),
computed(() => props.selectedTemplate),
computed(() => props.selectedIterateTemplate)
);
// 🆕 初始化 ContextUser 专属测试器
const contextUserTester = useContextUserTester(
services || ref(null),
computed(() => props.selectedTestModel),
variableManager
);
// ========================
// 计算属性
// ========================
@@ -530,14 +552,37 @@ const handleClearTemporaryVariables = () => {
};
/**
* 处理测试事件并获取测试变量
* 🆕 处理优化事件
*/
const handleOptimize = () => {
contextUserOptimization.optimize();
};
/**
* 🆕 处理迭代优化事件
*/
const handleIterate = (payload: IteratePayload) => {
contextUserOptimization.iterate({
originalPrompt: contextUserOptimization.prompt,
optimizedPrompt: contextUserOptimization.optimizedPrompt,
iterateInput: payload.iterationNote
});
};
/**
* 🆕 处理版本切换事件
*/
const handleSwitchVersion = (version: PromptRecord) => {
contextUserOptimization.switchVersion(version);
};
/**
* 🆕 处理测试事件(使用内部测试器)
*
* 工作流程:
* 1. 从 TestAreaPanel 获取用户输入的测试变量
* 2. 验证数据有效性
* 3. 触发 test 事件,传递变量给父组件
*
* @emits test 传递测试变量给父组件执行测试
* 3. 调用内部测试器执行测试
*/
const handleTestWithVariables = async () => {
try {
@@ -546,7 +591,6 @@ const handleTestWithVariables = async () => {
console.warn(
"[ContextUserWorkspace] testAreaPanelRef not available, using empty variables",
);
emit("test", {});
return;
}
@@ -556,7 +600,6 @@ const handleTestWithVariables = async () => {
console.warn(
"[ContextUserWorkspace] getVariableValues method not found, using empty variables",
);
emit("test", {});
return;
}
@@ -569,20 +612,23 @@ const handleTestWithVariables = async () => {
typeof testVariables,
);
window.$message?.error(t("test.invalidVariables"));
emit("test", {});
return;
}
// 触发测试事件,传递变量
emit("test", testVariables);
// 🆕 调用内部测试器执行测试
await contextUserTester.executeTest(
contextUserOptimization.prompt,
contextUserOptimization.optimizedPrompt,
props.testContent,
props.isCompareMode,
testVariables
);
} catch (error) {
console.error(
"[ContextUserWorkspace] Failed to get test variables:",
"[ContextUserWorkspace] Failed to execute test:",
error,
);
window.$message?.error(t("test.getVariablesFailed"));
// 即使出错也触发测试,使用空变量
emit("test", {});
}
};

View File

@@ -1,11 +1,15 @@
// 提示词相关 composables
export * from './usePromptOptimizer'
export * from './usePromptTester'
export * from './useConversationTester' // 🆕 多对话测试
export * from './usePromptHistory'
export * from './usePromptPreview'
export * from './useTemplateManager'
export * from './useVariableManager'
export * from './useConversationOptimization'
export * from './useContextUserOptimization' // 🆕 ContextUser 优化器
export * from './useContextUserTester' // 🆕 ContextUser 测试器
export * from './usePromptDisplayAdapter' // 🆕 提示词显示适配器
// 变量管理相关 composables
export * from '../variable'

View File

@@ -0,0 +1,288 @@
import { ref, nextTick, computed, reactive, type Ref } from 'vue'
import { useToast } from '../ui/useToast'
import { useI18n } from 'vue-i18n'
import { getErrorMessage } from '../../utils/error'
import { v4 as uuidv4 } from 'uuid'
import type {
Template,
PromptRecordChain,
OptimizationRequest,
ToolDefinition
} from '@prompt-optimizer/core'
import type { AppServices } from '../../types/services'
type PromptChain = PromptRecordChain
/**
* ContextUser 模式提示词优化器接口
*/
export interface UseContextUserOptimization {
// 状态
prompt: string
optimizedPrompt: string
optimizedReasoning: string
isOptimizing: boolean
isIterating: boolean
selectedTemplate: Template | null
selectedIterateTemplate: Template | null
currentChainId: string
currentVersions: PromptChain['versions']
currentVersionId: string
// 方法
optimize: () => Promise<void>
iterate: (payload: { originalPrompt: string, optimizedPrompt: string, iterateInput: string }) => Promise<void>
switchVersion: (version: PromptChain['versions'][number]) => Promise<void>
}
/**
* ContextUser 模式提示词优化器 Composable
*
* 专门用于 ContextUserWorkspace 的优化逻辑,特点:
* - 只处理单条用户消息优化
* - 独立的状态管理
* - 支持版本历史和迭代
* - 与 ContextSystem 的 useConversationOptimization 对称
*
* @param services 服务实例引用
* @param selectedOptimizeModel 优化模型选择
* @param selectedTemplate 优化模板(用户模式)
* @param selectedIterateTemplate 迭代模板
* @returns ContextUser 优化器接口
*
* @example
* ```ts
* const contextUserOptimization = useContextUserOptimization(
* services,
* computed(() => props.selectedOptimizeModel),
* computed(() => props.selectedTemplate),
* computed(() => props.selectedIterateTemplate)
* )
*
* // 执行优化
* await contextUserOptimization.optimize()
* ```
*/
export function useContextUserOptimization(
services: Ref<AppServices | null>,
selectedOptimizeModel: Ref<string>,
selectedTemplate: Ref<Template | null>,
selectedIterateTemplate: Ref<Template | null>
): UseContextUserOptimization {
const toast = useToast()
const { t } = useI18n()
// 服务引用
const historyManager = computed(() => services.value?.historyManager)
const promptService = computed(() => services.value?.promptService)
// 使用 reactive 创建响应式状态对象
const state = reactive<UseContextUserOptimization>({
// 状态
prompt: '',
optimizedPrompt: '',
optimizedReasoning: '',
isOptimizing: false,
isIterating: false,
selectedTemplate: null,
selectedIterateTemplate: null,
currentChainId: '',
currentVersions: [],
currentVersionId: '',
// 方法
optimize: async () => {
if (!state.prompt.trim() || state.isOptimizing) return
if (!selectedTemplate.value) {
toast.error(t('toast.error.noOptimizeTemplate'))
return
}
if (!selectedOptimizeModel.value) {
toast.error(t('toast.error.noOptimizeModel'))
return
}
// 在开始优化前立即清空状态
state.isOptimizing = true
state.optimizedPrompt = ''
state.optimizedReasoning = ''
// 等待一个微任务确保状态更新完成
await nextTick()
try {
// 构建优化请求
const request: OptimizationRequest = {
optimizationMode: 'user', // ContextUser 固定为 user 模式
targetPrompt: state.prompt,
templateId: selectedTemplate.value.id,
modelKey: selectedOptimizeModel.value
}
// 使用流式优化 API
await promptService.value!.optimizePromptStream(
request,
{
onToken: (token: string) => {
state.optimizedPrompt += token
},
onReasoningToken: (reasoningToken: string) => {
state.optimizedReasoning += reasoningToken
},
onComplete: async () => {
if (!selectedTemplate.value) return
try {
// 创建历史记录
const recordData = {
id: uuidv4(),
originalPrompt: state.prompt,
optimizedPrompt: state.optimizedPrompt,
type: 'contextUserOptimize' as const, // ContextUser 专用类型
modelKey: selectedOptimizeModel.value,
templateId: selectedTemplate.value.id,
timestamp: Date.now(),
metadata: {
optimizationMode: 'user' as const,
functionMode: 'pro' as const // ContextUser 属于 pro 模式
}
}
const newRecord = await historyManager.value!.createNewChain(recordData)
state.currentChainId = newRecord.chainId
state.currentVersions = newRecord.versions
state.currentVersionId = newRecord.currentRecord.id
toast.success(t('toast.success.optimizeSuccess'))
} catch (error: unknown) {
console.error('创建历史记录失败:', error)
toast.error('创建历史记录失败: ' + getErrorMessage(error))
} finally {
state.isOptimizing = false
}
},
onError: (error: Error) => {
console.error(t('toast.error.optimizeProcessFailed'), error)
toast.error(error.message || t('toast.error.optimizeFailed'))
state.isOptimizing = false
}
}
)
} catch (error: unknown) {
console.error(t('toast.error.optimizeFailed'), error)
toast.error(getErrorMessage(error) || t('toast.error.optimizeFailed'))
} finally {
state.isOptimizing = false
}
},
// 迭代优化
iterate: async ({ originalPrompt, optimizedPrompt: lastOptimizedPrompt, iterateInput }) => {
if (!originalPrompt || !lastOptimizedPrompt || !iterateInput || state.isIterating) return
if (!selectedIterateTemplate.value) {
toast.error(t('toast.error.noIterateTemplate'))
return
}
// 在开始迭代前立即清空状态
state.isIterating = true
state.optimizedPrompt = ''
state.optimizedReasoning = ''
// 等待一个微任务确保状态更新完成
await nextTick()
try {
await promptService.value!.iteratePromptStream(
originalPrompt,
lastOptimizedPrompt,
iterateInput,
selectedOptimizeModel.value,
{
onToken: (token: string) => {
state.optimizedPrompt += token
},
onReasoningToken: (reasoningToken: string) => {
state.optimizedReasoning += reasoningToken
},
onComplete: async () => {
if (!selectedIterateTemplate.value) {
state.isIterating = false
return
}
try {
// 保存迭代历史
const iterationData = {
chainId: state.currentChainId,
originalPrompt: originalPrompt,
optimizedPrompt: state.optimizedPrompt,
iterationNote: iterateInput,
modelKey: selectedOptimizeModel.value,
templateId: selectedIterateTemplate.value.id
}
const updatedChain = await historyManager.value!.addIteration(iterationData)
state.currentVersions = updatedChain.versions
state.currentVersionId = updatedChain.currentRecord.id
toast.success(t('toast.success.iterateComplete'))
} catch (error: unknown) {
console.error('[History] 迭代记录失败:', error)
toast.warning(t('toast.warning.historyFailed'))
} finally {
state.isIterating = false
}
},
onError: (error: Error) => {
console.error('[Iterate] 迭代失败:', error)
toast.error(t('toast.error.iterateFailed'))
state.isIterating = false
}
},
selectedIterateTemplate.value.id
)
} catch (error: unknown) {
console.error('[Iterate] 迭代失败:', error)
toast.error(t('toast.error.iterateFailed'))
state.isIterating = false
}
},
// 切换版本
switchVersion: async (version: PromptChain['versions'][number]) => {
// 强制更新内容确保UI同步
state.optimizedPrompt = version.optimizedPrompt
state.currentVersionId = version.id
// 等待一个微任务确保状态更新完成
await nextTick()
}
})
// 同步 selectedTemplate 和 selectedIterateTemplate
// 这样外部可以通过 props 控制,内部也能访问
const syncTemplates = () => {
state.selectedTemplate = selectedTemplate.value
state.selectedIterateTemplate = selectedIterateTemplate.value
}
// 初始同步
syncTemplates()
// 监听变化并同步(使用 Vue 的响应式系统自动处理)
const unwatchTemplate = () => {
state.selectedTemplate = selectedTemplate.value
}
const unwatchIterateTemplate = () => {
state.selectedIterateTemplate = selectedIterateTemplate.value
}
// 返回 reactive 对象
return state
}

View File

@@ -0,0 +1,247 @@
import { reactive, type Ref } from 'vue'
import { useToast } from '../ui/useToast'
import { useI18n } from 'vue-i18n'
import { getErrorMessage } from '../../utils/error'
import type { AppServices } from '../../types/services'
import type { ConversationMessage } from '../../types/variable'
import type { VariableManagerHooks } from './useVariableManager'
/**
* ContextUser 模式测试结果接口
*/
export interface ContextUserTestResults {
// 原始提示词结果
originalResult: string
originalReasoning: string
isTestingOriginal: boolean
// 优化提示词结果
optimizedResult: string
optimizedReasoning: string
isTestingOptimized: boolean
}
/**
* ContextUser 模式测试器接口
*/
export interface UseContextUserTester {
// 测试结果状态
testResults: ContextUserTestResults
// 方法
executeTest: (
prompt: string,
optimizedPrompt: string,
testContent: string,
isCompareMode: boolean,
testVariables?: Record<string, string>
) => Promise<void>
}
/**
* ContextUser 模式提示词测试器 Composable
*
* 专门用于 ContextUserWorkspace 的测试逻辑,特点:
* - 只处理用户模式测试user mode
* - 独立的测试结果状态管理
* - 支持对比模式(原始 vs 优化)
* - 与 ContextSystem 的 useConversationTester 对称
*
* @param services 服务实例引用
* @param selectedTestModel 测试模型选择
* @param variableManager 变量管理器
* @returns ContextUser 测试器接口
*
* @example
* ```ts
* const contextUserTester = useContextUserTester(
* services,
* computed(() => props.selectedTestModel),
* variableManager
* )
*
* // 执行测试
* await contextUserTester.executeTest(
* prompt,
* optimizedPrompt,
* testContent,
* isCompareMode,
* testVariables
* )
* ```
*/
export function useContextUserTester(
services: Ref<AppServices | null>,
selectedTestModel: Ref<string>,
variableManager: VariableManagerHooks | null
): UseContextUserTester {
const toast = useToast()
const { t } = useI18n()
// 创建响应式状态对象
const state = reactive<UseContextUserTester>({
// 测试结果状态
testResults: {
// 原始提示词结果
originalResult: '',
originalReasoning: '',
isTestingOriginal: false,
// 优化提示词结果
optimizedResult: '',
optimizedReasoning: '',
isTestingOptimized: false,
},
// 执行测试(支持对比模式)
executeTest: async (
prompt: string,
optimizedPrompt: string,
testContent: string,
isCompareMode: boolean,
testVariables?: Record<string, string>
) => {
if (!services.value?.promptService) {
toast.error(t('toast.error.serviceInit'))
return
}
if (!selectedTestModel.value) {
toast.error(t('test.error.noModel'))
return
}
if (isCompareMode) {
// 对比模式:测试原始和优化提示词
await state.testPromptWithType(
'original',
prompt,
optimizedPrompt,
testContent,
testVariables
)
await state.testPromptWithType(
'optimized',
prompt,
optimizedPrompt,
testContent,
testVariables
)
} else {
// 单一模式:只测试优化后的提示词
await state.testPromptWithType(
'optimized',
prompt,
optimizedPrompt,
testContent,
testVariables
)
}
},
/**
* 测试特定类型的提示词(内部方法)
*/
testPromptWithType: async (
type: 'original' | 'optimized',
prompt: string,
optimizedPrompt: string,
testContent: string,
testVars?: Record<string, string>
) => {
const isOriginal = type === 'original'
const selectedPrompt = isOriginal ? prompt : optimizedPrompt
// 检查提示词
if (!selectedPrompt) {
toast.error(
isOriginal ? t('test.error.noOriginalPrompt') : t('test.error.noOptimizedPrompt')
)
return
}
// 设置测试状态
if (isOriginal) {
state.testResults.isTestingOriginal = true
state.testResults.originalResult = ''
state.testResults.originalReasoning = ''
} else {
state.testResults.isTestingOptimized = true
state.testResults.optimizedResult = ''
state.testResults.optimizedReasoning = ''
}
try {
const streamHandler = {
onToken: (token: string) => {
if (isOriginal) {
state.testResults.originalResult += token
} else {
state.testResults.optimizedResult += token
}
},
onReasoningToken: (reasoningToken: string) => {
if (isOriginal) {
state.testResults.originalReasoning += reasoningToken
} else {
state.testResults.optimizedReasoning += reasoningToken
}
},
onComplete: () => {
// Test completed successfully
},
onError: (err: Error) => {
const errorMessage = err.message || t('test.error.failed')
console.error(`[useContextUserTester] ${type} test failed:`, errorMessage)
const testTypeKey = type === 'original' ? 'originalTestFailed' : 'optimizedTestFailed'
toast.error(`${t(`test.error.${testTypeKey}`)}: ${errorMessage}`)
},
}
// ContextUser 模式:提示词作为用户输入
// 固定 optimizationMode 为 'user'
const systemPrompt = ''
const userPrompt = selectedPrompt
// 变量:合并全局变量 + 测试变量
const baseVars = variableManager?.variableManager.value?.resolveAllVariables() || {}
const variables = {
...baseVars,
...(testVars || {}),
currentPrompt: selectedPrompt,
userQuestion: userPrompt,
}
// 构造简单的消息列表ContextUser 模式只有用户消息)
const messages: ConversationMessage[] = [
{ role: 'user' as const, content: userPrompt },
]
// 使用自定义会话测试
await services.value!.promptService.testCustomConversationStream(
{
modelKey: selectedTestModel.value,
messages,
variables,
tools: [], // ContextUser 模式基础不支持工具调用(如需支持可扩展)
},
streamHandler
)
} catch (error: unknown) {
console.error(`[useContextUserTester] ${type} test error:`, error)
const errorMessage = getErrorMessage(error) || t('test.error.failed')
const testTypeKey = type === 'original' ? 'originalTestFailed' : 'optimizedTestFailed'
toast.error(`${t(`test.error.${testTypeKey}`)}: ${errorMessage}`)
} finally {
// 重置测试状态
if (isOriginal) {
state.testResults.isTestingOriginal = false
} else {
state.testResults.isTestingOptimized = false
}
}
},
} as UseContextUserTester)
return state
}

View File

@@ -0,0 +1,201 @@
import { reactive, type Ref, type ComputedRef } from 'vue'
import { useToast } from '../ui/useToast'
import { useI18n } from 'vue-i18n'
import { getErrorMessage } from '../../utils/error'
import type { OptimizationMode, ToolDefinition, ToolCall, ToolCallResult, ConversationMessage } from '@prompt-optimizer/core'
import type { AppServices } from '../../types/services'
import type { VariableManagerHooks } from './useVariableManager'
import type { TestAreaPanelInstance } from '../../components/types/test-area'
/**
* 多对话模式专用测试 Composable
*
* 专门处理上下文-多消息模式的测试逻辑,包括:
* - 选中消息的 V0 对比
* - 会话上下文处理
* - 工具调用支持
*
* @param services 服务实例引用
* @param selectedTestModel 测试模型选择
* @param optimizationContext 优化上下文(会话消息)
* @param optimizationContextTools 上下文工具列表
* @param variableManager 变量管理器
* @param selectedMessageId 当前选中的消息ID用于对比模式
* @returns 多对话测试接口
*/
export function useConversationTester(
services: Ref<AppServices | null>,
selectedTestModel: Ref<string>,
optimizationContext: Ref<ConversationMessage[]>,
optimizationContextTools: Ref<ToolDefinition[]>,
variableManager: VariableManagerHooks | null,
selectedMessageId?: Ref<string>
) {
const toast = useToast()
const { t } = useI18n()
const state = reactive({
testResults: {
originalResult: '',
originalReasoning: '',
isTestingOriginal: false,
optimizedResult: '',
optimizedReasoning: '',
isTestingOptimized: false,
},
/**
* 执行多对话测试(支持对比模式)
* @param isCompareMode 是否对比模式
* @param testVariables 测试变量
* @param testPanelRef 测试面板引用(用于工具调用回调)
*/
executeTest: async (
isCompareMode: boolean,
testVariables?: Record<string, string>,
testPanelRef?: TestAreaPanelInstance | null
) => {
if (!services.value?.promptService) {
toast.error(t('toast.error.serviceInit'))
return
}
if (!selectedTestModel.value) {
toast.error(t('test.error.noModel'))
return
}
// 检查会话上下文
if (!optimizationContext.value || optimizationContext.value.length === 0) {
toast.error(t('test.error.noConversation'))
return
}
if (isCompareMode) {
// 对比模式:并发测试原始和优化会话
const originalTestPromise = state.testConversation('original', testVariables, testPanelRef)
const optimizedTestPromise = state.testConversation('optimized', testVariables, testPanelRef)
await Promise.all([originalTestPromise, optimizedTestPromise])
} else {
// 单一模式:只测试优化后的会话
await state.testConversation('optimized', testVariables, testPanelRef)
}
},
/**
* 测试特定类型的会话(原始 vs 优化)
*/
testConversation: async (
type: 'original' | 'optimized',
testVars?: Record<string, string>,
testPanelRef?: TestAreaPanelInstance | null
) => {
const isOriginal = type === 'original'
// 设置测试状态
if (isOriginal) {
state.testResults.isTestingOriginal = true
state.testResults.originalResult = ''
state.testResults.originalReasoning = ''
} else {
state.testResults.isTestingOptimized = true
state.testResults.optimizedResult = ''
state.testResults.optimizedReasoning = ''
}
// 清除对应类型的工具调用数据
testPanelRef?.clearToolCalls(isOriginal ? 'original' : 'optimized')
try {
const streamHandler = {
onToken: (token: string) => {
if (isOriginal) {
state.testResults.originalResult += token
} else {
state.testResults.optimizedResult += token
}
},
onReasoningToken: (reasoningToken: string) => {
if (isOriginal) {
state.testResults.originalReasoning += reasoningToken
} else {
state.testResults.optimizedReasoning += reasoningToken
}
},
onComplete: () => {
// Test completed successfully
},
onError: (err: Error) => {
const errorMessage = err.message || t('test.error.failed')
console.error(`[useConversationTester] ${type} test failed:`, errorMessage)
const testTypeKey = type === 'original' ? 'originalTestFailed' : 'optimizedTestFailed'
toast.error(`${t(`test.error.${testTypeKey}`)}: ${errorMessage}`)
},
}
// 变量:合并全局变量 + 测试变量
const baseVars = variableManager?.variableManager.value?.resolveAllVariables() || {}
const variables = {
...baseVars,
...(testVars || {}),
}
// 构造会话消息:
// - 原始会话original只有选中的消息使用 originalContentV0其他消息使用当前版本
// - 优化会话optimized所有消息都使用当前版本
const messages: ConversationMessage[] = isOriginal
? optimizationContext.value.map(msg => ({
...msg,
content: (selectedMessageId?.value && msg.id === selectedMessageId.value)
? (msg.originalContent || msg.content)
: msg.content
}))
: optimizationContext.value
// 检查是否有工具
const hasTools = optimizationContextTools.value?.length > 0
// 使用自定义会话测试
await services.value!.promptService.testCustomConversationStream(
{
modelKey: selectedTestModel.value,
messages,
variables,
tools: hasTools ? optimizationContextTools.value : [],
},
{
...streamHandler,
onToolCall: (toolCall: ToolCall) => {
if (!hasTools) return
console.log(
`[useConversationTester] ${type} test tool call received:`,
toolCall
)
const toolCallResult: ToolCallResult = {
toolCall,
status: 'success',
timestamp: new Date(),
}
testPanelRef?.handleToolCall(toolCallResult, type)
},
}
)
} catch (error: unknown) {
console.error(`[useConversationTester] ${type} test error:`, error)
const errorMessage = getErrorMessage(error) || t('test.error.failed')
const testTypeKey = type === 'original' ? 'originalTestFailed' : 'optimizedTestFailed'
toast.error(`${t(`test.error.${testTypeKey}`)}: ${errorMessage}`)
} finally {
// 重置测试状态
if (isOriginal) {
state.testResults.isTestingOriginal = false
} else {
state.testResults.isTestingOptimized = false
}
}
},
})
return state
}

View File

@@ -0,0 +1,148 @@
import { computed, type Ref, type ComputedRef } from 'vue'
import type { ConversationMessage, PromptRecord } from '@prompt-optimizer/core'
import type { UseConversationOptimization } from './useConversationOptimization'
/**
* 提示词显示适配器选项
*/
export interface PromptDisplayAdapterOptions {
// 启用消息优化模式
enableMessageOptimization: Ref<boolean>
// 上下文消息列表
optimizationContext: Ref<ConversationMessage[]>
// 全局优化链(用于历史记录查看)
globalVersions: Ref<PromptRecord[]>
globalCurrentVersionId: Ref<string | undefined>
globalIsOptimizing: Ref<boolean>
}
/**
* 提示词显示适配器返回值
*/
export interface UsePromptDisplayAdapter {
// 模式标识
isInMessageOptimizationMode: ComputedRef<boolean>
// 显示数据(自动根据模式切换数据源)
displayedOriginalPrompt: ComputedRef<string>
displayedOptimizedPrompt: ComputedRef<string>
displayedVersions: ComputedRef<PromptRecord[]>
displayedCurrentVersionId: ComputedRef<string | null>
displayedIsOptimizing: ComputedRef<boolean>
}
/**
* 提示词显示适配器 Composable
*
* 功能:
* - 根据"消息优化模式 vs 历史记录查看模式"自动切换数据源
* - 为 PromptPanel 提供统一的数据接口
* - 解决消息级优化和全局优化的数据隔离问题
*
* 使用场景:
* - ContextSystemWorkspace: 需要在消息优化和历史记录查看之间切换
* - 其他需要类似适配逻辑的组件
*
* @param conversationOptimization - 会话优化 composable 实例
* @param options - 适配器配置选项
* @returns 显示层数据和模式标识
*
* @example
* ```ts
* const displayAdapter = usePromptDisplayAdapter(
* conversationOptimization,
* {
* enableMessageOptimization: computed(() => props.enableMessageOptimization),
* optimizationContext: computed(() => props.optimizationContext),
* globalVersions: computed(() => props.versions || []),
* globalCurrentVersionId: computed(() => props.currentVersionId),
* globalIsOptimizing: computed(() => props.isOptimizing),
* }
* )
* ```
*/
export function usePromptDisplayAdapter(
conversationOptimization: UseConversationOptimization,
options: PromptDisplayAdapterOptions
): UsePromptDisplayAdapter {
const selectedMessageId = conversationOptimization.selectedMessageId
/**
* 消息优化模式判定
* 只有在启用消息优化 且 有选中消息时,才进入消息优化模式
*/
const isInMessageOptimizationMode = computed(() => {
return options.enableMessageOptimization.value && !!selectedMessageId.value
})
/**
* 显示的原始提示词
* - 消息优化模式: 当前选中消息的原始内容
* - 历史记录模式: 空字符串(不显示)
*/
const displayedOriginalPrompt = computed(() => {
if (!isInMessageOptimizationMode.value) return ''
const message = options.optimizationContext.value?.find(
m => m.id === selectedMessageId.value
)
return message?.originalContent || message?.content || ''
})
/**
* 显示的优化结果
* - 消息优化模式: 消息级优化结果
* - 历史记录模式: 空字符串(不显示)
*/
const displayedOptimizedPrompt = computed(() => {
return isInMessageOptimizationMode.value
? conversationOptimization.optimizedPrompt.value
: ''
})
/**
* 显示的版本列表
* - 消息优化模式: 消息级优化版本链
* - 历史记录模式: 全局优化版本链
*/
const displayedVersions = computed(() => {
if (isInMessageOptimizationMode.value) {
return conversationOptimization.currentVersions.value || []
}
return options.globalVersions.value || []
})
/**
* 显示的当前版本 ID
* - 消息优化模式: 消息级当前版本 ID
* - 历史记录模式: 全局当前版本 ID
*/
const displayedCurrentVersionId = computed(() => {
if (isInMessageOptimizationMode.value) {
return conversationOptimization.currentRecordId.value || null
}
return options.globalCurrentVersionId.value || null
})
/**
* 显示的优化中状态
* - 消息优化模式: 消息级优化状态
* - 历史记录模式: 全局优化状态
*/
const displayedIsOptimizing = computed(() => {
return isInMessageOptimizationMode.value
? conversationOptimization.isOptimizing.value
: options.globalIsOptimizing.value
})
return {
isInMessageOptimizationMode,
displayedOriginalPrompt,
displayedOptimizedPrompt,
displayedVersions,
displayedCurrentVersionId,
displayedIsOptimizing,
}
}

View File

@@ -296,8 +296,9 @@ export function usePromptOptimizer(
role: msg.role,
content: msg.content,
originalContent: msg.originalContent,
chainId: (msg as any).chainId,
appliedVersion: (msg as any).appliedVersion
// 运行时属性:消息被优化后动态添加的元数据
chainId: (msg as Record<string, unknown>).chainId as string | undefined,
appliedVersion: (msg as Record<string, unknown>).appliedVersion as number | undefined
}))
}
};

View File

@@ -3,26 +3,25 @@ import { reactive, type Ref, type ComputedRef } from 'vue'
import { useToast } from '../ui/useToast'
import { useI18n } from 'vue-i18n'
import { getErrorMessage } from '../../utils/error'
import type { OptimizationMode, ToolDefinition, ToolCall, ToolCallResult } from '@prompt-optimizer/core'
import type { OptimizationMode } from '@prompt-optimizer/core'
import type { AppServices } from '../../types/services'
import type { ConversationMessage } from '../../types/variable'
import type { VariableManagerHooks } from './useVariableManager'
import type { TestAreaPanelInstance } from '../components/types/test-area'
/**
* 高级提示词测试 Hook
* 支持变量注入、上下文、工具调用等高级特性
* 基础模式提示词测试 Composable
*
* 专门处理基础模式的提示词测试,支持:
* - System prompt 测试
* - User prompt 测试
* - 变量注入
* - 对比模式(原始 vs 优化)
*
* @param services 服务实例引用
* @param selectedTestModel 测试模型选择
* @param optimizationMode 当前优化模式(建议传入 computed 值,从 basicSubMode/proSubMode 动态计算)
* @param advancedModeEnabled 是否启用高级模式
* @param optimizationContext 优化上下文(会话消息)
* @param optimizationContextTools 上下文工具列表
* @param optimizationMode 当前优化模式
* @param variableManager 变量管理器
* @param selectedMessageId 当前选中的消息ID用于对比模式
* @returns 提示词测试接口
* @deprecated optimizationMode 参数建议传入 computed 值(从 basicSubMode/proSubMode 动态计算)
* @returns 基础测试接口
*/
type OptimizationModeSource = Ref<OptimizationMode> | ComputedRef<OptimizationMode>
@@ -30,11 +29,7 @@ export function usePromptTester(
services: Ref<AppServices | null>,
selectedTestModel: Ref<string>,
optimizationMode: OptimizationModeSource,
advancedModeEnabled: Ref<boolean>,
optimizationContext: Ref<ConversationMessage[]>,
optimizationContextTools: Ref<ToolDefinition[]>,
variableManager: VariableManagerHooks | null,
selectedMessageId?: Ref<string>
variableManager: VariableManagerHooks | null
) {
const toast = useToast()
const { t } = useI18n()
@@ -56,21 +51,19 @@ export function usePromptTester(
// Methods
/**
* 执行测试(支持对比模式)
* 执行基础模式测试(支持对比模式)
* @param prompt 原始提示词
* @param optimizedPrompt 优化后的提示词
* @param testContent 测试内容
* @param isCompareMode 是否对比模式
* @param testVariables 测试变量
* @param testPanelRef 测试面板引用(用于工具调用回调)
*/
executeTest: async (
prompt: string,
optimizedPrompt: string,
testContent: string,
isCompareMode: boolean,
testVariables?: Record<string, string>,
testPanelRef?: TestAreaPanelInstance | null
testVariables?: Record<string, string>
) => {
if (!services.value?.promptService) {
toast.error(t('toast.error.serviceInit'))
@@ -89,16 +82,14 @@ export function usePromptTester(
prompt,
optimizedPrompt,
testContent,
testVariables,
testPanelRef
testVariables
)
await state.testPromptWithType(
'optimized',
prompt,
optimizedPrompt,
testContent,
testVariables,
testPanelRef
testVariables
)
} else {
// 单一模式:只测试优化后的提示词
@@ -107,34 +98,26 @@ export function usePromptTester(
prompt,
optimizedPrompt,
testContent,
testVariables,
testPanelRef
testVariables
)
}
},
/**
* 测试特定类型的提示词(复用会话上下文 + 变量 + 工具
* 测试特定类型的提示词(基础模式
*/
testPromptWithType: async (
type: 'original' | 'optimized',
prompt: string,
optimizedPrompt: string,
testContent: string,
testVars?: Record<string, string>,
testPanelRef?: TestAreaPanelInstance | null
testVars?: Record<string, string>
) => {
const isOriginal = type === 'original'
const selectedPrompt = isOriginal ? prompt : optimizedPrompt
// 🔧 检查会话上下文
const hasConversationContext =
optimizationMode.value === 'system' &&
advancedModeEnabled.value &&
(optimizationContext.value?.length || 0) > 0
// 🔧 在上下文-多消息模式下,会话内容来自 optimizationContext不需要检查 selectedPrompt
if (!hasConversationContext && !selectedPrompt) {
// 检查提示词
if (!selectedPrompt) {
toast.error(
isOriginal ? t('test.error.noOriginalPrompt') : t('test.error.noOptimizedPrompt')
)
@@ -152,9 +135,6 @@ export function usePromptTester(
state.testResults.optimizedReasoning = ''
}
// 清除对应类型的工具调用数据
testPanelRef?.clearToolCalls(isOriginal ? 'original' : 'optimized')
try {
const streamHandler = {
onToken: (token: string) => {
@@ -182,7 +162,7 @@ export function usePromptTester(
},
}
// 统一构造对话与变量,尽量复用上下文
// 构造系统消息和用户消息
let systemPrompt = ''
let userPrompt = ''
@@ -196,76 +176,30 @@ export function usePromptTester(
userPrompt = testContent || '请按照你的角色设定,展示你的能力并与我互动。'
}
const hasConversationContext =
optimizationMode.value === 'system' &&
advancedModeEnabled.value &&
(optimizationContext.value?.length || 0) > 0
const hasTools =
advancedModeEnabled.value &&
(optimizationContextTools.value?.length || 0) > 0
// 变量:合并全局变量 + 测试变量 + 当前提示词/问题(用于会话模板中的占位符)
// 按优先级合并: 全局自定义变量 < 测试变量 < 预定义变量
const baseVars =
variableManager?.variableManager.value?.resolveAllVariables() || {}
// 使用传入的测试变量
// 变量:合并全局变量 + 测试变量
const baseVars = variableManager?.variableManager.value?.resolveAllVariables() || {}
const variables = {
...baseVars,
...(testVars || {}), // 测试变量优先级高于全局变量
...(testVars || {}),
currentPrompt: selectedPrompt,
userQuestion: userPrompt,
}
// 对话构造逻辑:
// - 系统模式 + 有会话上下文:
// - 原始会话original只有选中的消息使用 originalContentV0其他消息使用当前版本
// - 优化会话optimized所有消息都使用当前版本
// - 用户模式:无论是否有会话上下文,都直接发送优化后的提示词作为用户消息
// (因为用户提示词优化的目标是生成可直接使用的单条用户消息)
const messages: ConversationMessage[] =
optimizationMode.value === 'system' && hasConversationContext
? isOriginal
// 🆕 原始会话:只有选中的消息使用 V0其他消息保持当前版本
? optimizationContext.value.map(msg => ({
...msg,
content: (selectedMessageId?.value && msg.id === selectedMessageId.value)
? (msg.originalContent || msg.content)
: msg.content
}))
// 优化会话:所有消息都使用当前版本
: optimizationContext.value
: [
...(systemPrompt
? [{ role: 'system' as const, content: systemPrompt }]
: []),
{ role: 'user' as const, content: userPrompt },
]
// 构造简单的消息列表
const messages: ConversationMessage[] = [
...(systemPrompt ? [{ role: 'system' as const, content: systemPrompt }] : []),
{ role: 'user' as const, content: userPrompt },
]
// 统一使用自定义会话测试,以便支持上下文与工具
// 使用自定义会话测试
await services.value!.promptService.testCustomConversationStream(
{
modelKey: selectedTestModel.value,
messages,
variables,
tools: hasTools ? optimizationContextTools.value : [],
tools: [], // 基础模式不支持工具调用
},
{
...streamHandler,
onToolCall: (toolCall: ToolCall) => {
if (!hasTools) return
console.log(
`[usePromptTester] ${type} test tool call received:`,
toolCall
)
const toolCallResult: ToolCallResult = {
toolCall,
status: 'success',
timestamp: new Date(),
}
testPanelRef?.handleToolCall(toolCallResult, type)
},
}
streamHandler
)
} catch (error: unknown) {
console.error(`[usePromptTester] ${type} test error:`, error)

View File

@@ -186,7 +186,6 @@
@compare-toggle="handleTestAreaCompareToggle"
@optimize="handleOptimizePrompt"
@iterate="handleIteratePrompt"
@test="handleTestAreaTest"
@switch-version="handleSwitchVersion"
@save-favorite="handleSaveFavorite"
@open-global-variables="openVariableManager()"
@@ -196,20 +195,10 @@
@config-model="modelManager.showConfig = true"
@open-input-preview="handleOpenInputPreview"
@open-prompt-preview="handleOpenPromptPreview"
:selected-message-id="conversationOptimization.selectedMessageId.value"
:enable-message-optimization="true"
@message-select="handleMessageSelect"
:message-optimized-prompt="conversationOptimization.optimizedPrompt.value"
:message-versions="conversationOptimization.currentVersions.value"
:message-current-version-id="conversationOptimization.currentRecordId.value"
:is-message-optimizing="conversationOptimization.isOptimizing.value"
:versions="optimizer.currentVersions"
:current-version-id="optimizer.currentVersionId"
@message-switch-version="handleMessageSwitchVersion"
@message-switch-to-v0="handleMessageSwitchToV0"
@optimize-message="handleOptimizeMessage"
@message-change="handleMessageChange"
@message-apply-version="handleApplyMessageVersion"
:selected-optimize-model="modelManager.selectedOptimizeModel"
:selected-template="currentSelectedTemplate"
:selected-test-model="modelManager.selectedTestModel"
>
<!-- 优化模型选择插槽 -->
<template #optimize-model-select>
@@ -278,58 +267,17 @@
/>
</template>
<!-- 测试结果插槽 -->
<template #original-result>
<OutputDisplay
:content="testResults.originalResult"
:reasoning="testResults.originalReasoning"
:streaming="testResults.isTestingOriginal"
:enableDiff="false"
mode="readonly"
:style="{ height: '100%', minHeight: '0' }"
/>
</template>
<template #optimized-result>
<OutputDisplay
:content="testResults.optimizedResult"
:reasoning="testResults.optimizedReasoning"
:streaming="testResults.isTestingOptimized"
:enableDiff="false"
mode="readonly"
:style="{ height: '100%', minHeight: '0' }"
/>
</template>
<template #single-result>
<OutputDisplay
:content="testResults.optimizedResult"
:reasoning="testResults.optimizedReasoning"
:streaming="testResults.isTestingOptimized"
:enableDiff="false"
mode="readonly"
:style="{ height: '100%', minHeight: '0' }"
/>
</template>
<!-- 🔧 测试结果插槽已移除ContextSystemWorkspace 内部直接使用 useConversationTester 渲染 -->
</ContextSystemWorkspace>
<!-- 上下文-用户模式 -->
<!-- 上下文-用户模式(🆕 已独立,内部管理优化和测试逻辑) -->
<ContextUserWorkspace
ref="userWorkspaceRef"
v-else-if="contextMode === 'user'"
:prompt="optimizer.prompt"
@update:prompt="optimizer.prompt = $event"
:optimized-prompt="optimizer.optimizedPrompt"
@update:optimizedPrompt="
optimizer.optimizedPrompt = $event
"
:optimized-reasoning="optimizer.optimizedReasoning"
:optimization-mode="selectedOptimizationMode"
:is-optimizing="optimizer.isOptimizing"
:is-iterating="optimizer.isIterating"
:is-test-running="false"
:versions="optimizer.currentVersions"
:current-version-id="optimizer.currentVersionId"
:selected-optimize-model="modelManager.selectedOptimizeModel"
:selected-test-model="modelManager.selectedTestModel"
:selected-template="selectedTemplate"
:selected-iterate-template="
optimizer.selectedIterateTemplate
"
@@ -363,11 +311,7 @@
:result-vertical-layout="
responsiveLayout.isMobile.value
"
@optimize="handleOptimizePrompt"
@iterate="handleIteratePrompt"
@test="handleTestAreaTest"
@compare-toggle="handleTestAreaCompareToggle"
@switch-version="handleSwitchVersion"
@save-favorite="handleSaveFavorite"
@open-global-variables="openVariableManager()"
@open-tool-manager="
@@ -447,39 +391,7 @@
/>
</template>
<!-- 测试结果插槽 -->
<template #original-result>
<OutputDisplay
:content="testResults.originalResult"
:reasoning="testResults.originalReasoning"
:streaming="testResults.isTestingOriginal"
:enableDiff="false"
mode="readonly"
:style="{ height: '100%', minHeight: '0' }"
/>
</template>
<template #optimized-result>
<OutputDisplay
:content="testResults.optimizedResult"
:reasoning="testResults.optimizedReasoning"
:streaming="testResults.isTestingOptimized"
:enableDiff="false"
mode="readonly"
:style="{ height: '100%', minHeight: '0' }"
/>
</template>
<template #single-result>
<OutputDisplay
:content="testResults.optimizedResult"
:reasoning="testResults.optimizedReasoning"
:streaming="testResults.isTestingOptimized"
:enableDiff="false"
mode="readonly"
:style="{ height: '100%', minHeight: '0' }"
/>
</template>
<!-- 🔧 测试结果插槽已移除ContextUserWorkspace 内部直接使用 useContextUserTester 渲染 -->
</ContextUserWorkspace>
</template>
@@ -1028,7 +940,6 @@ import {
useContextManagement,
useAggregatedVariables,
useContextEditorUIState,
useConversationOptimization,
// i18n functions
initializeI18nWithStorage,
@@ -1087,7 +998,7 @@ watch(
{ immediate: true },
);
// 4. 向子组件提供服务
// 4. 向子组件提供服务(部分 provide 移至声明后)
provide("services", services);
// 5. 控制主UI渲染的标志
@@ -1331,72 +1242,19 @@ const handleContextEditorStateUpdate =
contextManagement.handleContextEditorStateUpdate;
const handleContextModeChange = contextManagement.handleContextModeChange;
// 🆕 多轮对话消息优化管理
const selectedOptimizationTemplate = computed<Template | null>(() => {
return selectedOptimizationMode.value === "system"
? optimizer.selectedOptimizeTemplate
: optimizer.selectedUserOptimizeTemplate;
});
// 🔧 提供依赖给子组件(必须在所有依赖项声明之后)
provide("variableManager", variableManager);
provide("optimizationContextTools", optimizationContextTools);
const conversationOptimization = useConversationOptimization(
services,
optimizationContext,
selectedOptimizationMode,
toRef(modelManager, "selectedOptimizeModel"),
selectedOptimizationTemplate,
toRef(optimizer, "selectedIterateTemplate") // 🔧 添加迭代模板
);
provide('conversationOptimization', conversationOptimization);
// 处理消息选择事件
const handleMessageSelect = async (message: ConversationMessage) => {
await conversationOptimization.selectMessage(message);
};
// 处理消息版本切换
const handleMessageSwitchVersion = async (version: PromptRecordChain['versions'][number]) => {
await conversationOptimization.switchVersion(version);
};
// 🆕 处理消息 V0 切换
const handleMessageSwitchToV0 = async (version: PromptRecordChain['versions'][number]) => {
await conversationOptimization.switchToV0(version);
};
// 处理消息优化
const handleOptimizeMessage = async () => {
await conversationOptimization.optimizeMessage();
};
// 处理消息变更(用于清理删除消息的映射)
const handleMessageChange = (index: number, message: ConversationMessage, action: 'add' | 'update' | 'delete') => {
if (!message?.id) return;
if (action === 'delete') {
conversationOptimization.cleanupDeletedMessageMapping(message.id);
} else if (action === 'update') {
conversationOptimization.cleanupDeletedMessageMapping(message.id, { keepSelection: true });
}
};
// 手动应用所选版本
const handleApplyMessageVersion = async () => {
await conversationOptimization.applyCurrentVersion();
};
// 🆕 提示词测试管理(支持变量注入、上下文、工具调用)
// 🆕 基础模式提示词测试(简化后只用于基础模式和 context-user
const promptTester = usePromptTester(
services as any,
toRef(modelManager, 'selectedTestModel'),
selectedOptimizationMode, // 保持兼容性,后续应改为使用 basicSubMode/proSubMode
advancedModeEnabled,
optimizationContext,
optimizationContextTools,
variableManager,
conversationOptimization.selectedMessageId // 🆕 传递选中的消息ID用于对比
selectedOptimizationMode,
variableManager
);
// 测试结果引用(从 promptTester 获取)
// 测试结果引用(从 promptTester 获取,用于基础模式和 context-user
const testResults = computed(() => promptTester.testResults);
// 处理测试面板的变量变化现在测试变量由TestAreaPanel自己管理不需要同步到会话
@@ -2276,20 +2134,16 @@ const getActiveTestPanelInstance = (): TestAreaPanelInstance | null => {
return null;
};
// 真实测试处理函数
// 基础模式和 context-user 模式的测试处理函数
// 注意context-system 模式已在 ContextSystemWorkspace 内部使用 useConversationTester 处理,不会调用此函数
const handleTestAreaTest = async (testVariables?: Record<string, string>) => {
// 🔧 多轮对话模式context-system不使用 testContent测试内容来自会话消息
// 但现在支持对比模式了,可以对比选中消息的 V0 和当前版本
const actualTestContent = contextMode.value === 'system' ? '' : testContent.value;
// 调用 promptTester 的 executeTest 方法
// 调用基础测试器(只用于基础模式和 context-user
await promptTester.executeTest(
optimizer.prompt,
optimizer.optimizedPrompt,
actualTestContent,
isCompareMode.value, // 🔧 直接使用 isCompareMode不再强制为 false
testVariables,
getActiveTestPanelInstance()
testContent.value,
isCompareMode.value,
testVariables
);
};