Merge branch 'dev' of https://github.com/MoFox-Studio/MoFox_Bot into dev
This commit is contained in:
@@ -550,20 +550,23 @@ async def search_memories(q: str, limit: int = 50):
|
|||||||
all_memories = memory_manager.graph_store.get_all_memories()
|
all_memories = memory_manager.graph_store.get_all_memories()
|
||||||
for memory in all_memories:
|
for memory in all_memories:
|
||||||
if q.lower() in memory.to_text().lower():
|
if q.lower() in memory.to_text().lower():
|
||||||
|
node_ids = [node.id for node in memory.nodes]
|
||||||
results.append(
|
results.append(
|
||||||
{
|
{
|
||||||
"id": memory.id,
|
"id": memory.id,
|
||||||
"type": memory.memory_type.value,
|
"type": memory.memory_type.value,
|
||||||
"importance": memory.importance,
|
"importance": memory.importance,
|
||||||
"text": memory.to_text(),
|
"text": memory.to_text(),
|
||||||
|
"node_ids": node_ids, # 返回关联的节点ID
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# 从文件加载的数据中搜索 (降级方案)
|
# 从文件加载的数据中搜索 (降级方案)
|
||||||
|
# 注意:此模式下无法直接获取关联节点,前端需要做兼容处理
|
||||||
data = await load_graph_data_from_file()
|
data = await load_graph_data_from_file()
|
||||||
for memory in data.get("memories", []):
|
for memory in data.get("memories", []):
|
||||||
if q.lower() in memory.get("text", "").lower():
|
if q.lower() in memory.get("text", "").lower():
|
||||||
results.append(memory)
|
results.append(memory) # node_ids 可能不存在
|
||||||
|
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
content={
|
content={
|
||||||
@@ -575,7 +578,11 @@ async def search_memories(q: str, limit: int = 50):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return JSONResponse(content={"success": False, "error": str(e)}, status_code=500)
|
# 确保即使在异常情况下也返回 data 字段
|
||||||
|
return JSONResponse(
|
||||||
|
content={"success": False, "error": str(e), "data": {"results": [], "count": 0}},
|
||||||
|
status_code=500,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/api/stats")
|
@router.get("/api/stats")
|
||||||
|
|||||||
@@ -1240,6 +1240,9 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 先重置高亮,清除上一次的搜索结果
|
||||||
|
resetNodeHighlight();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`api/search?q=${encodeURIComponent(query)}&limit=50`);
|
const response = await fetch(`api/search?q=${encodeURIComponent(query)}&limit=50`);
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
@@ -1257,28 +1260,98 @@
|
|||||||
|
|
||||||
// 高亮搜索结果
|
// 高亮搜索结果
|
||||||
function highlightSearchResults(results) {
|
function highlightSearchResults(results) {
|
||||||
const memoryIds = results.map(r => r.id);
|
|
||||||
const relatedNodeIds = new Set();
|
const relatedNodeIds = new Set();
|
||||||
|
results.forEach(result => {
|
||||||
// 找出相关的节点
|
if (result.node_ids && result.node_ids.length > 0) {
|
||||||
graphData.memories.forEach(memory => {
|
result.node_ids.forEach(id => relatedNodeIds.add(id));
|
||||||
if (memoryIds.includes(memory.id)) {
|
|
||||||
// 这里需要找到该记忆相关的所有节点
|
|
||||||
// 简化实现:高亮所有节点
|
|
||||||
graphData.nodes.forEach(node => relatedNodeIds.add(node.id));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 高亮节点
|
if (relatedNodeIds.size === 0) {
|
||||||
if (relatedNodeIds.size > 0) {
|
alert('未找到与搜索结果直接关联的节点。\n这可能发生在从旧版文件加载数据时。');
|
||||||
network.selectNodes([...relatedNodeIds]);
|
return;
|
||||||
network.fit({
|
|
||||||
nodes: [...relatedNodeIds],
|
|
||||||
animation: true
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
alert('未找到相关节点');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`高亮 ${relatedNodeIds.size} 个搜索结果节点`);
|
||||||
|
|
||||||
|
const allNodes = network.body.data.nodes;
|
||||||
|
const allEdges = network.body.data.edges;
|
||||||
|
const nodeUpdates = [];
|
||||||
|
const edgeUpdates = [];
|
||||||
|
|
||||||
|
const nodesInViewIds = new Set(allNodes.getIds());
|
||||||
|
const highlightedNodesInView = new Set();
|
||||||
|
relatedNodeIds.forEach(id => {
|
||||||
|
if (nodesInViewIds.has(id)) {
|
||||||
|
highlightedNodesInView.add(id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (highlightedNodesInView.size === 0) {
|
||||||
|
alert('搜索到的记忆节点不在当前视图中。\n请尝试切换到“完整加载”模式或调整分页/聚类设置。');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找出连接高亮节点的边
|
||||||
|
const relatedEdgeIds = new Set();
|
||||||
|
allEdges.get().forEach(edge => {
|
||||||
|
if (highlightedNodesInView.has(edge.from) && highlightedNodesInView.has(edge.to)) {
|
||||||
|
relatedEdgeIds.add(edge.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新节点样式
|
||||||
|
allNodes.get().forEach(node => {
|
||||||
|
const originalNodeData = graphData.nodes.find(n => n.id === node.id);
|
||||||
|
if (!originalNodeData) return;
|
||||||
|
|
||||||
|
if (highlightedNodesInView.has(node.id)) {
|
||||||
|
nodeUpdates.push({
|
||||||
|
id: node.id,
|
||||||
|
color: nodeColors[node.group] || '#999',
|
||||||
|
opacity: 1.0,
|
||||||
|
label: originalNodeData.label || '',
|
||||||
|
font: { color: '#333', size: 14, strokeWidth: 2, strokeColor: 'white' }
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const originalColor = nodeColors[node.group] || '#999';
|
||||||
|
const dimmedColor = hexToRgba(originalColor, 0.1);
|
||||||
|
nodeUpdates.push({
|
||||||
|
id: node.id,
|
||||||
|
color: { background: dimmedColor, border: dimmedColor },
|
||||||
|
opacity: 0.1,
|
||||||
|
label: '',
|
||||||
|
font: { size: 0 }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新边样式
|
||||||
|
allEdges.get().forEach(edge => {
|
||||||
|
if (relatedEdgeIds.has(edge.id)) {
|
||||||
|
edgeUpdates.push({
|
||||||
|
id: edge.id,
|
||||||
|
color: { color: '#667eea', opacity: 1.0 },
|
||||||
|
width: 2.5
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
edgeUpdates.push({
|
||||||
|
id: edge.id,
|
||||||
|
color: { color: '#848484', opacity: 0.05 },
|
||||||
|
width: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
allNodes.update(nodeUpdates);
|
||||||
|
allEdges.update(edgeUpdates);
|
||||||
|
|
||||||
|
network.fit({
|
||||||
|
nodes: [...highlightedNodesInView],
|
||||||
|
animation: { duration: 800, easingFunction: 'easeInOutQuad' }
|
||||||
|
});
|
||||||
|
|
||||||
|
alert(`高亮了 ${highlightedNodesInView.size} 个相关节点。\n注意:如果处于聚类或分页模式,可能只显示部分节点。`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 高亮与选中节点连接的节点(优化版本,使用缓存)
|
// 高亮与选中节点连接的节点(优化版本,使用缓存)
|
||||||
|
|||||||
@@ -307,6 +307,7 @@ class AffinityInterestCalculator(BaseInterestCalculator):
|
|||||||
self.post_reply_boost_max_count - self.post_reply_boost_remaining
|
self.post_reply_boost_max_count - self.post_reply_boost_remaining
|
||||||
)
|
)
|
||||||
post_reply_reduction = self.post_reply_threshold_reduction * decay_factor
|
post_reply_reduction = self.post_reply_threshold_reduction * decay_factor
|
||||||
|
self.post_reply_boost_remaining -= 1
|
||||||
total_reduction += post_reply_reduction
|
total_reduction += post_reply_reduction
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"[阈值调整] 回复后降低: {post_reply_reduction:.3f} "
|
f"[阈值调整] 回复后降低: {post_reply_reduction:.3f} "
|
||||||
@@ -319,17 +320,6 @@ class AffinityInterestCalculator(BaseInterestCalculator):
|
|||||||
|
|
||||||
return adjusted_reply_threshold, adjusted_action_threshold
|
return adjusted_reply_threshold, adjusted_action_threshold
|
||||||
|
|
||||||
def _apply_no_reply_boost(self, base_score: float) -> float:
|
|
||||||
"""【已弃用】应用连续不回复的概率提升
|
|
||||||
|
|
||||||
注意:此方法已被 _apply_no_reply_threshold_adjustment 替代
|
|
||||||
保留用于向后兼容
|
|
||||||
"""
|
|
||||||
if self.no_reply_count > 0 and self.no_reply_count < self.max_no_reply_count:
|
|
||||||
boost = self.no_reply_count * self.probability_boost_per_no_reply
|
|
||||||
return min(1.0, base_score + boost)
|
|
||||||
return base_score
|
|
||||||
|
|
||||||
def _extract_keywords_from_database(self, message: "DatabaseMessages") -> list[str]:
|
def _extract_keywords_from_database(self, message: "DatabaseMessages") -> list[str]:
|
||||||
"""从数据库消息中提取关键词"""
|
"""从数据库消息中提取关键词"""
|
||||||
keywords = []
|
keywords = []
|
||||||
@@ -394,7 +384,7 @@ class AffinityInterestCalculator(BaseInterestCalculator):
|
|||||||
|
|
||||||
def on_reply_sent(self):
|
def on_reply_sent(self):
|
||||||
"""当机器人发送回复后调用,激活回复后阈值降低机制"""
|
"""当机器人发送回复后调用,激活回复后阈值降低机制"""
|
||||||
if self.enable_post_reply_boost:
|
if self.enable_post_reply_boost and not self.post_reply_boost_remaining:
|
||||||
# 重置回复后降低计数器
|
# 重置回复后降低计数器
|
||||||
self.post_reply_boost_remaining = self.post_reply_boost_max_count
|
self.post_reply_boost_remaining = self.post_reply_boost_max_count
|
||||||
logger.debug(
|
logger.debug(
|
||||||
|
|||||||
@@ -156,9 +156,6 @@ class ChatterActionPlanner:
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Focus模式 - 处理消息 {message.message_id} 失败: {e}")
|
logger.warning(f"Focus模式 - 处理消息 {message.message_id} 失败: {e}")
|
||||||
message.interest_value = 0.0
|
|
||||||
message.should_reply = False
|
|
||||||
message.should_act = False
|
|
||||||
|
|
||||||
# 2. 检查兴趣度是否达到非回复动作阈值
|
# 2. 检查兴趣度是否达到非回复动作阈值
|
||||||
non_reply_action_interest_threshold = global_config.affinity_flow.non_reply_action_interest_threshold
|
non_reply_action_interest_threshold = global_config.affinity_flow.non_reply_action_interest_threshold
|
||||||
@@ -202,9 +199,9 @@ class ChatterActionPlanner:
|
|||||||
filtered_plan = await plan_filter.filter(initial_plan)
|
filtered_plan = await plan_filter.filter(initial_plan)
|
||||||
|
|
||||||
# 检查reply动作是否可用
|
# 检查reply动作是否可用
|
||||||
reply_action_available = "reply" in available_actions or "respond" in available_actions
|
has_reply_action = "reply" in available_actions or "respond" in available_actions
|
||||||
if filtered_plan.decided_actions and not reply_action_available:
|
if filtered_plan.decided_actions and has_reply_action and reply_not_available:
|
||||||
logger.info("Focus模式 - 回复动作不可用,移除所有回复相关动作")
|
logger.info("Focus模式 - 未达到回复动作阈值,移除所有回复相关动作")
|
||||||
filtered_plan.decided_actions = [
|
filtered_plan.decided_actions = [
|
||||||
action for action in filtered_plan.decided_actions
|
action for action in filtered_plan.decided_actions
|
||||||
if action.action_type not in ["reply", "respond"]
|
if action.action_type not in ["reply", "respond"]
|
||||||
|
|||||||
Reference in New Issue
Block a user