diff --git a/src/admin/routes.ts b/src/admin/routes.ts
index 6b16d0c..6de57a0 100644
--- a/src/admin/routes.ts
+++ b/src/admin/routes.ts
@@ -132,9 +132,38 @@ async function toggleDetail(id, el) {
loadedDetails[id] = true;
const reqStr = JSON.stringify(log.requestBody ?? {}, null, 2);
const respStr = typeof log.responseBody === 'string' ? log.responseBody : JSON.stringify(log.responseBody ?? {}, null, 2);
- let html = '
';
- html += '
📤 请求体 ('+escapeHtml(log.endpoint)+')
'+escapeHtml(reqStr)+'
';
- html += '
📥 响应体
'+(log.error ? ''+escapeHtml(log.error)+'
' : '')+escapeHtml(respStr)+'
';
+ const paramsStr = log.params ? JSON.stringify(log.params, null, 2) : null;
+ const errorHtml = log.error ? '
'+escapeHtml(log.error)+'' : '';
+
+ // 渲染 prompt messages:system 蓝色背景,user 灰色
+ const renderMessages = (msgs) => {
+ if (!msgs || msgs.length === 0) return '';
+ return msgs.map((m,i) => {
+ const label = m.role === 'system' ? '🤖 System Prompt' : '👤 User Message';
+ const bg = m.role === 'system' ? '#e3f2fd' : '#f5f5f5';
+ const border = m.role === 'system' ? '#90caf9' : '#e0e0e0';
+ return '
'+
+ '
'+label+''+
+ '
'+escapeHtml(m.content)+'
'+
+ '
';
+ }).join('');
+ };
+
+ let html = '';
+ // prompt 区域(如果有)
+ if (log.messages && log.messages.length > 0) {
+ html += '
'+(log.messages.filter(m=>m.role==='system').length>0?'🧠':'')+' Prompt
'+renderMessages(log.messages)+'';
+ }
+ // 参数 + 请求/响应
+ html += '
';
+ if (paramsStr) {
+ html += '
⚙️ 调用参数
'+escapeHtml(paramsStr)+'
';
+ }
+ html += '
⏱️ 耗时
'+log.durationMs+'ms
';
+ html += '
';
+ html += '
';
+ html += '
📤 原始请求体
'+escapeHtml(reqStr)+'
';
+ html += '
📥 响应体
'+errorHtml+escapeHtml(respStr)+'
';
html += '
';
row.querySelector('.detail-card').innerHTML = html;
}
diff --git a/src/admin/store.ts b/src/admin/store.ts
index dbab103..354a819 100644
--- a/src/admin/store.ts
+++ b/src/admin/store.ts
@@ -16,7 +16,9 @@ export interface LogEntry {
model: string;
durationMs: number;
status: number; // HTTP status code
- requestBody: unknown;
+ requestBody: unknown; // Java 端发来的原始请求体
+ messages?: unknown[]; // 构造后发给 LLM 的 messages(含 system prompt)
+ params?: Record
; // 模型调用参数(temperature、max_tokens 等)
responseBody: unknown;
error?: string;
}
diff --git a/src/routes/ai.ts b/src/routes/ai.ts
index 6466040..7d8f0af 100644
--- a/src/routes/ai.ts
+++ b/src/routes/ai.ts
@@ -35,12 +35,14 @@ export async function aiRoutes(app: FastifyInstance): Promise {
app.post<{ Body: AggregateReportBody }>("/ai/aggregate-report", async (request, reply) => {
const { taskName, fragments, expectation } = request.body ?? ({} as AggregateReportBody);
const t0 = Date.now();
+ const params = { temperature: 0.3, max_tokens: 2048 };
if (!taskName || !Array.isArray(fragments) || fragments.length === 0) {
logStore.add({
timestamp: new Date().toISOString(), endpoint: "/ai/aggregate-report",
model: config.model, durationMs: Date.now() - t0, status: 400,
- requestBody: request.body ?? {}, responseBody: { error: "taskName 与 fragments 不可为空" },
+ requestBody: request.body ?? {}, params,
+ responseBody: { error: "taskName 与 fragments 不可为空" },
});
return reply.status(400).send({ error: "taskName 与 fragments 不可为空" });
}
@@ -52,7 +54,7 @@ export async function aiRoutes(app: FastifyInstance): Promise {
logStore.add({
timestamp: new Date().toISOString(), endpoint: "/ai/aggregate-report",
model: config.model, durationMs: Date.now() - t0, status: 200,
- requestBody: request.body ?? {}, responseBody: resp,
+ requestBody: request.body ?? {}, messages, params, responseBody: resp,
});
return resp;
} catch (err) {
@@ -61,7 +63,7 @@ export async function aiRoutes(app: FastifyInstance): Promise {
logStore.add({
timestamp: new Date().toISOString(), endpoint: "/ai/aggregate-report",
model: config.model, durationMs: Date.now() - t0, status,
- requestBody: request.body ?? {}, responseBody: {}, error: errMsg,
+ requestBody: request.body ?? {}, params, responseBody: {}, error: errMsg,
});
if (err instanceof LlmError) {
return reply.status(err.statusCode).send({ error: err.message });
@@ -77,12 +79,14 @@ export async function aiRoutes(app: FastifyInstance): Promise {
const { taskName, taskDescription, reports, fragments } =
request.body ?? ({} as GenerateMindMapBody);
const t0 = Date.now();
+ const params = { temperature: 0.3, max_tokens: 4096 };
if (!taskName || ((reports?.length ?? 0) === 0 && (fragments?.length ?? 0) === 0)) {
logStore.add({
timestamp: new Date().toISOString(), endpoint: "/ai/generate-mind-map",
model: config.model, durationMs: Date.now() - t0, status: 400,
- requestBody: request.body ?? {}, responseBody: { error: "taskName 不可为空且 reports/fragments 至少一项非空" },
+ requestBody: request.body ?? {}, params,
+ responseBody: { error: "taskName 不可为空且 reports/fragments 至少一项非空" },
});
return reply.status(400).send({ error: "taskName 不可为空且 reports/fragments 至少一项非空" });
}
@@ -99,7 +103,7 @@ export async function aiRoutes(app: FastifyInstance): Promise {
logStore.add({
timestamp: new Date().toISOString(), endpoint: "/ai/generate-mind-map",
model: config.model, durationMs: Date.now() - t0, status: 200,
- requestBody: request.body ?? {}, responseBody: resp,
+ requestBody: request.body ?? {}, messages, params, responseBody: resp,
});
return resp;
} catch (err) {
@@ -108,7 +112,7 @@ export async function aiRoutes(app: FastifyInstance): Promise {
logStore.add({
timestamp: new Date().toISOString(), endpoint: "/ai/generate-mind-map",
model: config.model, durationMs: Date.now() - t0, status,
- requestBody: request.body ?? {}, responseBody: {}, error: errMsg,
+ requestBody: request.body ?? {}, params, responseBody: {}, error: errMsg,
});
if (err instanceof LlmError) {
return reply.status(err.statusCode).send({ error: err.message });