feat: AI测试输出渲染Markdown/流式响应、Coze/Dify适配器优化
- 新增 MarkdownPreview 组件,支持 AI 测试输出 Markdown 渲染 - Coze 适配器优化:支持流式响应、工作流接口调用、SSE事件处理 - Dify 适配器优化:支持停止接口、流式聊天、SSE事件解析 - web-admin 添加 markdown-it 和 highlight.js 依赖 - AI 配置列表页面优化测试对话框输出显示 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -296,6 +296,36 @@ function parseSseFrame(frame: string): AiRuntimeStreamEvent | null {
|
||||
}
|
||||
}
|
||||
|
||||
export function normalizeAiText(value?: string): string {
|
||||
if (!value) return ''
|
||||
const trimmed = value.trim()
|
||||
if (!trimmed.startsWith('{') || !trimmed.endsWith('}')) return value
|
||||
try {
|
||||
const parsed = JSON.parse(trimmed)
|
||||
const extracted = extractTextValue(parsed)
|
||||
return extracted ? normalizeAiText(extracted) : value
|
||||
} catch {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
function extractTextValue(value: any): string {
|
||||
if (!value || typeof value !== 'object' || Array.isArray(value)) return ''
|
||||
for (const key of ['output', 'answer', 'content', 'text', 'result']) {
|
||||
const item = value[key]
|
||||
if (typeof item === 'string' && item.trim()) return item
|
||||
if (item && typeof item === 'object' && !Array.isArray(item)) {
|
||||
const nested = extractTextValue(item)
|
||||
if (nested) return nested
|
||||
}
|
||||
}
|
||||
for (const key of ['data', 'outputs', 'message']) {
|
||||
const nested = extractTextValue(value[key])
|
||||
if (nested) return nested
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
async function fetchSseStream(
|
||||
url: string,
|
||||
body: Record<string, any>,
|
||||
@@ -327,7 +357,7 @@ async function fetchSseStream(
|
||||
const event = parseSseFrame(frame)
|
||||
if (!event) return
|
||||
if (event.type === 'delta') {
|
||||
output += event.content || ''
|
||||
output += normalizeAiText(event.content || '')
|
||||
}
|
||||
onEvent(event, output)
|
||||
if (event.type === 'error') {
|
||||
|
||||
Reference in New Issue
Block a user