Skip to content

Commit b0038d3

Browse files
committed
feat(agent): 添加智能体状态刷新功能及API
- 在AgentPopover组件中添加刷新按钮 - 新增获取AgentState的API接口 - 优化AgentState相关计算逻辑 - 实现状态刷新功能并集成到聊天组件
1 parent 9a8f1c4 commit b0038d3

File tree

4 files changed

+85
-10
lines changed

4 files changed

+85
-10
lines changed

server/routers/chat_router.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,34 @@ async def get_agent_history(
882882
raise HTTPException(status_code=500, detail=f"获取智能体历史消息出错: {str(e)}")
883883

884884

885+
@chat.get("/agent/{agent_id}/state")
886+
async def get_agent_state(
887+
agent_id: str,
888+
thread_id: str,
889+
current_user: User = Depends(get_required_user),
890+
db: Session = Depends(get_db),
891+
):
892+
try:
893+
if not agent_manager.get_agent(agent_id):
894+
raise HTTPException(status_code=404, detail=f"智能体 {agent_id} 不存在")
895+
896+
conv_manager = ConversationManager(db)
897+
_require_user_conversation(conv_manager, thread_id, str(current_user.id))
898+
899+
agent = agent_manager.get_agent(agent_id)
900+
graph = await agent.get_graph()
901+
langgraph_config = {"configurable": {"user_id": str(current_user.id), "thread_id": thread_id}}
902+
state = await graph.aget_state(langgraph_config)
903+
agent_state = _extract_agent_state(getattr(state, "values", {})) if state else {}
904+
905+
return {"agent_state": agent_state}
906+
except HTTPException:
907+
raise
908+
except Exception as e:
909+
logger.error(f"获取AgentState出错: {e}, {traceback.format_exc()}")
910+
raise HTTPException(status_code=500, detail=f"获取AgentState出错: {str(e)}")
911+
912+
885913
@chat.get("/agent/{agent_id}/config")
886914
async def get_agent_config(agent_id: str, current_user: User = Depends(get_required_user)):
887915
"""从YAML文件加载智能体配置(需要登录)"""

web/src/apis/agent_api.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,14 @@ export const agentApi = {
7373
*/
7474
getAgentHistory: (agentId, threadId) => apiGet(`/api/chat/agent/${agentId}/history?thread_id=${threadId}`),
7575

76+
/**
77+
* 获取指定会话的 AgentState
78+
* @param {string} agentId - 智能体ID
79+
* @param {string} threadId - 会话ID
80+
* @returns {Promise} - AgentState
81+
*/
82+
getAgentState: (agentId, threadId) => apiGet(`/api/chat/agent/${agentId}/state?thread_id=${threadId}`),
83+
7684
/**
7785
* Submit feedback for a message
7886
* @param {number} messageId - Message ID

web/src/components/AgentChatComponent.vue

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,10 @@
4545
<div class="header__right">
4646
<!-- AgentState 显示按钮 - 只在智能体支持 todo 或 files 能力时显示 -->
4747
<AgentPopover
48+
v-if="hasAgentStateContent"
4849
v-model:visible="agentStatePopoverVisible"
4950
:agent-state="currentAgentState"
51+
@refresh="handleAgentStateRefresh"
5052
>
5153
<div
5254
class="agent-nav-btn agent-state-btn"
@@ -369,18 +371,28 @@ const currentAgentState = computed(() => {
369371
return currentChatId.value ? getThreadState(currentChatId.value)?.agentState || null : null;
370372
});
371373
374+
const countFiles = (files) => {
375+
if (!Array.isArray(files)) return 0;
376+
let c = 0;
377+
for (const item of files) {
378+
if (item && typeof item === 'object') c += Object.keys(item).length;
379+
}
380+
return c;
381+
};
382+
372383
const hasAgentStateContent = computed(() => {
373-
const agentState = currentAgentState.value;
374-
if (!agentState) return false;
375-
return (agentState.todos && agentState.todos.length > 0) ||
376-
(agentState.files && Object.keys(agentState.files).length > 0);
384+
const s = currentAgentState.value;
385+
if (!s) return false;
386+
const todoCount = Array.isArray(s.todos) ? s.todos.length : 0;
387+
const fileCount = countFiles(s.files);
388+
return todoCount > 0 || fileCount > 0;
377389
});
378390
379391
const totalAgentStateItems = computed(() => {
380-
const agentState = currentAgentState.value;
381-
if (!agentState) return 0;
382-
const todoCount = agentState.todos ? agentState.todos.length : 0;
383-
const fileCount = agentState.files ? Object.keys(agentState.files).length : 0;
392+
const s = currentAgentState.value;
393+
if (!s) return 0;
394+
const todoCount = Array.isArray(s.todos) ? s.todos.length : 0;
395+
const fileCount = countFiles(s.files);
384396
return todoCount + fileCount;
385397
});
386398
@@ -756,6 +768,15 @@ const fetchThreadMessages = async ({ agentId, threadId, delay = 0 }) => {
756768
}
757769
};
758770
771+
const fetchAgentState = async (agentId, threadId) => {
772+
if (!agentId || !threadId) return;
773+
try {
774+
const res = await agentApi.getAgentState(agentId, threadId);
775+
const ts = getThreadState(threadId);
776+
if (ts) ts.agentState = res.agent_state || null;
777+
} catch (error) {}
778+
};
779+
759780
const loadThreadAttachments = async (threadId, { silent = false } = {}) => {
760781
if (!threadId) return;
761782
try {
@@ -954,6 +975,7 @@ const selectChat = async (chatId) => {
954975
try {
955976
await fetchThreadMessages({ agentId: currentAgentId.value, threadId: chatId });
956977
await loadThreadAttachments(chatId, { silent: true });
978+
await fetchAgentState(currentAgentId.value, chatId);
957979
} catch (error) {
958980
handleChatError(error, 'load');
959981
} finally {
@@ -1240,6 +1262,11 @@ const toggleSidebar = () => {
12401262
};
12411263
const openAgentModal = () => emit('open-agent-modal');
12421264
1265+
const handleAgentStateRefresh = async () => {
1266+
if (!currentAgentId.value || !currentChatId.value) return;
1267+
await fetchAgentState(currentAgentId.value, currentChatId.value);
1268+
};
1269+
12431270
// ==================== HELPER FUNCTIONS ====================
12441271
const getLastMessage = (conv) => {
12451272
if (!conv?.messages?.length) return null;

web/src/components/AgentPopover.vue

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
>
2626
文件 ({{ fileCount }})
2727
</button>
28+
<a-button type="text" class="refresh-btn" @click="emitRefresh">刷新</a-button>
2829
</div>
2930
<div class="tab-content">
3031
<!-- Todo Display -->
@@ -103,7 +104,7 @@ const props = defineProps({
103104
}
104105
});
105106
106-
const emit = defineEmits(['update:visible']);
107+
const emit = defineEmits(['update:visible', 'refresh']);
107108
108109
const activeTab = ref('todos');
109110
const modalVisible = ref(false);
@@ -209,6 +210,10 @@ const closeModal = () => {
209210
currentFile.value = null;
210211
currentFilePath.value = '';
211212
};
213+
214+
const emitRefresh = () => {
215+
emit('refresh');
216+
};
212217
</script>
213218
214219
<style scoped lang="less">
@@ -223,6 +228,7 @@ const closeModal = () => {
223228
display: flex;
224229
border-bottom: 1px solid var(--gray-200);
225230
position: relative;
231+
align-items: center;
226232
}
227233
228234
.tab {
@@ -496,4 +502,10 @@ const closeModal = () => {
496502
}
497503
}
498504
}
499-
</style>
505+
506+
.refresh-btn {
507+
margin-left: auto;
508+
color: var(--gray-700);
509+
}
510+
511+
</style>

0 commit comments

Comments
 (0)