true message

This commit is contained in:
Manus
2026-03-26 05:41:44 -04:00
parent d396004294
commit 8096ce4dfd
9 changed files with 409 additions and 626 deletions

View File

@@ -1,9 +1,10 @@
import { COOKIE_NAME } from "@shared/const";
import { z } from "zod";
import { getDb, getNodeMetricsHistory, getLatestNodeMetrics } from "./db";
import { getDb } from "./db";
import { getSessionCookieOptions } from "./_core/cookies";
import { systemRouter } from "./_core/systemRouter";
import { publicProcedure, router, protectedProcedure } from "./_core/trpc";
import { retryWithBackoff, isRetryableError, logRetryAttempt, DEFAULT_RETRY_CONFIG } from "./chat-resilience";
import { checkOllamaHealth, listModels, chatCompletion } from "./ollama";
import {
checkGatewayHealth,
@@ -531,24 +532,38 @@ export const appRouter = router({
})
)
.mutation(async ({ input }) => {
// Try Go Gateway first (preferred — full Go tool-use loop)
const gwAvailable = await isGatewayAvailable();
if (gwAvailable) {
const result = await gatewayChat(
input.messages,
input.model,
input.maxIterations ?? 10
);
return { ...result, source: "gateway" as const };
}
// Fallback: Node.js orchestrator
const { orchestratorChat } = await import("./orchestrator");
const result = await orchestratorChat(
input.messages,
input.model,
input.maxIterations ?? 10
// Wrap chat with retry logic for resilience
return retryWithBackoff(
async () => {
// Try Go Gateway first (preferred — full Go tool-use loop)
const gwAvailable = await isGatewayAvailable();
if (gwAvailable) {
const result = await gatewayChat(
input.messages,
input.model,
input.maxIterations ?? 10
);
return { ...result, source: "gateway" as const };
}
// Fallback: Node.js orchestrator
const { orchestratorChat } = await import("./orchestrator");
const result = await orchestratorChat(
input.messages,
input.model,
input.maxIterations ?? 10
);
return { ...result, source: "direct" as const };
},
DEFAULT_RETRY_CONFIG,
(attempt, error) => {
if (isRetryableError(error)) {
logRetryAttempt(attempt, error, { messageCount: input.messages.length });
} else {
// Non-retryable error, throw immediately
throw error;
}
}
);
return { ...result, source: "direct" as const };
}),
// List available tools — Go Gateway first
@@ -706,50 +721,6 @@ export const appRouter = router({
}
return result;
}),
/**
* Get historical metrics for a specific container (last 60 min, sampled every 30s)
*/
metricsHistory: publicProcedure
.input(z.object({ containerId: z.string() }))
.query(async ({ input }) => {
const history = await getNodeMetricsHistory(input.containerId, 60);
// Return in chronological order for sparkline rendering
const sorted = [...history].reverse();
return {
containerId: input.containerId,
points: sorted.map(m => ({
cpu: Number(m.cpuPercent),
mem: Number(m.memUsedMb),
ts: m.recordedAt.getTime(),
})),
count: sorted.length,
};
}),
/**
* Get latest metrics snapshot for all containers (last 30 min)
*/
allMetricsLatest: publicProcedure.query(async () => {
const metrics = await getLatestNodeMetrics();
// Group by containerId, keep last 120 points each
const grouped: Record<string, { cpu: number; mem: number; ts: number }[]> = {};
for (const m of metrics) {
if (!grouped[m.containerId]) grouped[m.containerId] = [];
if (grouped[m.containerId].length < 120) {
grouped[m.containerId].push({
cpu: Number(m.cpuPercent),
mem: Number(m.memUsedMb),
ts: m.recordedAt.getTime(),
});
}
}
// Reverse each group to chronological order
for (const id of Object.keys(grouped)) {
grouped[id] = grouped[id].reverse();
}
return { byContainer: grouped, fetchedAt: new Date().toISOString() };
}),
}),
});
export type AppRouter = typeof appRouter;