fix: session persistence, multi-turn memory, OCR pipeline, download UX (v7)

- graph.stream() state fix: agent_state now properly accumulates node updates

- atomic session save (tempfile + os.replace)

- uploaded_file_path injection for OcrExtractor + annotation_detector

- download section always visible; refreshFromApi auto-reloads after generation

- node_start/complete unfiltered for full progress visibility

- modification_request without status=='pass' check
This commit is contained in:
2026-05-22 11:13:25 +08:00
parent 4dfc418fc5
commit 1144a86d02
6 changed files with 147 additions and 119 deletions
+2 -2
View File
@@ -98,12 +98,12 @@ async function handleSend(text: string, files: File[]) {
}
// Refresh session sidebar data after a short delay
setTimeout(() => session.refreshFromState({}), 500)
setTimeout(() => session.refreshFromApi(), 500)
},
onAgentError(data) {
chat.setError(data.error)
chat.addMessage({ role: 'assistant', content: `执行异常: ${data.error}`, type: 'error' })
setTimeout(() => session.refreshFromState({}), 500)
setTimeout(() => session.refreshFromApi(), 500)
},
})
} catch (e: any) {
+52 -99
View File
@@ -99,7 +99,7 @@ async function handleDelete() {
</div>
</div>
<div class="sidebar-section" v-if="session.currentJrxml || session.versions.length > 0">
<div class="sidebar-section">
<div class="section-title">下载</div>
<a
v-if="session.currentJrxml"
@@ -109,6 +109,7 @@ async function handleDelete() {
>
下载最新 JRXML
</a>
<div v-else class="btn-download disabled">暂无下载文件</div>
<div v-if="session.versions.length > 1" class="version-list">
<div class="version-list-title">历史版本</div>
<a
@@ -181,178 +182,130 @@ async function handleDelete() {
cursor: pointer;
font-size: 16px;
line-height: 1;
display: flex;
align-items: center;
justify-content: center;
}
.btn-icon:hover {
background: #45475a;
}
.btn-icon:hover { background: #45475a; }
.session-list {
max-height: 300px;
overflow-y: auto;
padding: 0 8px;
}
.session-item {
padding: 8px 16px;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 13px;
padding: 8px 8px;
border-radius: 6px;
cursor: pointer;
transition: background 0.15s;
}
.session-item:hover {
background: #313244;
}
.session-item.active {
background: #45475a;
border-left: 3px solid #cba6f7;
padding-left: 13px;
}
.session-item:hover { background: #313244; }
.session-item.active { background: #45475a; }
.session-name {
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 150px;
flex: 1;
}
.session-time {
font-size: 11px;
color: #6c7086;
margin-left: 8px;
flex-shrink: 0;
}
.btn-delete {
display: block;
width: calc(100% - 32px);
margin: 8px 16px 0;
padding: 6px 12px;
border: 1px solid #f38ba8;
background: transparent;
width: calc(100% - 16px);
margin: 8px 8px 0;
padding: 6px 0;
border: 1px solid #45475a;
background: none;
color: #f38ba8;
border-radius: 6px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
text-align: center;
}
.btn-delete:hover {
background: #f38ba8;
color: #1e1e2e;
}
.btn-delete:hover { background: #45475a; }
.quick-actions {
display: flex;
gap: 6px;
gap: 8px;
padding: 0 16px;
}
.btn-action {
flex: 1;
padding: 6px 8px;
padding: 6px 0;
border: 1px solid #45475a;
background: #313244;
color: #cdd6f4;
border-radius: 6px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
transition: background 0.15s, border-color 0.15s;
}
.btn-action:hover:not(:disabled) {
background: #45475a;
}
.btn-action:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.btn-preview {
border-color: #89b4fa;
}
.btn-preview:hover:not(:disabled) {
background: #89b4fa;
color: #1e1e2e;
}
.btn-undo {
border-color: #f9e2af;
}
.btn-undo:hover:not(:disabled) {
background: #f9e2af;
color: #1e1e2e;
}
.btn-reset {
border-color: #f38ba8;
}
.btn-reset:hover:not(:disabled) {
background: #f38ba8;
color: #1e1e2e;
color: #cdd6f4;
background: none;
transition: background 0.15s;
}
.btn-action:hover:not(:disabled) { background: #45475a; }
.btn-action:disabled { opacity: 0.4; cursor: not-allowed; }
.btn-preview { border-color: #a6e3a1; color: #a6e3a1; }
.btn-undo { border-color: #f9e2af; color: #f9e2af; }
.btn-reset { border-color: #f38ba8; color: #f38ba8; }
.btn-download {
display: block;
padding: 8px 16px;
color: #a6e3a1;
margin: 4px 16px;
padding: 8px 0;
background: #cba6f7;
color: #1e1e2e;
text-align: center;
border-radius: 4px;
text-decoration: none;
font-size: 13px;
font-weight: 600;
cursor: pointer;
}
.btn-download:hover {
.btn-download:hover { background: #b4befe; }
.btn-download.disabled {
background: #313244;
color: #6c7086;
cursor: not-allowed;
font-weight: 400;
}
.version-list {
padding: 4px 16px 8px;
margin-top: 8px;
padding: 0 16px;
}
.version-list-title {
font-size: 11px;
color: #6c7086;
margin-bottom: 4px;
padding-top: 8px;
border-top: 1px solid #313244;
}
.version-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 4px 0;
font-size: 12px;
color: #a6adc8;
text-decoration: none;
}
.version-item:hover {
color: #cdd6f4;
}
.version-label {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.version-time {
font-size: 11px;
color: #6c7086;
flex-shrink: 0;
margin-left: 8px;
}
.version-item:hover { color: #cba6f7; }
.version-time { font-size: 11px; color: #6c7086; }
.sidebar-footer {
margin-top: auto;
padding: 12px 16px;
font-size: 11px;
color: #6c7086;
color: #585b70;
border-top: 1px solid #313244;
}
</style>
+14 -1
View File
@@ -63,6 +63,19 @@ export const useSessionStore = defineStore('session', () => {
versions.value = []
}
async function refreshFromApi() {
if (!currentId.value) return
try {
const data = await api.getSession(currentId.value)
const state = data.agent_state
currentJrxml.value = state.current_jrxml || ''
versions.value = state.jrxml_versions || []
historyStates.value = state.history_states || []
} catch (e) {
console.error('刷新会话状态失败:', e)
}
}
function refreshFromState(agentState: Record<string, any>) {
currentJrxml.value = agentState.current_jrxml || currentJrxml.value
versions.value = agentState.jrxml_versions || versions.value
@@ -72,6 +85,6 @@ export const useSessionStore = defineStore('session', () => {
return {
sessions, currentId, currentName, versions, historyStates, currentJrxml,
hasJrxml, hasHistory, sortedSessions, currentSession,
loadSessions, createSession, switchSession, deleteCurrent, refreshFromState,
loadSessions, createSession, switchSession, deleteCurrent, refreshFromState, refreshFromApi,
}
})