Merge branch 'main' of https://github.com/xerrors/Yuxi-Know
This commit is contained in:
commit
8e7cf7c7a5
17
README.md
17
README.md
@ -1,6 +1,8 @@
|
|||||||
# 语析 - 基于大模型的知识库与知识图谱智能体开发平台
|
# 📚 语析 - 基于大模型的知识库与知识图谱智能体开发平台
|
||||||
|
|
||||||
<div>
|
|
||||||
|
<div align="center">
|
||||||
|
<a href="https://trendshift.io/repositories/15845" target="_blank"><img src="https://trendshift.io/api/badge/repositories/15845" alt="Yuxi-Know | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||||
|
|
||||||
[](https://github.com/xerrors/Yuxi-Know/tree/v0.3.0)
|
[](https://github.com/xerrors/Yuxi-Know/tree/v0.3.0)
|
||||||
[](https://github.com/xerrors/Yuxi-Know/blob/main/docker-compose.yml)
|
[](https://github.com/xerrors/Yuxi-Know/blob/main/docker-compose.yml)
|
||||||
@ -9,7 +11,11 @@
|
|||||||
[](https://deepwiki.com/xerrors/Yuxi-Know)
|
[](https://deepwiki.com/xerrors/Yuxi-Know)
|
||||||
[](https://zread.ai/xerrors/Yuxi-Know)
|
[](https://zread.ai/xerrors/Yuxi-Know)
|
||||||
[](https://www.bilibili.com/video/BV1DF14BTETq/)
|
[](https://www.bilibili.com/video/BV1DF14BTETq/)
|
||||||

|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
📄 [**文档中心**](https://xerrors.github.io/Yuxi-Know/) |
|
||||||
|
📽️ [**视频演示**](https://www.bilibili.com/video/BV1DF14BTETq/)
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -20,11 +26,6 @@
|
|||||||
|
|
||||||
<img width="1632" height="392" alt="image" src="https://github.com/user-attachments/assets/ec381fde-53dd-4845-a79f-116b823fe989" />
|
<img width="1632" height="392" alt="image" src="https://github.com/user-attachments/assets/ec381fde-53dd-4845-a79f-116b823fe989" />
|
||||||
|
|
||||||
|
|
||||||
> [!tip]
|
|
||||||
> 详细文档请查看全新的 📄[**文档中心**](https://xerrors.github.io/Yuxi-Know/),
|
|
||||||
> 哔哩哔哩观看 📽️[**视频演示**](https://www.bilibili.com/video/BV1DF14BTETq/)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**🎉 最新动态**
|
**🎉 最新动态**
|
||||||
|
|||||||
@ -53,4 +53,8 @@ YUXI_BRAND_FILE_PATH=src/config/static/info.local.yaml
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**此外**,`web/src/stores/theme.js` 中也包含了主题相关的配置(需要修改 `colorPrimary`),可根据需要修改。
|
**此外**,`web/src/stores/theme.js` 中也包含了主题相关的配置(需要修改 `colorPrimary`),可根据需要修改。
|
||||||
|
|
||||||
|
## 修改首页
|
||||||
|
|
||||||
|
首页提供了一个插槽组件 `web/src/components/ProjectOverview.vue`,可以在该组件中自定义项目介绍,当前为空文件。(借助 AI 编程可以设计出更好看的首页的)
|
||||||
|
|||||||
@ -362,38 +362,4 @@ async def get_database_mindmap(db_id: str, current_user: User = Depends(get_admi
|
|||||||
raise HTTPException(status_code=500, detail=f"获取思维导图失败: {str(e)}")
|
raise HTTPException(status_code=500, detail=f"获取思维导图失败: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
@mindmap.post("/database/{db_id}")
|
|
||||||
async def save_database_mindmap(
|
|
||||||
db_id: str,
|
|
||||||
mindmap: dict = Body(..., description="思维导图数据"),
|
|
||||||
current_user: User = Depends(get_admin_user),
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
保存思维导图到知识库
|
|
||||||
|
|
||||||
Args:
|
|
||||||
db_id: 知识库ID
|
|
||||||
mindmap: 思维导图数据
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
保存结果
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
# 检查知识库是否存在
|
|
||||||
db_info = knowledge_base.get_database_info(db_id)
|
|
||||||
if not db_info:
|
|
||||||
raise HTTPException(status_code=404, detail=f"知识库 {db_id} 不存在")
|
|
||||||
|
|
||||||
# TODO: 将思维导图保存到知识库元数据中
|
|
||||||
# 这里需要实现一个方法来更新知识库的元数据
|
|
||||||
|
|
||||||
return {
|
|
||||||
"message": "success",
|
|
||||||
"db_id": db_id,
|
|
||||||
}
|
|
||||||
|
|
||||||
except HTTPException:
|
|
||||||
raise
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"保存思维导图失败: {e}, {traceback.format_exc()}")
|
|
||||||
raise HTTPException(status_code=500, detail=f"保存思维导图失败: {str(e)}")
|
|
||||||
|
|||||||
@ -13,12 +13,16 @@ class Context(BaseContext):
|
|||||||
default_factory=list,
|
default_factory=list,
|
||||||
metadata={
|
metadata={
|
||||||
"name": "工具",
|
"name": "工具",
|
||||||
"options": gen_tool_info(get_tools()), # 这里的选择是所有的工具
|
"options": lambda: gen_tool_info(get_tools()), # 这里的选择是所有的工具
|
||||||
"description": "工具列表",
|
"description": "工具列表",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
mcps: list[str] = field(
|
mcps: list[str] = field(
|
||||||
default_factory=list,
|
default_factory=list,
|
||||||
metadata={"name": "MCP服务器", "options": list(MCP_SERVERS.keys()), "description": "MCP服务器列表"},
|
metadata={
|
||||||
|
"name": "MCP服务器",
|
||||||
|
"options": lambda: list(MCP_SERVERS.keys()),
|
||||||
|
"description": "MCP服务器列表",
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import requests
|
|||||||
from langchain.tools import tool
|
from langchain.tools import tool
|
||||||
|
|
||||||
from src.agents.common import get_buildin_tools
|
from src.agents.common import get_buildin_tools
|
||||||
from src.agents.common.toolkits.mysql import get_mysql_tools
|
|
||||||
from src.storage.minio import aupload_file_to_minio
|
from src.storage.minio import aupload_file_to_minio
|
||||||
from src.utils import logger
|
from src.utils import logger
|
||||||
|
|
||||||
@ -52,5 +51,4 @@ def get_tools() -> list[Any]:
|
|||||||
"""获取所有可运行的工具(给大模型使用)"""
|
"""获取所有可运行的工具(给大模型使用)"""
|
||||||
tools = get_buildin_tools()
|
tools = get_buildin_tools()
|
||||||
tools.append(text_to_img_demo)
|
tools.append(text_to_img_demo)
|
||||||
tools.extend(get_mysql_tools())
|
|
||||||
return tools
|
return tools
|
||||||
|
|||||||
@ -107,10 +107,14 @@ class BaseContext:
|
|||||||
# 提取 Annotated 的元数据
|
# 提取 Annotated 的元数据
|
||||||
template_metadata = cls._extract_template_metadata(field_type)
|
template_metadata = cls._extract_template_metadata(field_type)
|
||||||
|
|
||||||
|
options = f.metadata.get("options", [])
|
||||||
|
if callable(options):
|
||||||
|
options = options()
|
||||||
|
|
||||||
configurable_items[f.name] = {
|
configurable_items[f.name] = {
|
||||||
"type": type_name,
|
"type": type_name,
|
||||||
"name": f.metadata.get("name", f.name),
|
"name": f.metadata.get("name", f.name),
|
||||||
"options": f.metadata.get("options", []),
|
"options": options,
|
||||||
"default": f.default
|
"default": f.default
|
||||||
if f.default is not MISSING
|
if f.default is not MISSING
|
||||||
else f.default_factory()
|
else f.default_factory()
|
||||||
|
|||||||
@ -16,15 +16,15 @@ branding:
|
|||||||
|
|
||||||
features:
|
features:
|
||||||
- label: "GitHub Stars"
|
- label: "GitHub Stars"
|
||||||
value: "2600+"
|
value: "3000+"
|
||||||
description: "开发者社区的认可与支持"
|
description: "开发者社区的认可与支持"
|
||||||
icon: "stars"
|
icon: "stars"
|
||||||
- label: "已解决 Issues"
|
- label: "已解决 Issues"
|
||||||
value: "210+"
|
value: "250+"
|
||||||
description: "持续改进和问题解决能力"
|
description: "持续改进和问题解决能力"
|
||||||
icon: "issues"
|
icon: "issues"
|
||||||
- label: "累计 Commits"
|
- label: "累计 Commits"
|
||||||
value: "1000+"
|
value: "1100+"
|
||||||
description: "活跃的开发迭代和功能更新"
|
description: "活跃的开发迭代和功能更新"
|
||||||
icon: "commits"
|
icon: "commits"
|
||||||
- label: "开源协议"
|
- label: "开源协议"
|
||||||
|
|||||||
@ -22,9 +22,6 @@ except ImportError:
|
|||||||
def validate_database_schema(db_path):
|
def validate_database_schema(db_path):
|
||||||
return True, []
|
return True, []
|
||||||
|
|
||||||
# TODO:[已完成]为DBManager添加异步支持
|
|
||||||
# TODO:[已完成]为DBManager添加单例模式
|
|
||||||
|
|
||||||
|
|
||||||
class DBManager(metaclass=SingletonMeta):
|
class DBManager(metaclass=SingletonMeta):
|
||||||
"""数据库管理器 - 提供异步数据库连接和会话管理"""
|
"""数据库管理器 - 提供异步数据库连接和会话管理"""
|
||||||
|
|||||||
@ -49,18 +49,6 @@ export const mindmapApi = {
|
|||||||
*/
|
*/
|
||||||
getByDatabase: async (dbId) => {
|
getByDatabase: async (dbId) => {
|
||||||
return apiAdminGet(`/api/mindmap/database/${dbId}`)
|
return apiAdminGet(`/api/mindmap/database/${dbId}`)
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 保存思维导图到知识库
|
|
||||||
* @param {string} dbId - 知识库ID
|
|
||||||
* @param {Object} mindmapData - 思维导图数据
|
|
||||||
* @returns {Promise} - 保存结果
|
|
||||||
*/
|
|
||||||
saveToDatabase: async (dbId, mindmapData) => {
|
|
||||||
return apiAdminPost(`/api/mindmap/database/${dbId}`, {
|
|
||||||
mindmap: mindmapData
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1043,7 +1043,7 @@ watch(conversations, () => {
|
|||||||
|
|
||||||
.floating-sidebar {
|
.floating-sidebar {
|
||||||
position: absolute !important;
|
position: absolute !important;
|
||||||
z-index: 100;
|
z-index: 101;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|||||||
@ -221,10 +221,6 @@
|
|||||||
<a-button @click="saveConfig" class="save-btn" :class="{'changed': agentStore.hasConfigChanges}">
|
<a-button @click="saveConfig" class="save-btn" :class="{'changed': agentStore.hasConfigChanges}">
|
||||||
保存配置
|
保存配置
|
||||||
</a-button>
|
</a-button>
|
||||||
<!-- TODO:BUG 目前有 bug 暂时不展示 -->
|
|
||||||
<!-- <a-button @click="resetConfig" class="reset-btn">
|
|
||||||
重置
|
|
||||||
</a-button> -->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -424,6 +420,10 @@ const getToolNameById = (toolId) => {
|
|||||||
const openToolsModal = async () => {
|
const openToolsModal = async () => {
|
||||||
console.log("availableTools.value", availableTools.value)
|
console.log("availableTools.value", availableTools.value)
|
||||||
try {
|
try {
|
||||||
|
// 强制刷新智能体详情以获取最新工具列表
|
||||||
|
if (selectedAgentId.value) {
|
||||||
|
await agentStore.fetchAgentDetail(selectedAgentId.value, true);
|
||||||
|
}
|
||||||
selectedTools.value = [...(agentConfig.value?.tools || [])];
|
selectedTools.value = [...(agentConfig.value?.tools || [])];
|
||||||
toolsModalOpen.value = true;
|
toolsModalOpen.value = true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -39,17 +39,13 @@
|
|||||||
:key="index"
|
:key="index"
|
||||||
class="todo-item"
|
class="todo-item"
|
||||||
>
|
>
|
||||||
<span class="todo-status" :class="todo.status">
|
<div class="todo-status">
|
||||||
<svg v-if="todo.status === 'completed'" viewBox="0 0 24 24" fill="currentColor" class="icon">
|
<CheckCircleOutlined v-if="todo.status === 'completed'" class="icon completed" />
|
||||||
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
|
<SyncOutlined v-else-if="todo.status === 'in_progress'" class="icon in-progress" spin />
|
||||||
</svg>
|
<ClockCircleOutlined v-else-if="todo.status === 'pending'" class="icon pending" />
|
||||||
<svg v-else-if="todo.status === 'in_progress'" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="icon spinning">
|
<CloseCircleOutlined v-else-if="todo.status === 'cancelled'" class="icon cancelled" />
|
||||||
<circle cx="12" cy="12" r="10" stroke-dasharray="31.416" stroke-dashoffset="31.416" stroke-linecap="round"/>
|
<QuestionCircleOutlined v-else class="icon unknown" />
|
||||||
</svg>
|
</div>
|
||||||
<svg v-else viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="icon">
|
|
||||||
<circle cx="12" cy="12" r="10"/>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
<span class="todo-text">{{ todo.content }}</span>
|
<span class="todo-text">{{ todo.content }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -110,6 +106,13 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref, watch } from 'vue';
|
import { computed, ref, watch } from 'vue';
|
||||||
import { Download } from 'lucide-vue-next';
|
import { Download } from 'lucide-vue-next';
|
||||||
|
import {
|
||||||
|
CheckCircleOutlined,
|
||||||
|
SyncOutlined,
|
||||||
|
ClockCircleOutlined,
|
||||||
|
CloseCircleOutlined,
|
||||||
|
QuestionCircleOutlined
|
||||||
|
} from '@ant-design/icons-vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
visible: {
|
visible: {
|
||||||
@ -367,59 +370,19 @@ const emitRefresh = () => {
|
|||||||
|
|
||||||
.todo-status {
|
.todo-status {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
border-radius: 50%;
|
|
||||||
transition: all 0.15s ease;
|
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
width: 14px;
|
font-size: 16px;
|
||||||
height: 14px;
|
|
||||||
transition: all 0.15s ease;
|
&.completed { color: #52c41a; }
|
||||||
}
|
&.in-progress { color: #1890ff; }
|
||||||
|
&.pending { color: #faad14; }
|
||||||
.spinning {
|
&.cancelled { color: #ff4d4f; }
|
||||||
animation: spin 1.5s linear infinite;
|
&.unknown { color: var(--gray-400); }
|
||||||
stroke-dasharray: 31.416;
|
|
||||||
stroke-dashoffset: 0;
|
|
||||||
animation: spin 1.5s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.completed {
|
|
||||||
background: var(--color-success-50);
|
|
||||||
color: var(--color-success-700);
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
transform: scale(1.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.in_progress {
|
|
||||||
background: var(--color-warning-50);
|
|
||||||
color: var(--color-warning-700);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.pending {
|
|
||||||
background: var(--gray-100);
|
|
||||||
color: var(--gray-500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
0% {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
stroke-dashoffset: 0;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
stroke-dashoffset: 15.708;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
stroke-dashoffset: 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
web/src/components/ProjectOverview.vue
Normal file
2
web/src/components/ProjectOverview.vue
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<template>
|
||||||
|
</template>
|
||||||
118
web/src/components/ToolCallingResult/TodoListResult.vue
Normal file
118
web/src/components/ToolCallingResult/TodoListResult.vue
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
<template>
|
||||||
|
<div class="todo-list-result">
|
||||||
|
<!-- <div class="todo-header">
|
||||||
|
<h4><UnorderedListOutlined /> 待办事项</h4>
|
||||||
|
</div> -->
|
||||||
|
<div class="todo-content">
|
||||||
|
<div v-for="(item, index) in data" :key="index" class="todo-item">
|
||||||
|
<div class="todo-status">
|
||||||
|
<CheckCircleOutlined v-if="item.status === 'completed'" class="icon completed" />
|
||||||
|
<SyncOutlined v-else-if="item.status === 'in_progress'" class="icon in-progress" spin />
|
||||||
|
<ClockCircleOutlined v-else-if="item.status === 'pending'" class="icon pending" />
|
||||||
|
<CloseCircleOutlined v-else-if="item.status === 'cancelled'" class="icon cancelled" />
|
||||||
|
<QuestionCircleOutlined v-else class="icon unknown" />
|
||||||
|
</div>
|
||||||
|
<div class="todo-text" :class="{ completed: item.status === 'completed', cancelled: item.status === 'cancelled' }">
|
||||||
|
{{ item.content }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
UnorderedListOutlined,
|
||||||
|
CheckCircleOutlined,
|
||||||
|
SyncOutlined,
|
||||||
|
ClockCircleOutlined,
|
||||||
|
CloseCircleOutlined,
|
||||||
|
QuestionCircleOutlined
|
||||||
|
} from '@ant-design/icons-vue'
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
data: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.todo-list-result {
|
||||||
|
background: var(--gray-0);
|
||||||
|
border-radius: 8px;
|
||||||
|
// border: 1px solid var(--gray-200);
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.todo-header {
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-bottom: 1px solid var(--gray-100);
|
||||||
|
background: var(--gray-25);
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--main-color);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-content {
|
||||||
|
padding: 12px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--gray-50);
|
||||||
|
border: 1px solid var(--gray-100);
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--gray-100);
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-status {
|
||||||
|
padding-top: 2px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
font-size: 16px;
|
||||||
|
|
||||||
|
&.completed { color: #52c41a; }
|
||||||
|
&.in-progress { color: #1890ff; }
|
||||||
|
&.pending { color: #faad14; }
|
||||||
|
&.cancelled { color: #ff4d4f; }
|
||||||
|
&.unknown { color: var(--gray-400); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.todo-text {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--gray-800);
|
||||||
|
word-break: break-word;
|
||||||
|
|
||||||
|
&.completed {
|
||||||
|
text-decoration: line-through;
|
||||||
|
color: var(--gray-500);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.cancelled {
|
||||||
|
text-decoration: line-through;
|
||||||
|
color: var(--gray-400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -19,6 +19,12 @@
|
|||||||
ref="graphResultRef"
|
ref="graphResultRef"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- 待办事项结果 -->
|
||||||
|
<TodoListResult
|
||||||
|
v-else-if="isTodoListResult"
|
||||||
|
:data="todoListData"
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- 计算器结果 -->
|
<!-- 计算器结果 -->
|
||||||
<CalculatorResult
|
<CalculatorResult
|
||||||
v-else-if="isCalculatorResult"
|
v-else-if="isCalculatorResult"
|
||||||
@ -49,6 +55,7 @@ import WebSearchResult from './WebSearchResult.vue'
|
|||||||
import KnowledgeBaseResult from './KnowledgeBaseResult.vue'
|
import KnowledgeBaseResult from './KnowledgeBaseResult.vue'
|
||||||
import KnowledgeGraphResult from './KnowledgeGraphResult.vue'
|
import KnowledgeGraphResult from './KnowledgeGraphResult.vue'
|
||||||
import CalculatorResult from './CalculatorResult.vue'
|
import CalculatorResult from './CalculatorResult.vue'
|
||||||
|
import TodoListResult from './TodoListResult.vue'
|
||||||
import { useAgentStore } from '@/stores/agent';
|
import { useAgentStore } from '@/stores/agent';
|
||||||
|
|
||||||
const agentStore = useAgentStore()
|
const agentStore = useAgentStore()
|
||||||
@ -81,6 +88,60 @@ const parsedData = computed(() => {
|
|||||||
return props.resultContent
|
return props.resultContent
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const todoListData = computed(() => {
|
||||||
|
if (props.toolName !== 'write_todos') return []
|
||||||
|
|
||||||
|
const raw = props.resultContent
|
||||||
|
|
||||||
|
// 1. Try from parsedData (JSON object)
|
||||||
|
const data = parsedData.value
|
||||||
|
if (data && typeof data === 'object') {
|
||||||
|
if (Array.isArray(data)) return data
|
||||||
|
if (data.todos && Array.isArray(data.todos)) return data.todos
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Try parsing string if it matches specific pattern
|
||||||
|
if (typeof raw === 'string') {
|
||||||
|
let str = raw
|
||||||
|
if (str.startsWith('Updated todo list to ')) {
|
||||||
|
str = str.replace('Updated todo list to ', '')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try regex parsing for Python-like string
|
||||||
|
const items = []
|
||||||
|
// Matches {'content': '...', 'status': '...'} with escaped quotes support
|
||||||
|
// content might contain escaped quotes
|
||||||
|
const contentRegex = /'content':\s*'((?:[^'\\]|\\.)*)'/
|
||||||
|
const statusRegex = /'status':\s*'((?:[^'\\]|\\.)*)'/
|
||||||
|
|
||||||
|
// Split by "}, {" roughly, or just look for objects
|
||||||
|
// Since it is a list of dicts, we can match individual dicts
|
||||||
|
const dictRegex = /\{.*?\}/g
|
||||||
|
const dictMatches = str.match(dictRegex)
|
||||||
|
|
||||||
|
if (dictMatches) {
|
||||||
|
for (const dictStr of dictMatches) {
|
||||||
|
const contentMatch = dictStr.match(contentRegex)
|
||||||
|
const statusMatch = dictStr.match(statusRegex)
|
||||||
|
|
||||||
|
if (contentMatch && statusMatch) {
|
||||||
|
items.push({
|
||||||
|
content: contentMatch[1].replace(/\\'/g, "'").replace(/\\\\/g, "\\"),
|
||||||
|
status: statusMatch[1]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (items.length > 0) return items
|
||||||
|
}
|
||||||
|
|
||||||
|
return []
|
||||||
|
})
|
||||||
|
|
||||||
|
const isTodoListResult = computed(() => {
|
||||||
|
return props.toolName === 'write_todos' && todoListData.value.length > 0
|
||||||
|
})
|
||||||
|
|
||||||
// 判断是否为网页搜索结果
|
// 判断是否为网页搜索结果
|
||||||
const isWebSearchResult = computed(() => {
|
const isWebSearchResult = computed(() => {
|
||||||
const toolNameLower = props.toolName.toLowerCase()
|
const toolNameLower = props.toolName.toLowerCase()
|
||||||
|
|||||||
@ -146,11 +146,11 @@ export const useAgentStore = defineStore('agent', () => {
|
|||||||
* 获取单个智能体的详细信息(包含配置选项)
|
* 获取单个智能体的详细信息(包含配置选项)
|
||||||
* @param {string} agentId - 智能体ID
|
* @param {string} agentId - 智能体ID
|
||||||
*/
|
*/
|
||||||
async function fetchAgentDetail(agentId) {
|
async function fetchAgentDetail(agentId, forceRefresh = false) {
|
||||||
if (!agentId) return
|
if (!agentId) return
|
||||||
|
|
||||||
// 如果已经缓存了详细信息,直接返回
|
// 如果已经缓存了详细信息且不强制刷新,直接返回
|
||||||
if (agentDetails.value[agentId]) {
|
if (!forceRefresh && agentDetails.value[agentId]) {
|
||||||
return agentDetails.value[agentId]
|
return agentDetails.value[agentId]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -27,17 +27,17 @@ export const useInfoStore = defineStore('info', () => {
|
|||||||
// 计算属性 - 功能特性
|
// 计算属性 - 功能特性
|
||||||
const features = computed(() => infoConfig.value.features || [{
|
const features = computed(() => infoConfig.value.features || [{
|
||||||
label: "GitHub Stars",
|
label: "GitHub Stars",
|
||||||
value: "2600+",
|
value: "3000+",
|
||||||
description: "开发者社区的认可与支持",
|
description: "开发者社区的认可与支持",
|
||||||
icon: "stars"
|
icon: "stars"
|
||||||
}, {
|
}, {
|
||||||
label: "已解决 Issues",
|
label: "已解决 Issues",
|
||||||
value: "200+",
|
value: "250+",
|
||||||
description: "持续改进和问题解决能力",
|
description: "持续改进和问题解决能力",
|
||||||
icon: "issues"
|
icon: "issues"
|
||||||
}, {
|
}, {
|
||||||
label: "累计 Commits",
|
label: "累计 Commits",
|
||||||
value: "1000+",
|
value: "1100+",
|
||||||
description: "活跃的开发迭代和功能更新",
|
description: "活跃的开发迭代和功能更新",
|
||||||
icon: "commits"
|
icon: "commits"
|
||||||
}, {
|
}, {
|
||||||
|
|||||||
@ -75,6 +75,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ProjectOverview />
|
||||||
|
|
||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
<div class="footer-content">
|
<div class="footer-content">
|
||||||
<p class="copyright">{{ infoStore.footer?.copyright || '© 2025 All rights reserved' }}</p>
|
<p class="copyright">{{ infoStore.footer?.copyright || '© 2025 All rights reserved' }}</p>
|
||||||
@ -91,6 +93,7 @@ import { useInfoStore } from '@/stores/info'
|
|||||||
import { useAgentStore } from '@/stores/agent'
|
import { useAgentStore } from '@/stores/agent'
|
||||||
import { useThemeStore } from '@/stores/theme'
|
import { useThemeStore } from '@/stores/theme'
|
||||||
import UserInfoComponent from '@/components/UserInfoComponent.vue'
|
import UserInfoComponent from '@/components/UserInfoComponent.vue'
|
||||||
|
import ProjectOverview from '@/components/ProjectOverview.vue'
|
||||||
import {
|
import {
|
||||||
BookText,
|
BookText,
|
||||||
Bug,
|
Bug,
|
||||||
@ -218,7 +221,7 @@ const actionLinks = computed(() => {
|
|||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.home-container {
|
.home-container {
|
||||||
height: 100vh;
|
min-height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
color: var(--main-900);
|
color: var(--main-900);
|
||||||
@ -226,7 +229,6 @@ const actionLinks = computed(() => {
|
|||||||
position: relative;
|
position: relative;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.glass-header {
|
.glass-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@ -266,8 +268,6 @@ const actionLinks = computed(() => {
|
|||||||
&:hover {
|
&:hover {
|
||||||
color: var(--gray-900);
|
color: var(--gray-900);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
transform: scale(1.1);
|
transform: scale(1.1);
|
||||||
}
|
}
|
||||||
@ -511,7 +511,7 @@ const actionLinks = computed(() => {
|
|||||||
.section {
|
.section {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 1200px;
|
max-width: 1200px;
|
||||||
margin: 0 auto;
|
margin: 50px auto 0px auto;
|
||||||
padding: 2rem 0;
|
padding: 2rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user