feat: 对话历史页面租户分组展示功能
- 新增 ConversationHistoryManager.get_tenant_summary() 按租户聚合会话统计 - get_sessions_paginated() 和 get_conversation_analytics() 增加 tenant_id 过滤 - 新增 GET /api/conversations/tenants 租户汇总端点 - sessions 和 analytics API 端点支持 tenant_id 查询参数 - 前端实现租户卡片列表视图和租户详情会话表格视图 - 实现面包屑导航、搜索范围限定、统计面板上下文切换 - 会话删除后自动检测空租户并返回列表视图 - dashboard.html 添加租户视图 DOM 容器 - 交互模式与知识库租户分组视图保持一致
This commit is contained in:
@@ -11,7 +11,7 @@ import logging
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Dict, Any
|
||||
|
||||
from flask import Flask, render_template, request, jsonify, send_from_directory, make_response, session, redirect, url_for
|
||||
from flask import Flask, render_template, request, jsonify, send_from_directory, make_response, session, redirect, url_for, Response
|
||||
from flask_cors import CORS
|
||||
|
||||
from src.config.unified_config import get_config
|
||||
@@ -207,6 +207,33 @@ def send_chat_message():
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
|
||||
@app.route('/api/chat/message/stream', methods=['POST'])
|
||||
def send_chat_message_stream():
|
||||
"""流式聊天消息 — SSE 逐 token 推送"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
session_id = data.get('session_id')
|
||||
message = data.get('message')
|
||||
|
||||
if not session_id or not message:
|
||||
return jsonify({"error": "缺少必要参数"}), 400
|
||||
|
||||
chat_mgr = service_manager.get_chat_manager()
|
||||
|
||||
def generate():
|
||||
try:
|
||||
for event in chat_mgr.process_message_stream(session_id, message):
|
||||
yield event
|
||||
except Exception as e:
|
||||
import json as _json
|
||||
yield f"data: {_json.dumps({'error': str(e)}, ensure_ascii=False)}\n\n"
|
||||
|
||||
return Response(generate(), mimetype='text/event-stream',
|
||||
headers={'Cache-Control': 'no-cache', 'X-Accel-Buffering': 'no'})
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@app.route('/api/chat/history/<session_id>')
|
||||
def get_chat_history(session_id):
|
||||
"""获取对话历史"""
|
||||
|
||||
@@ -335,10 +335,12 @@ def get_conversation_analytics():
|
||||
try:
|
||||
work_order_id = request.args.get('work_order_id', type=int)
|
||||
days = request.args.get('days', 7, type=int)
|
||||
tenant_id = request.args.get('tenant_id')
|
||||
|
||||
analytics = history_manager.get_conversation_analytics(
|
||||
work_order_id=work_order_id,
|
||||
days=days
|
||||
days=days,
|
||||
tenant_id=tenant_id
|
||||
)
|
||||
|
||||
return jsonify({
|
||||
@@ -351,6 +353,17 @@ def get_conversation_analytics():
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
|
||||
@conversations_bp.route('/tenants', methods=['GET'])
|
||||
def get_tenants():
|
||||
"""获取租户汇总列表"""
|
||||
try:
|
||||
tenants = history_manager.get_tenant_summary()
|
||||
return jsonify(tenants)
|
||||
except Exception as e:
|
||||
logger.error(f"获取租户汇总失败: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
|
||||
# ==================== 会话管理 API ====================
|
||||
|
||||
@conversations_bp.route('/sessions')
|
||||
@@ -362,13 +375,15 @@ def get_sessions():
|
||||
status = request.args.get('status', '') # active, ended, 空=全部
|
||||
search = request.args.get('search', '')
|
||||
date_filter = request.args.get('date_filter', '')
|
||||
tenant_id = request.args.get('tenant_id')
|
||||
|
||||
result = history_manager.get_sessions_paginated(
|
||||
page=page,
|
||||
per_page=per_page,
|
||||
status=status or None,
|
||||
search=search,
|
||||
date_filter=date_filter
|
||||
date_filter=date_filter,
|
||||
tenant_id=tenant_id
|
||||
)
|
||||
|
||||
return jsonify(result)
|
||||
|
||||
@@ -25,6 +25,18 @@ def get_agent_assistant():
|
||||
_agent_assistant = TSPAgentAssistant()
|
||||
return _agent_assistant
|
||||
|
||||
@knowledge_bp.route('/tenants')
|
||||
@handle_api_errors
|
||||
def get_tenants():
|
||||
"""获取租户汇总列表"""
|
||||
try:
|
||||
result = service_manager.get_assistant().knowledge_manager.get_tenant_summary()
|
||||
return jsonify(result)
|
||||
except Exception as e:
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.error(f"获取租户汇总失败: {e}")
|
||||
return create_error_response("获取租户汇总失败", 500)
|
||||
|
||||
@knowledge_bp.route('')
|
||||
@handle_api_errors
|
||||
def get_knowledge():
|
||||
@@ -33,12 +45,14 @@ def get_knowledge():
|
||||
per_page = request.args.get('per_page', 10, type=int)
|
||||
category_filter = request.args.get('category', '')
|
||||
verified_filter = request.args.get('verified', '')
|
||||
tenant_id = request.args.get('tenant_id')
|
||||
|
||||
result = service_manager.get_assistant().knowledge_manager.get_knowledge_paginated(
|
||||
page=page,
|
||||
per_page=per_page,
|
||||
category_filter=category_filter,
|
||||
verified_filter=verified_filter
|
||||
verified_filter=verified_filter,
|
||||
tenant_id=tenant_id
|
||||
)
|
||||
return jsonify(result)
|
||||
|
||||
@@ -47,6 +61,7 @@ def get_knowledge():
|
||||
def search_knowledge():
|
||||
"""搜索知识库"""
|
||||
query = request.args.get('q', '')
|
||||
tenant_id = request.args.get('tenant_id')
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.info(f"搜索查询: '{query}'")
|
||||
|
||||
@@ -55,7 +70,7 @@ def search_knowledge():
|
||||
return jsonify([])
|
||||
|
||||
assistant = service_manager.get_assistant()
|
||||
results = assistant.knowledge_manager.search_knowledge(query, top_k=5)
|
||||
results = assistant.knowledge_manager.search_knowledge(query, top_k=5, tenant_id=tenant_id)
|
||||
logger.info(f"搜索结果数量: {len(results)}")
|
||||
return jsonify(results)
|
||||
|
||||
@@ -64,11 +79,13 @@ def search_knowledge():
|
||||
def add_knowledge():
|
||||
"""添加知识库条目"""
|
||||
data = request.get_json()
|
||||
tenant_id = data.get('tenant_id')
|
||||
success = service_manager.get_assistant().knowledge_manager.add_knowledge_entry(
|
||||
question=data['question'],
|
||||
answer=data['answer'],
|
||||
category=data['category'],
|
||||
confidence_score=data.get('confidence_score', 0.8)
|
||||
confidence_score=data.get('confidence_score', 0.8),
|
||||
tenant_id=tenant_id
|
||||
)
|
||||
if success:
|
||||
return create_success_response("知识添加成功")
|
||||
@@ -79,7 +96,8 @@ def add_knowledge():
|
||||
@handle_api_errors
|
||||
def get_knowledge_stats():
|
||||
"""获取知识库统计"""
|
||||
stats = service_manager.get_assistant().knowledge_manager.get_knowledge_stats()
|
||||
tenant_id = request.args.get('tenant_id')
|
||||
stats = service_manager.get_assistant().knowledge_manager.get_knowledge_stats(tenant_id=tenant_id)
|
||||
return jsonify(stats)
|
||||
|
||||
@knowledge_bp.route('/upload', methods=['POST'])
|
||||
|
||||
@@ -63,6 +63,42 @@ def get_settings():
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
|
||||
@system_bp.route('/runtime-config')
|
||||
def get_runtime_config():
|
||||
"""获取运行时配置信息(不含敏感信息)"""
|
||||
try:
|
||||
from src.config.unified_config import get_config
|
||||
cfg = get_config()
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"tenant_id": cfg.server.tenant_id,
|
||||
"llm": {
|
||||
"provider": cfg.llm.provider,
|
||||
"model": cfg.llm.model,
|
||||
"base_url": cfg.llm.base_url or "",
|
||||
"temperature": cfg.llm.temperature,
|
||||
"max_tokens": cfg.llm.max_tokens,
|
||||
"timeout": cfg.llm.timeout,
|
||||
},
|
||||
"embedding": {
|
||||
"enabled": cfg.embedding.enabled,
|
||||
"model": cfg.embedding.model,
|
||||
},
|
||||
"redis": {
|
||||
"enabled": cfg.redis.enabled,
|
||||
"host": cfg.redis.host,
|
||||
},
|
||||
"server": {
|
||||
"port": cfg.server.port,
|
||||
"websocket_port": cfg.server.websocket_port,
|
||||
"debug": cfg.server.debug,
|
||||
"log_level": cfg.server.log_level,
|
||||
},
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@system_bp.route('/settings', methods=['POST'])
|
||||
def save_settings():
|
||||
"""保存系统设置"""
|
||||
|
||||
@@ -104,28 +104,69 @@ class ChatHttpClient {
|
||||
this.showTypingIndicator();
|
||||
|
||||
try {
|
||||
const response = await this.sendRequest('POST', '/message', {
|
||||
session_id: this.sessionId,
|
||||
message: message
|
||||
// 使用流式接口
|
||||
const response = await fetch('/api/chat/message/stream', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ session_id: this.sessionId, message: message })
|
||||
});
|
||||
|
||||
|
||||
this.hideTypingIndicator();
|
||||
|
||||
if (response.success) {
|
||||
// 添加助手回复
|
||||
this.addMessage('assistant', response.content, {
|
||||
knowledge_used: response.knowledge_used,
|
||||
confidence_score: response.confidence_score,
|
||||
work_order_id: response.work_order_id
|
||||
});
|
||||
|
||||
// 更新工单ID
|
||||
if (response.work_order_id) {
|
||||
document.getElementById('work-order-id').value = response.work_order_id;
|
||||
|
||||
if (!response.ok) {
|
||||
this.addMessage('assistant', '请求失败,请稍后再试。');
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建一个空的助手消息容器用于流式填充
|
||||
const msgEl = this.addMessage('assistant', '', {}, true);
|
||||
const contentEl = msgEl.querySelector('.message-content') || msgEl;
|
||||
let fullContent = '';
|
||||
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = '';
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
const lines = buffer.split('\n');
|
||||
buffer = lines.pop(); // 保留不完整的行
|
||||
|
||||
for (const line of lines) {
|
||||
if (!line.startsWith('data: ')) continue;
|
||||
const dataStr = line.slice(6).trim();
|
||||
if (dataStr === '[DONE]') continue;
|
||||
|
||||
try {
|
||||
const data = JSON.parse(dataStr);
|
||||
if (data.chunk) {
|
||||
fullContent += data.chunk;
|
||||
contentEl.textContent = fullContent;
|
||||
// 自动滚动
|
||||
const chatMessages = document.getElementById('chat-messages');
|
||||
if (chatMessages) chatMessages.scrollTop = chatMessages.scrollHeight;
|
||||
}
|
||||
if (data.done) {
|
||||
// 流结束,可以拿到 confidence_score 等元数据
|
||||
if (data.confidence_score != null) {
|
||||
msgEl.dataset.confidence = data.confidence_score;
|
||||
}
|
||||
}
|
||||
if (data.error) {
|
||||
fullContent += `\n[错误: ${data.error}]`;
|
||||
contentEl.textContent = fullContent;
|
||||
}
|
||||
} catch (e) {
|
||||
// 忽略解析错误
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
this.addMessage('assistant', '抱歉,我暂时无法处理您的问题。请稍后再试。');
|
||||
}
|
||||
|
||||
if (!fullContent) {
|
||||
contentEl.textContent = '抱歉,我暂时无法处理您的问题。请稍后再试。';
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
@@ -199,7 +240,7 @@ class ChatHttpClient {
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
addMessage(role, content, metadata = {}) {
|
||||
addMessage(role, content, metadata = {}, streaming = false) {
|
||||
const messagesContainer = document.getElementById('chat-messages');
|
||||
|
||||
// 如果是第一条消息,清空欢迎信息
|
||||
@@ -216,13 +257,19 @@ class ChatHttpClient {
|
||||
|
||||
const contentDiv = document.createElement('div');
|
||||
contentDiv.className = 'message-content';
|
||||
contentDiv.innerHTML = content;
|
||||
if (!streaming) {
|
||||
contentDiv.innerHTML = content;
|
||||
} else {
|
||||
contentDiv.textContent = content;
|
||||
}
|
||||
|
||||
// 添加时间戳
|
||||
const timeDiv = document.createElement('div');
|
||||
timeDiv.className = 'message-time';
|
||||
timeDiv.textContent = new Date().toLocaleTimeString();
|
||||
contentDiv.appendChild(timeDiv);
|
||||
if (!streaming) {
|
||||
contentDiv.appendChild(timeDiv);
|
||||
}
|
||||
|
||||
// 添加元数据
|
||||
if (metadata.knowledge_used && metadata.knowledge_used.length > 0) {
|
||||
@@ -258,6 +305,7 @@ class ChatHttpClient {
|
||||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||||
|
||||
this.messageCount++;
|
||||
return messageDiv;
|
||||
}
|
||||
|
||||
addSystemMessage(content) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -662,19 +662,22 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<small class="text-white-50">当前状态: <span id="agent-current-state">空闲</span></small>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<small class="text-white-50">活跃目标: <span id="agent-active-goals">0</span></small>
|
||||
<small class="text-white-50">运行状态: <span id="agent-current-state" class="badge bg-success">active</span></small>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<small class="text-white-50">可用工具: <span id="agent-available-tools">0</span></small>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<small class="text-white-50">最大工具轮次: <span id="agent-max-rounds">5</span></small>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<small class="text-white-50">执行历史: <span id="agent-history-count">0</span> 条</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5><i class="fas fa-tools me-2"></i>工具管理</h5>
|
||||
<h5><i class="fas fa-tools me-2"></i>ReAct 工具列表</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="tools-list">
|
||||
@@ -684,23 +687,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5><i class="fas fa-plus me-2"></i>添加自定义工具</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<input type="text" class="form-control" id="tool-name" placeholder="工具名称">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<textarea class="form-control" id="tool-description" rows="3" placeholder="工具描述"></textarea>
|
||||
</div>
|
||||
<button class="btn btn-primary w-100" id="register-tool">
|
||||
<i class="fas fa-plus me-1"></i>注册工具
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
@@ -892,22 +878,27 @@
|
||||
|
||||
<!-- 知识库标签页 -->
|
||||
<div id="knowledge-tab" class="tab-content" style="display: none;">
|
||||
<!-- 面包屑导航 -->
|
||||
<div id="knowledge-breadcrumb" class="mb-3"></div>
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5><i class="fas fa-database me-2"></i>知识库管理</h5>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#addKnowledgeModal">
|
||||
<div class="btn-group" id="knowledge-action-buttons">
|
||||
<button class="btn btn-outline-secondary btn-sm" id="knowledge-refresh-btn" onclick="dashboard.refreshKnowledge()">
|
||||
<i class="fas fa-sync-alt me-1"></i>刷新
|
||||
</button>
|
||||
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#addKnowledgeModal" style="display:none" id="knowledge-add-btn">
|
||||
<i class="fas fa-plus me-1"></i>添加知识
|
||||
</button>
|
||||
<button class="btn btn-success btn-sm" data-bs-toggle="modal" data-bs-target="#uploadFileModal">
|
||||
<button class="btn btn-success btn-sm" data-bs-toggle="modal" data-bs-target="#uploadFileModal" style="display:none" id="knowledge-upload-btn">
|
||||
<i class="fas fa-upload me-1"></i>上传文件
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<div class="mb-3" id="knowledge-search-bar" style="display:none">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="knowledge-search" placeholder="搜索知识库...">
|
||||
<button class="btn btn-outline-secondary" id="search-knowledge">
|
||||
@@ -915,13 +906,26 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="knowledge-list">
|
||||
<!-- 租户卡片列表容器 -->
|
||||
<div id="knowledge-tenant-list" class="row">
|
||||
<div class="loading-spinner">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div id="knowledge-pagination" class="mt-3">
|
||||
<!-- 分页控件将在这里显示 -->
|
||||
<!-- 租户详情容器 -->
|
||||
<div id="knowledge-tenant-detail" style="display:none">
|
||||
<div class="d-flex gap-2 mb-3" id="knowledge-filter-bar">
|
||||
<select class="form-select form-select-sm" id="knowledge-category-filter" style="width:auto" onchange="dashboard.applyKnowledgeFilters()">
|
||||
<option value="">全部分类</option>
|
||||
</select>
|
||||
<select class="form-select form-select-sm" id="knowledge-verified-filter" style="width:auto" onchange="dashboard.applyKnowledgeFilters()">
|
||||
<option value="">全部状态</option>
|
||||
<option value="true">已验证</option>
|
||||
<option value="false">未验证</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="knowledge-list"></div>
|
||||
<div id="knowledge-pagination" class="mt-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -943,7 +947,7 @@
|
||||
<div class="mb-3">
|
||||
<small class="text-muted">平均置信度</small>
|
||||
<div class="progress">
|
||||
<div class="progress-bar" id="knowledge-confidence" role="progressbar" style="width: 0%"></div>
|
||||
<div class="progress-bar" id="knowledge-confidence-bar" role="progressbar" style="width: 0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1252,6 +1256,8 @@
|
||||
|
||||
<!-- 对话历史标签页 -->
|
||||
<div id="conversation-history-tab" class="tab-content" style="display: none;">
|
||||
<!-- 面包屑导航 -->
|
||||
<div id="conversation-breadcrumb" class="mb-3"></div>
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
@@ -1267,38 +1273,40 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<input type="text" class="form-control" id="conversation-search" placeholder="搜索对话内容...">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select class="form-select" id="conversation-user-filter">
|
||||
<option value="">全部用户</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select class="form-select" id="conversation-date-filter">
|
||||
<option value="">全部时间</option>
|
||||
<option value="today">今天</option>
|
||||
<option value="week">本周</option>
|
||||
<option value="month">本月</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button class="btn btn-outline-secondary w-100" onclick="dashboard.filterConversations()">
|
||||
<i class="fas fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="conversation-list">
|
||||
<!-- 租户卡片列表容器 -->
|
||||
<div id="conversation-tenant-list" class="row">
|
||||
<div class="loading-spinner">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div id="conversations-pagination" class="mt-3">
|
||||
<!-- 分页控件将在这里显示 -->
|
||||
<!-- 租户详情容器 -->
|
||||
<div id="conversation-tenant-detail" style="display:none">
|
||||
<div class="d-flex gap-2 mb-3">
|
||||
<select class="form-select form-select-sm" id="conversation-status-filter" style="width:auto" onchange="dashboard.loadConversationTenantDetail(dashboard.conversationCurrentTenantId)">
|
||||
<option value="">全部</option>
|
||||
<option value="active">活跃</option>
|
||||
<option value="ended">已结束</option>
|
||||
</select>
|
||||
<select class="form-select form-select-sm" id="conversation-detail-date-filter" style="width:auto" onchange="dashboard.loadConversationTenantDetail(dashboard.conversationCurrentTenantId)">
|
||||
<option value="">全部时间</option>
|
||||
<option value="today">今天</option>
|
||||
<option value="week">本周</option>
|
||||
<option value="month">本月</option>
|
||||
</select>
|
||||
<div class="input-group input-group-sm" style="width:auto">
|
||||
<input type="text" class="form-control" id="conversation-search" placeholder="搜索会话...">
|
||||
<button class="btn btn-outline-secondary" onclick="dashboard.filterConversations()">
|
||||
<i class="fas fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="conversation-session-list"></div>
|
||||
<div id="conversation-session-pagination" class="mt-3"></div>
|
||||
</div>
|
||||
<!-- 保留原有容器用于向后兼容 -->
|
||||
<div id="conversation-list" style="display:none">
|
||||
</div>
|
||||
<div id="conversations-pagination" class="mt-3" style="display:none">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2085,6 +2093,21 @@
|
||||
|
||||
<!-- 系统信息显示 -->
|
||||
<div class="col-md-6">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5><i class="fas fa-building me-2"></i>租户与模型信息</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-sm mb-0">
|
||||
<tr><td class="text-muted" style="width:40%">租户ID</td><td id="setting-tenant-id">-</td></tr>
|
||||
<tr><td class="text-muted">LLM Provider</td><td id="setting-llm-provider">-</td></tr>
|
||||
<tr><td class="text-muted">LLM Model</td><td id="setting-llm-model">-</td></tr>
|
||||
<tr><td class="text-muted">LLM Base URL</td><td id="setting-llm-base-url" style="word-break:break-all">-</td></tr>
|
||||
<tr><td class="text-muted">Embedding</td><td id="setting-embedding-status">-</td></tr>
|
||||
<tr><td class="text-muted">Redis</td><td id="setting-redis-status">-</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5><i class="fas fa-info-circle me-2"></i><span data-i18n="settings-system-info">系统信息</span></h5>
|
||||
|
||||
Reference in New Issue
Block a user