fix hermes markdown media and sync retry (#550)
This commit is contained in:
@@ -83,7 +83,7 @@ const renderedHtml = computed(() => {
|
||||
const ext = path.split('.').pop()?.toLowerCase()
|
||||
|
||||
// Video files: render as video player
|
||||
if (ext === 'mp4' || ext === 'webm') {
|
||||
if (ext === 'mp4' || ext === 'webm' || ext === 'mov') {
|
||||
const downloadUrl = getDownloadUrl(path)
|
||||
return `<div class="markdown-video-container">
|
||||
<video class="markdown-video" controls preload="metadata" src="${downloadUrl}"></video>
|
||||
|
||||
@@ -25,7 +25,7 @@ export const AI_OUTPUT_FORMAT_GUIDELINES = `
|
||||
\`\`\`
|
||||
|
||||
## 视频格式
|
||||
使用 Markdown 链接语法引用视频文件,路径必须是本地绝对路径(以 / 开头),支持的格式:mp4, webm
|
||||
使用 Markdown 链接语法引用视频文件,路径必须是本地绝对路径(以 / 开头),支持的格式:mp4, webm, mov
|
||||
\`\`\`
|
||||
[视频名称](/tmp/recording.mp4)
|
||||
\`\`\`
|
||||
@@ -33,9 +33,25 @@ export const AI_OUTPUT_FORMAT_GUIDELINES = `
|
||||
\`\`\`
|
||||
[屏幕录制](/tmp/screen-recording.mp4)
|
||||
[操作演示](/tmp/demo.webm)
|
||||
[录屏2026-05-08 15.19.46](/Users/ekko/Desktop/录屏2026-05-08%2015.19.46.mov)
|
||||
\`\`\`
|
||||
视频会显示为可播放的视频播放器(最大 640x480),支持原生播放控件。
|
||||
|
||||
如果路径包含空格、中文或其他特殊字符,必须使用以下两种写法之一:
|
||||
1. 对路径做 URL 编码,至少把空格写成 \`%20\`
|
||||
2. 用尖括号包住链接目标
|
||||
|
||||
示例:
|
||||
\`\`\`
|
||||
[录屏2026-05-08 15.19.46](/Users/ekko/Desktop/录屏2026-05-08%2015.19.46.mov)
|
||||
[录屏2026-05-08 15.19.46](</Users/ekko/Desktop/录屏2026-05-08 15.19.46.mov>)
|
||||
\`\`\`
|
||||
|
||||
错误示例:
|
||||
\`\`\`
|
||||
[录屏2026-05-08 15.19.46](/Users/ekko/Desktop/录屏2026-05-08 15.19.46.mov)
|
||||
\`\`\`
|
||||
|
||||
## 文件链接格式
|
||||
使用 Markdown 链接语法,路径必须是本地绝对路径(以 / 开头):
|
||||
\`\`\`
|
||||
@@ -49,13 +65,15 @@ export const AI_OUTPUT_FORMAT_GUIDELINES = `
|
||||
## 注意事项
|
||||
1. 图片、视频、文件路径必须使用本地绝对路径(以 / 开头)
|
||||
2. 确保文件确实存在且路径正确
|
||||
3. 视频支持格式:.mp4, .webm
|
||||
3. 视频支持格式:.mp4, .webm, .mov
|
||||
4. 路径中如果有空格或特殊字符,必须编码或使用尖括号包裹链接目标
|
||||
|
||||
## 发送文件给用户
|
||||
当用户要求"发给我"、"发送给我"、"传给我"等请求文件时,使用上述格式返回文件路径:
|
||||
- 图片:\`\`
|
||||
- 视频:\`[视频名](/path/to/video.mp4)\`
|
||||
- 文件:\`[文件名](/path/to/file.pdf)\`
|
||||
- 如果路径中有空格,优先输出编码后的路径,例如:\`[录屏](</tmp/录屏 15.19.46.mov>)\` 或 \`[录屏](/tmp/录屏%2015.19.46.mov)\`
|
||||
`;
|
||||
|
||||
/**
|
||||
@@ -74,4 +92,3 @@ export function getSystemPrompt(customPrompt?: string): string {
|
||||
|
||||
return parts.join('\n\n');
|
||||
}
|
||||
|
||||
|
||||
@@ -1304,7 +1304,10 @@ export class ChatRunSocket {
|
||||
const prof = state.profile
|
||||
this.hermesSessionIds.delete(sessionId)
|
||||
state.profile = undefined
|
||||
await this.syncFromHermes(socket, sessionId, hermesId, prof)
|
||||
await this.syncFromHermes(socket, sessionId, hermesId, prof, {
|
||||
maxAttempts: 4,
|
||||
delayMs: 1000,
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1341,7 +1344,7 @@ export class ChatRunSocket {
|
||||
this.hermesSessionIds.delete(sessionId)
|
||||
logger.info({ sessionId, hermesId, profile: profile || 'default' }, '[chat-run-socket][abort] syncing stopped run from Hermes')
|
||||
synced = await this.syncFromHermes(socket, sessionId, hermesId, profile, {
|
||||
maxAttempts: 10,
|
||||
maxAttempts: 4,
|
||||
delayMs: 1000,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -36,6 +36,11 @@ vi.mock('naive-ui', () => ({
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@/api/hermes/download', () => ({
|
||||
downloadFile: vi.fn(),
|
||||
getDownloadUrl: (path: string) => `http://test.local/api/hermes/download?path=${encodeURIComponent(path)}`,
|
||||
}))
|
||||
|
||||
import MarkdownRenderer from '@/components/hermes/chat/MarkdownRenderer.vue'
|
||||
|
||||
describe('MarkdownRenderer', () => {
|
||||
@@ -218,6 +223,21 @@ describe('MarkdownRenderer', () => {
|
||||
expect(wrapper.find('.markdown-body').text()).toContain('Done outside.')
|
||||
})
|
||||
|
||||
it('renders local mov links as inline video players', () => {
|
||||
const wrapper = mount(MarkdownRenderer, {
|
||||
props: {
|
||||
content: '[录屏2026-05-08 15.19.46.mov](/Users/ekko/Desktop/录屏2026-05-08%2015.19.46.mov)',
|
||||
},
|
||||
})
|
||||
|
||||
const video = wrapper.find('video.markdown-video')
|
||||
expect(video.exists()).toBe(true)
|
||||
expect(video.attributes('src')).toContain('/api/hermes/download?path=')
|
||||
const src = new URL(video.attributes('src'))
|
||||
expect(decodeURIComponent(src.searchParams.get('path') || '')).toBe('/Users/ekko/Desktop/录屏2026-05-08 15.19.46.mov')
|
||||
expect(wrapper.find('.markdown-video-footer .att-name').text()).toBe('录屏2026-05-08 15.19.46.mov')
|
||||
})
|
||||
|
||||
it('keeps tilde-fenced markdown examples with nested tilde fences intact', () => {
|
||||
const wrapper = mount(MarkdownRenderer, {
|
||||
props: {
|
||||
|
||||
Reference in New Issue
Block a user