server session initial
This commit is contained in:
parent
988426a5f5
commit
fbbefe0a63
24
src/models/token_model.py
Normal file
24
src/models/token_model.py
Normal file
@ -0,0 +1,24 @@
|
||||
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.sql import func
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
class AgentToken(Base):
|
||||
"""智能体访问令牌模型"""
|
||||
__tablename__ = 'agent_tokens'
|
||||
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
agent_id = Column(String, nullable=False, index=True) # 智能体ID
|
||||
name = Column(String, nullable=False) # 令牌名称
|
||||
token = Column(String, nullable=False, unique=True) # 令牌值
|
||||
created_at = Column(DateTime, default=func.now()) # 创建时间
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"id": self.id,
|
||||
"agent_id": self.agent_id,
|
||||
"name": self.name,
|
||||
"token": self.token,
|
||||
"created_at": self.created_at.isoformat() if self.created_at else None
|
||||
}
|
||||
@ -3,9 +3,11 @@ from src.routers.chat_router import chat
|
||||
from src.routers.data_router import data
|
||||
from src.routers.base_router import base
|
||||
from src.routers.tool_router import tool
|
||||
from src.routers.admin_router import admin
|
||||
|
||||
router = APIRouter()
|
||||
router.include_router(base)
|
||||
router.include_router(chat)
|
||||
router.include_router(data)
|
||||
router.include_router(tool)
|
||||
router.include_router(admin)
|
||||
|
||||
83
src/routers/admin_router.py
Normal file
83
src/routers/admin_router.py
Normal file
@ -0,0 +1,83 @@
|
||||
import secrets
|
||||
import string
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Optional
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from src.utils.db_manager import db_manager
|
||||
from src.models.token_model import AgentToken
|
||||
|
||||
admin = APIRouter(prefix="/admin", tags=["admin"])
|
||||
|
||||
# 依赖项:获取数据库会话
|
||||
def get_db():
|
||||
db = db_manager.get_session()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
# 请求和响应模型
|
||||
class TokenCreate(BaseModel):
|
||||
agent_id: str
|
||||
name: str
|
||||
|
||||
class TokenResponse(BaseModel):
|
||||
id: int
|
||||
agent_id: str
|
||||
name: str
|
||||
token: str
|
||||
created_at: str
|
||||
|
||||
# 生成随机token
|
||||
def generate_token(length=32):
|
||||
alphabet = string.ascii_letters + string.digits
|
||||
return ''.join(secrets.choice(alphabet) for _ in range(length))
|
||||
|
||||
@admin.get("/tokens", response_model=List[TokenResponse])
|
||||
async def get_agent_tokens(
|
||||
agent_id: Optional[str] = Query(None),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""获取智能体的token列表"""
|
||||
query = db.query(AgentToken)
|
||||
if agent_id:
|
||||
query = query.filter(AgentToken.agent_id == agent_id)
|
||||
tokens = query.all()
|
||||
return [token.to_dict() for token in tokens]
|
||||
|
||||
@admin.post("/tokens", response_model=TokenResponse)
|
||||
async def create_token(
|
||||
token_data: TokenCreate,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""创建新的token"""
|
||||
# 生成随机token
|
||||
token_value = generate_token()
|
||||
|
||||
# 创建token记录
|
||||
new_token = AgentToken(
|
||||
agent_id=token_data.agent_id,
|
||||
name=token_data.name,
|
||||
token=token_value
|
||||
)
|
||||
|
||||
# 保存到数据库
|
||||
db.add(new_token)
|
||||
db.commit()
|
||||
db.refresh(new_token)
|
||||
|
||||
return new_token.to_dict()
|
||||
|
||||
@admin.delete("/tokens/{token_id}", response_model=dict)
|
||||
async def delete_token(token_id: int, db: Session = Depends(get_db)):
|
||||
"""删除token"""
|
||||
token = db.query(AgentToken).filter(AgentToken.id == token_id).first()
|
||||
if not token:
|
||||
raise HTTPException(status_code=404, detail="Token not found")
|
||||
|
||||
db.delete(token)
|
||||
db.commit()
|
||||
|
||||
return {"success": True, "message": "Token deleted"}
|
||||
@ -3,19 +3,49 @@ import json
|
||||
import asyncio
|
||||
import traceback
|
||||
import uuid
|
||||
from fastapi import APIRouter, Body
|
||||
from fastapi import APIRouter, Body, Depends, HTTPException
|
||||
from fastapi.responses import StreamingResponse
|
||||
from langchain_core.messages import AIMessageChunk
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from src import executor, config, retriever
|
||||
from src.core import HistoryManager
|
||||
from src.agents import agent_manager
|
||||
from src.models import select_model
|
||||
from src.utils.logging_config import logger
|
||||
from src.utils.db_manager import db_manager
|
||||
from src.models.token_model import AgentToken
|
||||
|
||||
chat = APIRouter(prefix="/chat")
|
||||
|
||||
# 依赖项:获取数据库会话
|
||||
def get_db():
|
||||
db = db_manager.get_session()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
class TokenVerify(BaseModel):
|
||||
agent_id: str
|
||||
token: str
|
||||
|
||||
@chat.post("/verify_token")
|
||||
async def verify_agent_token(
|
||||
token_data: TokenVerify,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""验证智能体访问令牌"""
|
||||
token = db.query(AgentToken).filter(
|
||||
AgentToken.agent_id == token_data.agent_id,
|
||||
AgentToken.token == token_data.token
|
||||
).first()
|
||||
|
||||
if not token:
|
||||
raise HTTPException(status_code=401, detail="Invalid token")
|
||||
|
||||
return {"success": True, "message": "Token verified"}
|
||||
|
||||
@chat.get("/")
|
||||
async def chat_get():
|
||||
|
||||
40
src/utils/db_manager.py
Normal file
40
src/utils/db_manager.py
Normal file
@ -0,0 +1,40 @@
|
||||
import os
|
||||
import sqlite3
|
||||
import pathlib
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
|
||||
from src.models.token_model import Base, AgentToken
|
||||
|
||||
class DBManager:
|
||||
"""数据库管理器"""
|
||||
|
||||
def __init__(self):
|
||||
self.db_path = os.path.join("saves", "data", "server.db")
|
||||
self.ensure_db_dir()
|
||||
|
||||
# 创建SQLAlchemy引擎
|
||||
self.engine = create_engine(f"sqlite:///{self.db_path}")
|
||||
|
||||
# 创建会话工厂
|
||||
self.Session = sessionmaker(bind=self.engine)
|
||||
|
||||
# 确保表存在
|
||||
self.create_tables()
|
||||
|
||||
def ensure_db_dir(self):
|
||||
"""确保数据库目录存在"""
|
||||
db_dir = os.path.dirname(self.db_path)
|
||||
pathlib.Path(db_dir).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
def create_tables(self):
|
||||
"""创建数据库表"""
|
||||
Base.metadata.create_all(self.engine)
|
||||
|
||||
def get_session(self):
|
||||
"""获取数据库会话"""
|
||||
return self.Session()
|
||||
|
||||
# 创建全局数据库管理器实例
|
||||
db_manager = DBManager()
|
||||
253
web/src/components/TokenManagerComponent.vue
Normal file
253
web/src/components/TokenManagerComponent.vue
Normal file
@ -0,0 +1,253 @@
|
||||
<template>
|
||||
<div class="token-manager">
|
||||
<h2 class="manager-title">访问令牌管理</h2>
|
||||
<div class="token-tools">
|
||||
<a-button type="primary" size="small" @click="showAddTokenModal">
|
||||
<PlusOutlined /> 创建令牌
|
||||
</a-button>
|
||||
</div>
|
||||
<!-- 令牌列表 -->
|
||||
<div class="token-list" v-if="tokens.length > 0">
|
||||
<a-spin :spinning="loading">
|
||||
<a-list size="small">
|
||||
<a-list-item v-for="token in tokens" :key="token.id">
|
||||
<div class="token-item">
|
||||
<div class="token-info">
|
||||
<div class="token-name">{{ token.name }}</div>
|
||||
<div class="token-value">
|
||||
<code>{{ token.token }}</code>
|
||||
<a-button type="link" size="small" @click="copyToken(token.token)">
|
||||
<CopyOutlined />
|
||||
</a-button>
|
||||
</div>
|
||||
<div class="token-time">创建时间: {{ formatDate(token.created_at) }}</div>
|
||||
</div>
|
||||
<div class="token-actions">
|
||||
<a-popconfirm
|
||||
title="确定要删除这个令牌吗?"
|
||||
ok-text="确定"
|
||||
cancel-text="取消"
|
||||
@confirm="deleteToken(token.id)"
|
||||
>
|
||||
<a-button type="text" danger size="small">
|
||||
<DeleteOutlined />
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
</div>
|
||||
</div>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</a-spin>
|
||||
</div>
|
||||
<a-empty v-else description="暂无访问令牌" :image="Empty.PRESENTED_IMAGE_SIMPLE" />
|
||||
|
||||
<!-- 添加令牌弹窗 -->
|
||||
<a-modal
|
||||
v-model:visible="addTokenModalVisible"
|
||||
title="添加访问令牌"
|
||||
ok-text="创建"
|
||||
cancel-text="取消"
|
||||
@ok="createToken"
|
||||
>
|
||||
<a-form :model="newToken" layout="vertical">
|
||||
<a-form-item label="令牌名称" name="name">
|
||||
<a-input v-model:value="newToken.name" placeholder="请输入令牌名称" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
import { message, Empty } from 'ant-design-vue';
|
||||
import { PlusOutlined, DeleteOutlined, CopyOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
const props = defineProps({
|
||||
agentId: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
// 状态
|
||||
const tokens = ref([]);
|
||||
const loading = ref(false);
|
||||
const addTokenModalVisible = ref(false);
|
||||
const newToken = ref({
|
||||
name: ''
|
||||
});
|
||||
|
||||
// 获取令牌列表
|
||||
const fetchTokens = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const response = await fetch(`/api/admin/tokens?agent_id=${props.agentId}`);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
tokens.value = data;
|
||||
} else {
|
||||
message.error('获取令牌列表失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取令牌列表出错:', error);
|
||||
message.error('获取令牌列表出错');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 创建新令牌
|
||||
const createToken = async () => {
|
||||
if (!newToken.value.name.trim()) {
|
||||
message.warning('请输入令牌名称');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/admin/tokens', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
agent_id: props.agentId,
|
||||
name: newToken.value.name
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
tokens.value.push(data);
|
||||
message.success('令牌创建成功');
|
||||
addTokenModalVisible.value = false;
|
||||
newToken.value.name = '';
|
||||
} else {
|
||||
message.error('创建令牌失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('创建令牌出错:', error);
|
||||
message.error('创建令牌出错');
|
||||
}
|
||||
};
|
||||
|
||||
// 删除令牌
|
||||
const deleteToken = async (tokenId) => {
|
||||
try {
|
||||
const response = await fetch(`/api/admin/tokens/${tokenId}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
tokens.value = tokens.value.filter(token => token.id !== tokenId);
|
||||
message.success('令牌已删除');
|
||||
} else {
|
||||
message.error('删除令牌失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除令牌出错:', error);
|
||||
message.error('删除令牌出错');
|
||||
}
|
||||
};
|
||||
|
||||
// 复制令牌到剪贴板
|
||||
const copyToken = (token) => {
|
||||
navigator.clipboard.writeText(token).then(() => {
|
||||
message.success('令牌已复制到剪贴板');
|
||||
});
|
||||
};
|
||||
|
||||
// 显示添加令牌弹窗
|
||||
const showAddTokenModal = () => {
|
||||
newToken.value.name = '';
|
||||
addTokenModalVisible.value = true;
|
||||
};
|
||||
|
||||
// 格式化日期
|
||||
const formatDate = (dateString) => {
|
||||
if (!dateString) return '';
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleString();
|
||||
};
|
||||
|
||||
// 监听agentId变化
|
||||
watch(() => props.agentId, (newAgentId) => {
|
||||
if (newAgentId) {
|
||||
fetchTokens();
|
||||
} else {
|
||||
tokens.value = [];
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
// 组件挂载时获取令牌列表
|
||||
onMounted(() => {
|
||||
if (props.agentId) {
|
||||
fetchTokens();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.token-manager {
|
||||
margin-top: 1rem;
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
|
||||
.manager-title {
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.token-tools {
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.token-list {
|
||||
max-height: calc(100vh - 400px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.token-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.token-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.token-name {
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.token-value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.8rem;
|
||||
background-color: var(--main-light-4);
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 0.25rem;
|
||||
overflow-x: auto;
|
||||
user-select: all;
|
||||
|
||||
code {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.token-time {
|
||||
font-size: 0.75rem;
|
||||
color: var(--gray-500);
|
||||
}
|
||||
|
||||
.token-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
@ -1,16 +1,106 @@
|
||||
<template>
|
||||
<div class="agent-single-view">
|
||||
<AgentChatComponent :agent-id="agentId" />
|
||||
<!-- Token验证弹窗 -->
|
||||
<a-modal
|
||||
v-model:visible="tokenModalVisible"
|
||||
title="访问验证"
|
||||
:closable="false"
|
||||
:maskClosable="false"
|
||||
:keyboard="false"
|
||||
:footer="null"
|
||||
>
|
||||
<div class="token-verify-form">
|
||||
<p>需要输入访问令牌才能使用该智能体</p>
|
||||
<a-input-password
|
||||
v-model:value="tokenInput"
|
||||
placeholder="请输入访问令牌"
|
||||
@pressEnter="verifyToken"
|
||||
/>
|
||||
<div class="error-message" v-if="errorMessage">{{ errorMessage }}</div>
|
||||
<div class="token-actions">
|
||||
<a-button type="primary" :loading="verifying" @click="verifyToken">验证</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
|
||||
<!-- 智能体聊天界面 -->
|
||||
<AgentChatComponent v-if="isVerified" :agent-id="agentId" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import AgentChatComponent from '@/components/AgentChatComponent.vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const agentId = computed(() => route.params.agent_id);
|
||||
|
||||
// Token验证相关状态
|
||||
const tokenModalVisible = ref(false);
|
||||
const tokenInput = ref('');
|
||||
const isVerified = ref(false);
|
||||
const verifying = ref(false);
|
||||
const errorMessage = ref('');
|
||||
|
||||
// 验证Token
|
||||
const verifyToken = async () => {
|
||||
if (!tokenInput.value) {
|
||||
errorMessage.value = '请输入访问令牌';
|
||||
return;
|
||||
}
|
||||
|
||||
verifying.value = true;
|
||||
errorMessage.value = '';
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/chat/verify_token', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
agent_id: agentId.value,
|
||||
token: tokenInput.value
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
// 验证成功
|
||||
isVerified.value = true;
|
||||
tokenModalVisible.value = false;
|
||||
|
||||
// 保存验证状态到localStorage
|
||||
localStorage.setItem(`agent-token-verified-${agentId.value}`, 'true');
|
||||
} else {
|
||||
// 验证失败
|
||||
const data = await response.json();
|
||||
errorMessage.value = data.detail || '令牌验证失败';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('验证令牌出错:', error);
|
||||
errorMessage.value = '验证令牌时发生错误';
|
||||
} finally {
|
||||
verifying.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 检查是否已经验证
|
||||
const checkVerification = () => {
|
||||
const verified = localStorage.getItem(`agent-token-verified-${agentId.value}`);
|
||||
if (verified === 'true') {
|
||||
isVerified.value = true;
|
||||
} else {
|
||||
tokenModalVisible.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
// 组件挂载时检查验证状态
|
||||
onMounted(() => {
|
||||
checkVerification();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@ -19,6 +109,23 @@ const agentId = computed(() => route.params.agent_id);
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.token-verify-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
|
||||
.error-message {
|
||||
color: #ff4d4f;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.token-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
@ -79,10 +79,38 @@
|
||||
<CloseOutlined class="iconfont icon-20" />
|
||||
</div>
|
||||
</h2>
|
||||
<div v-if="selectedAgentId && configSchema" class="config-form">
|
||||
<div v-if="selectedAgentId" class="config-form">
|
||||
<!-- 打开配置弹窗的按钮 -->
|
||||
<a-button
|
||||
type="primary"
|
||||
block
|
||||
@click="openConfigModal"
|
||||
>
|
||||
打开智能体配置
|
||||
</a-button>
|
||||
|
||||
<!-- 添加Token管理组件 -->
|
||||
<div class="token-section">
|
||||
<TokenManagerComponent :agent-id="selectedAgentId" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div v-else class="no-agent-selected">
|
||||
请先选择一个智能体
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 配置弹窗 -->
|
||||
<a-modal
|
||||
v-model:visible="state.configModalVisible"
|
||||
title="智能体配置"
|
||||
width="650px"
|
||||
:footer="null"
|
||||
@cancel="closeConfigModal"
|
||||
>
|
||||
<div v-if="selectedAgentId && configSchema" class="config-modal-content">
|
||||
<!-- 配置表单 -->
|
||||
<a-form :model="agentConfig" layout="vertical">
|
||||
<!-- 系统提示词 -->
|
||||
<div class="empty-config" v-if="state.isEmptyConfig">
|
||||
<a-alert type="warning" message="该智能体没有配置项" show-icon/>
|
||||
</div>
|
||||
@ -93,8 +121,6 @@
|
||||
:name="key"
|
||||
class="config-item"
|
||||
>
|
||||
<!-- <p>{{ value }}</p> -->
|
||||
<!-- 根据值的类型使用不同的输入控件 -->
|
||||
<p v-if="value.description" class="description">{{ value.description }}</p>
|
||||
<a-switch
|
||||
v-if="typeof agentConfig[key] === 'boolean'"
|
||||
@ -120,17 +146,15 @@
|
||||
</a-form-item>
|
||||
</template>
|
||||
|
||||
<!-- 保存和重置按钮 -->
|
||||
<!-- 弹窗底部按钮 -->
|
||||
<div class="form-actions" v-if="!state.isEmptyConfig">
|
||||
<a-button type="primary" @click="saveConfig">保存配置</a-button>
|
||||
<a-button @click="resetConfig">重置</a-button>
|
||||
<a-button @click="closeConfigModal">取消</a-button>
|
||||
</div>
|
||||
</a-form>
|
||||
</div>
|
||||
<div v-else class="no-agent-selected">
|
||||
请先选择一个智能体
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -145,6 +169,7 @@ import {
|
||||
} from '@ant-design/icons-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import AgentChatComponent from '@/components/AgentChatComponent.vue';
|
||||
import TokenManagerComponent from '@/components/TokenManagerComponent.vue';
|
||||
|
||||
// 状态
|
||||
const agents = ref({});
|
||||
@ -153,6 +178,7 @@ const state = reactive({
|
||||
debug_mode: false,
|
||||
isSidebarOpen: JSON.parse(localStorage.getItem('agent-sidebar-open') || 'true'),
|
||||
isConfigSidebarOpen: false,
|
||||
configModalVisible: false,
|
||||
isEmptyConfig: computed(() =>
|
||||
!selectedAgentId.value ||
|
||||
Object.keys(configurableItems.value).length === 0
|
||||
@ -169,6 +195,16 @@ const toggleDebugMode = () => {
|
||||
state.debug_mode = !state.debug_mode;
|
||||
};
|
||||
|
||||
// 打开配置弹窗
|
||||
const openConfigModal = () => {
|
||||
state.configModalVisible = true;
|
||||
};
|
||||
|
||||
// 关闭配置弹窗
|
||||
const closeConfigModal = () => {
|
||||
state.configModalVisible = false;
|
||||
};
|
||||
|
||||
// 根据选中的智能体加载配置
|
||||
const loadAgentConfig = () => {
|
||||
// BUG: 目前消息重置有问题,需要重置消息
|
||||
@ -222,6 +258,7 @@ const saveConfig = () => {
|
||||
|
||||
// 提示保存成功
|
||||
message.success('配置已保存');
|
||||
closeConfigModal();
|
||||
};
|
||||
|
||||
// 重置配置
|
||||
@ -379,18 +416,13 @@ const getPlaceholder = (key, value) => {
|
||||
overflow-y: auto;
|
||||
max-height: calc(100vh - 100px);
|
||||
|
||||
.description {
|
||||
font-size: 12px;
|
||||
color: var(--gray-700);
|
||||
.token-section {
|
||||
margin-top: 1.5rem;
|
||||
border-top: 1px solid var(--main-light-3);
|
||||
padding-top: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.no-agent-selected {
|
||||
padding: 16px;
|
||||
color: var(--gray-500);
|
||||
@ -548,6 +580,23 @@ const getPlaceholder = (key, value) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.config-modal-content {
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
|
||||
.description {
|
||||
font-size: 12px;
|
||||
color: var(--gray-700);
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 20px;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user