Add AI request audit detail drawer
This commit is contained in:
@@ -34,6 +34,8 @@ const operationLogs = ref<Record<string, unknown>[]>([]);
|
||||
const promptContent = ref("");
|
||||
const chatDetail = ref<ChatDetail | null>(null);
|
||||
const chatDetailOpen = ref(false);
|
||||
const selectedAiLog = ref<AiLogRecord | null>(null);
|
||||
const aiLogDetailOpen = ref(false);
|
||||
const recordTab = ref("chats");
|
||||
const editingModelId = ref<number | null>(null);
|
||||
const editingKnowledgeId = ref<number | null>(null);
|
||||
@@ -960,6 +962,11 @@ async function openChatDetail(row: ChatRecord) {
|
||||
chatDetailOpen.value = true;
|
||||
}
|
||||
|
||||
async function openAiLogDetail(row: AiLogRecord) {
|
||||
selectedAiLog.value = await api.aiLogDetail(row.id);
|
||||
aiLogDetailOpen.value = true;
|
||||
}
|
||||
|
||||
async function exportChats() {
|
||||
await api.exportChats(buildChatQuery());
|
||||
}
|
||||
@@ -1463,29 +1470,24 @@ function buildChatQuery() {
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="AI 请求" name="aiLogs">
|
||||
<el-table :data="aiLogs" stripe>
|
||||
<el-table-column type="expand" width="48">
|
||||
<template #default="{ row }">
|
||||
<section v-if="row.retrievedChunks?.length" class="retrieval-chunks">
|
||||
<article v-for="chunk in row.retrievedChunks" :key="`${row.id}-${chunk.index}`" class="retrieval-chunk">
|
||||
<div class="chunk-head">
|
||||
<strong>#{{ chunk.index }} {{ chunk.knowledgeName || '未知知识库' }}</strong>
|
||||
<span>{{ chunk.title || '未命名片段' }}</span>
|
||||
</div>
|
||||
<pre>{{ chunk.content }}</pre>
|
||||
<a v-if="chunk.sourceUrl" :href="chunk.sourceUrl" target="_blank" rel="noreferrer">查看来源</a>
|
||||
</article>
|
||||
</section>
|
||||
<el-empty v-else description="本条请求未保存召回片段" :image-size="64" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="sessionId" label="会话ID" width="90" />
|
||||
<el-table-column prop="modelName" label="模型" width="160" />
|
||||
<el-table-column prop="knowledgeIds" label="知识库" width="120" />
|
||||
<el-table-column prop="retrieveCount" label="命中" width="80" />
|
||||
<el-table-column label="片段" width="80">
|
||||
<template #default="{ row }">
|
||||
{{ row.retrievedChunks?.length || 0 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="totalToken" label="Token" width="90" />
|
||||
<el-table-column prop="costMs" label="耗时(ms)" width="100" />
|
||||
<el-table-column prop="status" label="状态" width="100" />
|
||||
<el-table-column prop="errorMessage" label="错误" min-width="220" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="100" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button size="small" @click="openAiLogDetail(row)">详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="操作日志" name="operationLogs">
|
||||
@@ -1560,5 +1562,53 @@ function buildChatQuery() {
|
||||
</el-collapse>
|
||||
</template>
|
||||
</el-drawer>
|
||||
|
||||
<el-drawer v-model="aiLogDetailOpen" size="820px" title="AI 请求详情">
|
||||
<template v-if="selectedAiLog">
|
||||
<section class="chat-summary">
|
||||
<div><span>请求ID</span><strong>{{ selectedAiLog.id }}</strong></div>
|
||||
<div><span>会话ID</span><strong>{{ selectedAiLog.sessionId || '-' }}</strong></div>
|
||||
<div><span>消息ID</span><strong>{{ selectedAiLog.messageId || '-' }}</strong></div>
|
||||
<div><span>用户ID</span><strong>{{ selectedAiLog.userId || '-' }}</strong></div>
|
||||
<div><span>模型</span><strong>{{ selectedAiLog.modelName || '-' }}</strong></div>
|
||||
<div><span>状态</span><strong>{{ selectedAiLog.status }}</strong></div>
|
||||
</section>
|
||||
|
||||
<section class="audit-detail-grid">
|
||||
<span>知识库:{{ selectedAiLog.knowledgeIds || '-' }}</span>
|
||||
<span>命中数:{{ selectedAiLog.retrieveCount }}</span>
|
||||
<span>片段数:{{ selectedAiLog.retrievedChunks?.length || 0 }}</span>
|
||||
<span>输入 Token:{{ selectedAiLog.inputToken || '-' }}</span>
|
||||
<span>输出 Token:{{ selectedAiLog.outputToken || '-' }}</span>
|
||||
<span>总 Token:{{ selectedAiLog.totalToken || '-' }}</span>
|
||||
<span>耗时:{{ selectedAiLog.costMs || '-' }}ms</span>
|
||||
<span>时间:{{ selectedAiLog.createdAt }}</span>
|
||||
</section>
|
||||
|
||||
<p v-if="selectedAiLog.errorMessage" class="error-text">{{ selectedAiLog.errorMessage }}</p>
|
||||
|
||||
<h3 class="detail-title">召回知识片段</h3>
|
||||
<section v-if="selectedAiLog.retrievedChunks?.length" class="retrieval-chunks">
|
||||
<article
|
||||
v-for="chunk in selectedAiLog.retrievedChunks"
|
||||
:key="`${selectedAiLog.id}-${chunk.index}`"
|
||||
class="retrieval-chunk"
|
||||
>
|
||||
<div class="chunk-head">
|
||||
<strong>片段 {{ chunk.index }}/{{ selectedAiLog.retrievedChunks.length }}</strong>
|
||||
<span>{{ chunk.knowledgeName || '未知知识库' }}</span>
|
||||
<span v-if="chunk.knowledgeId">知识库ID:{{ chunk.knowledgeId }}</span>
|
||||
</div>
|
||||
<div class="chunk-title">{{ chunk.title || '未命名片段' }}</div>
|
||||
<pre>{{ chunk.content }}</pre>
|
||||
<a v-if="chunk.sourceUrl" :href="chunk.sourceUrl" target="_blank" rel="noreferrer">查看来源</a>
|
||||
</article>
|
||||
</section>
|
||||
<el-empty v-else description="本条请求未保存召回片段" :image-size="72" />
|
||||
|
||||
<h3 class="detail-title">完整 Prompt</h3>
|
||||
<pre class="prompt-preview">{{ selectedAiLog.prompt || '无 Prompt 记录' }}</pre>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user