115 lines
5.1 KiB
TypeScript
115 lines
5.1 KiB
TypeScript
/**
|
|
* NextJS Integration for Homelab LLM & Grafana
|
|
*
|
|
* ═══════════════════════════════════════════════════════════════════════════════
|
|
* SETUP
|
|
* ═══════════════════════════════════════════════════════════════════════════════
|
|
*
|
|
* 1. Copy files to your NextJS project:
|
|
* cp integrations/nextjs/*.ts your-nextjs-app/lib/
|
|
*
|
|
* 2. Environment variables (.env.local):
|
|
* LLM_BASE_URL=https://api.riotpiao.com/v1
|
|
* GRAFANA_URL=https://grafana.riotpiao.com
|
|
* GRAFANA_API_TOKEN=<service-account-token>
|
|
* NEXT_PUBLIC_GRAFANA_URL=https://grafana.riotpiao.com
|
|
*
|
|
* 3. Create Grafana service account (for API access):
|
|
* - Grafana UI > Administration > Service Accounts
|
|
* - Create account with Viewer role
|
|
* - Generate token, save to GRAFANA_API_TOKEN
|
|
*
|
|
* 4. Copy API routes (see api-routes.ts for templates):
|
|
* - app/api/chat/route.ts (LLM chat endpoint)
|
|
* - app/api/metrics/route.ts (Grafana metrics proxy)
|
|
*
|
|
* ═══════════════════════════════════════════════════════════════════════════════
|
|
* USAGE EXAMPLES
|
|
* ═══════════════════════════════════════════════════════════════════════════════
|
|
*
|
|
* Server-side (API routes, server components):
|
|
*
|
|
* import { chat, streamChat } from '@/lib/llm-client';
|
|
* import { getLLMMetrics, getDashboard } from '@/lib/grafana-client';
|
|
*
|
|
* // Non-streaming chat
|
|
* const response = await chat({
|
|
* model: 'reasoning',
|
|
* messages: [{ role: 'user', content: 'Hello' }],
|
|
* });
|
|
*
|
|
* // Streaming chat
|
|
* for await (const chunk of streamChat({ model: 'reasoning', messages })) {
|
|
* process.stdout.write(chunk);
|
|
* }
|
|
*
|
|
* // Get LLM pod metrics
|
|
* const metrics = await getLLMMetrics();
|
|
* console.log(`Pods ready: ${metrics.podsReady}`);
|
|
*
|
|
* Client-side (React components):
|
|
*
|
|
* import { useChat, useLLMMetrics, useGrafanaEmbed } from '@/lib/hooks';
|
|
*
|
|
* function ChatUI() {
|
|
* const { messages, send, isLoading } = useChat({ model: 'reasoning' });
|
|
*
|
|
* return (
|
|
* <div>
|
|
* {messages.map((m, i) => <p key={i}>{m.role}: {m.content}</p>)}
|
|
* <button onClick={() => send('Hello')} disabled={isLoading}>
|
|
* Send
|
|
* </button>
|
|
* </div>
|
|
* );
|
|
* }
|
|
*
|
|
* function MetricsDashboard() {
|
|
* const { metrics, isLoading } = useLLMMetrics(30000);
|
|
* const { iframeProps } = useGrafanaEmbed('llm-frontend', 2);
|
|
*
|
|
* if (isLoading) return <p>Loading...</p>;
|
|
*
|
|
* return (
|
|
* <div>
|
|
* <p>Pods ready: {metrics?.podsReady}</p>
|
|
* {iframeProps && <iframe {...iframeProps} />}
|
|
* </div>
|
|
* );
|
|
* }
|
|
*
|
|
* ═══════════════════════════════════════════════════════════════════════════════
|
|
* AVAILABLE MODELS
|
|
* ═══════════════════════════════════════════════════════════════════════════════
|
|
*
|
|
* | model | Engine | Notes |
|
|
* |--------------------|---------|--------------------------------|
|
|
* | reasoning | vLLM | Qwen3-32B, best for complex |
|
|
* | ornith:35b | Ollama | General purpose |
|
|
* | qwen2.5:3b-instruct| Ollama | Fast, smaller tasks |
|
|
*
|
|
* ═══════════════════════════════════════════════════════════════════════════════
|
|
* GRAFANA EMBEDDING (optional, requires config change)
|
|
* ═══════════════════════════════════════════════════════════════════════════════
|
|
*
|
|
* To enable iframe embedding, add to grafana.ini:
|
|
* security:
|
|
* allow_embedding: true
|
|
*
|
|
* Or via Helm values (k8s/infra/logging/grafana-values.yaml):
|
|
* grafana.ini:
|
|
* security:
|
|
* allow_embedding: true
|
|
*
|
|
* Panel embed URLs follow pattern:
|
|
* https://grafana.riotpiao.com/d-solo/llm-frontend?panelId=2&from=now-6h&to=now
|
|
*
|
|
* Dashboard UID for LLM: llm-frontend
|
|
* Panel IDs: 2 (pods ready), 11 (CPU), 12 (Memory), 13 (Restarts)
|
|
*/
|
|
|
|
// Re-export all modules
|
|
export * from './llm-client';
|
|
export * from './grafana-client';
|
|
export * from './hooks';
|