Final UI V3

# UI V3 Changelog

Major updates and improvements in this release:

## Core Changes
- Complete NEW REWRITTEN UI system overhaul (V3) with semantic design tokens
- New settings management system with drag-and-drop capabilities
- Enhanced provider system supporting multiple AI services
- Improved theme system with better dark mode support
- New component library with consistent design patterns

## Technical Updates
- Reorganized project architecture for better maintainability
- Performance optimizations and bundle size improvements
- Enhanced security features and access controls
- Improved developer experience with better tooling
- Comprehensive testing infrastructure

## New Features
- Background rays effect for improved visual feedback
- Advanced tab management system
- Automatic and manual update support
- Enhanced error handling and visualization
- Improved accessibility across all components

For detailed information about all changes and improvements, please see the full changelog.
This commit is contained in:
Stijnus
2025-02-02 01:42:30 +01:00
parent 999d87b1e8
commit fc3dd8c84c
76 changed files with 4540 additions and 4936 deletions

View File

@@ -0,0 +1,135 @@
import { useState, useEffect } from 'react';
import type { ServiceStatus } from './types';
import { ProviderStatusCheckerFactory } from './provider-factory';
export default function ServiceStatusTab() {
const [serviceStatuses, setServiceStatuses] = useState<ServiceStatus[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const checkAllProviders = async () => {
try {
setLoading(true);
setError(null);
const providers = ProviderStatusCheckerFactory.getProviderNames();
const statuses: ServiceStatus[] = [];
for (const provider of providers) {
try {
const checker = ProviderStatusCheckerFactory.getChecker(provider);
const result = await checker.checkStatus();
statuses.push({
provider,
...result,
lastChecked: new Date().toISOString(),
});
} catch (err) {
console.error(`Error checking ${provider} status:`, err);
statuses.push({
provider,
status: 'degraded',
message: 'Unable to check service status',
incidents: ['Error checking service status'],
lastChecked: new Date().toISOString(),
});
}
}
setServiceStatuses(statuses);
} catch (err) {
console.error('Error checking provider statuses:', err);
setError('Failed to check service statuses');
} finally {
setLoading(false);
}
};
checkAllProviders();
// Set up periodic checks every 5 minutes
const interval = setInterval(checkAllProviders, 5 * 60 * 1000);
return () => clearInterval(interval);
}, []);
const getStatusColor = (status: ServiceStatus['status']) => {
switch (status) {
case 'operational':
return 'text-green-500 dark:text-green-400';
case 'degraded':
return 'text-yellow-500 dark:text-yellow-400';
case 'down':
return 'text-red-500 dark:text-red-400';
default:
return 'text-gray-500 dark:text-gray-400';
}
};
const getStatusIcon = (status: ServiceStatus['status']) => {
switch (status) {
case 'operational':
return 'i-ph:check-circle';
case 'degraded':
return 'i-ph:warning';
case 'down':
return 'i-ph:x-circle';
default:
return 'i-ph:question';
}
};
if (loading) {
return (
<div className="flex items-center justify-center h-full">
<div className="animate-spin i-ph:circle-notch w-8 h-8 text-purple-500" />
</div>
);
}
if (error) {
return (
<div className="flex flex-col items-center justify-center h-full text-red-500 dark:text-red-400">
<div className="i-ph:warning w-8 h-8 mb-2" />
<p>{error}</p>
</div>
);
}
return (
<div className="space-y-6">
<div className="grid grid-cols-1 gap-4">
{serviceStatuses.map((service) => (
<div
key={service.provider}
className="p-4 bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700"
>
<div className="flex items-center justify-between mb-2">
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">{service.provider}</h3>
<div className={`flex items-center ${getStatusColor(service.status)}`}>
<div className={`${getStatusIcon(service.status)} w-5 h-5 mr-2`} />
<span className="capitalize">{service.status}</span>
</div>
</div>
<p className="text-gray-600 dark:text-gray-300 mb-2">{service.message}</p>
{service.incidents && service.incidents.length > 0 && (
<div className="mt-2">
<h4 className="text-sm font-semibold text-gray-700 dark:text-gray-300 mb-1">Recent Incidents:</h4>
<ul className="text-sm text-gray-600 dark:text-gray-400 space-y-1">
{service.incidents.map((incident, index) => (
<li key={index}>{incident}</li>
))}
</ul>
</div>
)}
<div className="mt-2 text-xs text-gray-500 dark:text-gray-400">
Last checked: {new Date(service.lastChecked).toLocaleString()}
</div>
</div>
))}
</div>
</div>
);
}

View File

@@ -0,0 +1,121 @@
import type { ProviderConfig, StatusCheckResult, ApiResponse } from './types';
export abstract class BaseProviderChecker {
protected config: ProviderConfig;
constructor(config: ProviderConfig) {
this.config = config;
}
protected async checkApiEndpoint(
url: string,
headers?: Record<string, string>,
testModel?: string,
): Promise<{ ok: boolean; status: number | string; message?: string; responseTime: number }> {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000);
const startTime = performance.now();
// Add common headers
const processedHeaders = {
'Content-Type': 'application/json',
...headers,
};
const response = await fetch(url, {
method: 'GET',
headers: processedHeaders,
signal: controller.signal,
});
const endTime = performance.now();
const responseTime = endTime - startTime;
clearTimeout(timeoutId);
const data = (await response.json()) as ApiResponse;
if (!response.ok) {
let errorMessage = `API returned status: ${response.status}`;
if (data.error?.message) {
errorMessage = data.error.message;
} else if (data.message) {
errorMessage = data.message;
}
return {
ok: false,
status: response.status,
message: errorMessage,
responseTime,
};
}
// Different providers have different model list formats
let models: string[] = [];
if (Array.isArray(data)) {
models = data.map((model: { id?: string; name?: string }) => model.id || model.name || '');
} else if (data.data && Array.isArray(data.data)) {
models = data.data.map((model) => model.id || model.name || '');
} else if (data.models && Array.isArray(data.models)) {
models = data.models.map((model) => model.id || model.name || '');
} else if (data.model) {
models = [data.model];
}
if (!testModel || models.length > 0) {
return {
ok: true,
status: response.status,
responseTime,
message: 'API key is valid',
};
}
if (testModel && !models.includes(testModel)) {
return {
ok: true,
status: 'model_not_found',
message: `API key is valid (test model ${testModel} not found in ${models.length} available models)`,
responseTime,
};
}
return {
ok: true,
status: response.status,
message: 'API key is valid',
responseTime,
};
} catch (error) {
console.error(`Error checking API endpoint ${url}:`, error);
return {
ok: false,
status: error instanceof Error ? error.message : 'Unknown error',
message: error instanceof Error ? `Connection failed: ${error.message}` : 'Connection failed',
responseTime: 0,
};
}
}
protected async checkEndpoint(url: string): Promise<'reachable' | 'unreachable'> {
try {
const response = await fetch(url, {
mode: 'no-cors',
headers: {
Accept: 'text/html',
},
});
return response.type === 'opaque' ? 'reachable' : 'unreachable';
} catch (error) {
console.error(`Error checking ${url}:`, error);
return 'unreachable';
}
}
abstract checkStatus(): Promise<StatusCheckResult>;
}

View File

@@ -0,0 +1,154 @@
import type { ProviderName, ProviderConfig, StatusCheckResult } from './types';
import { BaseProviderChecker } from './base-provider';
import { AmazonBedrockStatusChecker } from './providers/amazon-bedrock';
import { CohereStatusChecker } from './providers/cohere';
import { DeepseekStatusChecker } from './providers/deepseek';
import { GoogleStatusChecker } from './providers/google';
import { GroqStatusChecker } from './providers/groq';
import { HuggingFaceStatusChecker } from './providers/huggingface';
import { HyperbolicStatusChecker } from './providers/hyperbolic';
import { MistralStatusChecker } from './providers/mistral';
import { OpenRouterStatusChecker } from './providers/openrouter';
import { PerplexityStatusChecker } from './providers/perplexity';
import { TogetherStatusChecker } from './providers/together';
import { XAIStatusChecker } from './providers/xai';
export class ProviderStatusCheckerFactory {
private static _providerConfigs: Record<ProviderName, ProviderConfig> = {
AmazonBedrock: {
statusUrl: 'https://health.aws.amazon.com/health/status',
apiUrl: 'https://bedrock.us-east-1.amazonaws.com/models',
headers: {},
testModel: 'anthropic.claude-3-sonnet-20240229-v1:0',
},
Cohere: {
statusUrl: 'https://status.cohere.com/',
apiUrl: 'https://api.cohere.ai/v1/models',
headers: {},
testModel: 'command',
},
Deepseek: {
statusUrl: 'https://status.deepseek.com/',
apiUrl: 'https://api.deepseek.com/v1/models',
headers: {},
testModel: 'deepseek-chat',
},
Google: {
statusUrl: 'https://status.cloud.google.com/',
apiUrl: 'https://generativelanguage.googleapis.com/v1/models',
headers: {},
testModel: 'gemini-pro',
},
Groq: {
statusUrl: 'https://groqstatus.com/',
apiUrl: 'https://api.groq.com/v1/models',
headers: {},
testModel: 'mixtral-8x7b-32768',
},
HuggingFace: {
statusUrl: 'https://status.huggingface.co/',
apiUrl: 'https://api-inference.huggingface.co/models',
headers: {},
testModel: 'mistralai/Mixtral-8x7B-Instruct-v0.1',
},
Hyperbolic: {
statusUrl: 'https://status.hyperbolic.ai/',
apiUrl: 'https://api.hyperbolic.ai/v1/models',
headers: {},
testModel: 'hyperbolic-1',
},
Mistral: {
statusUrl: 'https://status.mistral.ai/',
apiUrl: 'https://api.mistral.ai/v1/models',
headers: {},
testModel: 'mistral-tiny',
},
OpenRouter: {
statusUrl: 'https://status.openrouter.ai/',
apiUrl: 'https://openrouter.ai/api/v1/models',
headers: {},
testModel: 'anthropic/claude-3-sonnet',
},
Perplexity: {
statusUrl: 'https://status.perplexity.com/',
apiUrl: 'https://api.perplexity.ai/v1/models',
headers: {},
testModel: 'pplx-7b-chat',
},
Together: {
statusUrl: 'https://status.together.ai/',
apiUrl: 'https://api.together.xyz/v1/models',
headers: {},
testModel: 'mistralai/Mixtral-8x7B-Instruct-v0.1',
},
XAI: {
statusUrl: 'https://status.x.ai/',
apiUrl: 'https://api.x.ai/v1/models',
headers: {},
testModel: 'grok-1',
},
};
static getChecker(provider: ProviderName): BaseProviderChecker {
const config = this._providerConfigs[provider];
if (!config) {
throw new Error(`No configuration found for provider: ${provider}`);
}
switch (provider) {
case 'AmazonBedrock':
return new AmazonBedrockStatusChecker(config);
case 'Cohere':
return new CohereStatusChecker(config);
case 'Deepseek':
return new DeepseekStatusChecker(config);
case 'Google':
return new GoogleStatusChecker(config);
case 'Groq':
return new GroqStatusChecker(config);
case 'HuggingFace':
return new HuggingFaceStatusChecker(config);
case 'Hyperbolic':
return new HyperbolicStatusChecker(config);
case 'Mistral':
return new MistralStatusChecker(config);
case 'OpenRouter':
return new OpenRouterStatusChecker(config);
case 'Perplexity':
return new PerplexityStatusChecker(config);
case 'Together':
return new TogetherStatusChecker(config);
case 'XAI':
return new XAIStatusChecker(config);
default:
return new (class extends BaseProviderChecker {
async checkStatus(): Promise<StatusCheckResult> {
const endpointStatus = await this.checkEndpoint(this.config.statusUrl);
const apiStatus = await this.checkEndpoint(this.config.apiUrl);
return {
status: endpointStatus === 'reachable' && apiStatus === 'reachable' ? 'operational' : 'degraded',
message: `Status page: ${endpointStatus}, API: ${apiStatus}`,
incidents: ['Note: Limited status information due to CORS restrictions'],
};
}
})(config);
}
}
static getProviderNames(): ProviderName[] {
return Object.keys(this._providerConfigs) as ProviderName[];
}
static getProviderConfig(provider: ProviderName): ProviderConfig {
const config = this._providerConfigs[provider];
if (!config) {
throw new Error(`Unknown provider: ${provider}`);
}
return config;
}
}

View File

@@ -0,0 +1,76 @@
import { BaseProviderChecker } from '~/components/@settings/tabs/providers/service-status/base-provider';
import type { StatusCheckResult } from '~/components/@settings/tabs/providers/service-status/types';
export class AmazonBedrockStatusChecker extends BaseProviderChecker {
async checkStatus(): Promise<StatusCheckResult> {
try {
// Check AWS health status page
const statusPageResponse = await fetch('https://health.aws.amazon.com/health/status');
const text = await statusPageResponse.text();
// Check for Bedrock and general AWS status
const hasBedrockIssues =
text.includes('Amazon Bedrock') &&
(text.includes('Service is experiencing elevated error rates') ||
text.includes('Service disruption') ||
text.includes('Degraded Service'));
const hasGeneralIssues = text.includes('Service disruption') || text.includes('Multiple services affected');
// Extract incidents
const incidents: string[] = [];
const incidentMatches = text.matchAll(/(\d{4}-\d{2}-\d{2})\s+(.*?)\s+Impact:(.*?)(?=\n|$)/g);
for (const match of incidentMatches) {
const [, date, title, impact] = match;
if (title.includes('Bedrock') || title.includes('AWS')) {
incidents.push(`${date}: ${title.trim()} - Impact: ${impact.trim()}`);
}
}
let status: StatusCheckResult['status'] = 'operational';
let message = 'All services operational';
if (hasBedrockIssues) {
status = 'degraded';
message = 'Amazon Bedrock service issues reported';
} else if (hasGeneralIssues) {
status = 'degraded';
message = 'AWS experiencing general issues';
}
// If status page check fails, fallback to endpoint check
if (!statusPageResponse.ok) {
const endpointStatus = await this.checkEndpoint('https://health.aws.amazon.com/health/status');
const apiEndpoint = 'https://bedrock.us-east-1.amazonaws.com/models';
const apiStatus = await this.checkEndpoint(apiEndpoint);
return {
status: endpointStatus === 'reachable' && apiStatus === 'reachable' ? 'operational' : 'degraded',
message: `Status page: ${endpointStatus}, API: ${apiStatus}`,
incidents: ['Note: Limited status information due to CORS restrictions'],
};
}
return {
status,
message,
incidents: incidents.slice(0, 5),
};
} catch (error) {
console.error('Error checking Amazon Bedrock status:', error);
// Fallback to basic endpoint check
const endpointStatus = await this.checkEndpoint('https://health.aws.amazon.com/health/status');
const apiEndpoint = 'https://bedrock.us-east-1.amazonaws.com/models';
const apiStatus = await this.checkEndpoint(apiEndpoint);
return {
status: endpointStatus === 'reachable' && apiStatus === 'reachable' ? 'operational' : 'degraded',
message: `Status page: ${endpointStatus}, API: ${apiStatus}`,
incidents: ['Note: Limited status information due to CORS restrictions'],
};
}
}
}

View File

@@ -0,0 +1,80 @@
import { BaseProviderChecker } from '~/components/@settings/tabs/providers/service-status/base-provider';
import type { StatusCheckResult } from '~/components/@settings/tabs/providers/service-status/types';
export class AnthropicStatusChecker extends BaseProviderChecker {
async checkStatus(): Promise<StatusCheckResult> {
try {
// Check status page
const statusPageResponse = await fetch('https://status.anthropic.com/');
const text = await statusPageResponse.text();
// Check for specific Anthropic status indicators
const isOperational = text.includes('All Systems Operational');
const hasDegradedPerformance = text.includes('Degraded Performance');
const hasPartialOutage = text.includes('Partial Outage');
const hasMajorOutage = text.includes('Major Outage');
// Extract incidents
const incidents: string[] = [];
const incidentSection = text.match(/Past Incidents(.*?)(?=\n\n)/s);
if (incidentSection) {
const incidentLines = incidentSection[1]
.split('\n')
.map((line) => line.trim())
.filter((line) => line && line.includes('202')); // Only get dated incidents
incidents.push(...incidentLines.slice(0, 5));
}
let status: StatusCheckResult['status'] = 'operational';
let message = 'All systems operational';
if (hasMajorOutage) {
status = 'down';
message = 'Major service outage';
} else if (hasPartialOutage) {
status = 'down';
message = 'Partial service outage';
} else if (hasDegradedPerformance) {
status = 'degraded';
message = 'Service experiencing degraded performance';
} else if (!isOperational) {
status = 'degraded';
message = 'Service status unknown';
}
// If status page check fails, fallback to endpoint check
if (!statusPageResponse.ok) {
const endpointStatus = await this.checkEndpoint('https://status.anthropic.com/');
const apiEndpoint = 'https://api.anthropic.com/v1/messages';
const apiStatus = await this.checkEndpoint(apiEndpoint);
return {
status: endpointStatus === 'reachable' && apiStatus === 'reachable' ? 'operational' : 'degraded',
message: `Status page: ${endpointStatus}, API: ${apiStatus}`,
incidents: ['Note: Limited status information due to CORS restrictions'],
};
}
return {
status,
message,
incidents,
};
} catch (error) {
console.error('Error checking Anthropic status:', error);
// Fallback to basic endpoint check
const endpointStatus = await this.checkEndpoint('https://status.anthropic.com/');
const apiEndpoint = 'https://api.anthropic.com/v1/messages';
const apiStatus = await this.checkEndpoint(apiEndpoint);
return {
status: endpointStatus === 'reachable' && apiStatus === 'reachable' ? 'operational' : 'degraded',
message: `Status page: ${endpointStatus}, API: ${apiStatus}`,
incidents: ['Note: Limited status information due to CORS restrictions'],
};
}
}
}

View File

@@ -0,0 +1,91 @@
import { BaseProviderChecker } from '~/components/@settings/tabs/providers/service-status/base-provider';
import type { StatusCheckResult } from '~/components/@settings/tabs/providers/service-status/types';
export class CohereStatusChecker extends BaseProviderChecker {
async checkStatus(): Promise<StatusCheckResult> {
try {
// Check status page
const statusPageResponse = await fetch('https://status.cohere.com/');
const text = await statusPageResponse.text();
// Check for specific Cohere status indicators
const isOperational = text.includes('All Systems Operational');
const hasIncidents = text.includes('Active Incidents');
const hasDegradation = text.includes('Degraded Performance');
const hasOutage = text.includes('Service Outage');
// Extract incidents
const incidents: string[] = [];
const incidentSection = text.match(/Past Incidents(.*?)(?=\n\n)/s);
if (incidentSection) {
const incidentLines = incidentSection[1]
.split('\n')
.map((line) => line.trim())
.filter((line) => line && line.includes('202')); // Only get dated incidents
incidents.push(...incidentLines.slice(0, 5));
}
// Check specific services
const services = {
api: {
operational: text.includes('API Service') && text.includes('Operational'),
degraded: text.includes('API Service') && text.includes('Degraded Performance'),
outage: text.includes('API Service') && text.includes('Service Outage'),
},
generation: {
operational: text.includes('Generation Service') && text.includes('Operational'),
degraded: text.includes('Generation Service') && text.includes('Degraded Performance'),
outage: text.includes('Generation Service') && text.includes('Service Outage'),
},
};
let status: StatusCheckResult['status'] = 'operational';
let message = 'All systems operational';
if (services.api.outage || services.generation.outage || hasOutage) {
status = 'down';
message = 'Service outage detected';
} else if (services.api.degraded || services.generation.degraded || hasDegradation || hasIncidents) {
status = 'degraded';
message = 'Service experiencing issues';
} else if (!isOperational) {
status = 'degraded';
message = 'Service status unknown';
}
// If status page check fails, fallback to endpoint check
if (!statusPageResponse.ok) {
const endpointStatus = await this.checkEndpoint('https://status.cohere.com/');
const apiEndpoint = 'https://api.cohere.ai/v1/models';
const apiStatus = await this.checkEndpoint(apiEndpoint);
return {
status: endpointStatus === 'reachable' && apiStatus === 'reachable' ? 'operational' : 'degraded',
message: `Status page: ${endpointStatus}, API: ${apiStatus}`,
incidents: ['Note: Limited status information due to CORS restrictions'],
};
}
return {
status,
message,
incidents,
};
} catch (error) {
console.error('Error checking Cohere status:', error);
// Fallback to basic endpoint check
const endpointStatus = await this.checkEndpoint('https://status.cohere.com/');
const apiEndpoint = 'https://api.cohere.ai/v1/models';
const apiStatus = await this.checkEndpoint(apiEndpoint);
return {
status: endpointStatus === 'reachable' && apiStatus === 'reachable' ? 'operational' : 'degraded',
message: `Status page: ${endpointStatus}, API: ${apiStatus}`,
incidents: ['Note: Limited status information due to CORS restrictions'],
};
}
}
}

View File

@@ -0,0 +1,40 @@
import { BaseProviderChecker } from '~/components/@settings/tabs/providers/service-status/base-provider';
import type { StatusCheckResult } from '~/components/@settings/tabs/providers/service-status/types';
export class DeepseekStatusChecker extends BaseProviderChecker {
async checkStatus(): Promise<StatusCheckResult> {
try {
/*
* Check status page - Note: Deepseek doesn't have a public status page yet
* so we'll check their API endpoint directly
*/
const apiEndpoint = 'https://api.deepseek.com/v1/models';
const apiStatus = await this.checkEndpoint(apiEndpoint);
// Check their website as a secondary indicator
const websiteStatus = await this.checkEndpoint('https://deepseek.com');
let status: StatusCheckResult['status'] = 'operational';
let message = 'All systems operational';
if (apiStatus !== 'reachable' || websiteStatus !== 'reachable') {
status = apiStatus !== 'reachable' ? 'down' : 'degraded';
message = apiStatus !== 'reachable' ? 'API appears to be down' : 'Service may be experiencing issues';
}
return {
status,
message,
incidents: [], // No public incident tracking available yet
};
} catch (error) {
console.error('Error checking Deepseek status:', error);
return {
status: 'degraded',
message: 'Unable to determine service status',
incidents: ['Note: Limited status information available'],
};
}
}
}

View File

@@ -0,0 +1,77 @@
import { BaseProviderChecker } from '~/components/@settings/tabs/providers/service-status/base-provider';
import type { StatusCheckResult } from '~/components/@settings/tabs/providers/service-status/types';
export class GoogleStatusChecker extends BaseProviderChecker {
async checkStatus(): Promise<StatusCheckResult> {
try {
// Check status page
const statusPageResponse = await fetch('https://status.cloud.google.com/');
const text = await statusPageResponse.text();
// Check for Vertex AI and general cloud status
const hasVertexAIIssues =
text.includes('Vertex AI') &&
(text.includes('Incident') ||
text.includes('Disruption') ||
text.includes('Outage') ||
text.includes('degraded'));
const hasGeneralIssues = text.includes('Major Incidents') || text.includes('Service Disruption');
// Extract incidents
const incidents: string[] = [];
const incidentMatches = text.matchAll(/(\d{4}-\d{2}-\d{2})\s+(.*?)\s+Impact:(.*?)(?=\n|$)/g);
for (const match of incidentMatches) {
const [, date, title, impact] = match;
if (title.includes('Vertex AI') || title.includes('Cloud')) {
incidents.push(`${date}: ${title.trim()} - Impact: ${impact.trim()}`);
}
}
let status: StatusCheckResult['status'] = 'operational';
let message = 'All services operational';
if (hasVertexAIIssues) {
status = 'degraded';
message = 'Vertex AI service issues reported';
} else if (hasGeneralIssues) {
status = 'degraded';
message = 'Google Cloud experiencing issues';
}
// If status page check fails, fallback to endpoint check
if (!statusPageResponse.ok) {
const endpointStatus = await this.checkEndpoint('https://status.cloud.google.com/');
const apiEndpoint = 'https://generativelanguage.googleapis.com/v1/models';
const apiStatus = await this.checkEndpoint(apiEndpoint);
return {
status: endpointStatus === 'reachable' && apiStatus === 'reachable' ? 'operational' : 'degraded',
message: `Status page: ${endpointStatus}, API: ${apiStatus}`,
incidents: ['Note: Limited status information due to CORS restrictions'],
};
}
return {
status,
message,
incidents: incidents.slice(0, 5),
};
} catch (error) {
console.error('Error checking Google status:', error);
// Fallback to basic endpoint check
const endpointStatus = await this.checkEndpoint('https://status.cloud.google.com/');
const apiEndpoint = 'https://generativelanguage.googleapis.com/v1/models';
const apiStatus = await this.checkEndpoint(apiEndpoint);
return {
status: endpointStatus === 'reachable' && apiStatus === 'reachable' ? 'operational' : 'degraded',
message: `Status page: ${endpointStatus}, API: ${apiStatus}`,
incidents: ['Note: Limited status information due to CORS restrictions'],
};
}
}
}

View File

@@ -0,0 +1,72 @@
import { BaseProviderChecker } from '~/components/@settings/tabs/providers/service-status/base-provider';
import type { StatusCheckResult } from '~/components/@settings/tabs/providers/service-status/types';
export class GroqStatusChecker extends BaseProviderChecker {
async checkStatus(): Promise<StatusCheckResult> {
try {
// Check status page
const statusPageResponse = await fetch('https://groqstatus.com/');
const text = await statusPageResponse.text();
const isOperational = text.includes('All Systems Operational');
const hasIncidents = text.includes('Active Incidents');
const hasDegradation = text.includes('Degraded Performance');
const hasOutage = text.includes('Service Outage');
// Extract incidents
const incidents: string[] = [];
const incidentMatches = text.matchAll(/(\d{4}-\d{2}-\d{2})\s+(.*?)\s+Status:(.*?)(?=\n|$)/g);
for (const match of incidentMatches) {
const [, date, title, status] = match;
incidents.push(`${date}: ${title.trim()} - ${status.trim()}`);
}
let status: StatusCheckResult['status'] = 'operational';
let message = 'All systems operational';
if (hasOutage) {
status = 'down';
message = 'Service outage detected';
} else if (hasDegradation || hasIncidents) {
status = 'degraded';
message = 'Service experiencing issues';
} else if (!isOperational) {
status = 'degraded';
message = 'Service status unknown';
}
// If status page check fails, fallback to endpoint check
if (!statusPageResponse.ok) {
const endpointStatus = await this.checkEndpoint('https://groqstatus.com/');
const apiEndpoint = 'https://api.groq.com/v1/models';
const apiStatus = await this.checkEndpoint(apiEndpoint);
return {
status: endpointStatus === 'reachable' && apiStatus === 'reachable' ? 'operational' : 'degraded',
message: `Status page: ${endpointStatus}, API: ${apiStatus}`,
incidents: ['Note: Limited status information due to CORS restrictions'],
};
}
return {
status,
message,
incidents: incidents.slice(0, 5),
};
} catch (error) {
console.error('Error checking Groq status:', error);
// Fallback to basic endpoint check
const endpointStatus = await this.checkEndpoint('https://groqstatus.com/');
const apiEndpoint = 'https://api.groq.com/v1/models';
const apiStatus = await this.checkEndpoint(apiEndpoint);
return {
status: endpointStatus === 'reachable' && apiStatus === 'reachable' ? 'operational' : 'degraded',
message: `Status page: ${endpointStatus}, API: ${apiStatus}`,
incidents: ['Note: Limited status information due to CORS restrictions'],
};
}
}
}

View File

@@ -0,0 +1,98 @@
import { BaseProviderChecker } from '~/components/@settings/tabs/providers/service-status/base-provider';
import type { StatusCheckResult } from '~/components/@settings/tabs/providers/service-status/types';
export class HuggingFaceStatusChecker extends BaseProviderChecker {
async checkStatus(): Promise<StatusCheckResult> {
try {
// Check status page
const statusPageResponse = await fetch('https://status.huggingface.co/');
const text = await statusPageResponse.text();
// Check for "All services are online" message
const allServicesOnline = text.includes('All services are online');
// Get last update time
const lastUpdateMatch = text.match(/Last updated on (.*?)(EST|PST|GMT)/);
const lastUpdate = lastUpdateMatch ? `${lastUpdateMatch[1]}${lastUpdateMatch[2]}` : '';
// Check individual services and their uptime percentages
const services = {
'Huggingface Hub': {
operational: text.includes('Huggingface Hub') && text.includes('Operational'),
uptime: text.match(/Huggingface Hub[\s\S]*?(\d+\.\d+)%\s*uptime/)?.[1],
},
'Git Hosting and Serving': {
operational: text.includes('Git Hosting and Serving') && text.includes('Operational'),
uptime: text.match(/Git Hosting and Serving[\s\S]*?(\d+\.\d+)%\s*uptime/)?.[1],
},
'Inference API': {
operational: text.includes('Inference API') && text.includes('Operational'),
uptime: text.match(/Inference API[\s\S]*?(\d+\.\d+)%\s*uptime/)?.[1],
},
'HF Endpoints': {
operational: text.includes('HF Endpoints') && text.includes('Operational'),
uptime: text.match(/HF Endpoints[\s\S]*?(\d+\.\d+)%\s*uptime/)?.[1],
},
Spaces: {
operational: text.includes('Spaces') && text.includes('Operational'),
uptime: text.match(/Spaces[\s\S]*?(\d+\.\d+)%\s*uptime/)?.[1],
},
};
// Create service status messages with uptime
const serviceMessages = Object.entries(services).map(([name, info]) => {
if (info.uptime) {
return `${name}: ${info.uptime}% uptime`;
}
return `${name}: ${info.operational ? 'Operational' : 'Issues detected'}`;
});
// Determine overall status
let status: StatusCheckResult['status'] = 'operational';
let message = allServicesOnline
? `All services are online (Last updated on ${lastUpdate})`
: 'Checking individual services';
// Only mark as degraded if we explicitly detect issues
const hasIssues = Object.values(services).some((service) => !service.operational);
if (hasIssues) {
status = 'degraded';
message = `Service issues detected (Last updated on ${lastUpdate})`;
}
// If status page check fails, fallback to endpoint check
if (!statusPageResponse.ok) {
const endpointStatus = await this.checkEndpoint('https://status.huggingface.co/');
const apiEndpoint = 'https://api-inference.huggingface.co/models';
const apiStatus = await this.checkEndpoint(apiEndpoint);
return {
status: endpointStatus === 'reachable' && apiStatus === 'reachable' ? 'operational' : 'degraded',
message: `Status page: ${endpointStatus}, API: ${apiStatus}`,
incidents: ['Note: Limited status information due to CORS restrictions'],
};
}
return {
status,
message,
incidents: serviceMessages,
};
} catch (error) {
console.error('Error checking HuggingFace status:', error);
// Fallback to basic endpoint check
const endpointStatus = await this.checkEndpoint('https://status.huggingface.co/');
const apiEndpoint = 'https://api-inference.huggingface.co/models';
const apiStatus = await this.checkEndpoint(apiEndpoint);
return {
status: endpointStatus === 'reachable' && apiStatus === 'reachable' ? 'operational' : 'degraded',
message: `Status page: ${endpointStatus}, API: ${apiStatus}`,
incidents: ['Note: Limited status information due to CORS restrictions'],
};
}
}
}

View File

@@ -0,0 +1,40 @@
import { BaseProviderChecker } from '~/components/@settings/tabs/providers/service-status/base-provider';
import type { StatusCheckResult } from '~/components/@settings/tabs/providers/service-status/types';
export class HyperbolicStatusChecker extends BaseProviderChecker {
async checkStatus(): Promise<StatusCheckResult> {
try {
/*
* Check API endpoint directly since Hyperbolic is a newer provider
* and may not have a public status page yet
*/
const apiEndpoint = 'https://api.hyperbolic.ai/v1/models';
const apiStatus = await this.checkEndpoint(apiEndpoint);
// Check their website as a secondary indicator
const websiteStatus = await this.checkEndpoint('https://hyperbolic.ai');
let status: StatusCheckResult['status'] = 'operational';
let message = 'All systems operational';
if (apiStatus !== 'reachable' || websiteStatus !== 'reachable') {
status = apiStatus !== 'reachable' ? 'down' : 'degraded';
message = apiStatus !== 'reachable' ? 'API appears to be down' : 'Service may be experiencing issues';
}
return {
status,
message,
incidents: [], // No public incident tracking available yet
};
} catch (error) {
console.error('Error checking Hyperbolic status:', error);
return {
status: 'degraded',
message: 'Unable to determine service status',
incidents: ['Note: Limited status information available'],
};
}
}
}

View File

@@ -0,0 +1,76 @@
import { BaseProviderChecker } from '~/components/@settings/tabs/providers/service-status/base-provider';
import type { StatusCheckResult } from '~/components/@settings/tabs/providers/service-status/types';
export class MistralStatusChecker extends BaseProviderChecker {
async checkStatus(): Promise<StatusCheckResult> {
try {
// Check status page
const statusPageResponse = await fetch('https://status.mistral.ai/');
const text = await statusPageResponse.text();
const isOperational = text.includes('All Systems Operational');
const hasIncidents = text.includes('Active Incidents');
const hasDegradation = text.includes('Degraded Performance');
const hasOutage = text.includes('Service Outage');
// Extract incidents
const incidents: string[] = [];
const incidentSection = text.match(/Recent Events(.*?)(?=\n\n)/s);
if (incidentSection) {
const incidentLines = incidentSection[1]
.split('\n')
.map((line) => line.trim())
.filter((line) => line && !line.includes('No incidents'));
incidents.push(...incidentLines.slice(0, 5));
}
let status: StatusCheckResult['status'] = 'operational';
let message = 'All systems operational';
if (hasOutage) {
status = 'down';
message = 'Service outage detected';
} else if (hasDegradation || hasIncidents) {
status = 'degraded';
message = 'Service experiencing issues';
} else if (!isOperational) {
status = 'degraded';
message = 'Service status unknown';
}
// If status page check fails, fallback to endpoint check
if (!statusPageResponse.ok) {
const endpointStatus = await this.checkEndpoint('https://status.mistral.ai/');
const apiEndpoint = 'https://api.mistral.ai/v1/models';
const apiStatus = await this.checkEndpoint(apiEndpoint);
return {
status: endpointStatus === 'reachable' && apiStatus === 'reachable' ? 'operational' : 'degraded',
message: `Status page: ${endpointStatus}, API: ${apiStatus}`,
incidents: ['Note: Limited status information due to CORS restrictions'],
};
}
return {
status,
message,
incidents,
};
} catch (error) {
console.error('Error checking Mistral status:', error);
// Fallback to basic endpoint check
const endpointStatus = await this.checkEndpoint('https://status.mistral.ai/');
const apiEndpoint = 'https://api.mistral.ai/v1/models';
const apiStatus = await this.checkEndpoint(apiEndpoint);
return {
status: endpointStatus === 'reachable' && apiStatus === 'reachable' ? 'operational' : 'degraded',
message: `Status page: ${endpointStatus}, API: ${apiStatus}`,
incidents: ['Note: Limited status information due to CORS restrictions'],
};
}
}
}

View File

@@ -0,0 +1,99 @@
import { BaseProviderChecker } from '~/components/@settings/tabs/providers/service-status/base-provider';
import type { StatusCheckResult } from '~/components/@settings/tabs/providers/service-status/types';
export class OpenAIStatusChecker extends BaseProviderChecker {
async checkStatus(): Promise<StatusCheckResult> {
try {
// Check status page
const statusPageResponse = await fetch('https://status.openai.com/');
const text = await statusPageResponse.text();
// Check individual services
const services = {
api: {
operational: text.includes('API ? Operational'),
degraded: text.includes('API ? Degraded Performance'),
outage: text.includes('API ? Major Outage') || text.includes('API ? Partial Outage'),
},
chat: {
operational: text.includes('ChatGPT ? Operational'),
degraded: text.includes('ChatGPT ? Degraded Performance'),
outage: text.includes('ChatGPT ? Major Outage') || text.includes('ChatGPT ? Partial Outage'),
},
};
// Extract recent incidents
const incidents: string[] = [];
const incidentMatches = text.match(/Past Incidents(.*?)(?=\w+ \d+, \d{4})/s);
if (incidentMatches) {
const recentIncidents = incidentMatches[1]
.split('\n')
.map((line) => line.trim())
.filter((line) => line && line.includes('202')); // Get only dated incidents
incidents.push(...recentIncidents.slice(0, 5));
}
// Determine overall status
let status: StatusCheckResult['status'] = 'operational';
const messages: string[] = [];
if (services.api.outage || services.chat.outage) {
status = 'down';
if (services.api.outage) {
messages.push('API: Major Outage');
}
if (services.chat.outage) {
messages.push('ChatGPT: Major Outage');
}
} else if (services.api.degraded || services.chat.degraded) {
status = 'degraded';
if (services.api.degraded) {
messages.push('API: Degraded Performance');
}
if (services.chat.degraded) {
messages.push('ChatGPT: Degraded Performance');
}
} else if (services.api.operational) {
messages.push('API: Operational');
}
// If status page check fails, fallback to endpoint check
if (!statusPageResponse.ok) {
const endpointStatus = await this.checkEndpoint('https://status.openai.com/');
const apiEndpoint = 'https://api.openai.com/v1/models';
const apiStatus = await this.checkEndpoint(apiEndpoint);
return {
status: endpointStatus === 'reachable' && apiStatus === 'reachable' ? 'operational' : 'degraded',
message: `Status page: ${endpointStatus}, API: ${apiStatus}`,
incidents: ['Note: Limited status information due to CORS restrictions'],
};
}
return {
status,
message: messages.join(', ') || 'Status unknown',
incidents,
};
} catch (error) {
console.error('Error checking OpenAI status:', error);
// Fallback to basic endpoint check
const endpointStatus = await this.checkEndpoint('https://status.openai.com/');
const apiEndpoint = 'https://api.openai.com/v1/models';
const apiStatus = await this.checkEndpoint(apiEndpoint);
return {
status: endpointStatus === 'reachable' && apiStatus === 'reachable' ? 'operational' : 'degraded',
message: `Status page: ${endpointStatus}, API: ${apiStatus}`,
incidents: ['Note: Limited status information due to CORS restrictions'],
};
}
}
}

View File

@@ -0,0 +1,91 @@
import { BaseProviderChecker } from '~/components/@settings/tabs/providers/service-status/base-provider';
import type { StatusCheckResult } from '~/components/@settings/tabs/providers/service-status/types';
export class OpenRouterStatusChecker extends BaseProviderChecker {
async checkStatus(): Promise<StatusCheckResult> {
try {
// Check status page
const statusPageResponse = await fetch('https://status.openrouter.ai/');
const text = await statusPageResponse.text();
// Check for specific OpenRouter status indicators
const isOperational = text.includes('All Systems Operational');
const hasIncidents = text.includes('Active Incidents');
const hasDegradation = text.includes('Degraded Performance');
const hasOutage = text.includes('Service Outage');
// Extract incidents
const incidents: string[] = [];
const incidentSection = text.match(/Past Incidents(.*?)(?=\n\n)/s);
if (incidentSection) {
const incidentLines = incidentSection[1]
.split('\n')
.map((line) => line.trim())
.filter((line) => line && line.includes('202')); // Only get dated incidents
incidents.push(...incidentLines.slice(0, 5));
}
// Check specific services
const services = {
api: {
operational: text.includes('API Service') && text.includes('Operational'),
degraded: text.includes('API Service') && text.includes('Degraded Performance'),
outage: text.includes('API Service') && text.includes('Service Outage'),
},
routing: {
operational: text.includes('Routing Service') && text.includes('Operational'),
degraded: text.includes('Routing Service') && text.includes('Degraded Performance'),
outage: text.includes('Routing Service') && text.includes('Service Outage'),
},
};
let status: StatusCheckResult['status'] = 'operational';
let message = 'All systems operational';
if (services.api.outage || services.routing.outage || hasOutage) {
status = 'down';
message = 'Service outage detected';
} else if (services.api.degraded || services.routing.degraded || hasDegradation || hasIncidents) {
status = 'degraded';
message = 'Service experiencing issues';
} else if (!isOperational) {
status = 'degraded';
message = 'Service status unknown';
}
// If status page check fails, fallback to endpoint check
if (!statusPageResponse.ok) {
const endpointStatus = await this.checkEndpoint('https://status.openrouter.ai/');
const apiEndpoint = 'https://openrouter.ai/api/v1/models';
const apiStatus = await this.checkEndpoint(apiEndpoint);
return {
status: endpointStatus === 'reachable' && apiStatus === 'reachable' ? 'operational' : 'degraded',
message: `Status page: ${endpointStatus}, API: ${apiStatus}`,
incidents: ['Note: Limited status information due to CORS restrictions'],
};
}
return {
status,
message,
incidents,
};
} catch (error) {
console.error('Error checking OpenRouter status:', error);
// Fallback to basic endpoint check
const endpointStatus = await this.checkEndpoint('https://status.openrouter.ai/');
const apiEndpoint = 'https://openrouter.ai/api/v1/models';
const apiStatus = await this.checkEndpoint(apiEndpoint);
return {
status: endpointStatus === 'reachable' && apiStatus === 'reachable' ? 'operational' : 'degraded',
message: `Status page: ${endpointStatus}, API: ${apiStatus}`,
incidents: ['Note: Limited status information due to CORS restrictions'],
};
}
}
}

View File

@@ -0,0 +1,91 @@
import { BaseProviderChecker } from '~/components/@settings/tabs/providers/service-status/base-provider';
import type { StatusCheckResult } from '~/components/@settings/tabs/providers/service-status/types';
export class PerplexityStatusChecker extends BaseProviderChecker {
async checkStatus(): Promise<StatusCheckResult> {
try {
// Check status page
const statusPageResponse = await fetch('https://status.perplexity.ai/');
const text = await statusPageResponse.text();
// Check for specific Perplexity status indicators
const isOperational = text.includes('All Systems Operational');
const hasIncidents = text.includes('Active Incidents');
const hasDegradation = text.includes('Degraded Performance');
const hasOutage = text.includes('Service Outage');
// Extract incidents
const incidents: string[] = [];
const incidentSection = text.match(/Past Incidents(.*?)(?=\n\n)/s);
if (incidentSection) {
const incidentLines = incidentSection[1]
.split('\n')
.map((line) => line.trim())
.filter((line) => line && line.includes('202')); // Only get dated incidents
incidents.push(...incidentLines.slice(0, 5));
}
// Check specific services
const services = {
api: {
operational: text.includes('API Service') && text.includes('Operational'),
degraded: text.includes('API Service') && text.includes('Degraded Performance'),
outage: text.includes('API Service') && text.includes('Service Outage'),
},
inference: {
operational: text.includes('Inference Service') && text.includes('Operational'),
degraded: text.includes('Inference Service') && text.includes('Degraded Performance'),
outage: text.includes('Inference Service') && text.includes('Service Outage'),
},
};
let status: StatusCheckResult['status'] = 'operational';
let message = 'All systems operational';
if (services.api.outage || services.inference.outage || hasOutage) {
status = 'down';
message = 'Service outage detected';
} else if (services.api.degraded || services.inference.degraded || hasDegradation || hasIncidents) {
status = 'degraded';
message = 'Service experiencing issues';
} else if (!isOperational) {
status = 'degraded';
message = 'Service status unknown';
}
// If status page check fails, fallback to endpoint check
if (!statusPageResponse.ok) {
const endpointStatus = await this.checkEndpoint('https://status.perplexity.ai/');
const apiEndpoint = 'https://api.perplexity.ai/v1/models';
const apiStatus = await this.checkEndpoint(apiEndpoint);
return {
status: endpointStatus === 'reachable' && apiStatus === 'reachable' ? 'operational' : 'degraded',
message: `Status page: ${endpointStatus}, API: ${apiStatus}`,
incidents: ['Note: Limited status information due to CORS restrictions'],
};
}
return {
status,
message,
incidents,
};
} catch (error) {
console.error('Error checking Perplexity status:', error);
// Fallback to basic endpoint check
const endpointStatus = await this.checkEndpoint('https://status.perplexity.ai/');
const apiEndpoint = 'https://api.perplexity.ai/v1/models';
const apiStatus = await this.checkEndpoint(apiEndpoint);
return {
status: endpointStatus === 'reachable' && apiStatus === 'reachable' ? 'operational' : 'degraded',
message: `Status page: ${endpointStatus}, API: ${apiStatus}`,
incidents: ['Note: Limited status information due to CORS restrictions'],
};
}
}
}

View File

@@ -0,0 +1,91 @@
import { BaseProviderChecker } from '~/components/@settings/tabs/providers/service-status/base-provider';
import type { StatusCheckResult } from '~/components/@settings/tabs/providers/service-status/types';
export class TogetherStatusChecker extends BaseProviderChecker {
async checkStatus(): Promise<StatusCheckResult> {
try {
// Check status page
const statusPageResponse = await fetch('https://status.together.ai/');
const text = await statusPageResponse.text();
// Check for specific Together status indicators
const isOperational = text.includes('All Systems Operational');
const hasIncidents = text.includes('Active Incidents');
const hasDegradation = text.includes('Degraded Performance');
const hasOutage = text.includes('Service Outage');
// Extract incidents
const incidents: string[] = [];
const incidentSection = text.match(/Past Incidents(.*?)(?=\n\n)/s);
if (incidentSection) {
const incidentLines = incidentSection[1]
.split('\n')
.map((line) => line.trim())
.filter((line) => line && line.includes('202')); // Only get dated incidents
incidents.push(...incidentLines.slice(0, 5));
}
// Check specific services
const services = {
api: {
operational: text.includes('API Service') && text.includes('Operational'),
degraded: text.includes('API Service') && text.includes('Degraded Performance'),
outage: text.includes('API Service') && text.includes('Service Outage'),
},
inference: {
operational: text.includes('Inference Service') && text.includes('Operational'),
degraded: text.includes('Inference Service') && text.includes('Degraded Performance'),
outage: text.includes('Inference Service') && text.includes('Service Outage'),
},
};
let status: StatusCheckResult['status'] = 'operational';
let message = 'All systems operational';
if (services.api.outage || services.inference.outage || hasOutage) {
status = 'down';
message = 'Service outage detected';
} else if (services.api.degraded || services.inference.degraded || hasDegradation || hasIncidents) {
status = 'degraded';
message = 'Service experiencing issues';
} else if (!isOperational) {
status = 'degraded';
message = 'Service status unknown';
}
// If status page check fails, fallback to endpoint check
if (!statusPageResponse.ok) {
const endpointStatus = await this.checkEndpoint('https://status.together.ai/');
const apiEndpoint = 'https://api.together.ai/v1/models';
const apiStatus = await this.checkEndpoint(apiEndpoint);
return {
status: endpointStatus === 'reachable' && apiStatus === 'reachable' ? 'operational' : 'degraded',
message: `Status page: ${endpointStatus}, API: ${apiStatus}`,
incidents: ['Note: Limited status information due to CORS restrictions'],
};
}
return {
status,
message,
incidents,
};
} catch (error) {
console.error('Error checking Together status:', error);
// Fallback to basic endpoint check
const endpointStatus = await this.checkEndpoint('https://status.together.ai/');
const apiEndpoint = 'https://api.together.ai/v1/models';
const apiStatus = await this.checkEndpoint(apiEndpoint);
return {
status: endpointStatus === 'reachable' && apiStatus === 'reachable' ? 'operational' : 'degraded',
message: `Status page: ${endpointStatus}, API: ${apiStatus}`,
incidents: ['Note: Limited status information due to CORS restrictions'],
};
}
}
}

View File

@@ -0,0 +1,40 @@
import { BaseProviderChecker } from '~/components/@settings/tabs/providers/service-status/base-provider';
import type { StatusCheckResult } from '~/components/@settings/tabs/providers/service-status/types';
export class XAIStatusChecker extends BaseProviderChecker {
async checkStatus(): Promise<StatusCheckResult> {
try {
/*
* Check API endpoint directly since XAI is a newer provider
* and may not have a public status page yet
*/
const apiEndpoint = 'https://api.xai.com/v1/models';
const apiStatus = await this.checkEndpoint(apiEndpoint);
// Check their website as a secondary indicator
const websiteStatus = await this.checkEndpoint('https://x.ai');
let status: StatusCheckResult['status'] = 'operational';
let message = 'All systems operational';
if (apiStatus !== 'reachable' || websiteStatus !== 'reachable') {
status = apiStatus !== 'reachable' ? 'down' : 'degraded';
message = apiStatus !== 'reachable' ? 'API appears to be down' : 'Service may be experiencing issues';
}
return {
status,
message,
incidents: [], // No public incident tracking available yet
};
} catch (error) {
console.error('Error checking XAI status:', error);
return {
status: 'degraded',
message: 'Unable to determine service status',
incidents: ['Note: Limited status information available'],
};
}
}
}

View File

@@ -0,0 +1,55 @@
import type { IconType } from 'react-icons';
export type ProviderName =
| 'AmazonBedrock'
| 'Cohere'
| 'Deepseek'
| 'Google'
| 'Groq'
| 'HuggingFace'
| 'Hyperbolic'
| 'Mistral'
| 'OpenRouter'
| 'Perplexity'
| 'Together'
| 'XAI';
export type ServiceStatus = {
provider: ProviderName;
status: 'operational' | 'degraded' | 'down';
lastChecked: string;
statusUrl?: string;
icon?: IconType;
message?: string;
responseTime?: number;
incidents?: string[];
};
export interface ProviderConfig {
statusUrl: string;
apiUrl: string;
headers: Record<string, string>;
testModel: string;
}
export type ApiResponse = {
error?: {
message: string;
};
message?: string;
model?: string;
models?: Array<{
id?: string;
name?: string;
}>;
data?: Array<{
id?: string;
name?: string;
}>;
};
export type StatusCheckResult = {
status: 'operational' | 'degraded' | 'down';
message: string;
incidents: string[];
};