Shyamnath's picture
Push UI dashboard and deployment files
c40c75a
import React from "react";
import { Card, Text, Button, TabGroup, TabList, Tab, TabPanel, TabPanels } from "@tremor/react";
import { CheckCircleIcon, XCircleIcon, ClipboardCopyIcon } from "@heroicons/react/outline";
import { ResponseTimeIndicator } from "./response_time_indicator";
// Helper function to deep-parse a JSON string if possible
const deepParse = (input: any) => {
let parsed = input;
if (typeof parsed === "string") {
try {
parsed = JSON.parse(parsed);
} catch {
return parsed;
}
}
return parsed;
};
// TableClickableErrorField component with copy-to-clipboard functionality
const TableClickableErrorField: React.FC<{ label: string; value: string | null | undefined }> = ({
label,
value,
}) => {
const [isExpanded, setIsExpanded] = React.useState(false);
const [copied, setCopied] = React.useState(false);
const safeValue = value?.toString() || "N/A";
const truncated = safeValue.length > 50 ? safeValue.substring(0, 50) + "..." : safeValue;
const handleCopy = () => {
navigator.clipboard.writeText(safeValue);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
return (
<tr className="hover:bg-gray-50">
<td className="px-4 py-2 align-top" colSpan={2}>
<div className="flex items-center justify-between group">
<div className="flex items-center flex-1">
<button
onClick={() => setIsExpanded(!isExpanded)}
className="text-gray-400 hover:text-gray-600 mr-2"
>
{isExpanded ? "▼" : "▶"}
</button>
<div>
<div className="text-sm text-gray-600">{label}</div>
<pre className="mt-1 text-sm font-mono text-gray-800 whitespace-pre-wrap">
{isExpanded ? safeValue : truncated}
</pre>
</div>
</div>
<button
onClick={handleCopy}
className="opacity-0 group-hover:opacity-100 text-gray-400 hover:text-gray-600"
>
<ClipboardCopyIcon className="h-4 w-4" />
</button>
</div>
</td>
</tr>
);
};
// Add new interface for Redis details
interface RedisDetails {
redis_host?: string;
redis_port?: string;
redis_version?: string;
startup_nodes?: string;
namespace?: string;
}
// Add new interface for Error Details
interface ErrorDetails {
message: string;
traceback: string;
litellm_params?: any;
health_check_cache_params?: any;
}
// Update HealthCheckDetails component to handle errors
const HealthCheckDetails: React.FC<{ response: any }> = ({ response }) => {
// Initialize with safe default values
let errorDetails: ErrorDetails | null = null;
let parsedLitellmParams: any = {};
let parsedRedisParams: any = {};
try {
if (response?.error) {
try {
const errorMessage = typeof response.error.message === 'string'
? JSON.parse(response.error.message)
: response.error.message;
errorDetails = {
message: errorMessage?.message || 'Unknown error',
traceback: errorMessage?.traceback || 'No traceback available',
litellm_params: errorMessage?.litellm_cache_params || {},
health_check_cache_params: errorMessage?.health_check_cache_params || {}
};
parsedLitellmParams = deepParse(errorDetails.litellm_params) || {};
parsedRedisParams = deepParse(errorDetails.health_check_cache_params) || {};
} catch (e) {
console.warn("Error parsing error details:", e);
errorDetails = {
message: String(response.error.message || 'Unknown error'),
traceback: 'Error parsing details',
litellm_params: {},
health_check_cache_params: {}
};
}
} else {
parsedLitellmParams = deepParse(response?.litellm_cache_params) || {};
parsedRedisParams = deepParse(response?.health_check_cache_params) || {};
}
} catch (e) {
console.warn("Error in response parsing:", e);
// Provide safe fallback values
parsedLitellmParams = {};
parsedRedisParams = {};
}
// Safely extract Redis details with fallbacks
const redisDetails: RedisDetails = {
redis_host: parsedRedisParams?.redis_client?.connection_pool?.connection_kwargs?.host ||
parsedRedisParams?.redis_async_client?.connection_pool?.connection_kwargs?.host ||
parsedRedisParams?.connection_kwargs?.host ||
parsedRedisParams?.host ||
"N/A",
redis_port: parsedRedisParams?.redis_client?.connection_pool?.connection_kwargs?.port ||
parsedRedisParams?.redis_async_client?.connection_pool?.connection_kwargs?.port ||
parsedRedisParams?.connection_kwargs?.port ||
parsedRedisParams?.port ||
"N/A",
redis_version: parsedRedisParams?.redis_version || "N/A",
startup_nodes: (() => {
try {
if (parsedRedisParams?.redis_kwargs?.startup_nodes) {
return JSON.stringify(parsedRedisParams.redis_kwargs.startup_nodes);
}
const host = parsedRedisParams?.redis_client?.connection_pool?.connection_kwargs?.host ||
parsedRedisParams?.redis_async_client?.connection_pool?.connection_kwargs?.host;
const port = parsedRedisParams?.redis_client?.connection_pool?.connection_kwargs?.port ||
parsedRedisParams?.redis_async_client?.connection_pool?.connection_kwargs?.port;
return host && port ? JSON.stringify([{ host, port }]) : "N/A";
} catch (e) {
return "N/A";
}
})(),
namespace: parsedRedisParams?.namespace || "N/A",
};
return (
<div className="bg-white rounded-lg shadow">
<TabGroup>
<TabList className="border-b border-gray-200 px-4">
<Tab className="px-4 py-2 text-sm font-medium text-gray-600 hover:text-gray-800">Summary</Tab>
<Tab className="px-4 py-2 text-sm font-medium text-gray-600 hover:text-gray-800">Raw Response</Tab>
</TabList>
<TabPanels>
<TabPanel className="p-4">
<div>
<div className="flex items-center mb-6">
{(response?.status === "healthy") ? (
<CheckCircleIcon className="h-5 w-5 text-green-500 mr-2" />
) : (
<XCircleIcon className="h-5 w-5 text-red-500 mr-2" />
)}
<Text className={`text-sm font-medium ${response?.status === "healthy" ? "text-green-500" : "text-red-500"}`}>
Cache Status: {response?.status || "unhealthy"}
</Text>
</div>
<table className="w-full border-collapse">
<tbody>
{/* Show error message if present */}
{errorDetails && (
<>
<tr><td colSpan={2} className="pt-4 pb-2 font-semibold text-red-600">Error Details</td></tr>
<TableClickableErrorField
label="Error Message"
value={errorDetails.message}
/>
<TableClickableErrorField
label="Traceback"
value={errorDetails.traceback}
/>
</>
)}
{/* Always show cache details, regardless of error state */}
<tr><td colSpan={2} className="pt-4 pb-2 font-semibold">Cache Details</td></tr>
<TableClickableErrorField
label="Cache Configuration"
value={String(parsedLitellmParams?.type)}
/>
<TableClickableErrorField
label="Ping Response"
value={String(response.ping_response)}
/>
<TableClickableErrorField
label="Set Cache Response"
value={response.set_cache_response || "N/A"}
/>
<TableClickableErrorField
label="litellm_settings.cache_params"
value={JSON.stringify(parsedLitellmParams, null, 2)}
/>
{/* Redis Details Section */}
{parsedLitellmParams?.type === "redis" && (
<>
<tr><td colSpan={2} className="pt-4 pb-2 font-semibold">Redis Details</td></tr>
<TableClickableErrorField
label="Redis Host"
value={redisDetails.redis_host || "N/A"}
/>
<TableClickableErrorField
label="Redis Port"
value={redisDetails.redis_port || "N/A"}
/>
<TableClickableErrorField
label="Redis Version"
value={redisDetails.redis_version || "N/A"}
/>
<TableClickableErrorField
label="Startup Nodes"
value={redisDetails.startup_nodes || "N/A"}
/>
<TableClickableErrorField
label="Namespace"
value={redisDetails.namespace || "N/A"}
/>
</>
)}
</tbody>
</table>
</div>
</TabPanel>
<TabPanel className="p-4">
<div className="bg-gray-50 rounded-md p-4 font-mono text-sm">
<pre className="whitespace-pre-wrap break-words overflow-auto max-h-[500px]">
{(() => {
try {
const data = {
...response,
litellm_cache_params: parsedLitellmParams,
health_check_cache_params: parsedRedisParams
};
// First parse any string JSON values
const prettyData = JSON.parse(JSON.stringify(data, (key, value) => {
if (typeof value === 'string') {
try {
return JSON.parse(value);
} catch {
return value;
}
}
return value;
}));
// Then stringify with proper formatting
return JSON.stringify(prettyData, null, 2);
} catch (e) {
return "Error formatting JSON: " + (e as Error).message;
}
})()}
</pre>
</div>
</TabPanel>
</TabPanels>
</TabGroup>
</div>
);
};
export const CacheHealthTab: React.FC<{
accessToken: string | null;
healthCheckResponse: any;
runCachingHealthCheck: () => void;
responseTimeMs?: number | null;
}> = ({ accessToken, healthCheckResponse, runCachingHealthCheck, responseTimeMs }) => {
const [localResponseTimeMs, setLocalResponseTimeMs] = React.useState<number | null>(null);
const [isLoading, setIsLoading] = React.useState<boolean>(false);
const handleHealthCheck = async () => {
setIsLoading(true);
const startTime = performance.now();
await runCachingHealthCheck();
const endTime = performance.now();
setLocalResponseTimeMs(endTime - startTime);
setIsLoading(false);
};
return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<Button
onClick={handleHealthCheck}
disabled={isLoading}
className="bg-indigo-600 hover:bg-indigo-700 disabled:bg-indigo-400 text-white text-sm px-4 py-2 rounded-md"
>
{isLoading ? "Running Health Check..." : "Run Health Check"}
</Button>
<ResponseTimeIndicator responseTimeMs={localResponseTimeMs} />
</div>
{healthCheckResponse && (
<HealthCheckDetails response={healthCheckResponse} />
)}
</div>
);
};