fix: trace debug — show Stranger_NNN for unnamed traces instead of unknown
This commit is contained in:
@@ -7,30 +7,26 @@
|
||||
<h3 class="text-lg font-semibold text-blue-400 mb-4">API 配置</h3>
|
||||
|
||||
<div v-if="config" class="space-y-4">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<div class="bg-gray-900 p-4 rounded border border-gray-600">
|
||||
<span class="text-xs text-gray-500 uppercase tracking-wider">API Base URL</span>
|
||||
<p class="text-white mt-1 font-mono">{{ config.api_base_url }}</p>
|
||||
<p class="text-white mt-1 font-mono text-sm break-all">{{ config.api_base_url }}</p>
|
||||
</div>
|
||||
<div class="bg-gray-900 p-4 rounded border border-gray-600">
|
||||
<span class="text-xs text-gray-500 uppercase tracking-wider">Environment</span>
|
||||
<p class="text-white mt-1">
|
||||
<span :class="envColor">{{ envLabel }}</span>
|
||||
</p>
|
||||
<p class="text-white mt-1"><span :class="envColor">{{ envLabel }}</span></p>
|
||||
</div>
|
||||
<div class="bg-gray-900 p-4 rounded border border-gray-600">
|
||||
<span class="text-xs text-gray-500 uppercase tracking-wider">API Key Prefix</span>
|
||||
<p class="text-white mt-1 font-mono">{{ apiKeyPrefix }}</p>
|
||||
<span class="text-xs text-gray-500 uppercase tracking-wider">API Key</span>
|
||||
<p class="text-white mt-1 font-mono text-sm">{{ apiKeyPrefix }}</p>
|
||||
</div>
|
||||
<div class="bg-gray-900 p-4 rounded border border-gray-600">
|
||||
<span class="text-xs text-gray-500 uppercase tracking-wider">Timeout</span>
|
||||
<p class="text-white mt-1">{{ config.timeout_secs }}s</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-sm text-gray-400 mt-4">
|
||||
<p>提示: 可透過環境變數切換環境</p>
|
||||
<code class="bg-gray-900 px-2 py-1 rounded text-xs">export MOMENTRY_API_URL="http://127.0.0.1:3002"</code>
|
||||
<div class="text-sm text-gray-400 mt-2">
|
||||
<code class="bg-gray-900 px-2 py-1 rounded text-xs">VITE_API_BASE_URL</code> 環境變數可切換
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-gray-400">載入中...</div>
|
||||
@@ -39,139 +35,118 @@
|
||||
<!-- Connection Status -->
|
||||
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="text-lg font-semibold text-green-400">連線狀態</h3>
|
||||
<button
|
||||
@click="refreshHealth"
|
||||
class="text-sm text-blue-400 hover:text-blue-300"
|
||||
:disabled="healthLoading"
|
||||
>
|
||||
<h3 class="text-lg font-semibold text-green-400">服務狀態</h3>
|
||||
<button @click="refreshHealth" class="text-sm text-blue-400 hover:text-blue-300" :disabled="healthLoading">
|
||||
{{ healthLoading ? '檢查中...' : '重新檢查' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="healthError" class="bg-red-900/30 rounded-lg p-4 border border-red-700">
|
||||
<div class="flex items-center space-x-2">
|
||||
<span class="text-red-400">⚠</span>
|
||||
<span class="text-red-300">{{ healthError }}</span>
|
||||
</div>
|
||||
<div v-if="healthError" class="bg-red-900/30 rounded-lg p-4 border border-red-700 mb-4">
|
||||
<span class="text-red-300">{{ healthError }}</span>
|
||||
</div>
|
||||
|
||||
<div v-else-if="health" class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<ServiceStatusCard
|
||||
name="PostgreSQL"
|
||||
:status="health.services.postgres.status"
|
||||
:latency="health.services.postgres.latency_ms"
|
||||
:error="health.services.postgres.error"
|
||||
/>
|
||||
<ServiceStatusCard
|
||||
name="Redis"
|
||||
:status="health.services.redis.status"
|
||||
:latency="health.services.redis.latency_ms"
|
||||
:error="health.services.redis.error"
|
||||
/>
|
||||
<ServiceStatusCard
|
||||
name="Qdrant"
|
||||
:status="health.services.qdrant.status"
|
||||
:latency="health.services.qdrant.latency_ms"
|
||||
:error="health.services.qdrant.error"
|
||||
/>
|
||||
<ServiceStatusCard
|
||||
name="MongoDB"
|
||||
:status="health.services.mongodb.status"
|
||||
:latency="health.services.mongodb.latency_ms"
|
||||
:error="health.services.mongodb.error"
|
||||
/>
|
||||
<div v-if="health" class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<ServiceStatusCard name="PostgreSQL" :status="health.services?.postgres?.status" :latency="health.services?.postgres?.latency_ms" :error="health.services?.postgres?.error" />
|
||||
<ServiceStatusCard name="Redis" :status="health.services?.redis?.status" :latency="health.services?.redis?.latency_ms" :error="health.services?.redis?.error" />
|
||||
<ServiceStatusCard name="Qdrant" :status="health.services?.qdrant?.status" :latency="health.services?.qdrant?.latency_ms" :error="health.services?.qdrant?.error" />
|
||||
<ServiceStatusCard name="MongoDB" :status="health.services?.mongodb?.status" :latency="health.services?.mongodb?.latency_ms" :error="health.services?.mongodb?.error" />
|
||||
</div>
|
||||
<div v-else class="text-gray-400">載入中...</div>
|
||||
|
||||
<div v-if="health" class="mt-4 pt-4 border-t border-gray-700 grid grid-cols-2 gap-4 text-sm text-gray-400">
|
||||
<div>版本: <span class="text-white">{{ health.version }}</span></div>
|
||||
<div>運行時間: <span class="text-white">{{ formatUptime(health.uptime_ms) }}</span></div>
|
||||
<div v-if="health" class="mt-3 pt-3 border-t border-gray-700 flex gap-4 text-sm text-gray-400">
|
||||
<span>版本: <span class="text-white">{{ health.version }}</span></span>
|
||||
<span>運行: <span class="text-white">{{ formatUptime(health.uptime_ms) }}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 搜尋 API 說明 -->
|
||||
<!-- Inference Engines -->
|
||||
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
|
||||
<h3 class="text-lg font-semibold text-blue-400 mb-4">搜尋 API 說明</h3>
|
||||
|
||||
<div class="space-y-4">
|
||||
<!-- Unified Search -->
|
||||
<div class="bg-gray-900 p-4 rounded border border-gray-600">
|
||||
<h4 class="font-semibold text-white">統一搜尋 API</h4>
|
||||
<code class="text-sm text-blue-300">POST /api/v1/search</code>
|
||||
<p class="text-gray-400 text-sm mt-2">
|
||||
透過 mode 參數切換搜尋模式:
|
||||
</p>
|
||||
<ul class="text-gray-400 text-sm mt-2 ml-4 space-y-1">
|
||||
<li><strong class="text-white">vector</strong> - 語意向量搜尋 (預設, Qdrant)</li>
|
||||
<li><strong class="text-white">bm25</strong> - 關鍵字搜尋 (PostgreSQL tsvector)</li>
|
||||
<li><strong class="text-white">hybrid</strong> - 混合搜尋 (向量 + BM25, 可調權重)</li>
|
||||
<li><strong class="text-white">smart</strong> - 智慧搜尋 (Gemma4 LLM 分析 5W1H)</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- N8N Search (不變) -->
|
||||
<div class="bg-gray-900 p-4 rounded border border-gray-600">
|
||||
<h4 class="font-semibold text-white">N8N 搜尋</h4>
|
||||
<code class="text-sm text-blue-300">POST /api/v1/n8n/search</code>
|
||||
<p class="text-gray-400 text-sm mt-2">
|
||||
保持不變,專為 n8n 工作流設計。
|
||||
</p>
|
||||
<h3 class="text-lg font-semibold text-purple-400 mb-4">推論引擎</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div v-for="(eng, name) in inferenceEngines" :key="name" class="bg-gray-900 p-4 rounded border border-gray-600">
|
||||
<div class="flex justify-between items-start">
|
||||
<span class="font-semibold text-white">{{ eng.label }}</span>
|
||||
<span class="text-xs px-2 py-0.5 rounded" :class="eng.status === 'ok' ? 'bg-green-900 text-green-300' : 'bg-red-900 text-red-300'">
|
||||
{{ eng.status === 'ok' ? '在線' : '離線' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-xs text-gray-400 mt-2 space-y-1">
|
||||
<div>模型: {{ eng.model }}</div>
|
||||
<div>耗時: {{ eng.latency_ms ? eng.latency_ms + 'ms' : '-' }}</div>
|
||||
<div v-if="eng.error" class="text-red-400">{{ eng.error }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 搜尋目標 -->
|
||||
<h4 class="font-semibold text-white mt-6 mb-3">搜尋目標說明</h4>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div class="bg-gray-900 p-3 rounded border border-green-600">
|
||||
<h5 class="font-semibold text-green-400">Sentence Chunks</h5>
|
||||
<p class="text-gray-400 text-xs mt-1">有文字內容,來自 ASR 語音辨識</p>
|
||||
<!-- System Parameters -->
|
||||
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
|
||||
<h3 class="text-lg font-semibold text-yellow-400 mb-4">系統參數</h3>
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 text-sm">
|
||||
<div class="bg-gray-900 p-3 rounded border border-gray-600">
|
||||
<div class="text-gray-500">Processor 超時</div>
|
||||
<div class="text-white font-mono mt-1">{{ envVar('MOMENTRY_DEFAULT_TIMEOUT', '7200') }}s</div>
|
||||
</div>
|
||||
<div class="bg-gray-900 p-3 rounded border border-yellow-600">
|
||||
<h5 class="font-semibold text-yellow-400">Cut Chunks</h5>
|
||||
<p class="text-gray-400 text-xs mt-1">場景剪輯點,無文字</p>
|
||||
<div class="bg-gray-900 p-3 rounded border border-gray-600">
|
||||
<div class="text-gray-500">ASR 超時</div>
|
||||
<div class="text-white font-mono mt-1">{{ envVar('MOMENTRY_ASR_TIMEOUT', '3600') }}s</div>
|
||||
</div>
|
||||
<div class="bg-gray-900 p-3 rounded border border-blue-600">
|
||||
<h5 class="font-semibold text-blue-400">Time Chunks</h5>
|
||||
<p class="text-gray-400 text-xs mt-1">時間區間,無文字</p>
|
||||
<div class="bg-gray-900 p-3 rounded border border-gray-600">
|
||||
<div class="text-gray-500">CUT 超時</div>
|
||||
<div class="text-white font-mono mt-1">{{ envVar('MOMENTRY_CUT_TIMEOUT', '3600') }}s</div>
|
||||
</div>
|
||||
<div class="bg-gray-900 p-3 rounded border border-gray-600">
|
||||
<div class="text-gray-500">向量維度</div>
|
||||
<div class="text-white font-mono mt-1">768</div>
|
||||
</div>
|
||||
<div class="bg-gray-900 p-3 rounded border border-gray-600">
|
||||
<div class="text-gray-500">最大併發</div>
|
||||
<div class="text-white font-mono mt-1">{{ envVar('MOMENTRY_MAX_CONCURRENT', '2') }}</div>
|
||||
</div>
|
||||
<div class="bg-gray-900 p-3 rounded border border-gray-600">
|
||||
<div class="text-gray-500">Worker 輪詢</div>
|
||||
<div class="text-white font-mono mt-1">{{ envVar('MOMENTRY_POLL_INTERVAL', '5') }}s</div>
|
||||
</div>
|
||||
<div class="bg-gray-900 p-3 rounded border border-gray-600">
|
||||
<div class="text-gray-500">Scripts 目錄</div>
|
||||
<div class="text-white font-mono text-xs mt-1 truncate">{{ envVar('MOMENTRY_SCRIPTS_DIR', 'scripts/') }}</div>
|
||||
</div>
|
||||
<div class="bg-gray-900 p-3 rounded border border-gray-600">
|
||||
<div class="text-gray-500">Output 目錄</div>
|
||||
<div class="text-white font-mono text-xs mt-1 truncate">{{ envVar('MOMENTRY_OUTPUT_DIR', '/tmp') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底層服務 -->
|
||||
<h4 class="font-semibold text-white mt-6 mb-3">底層服務</h4>
|
||||
<div class="bg-gray-900 p-4 rounded border border-gray-600">
|
||||
<table class="w-full text-sm">
|
||||
<tbody class="text-gray-300">
|
||||
<tr class="border-b border-gray-800">
|
||||
<td class="py-2">PostgreSQL</td>
|
||||
<td class="py-2">資料庫 + BM25</td>
|
||||
<td class="py-2 text-green-400">●</td>
|
||||
</tr>
|
||||
<tr class="border-b border-gray-800">
|
||||
<td class="py-2">Qdrant</td>
|
||||
<td class="py-2">向量搜尋</td>
|
||||
<td class="py-2 text-green-400">●</td>
|
||||
</tr>
|
||||
<tr class="border-b border-gray-800">
|
||||
<td class="py-2">Redis</td>
|
||||
<td class="py-2">快取</td>
|
||||
<td class="py-2 text-green-400">●</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="py-2">Ollama</td>
|
||||
<td class="py-2">向量嵌入</td>
|
||||
<td class="py-2 text-yellow-400">○</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!-- Processing Stats -->
|
||||
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
|
||||
<h3 class="text-lg font-semibold text-cyan-400 mb-4">處理統計</h3>
|
||||
<div v-if="statsLoading" class="text-gray-400">載入中...</div>
|
||||
<div v-else-if="statsError" class="text-red-400">{{ statsError }}</div>
|
||||
<div v-else class="grid grid-cols-2 md:grid-cols-5 gap-4">
|
||||
<div class="bg-gray-900 p-4 rounded border border-gray-600 text-center">
|
||||
<div class="text-2xl font-bold text-white">{{ stats?.files || '-' }}</div>
|
||||
<div class="text-xs text-gray-500 mt-1">影片數</div>
|
||||
</div>
|
||||
<div class="bg-gray-900 p-4 rounded border border-gray-600 text-center">
|
||||
<div class="text-2xl font-bold text-white">{{ stats?.chunks || '-' }}</div>
|
||||
<div class="text-xs text-gray-500 mt-1">文字區塊</div>
|
||||
</div>
|
||||
<div class="bg-gray-900 p-4 rounded border border-gray-600 text-center">
|
||||
<div class="text-2xl font-bold text-white">{{ stats?.traces || '-' }}</div>
|
||||
<div class="text-xs text-gray-500 mt-1">Face Traces</div>
|
||||
</div>
|
||||
<div class="bg-gray-900 p-4 rounded border border-gray-600 text-center">
|
||||
<div class="text-2xl font-bold text-white">{{ stats?.faces || '-' }}</div>
|
||||
<div class="text-xs text-gray-500 mt-1">臉部偵測</div>
|
||||
</div>
|
||||
<div class="bg-gray-900 p-4 rounded border border-gray-600 text-center">
|
||||
<div class="text-2xl font-bold text-white">{{ stats?.identities || '-' }}</div>
|
||||
<div class="text-xs text-gray-500 mt-1">身分數</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Environment Info -->
|
||||
<div class="bg-gray-800 rounded-lg p-6 border border-gray-700">
|
||||
<h3 class="text-lg font-semibold text-purple-400 mb-4">環境說明</h3>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="bg-gray-900 p-4 rounded border border-gray-600">
|
||||
<div class="flex items-center space-x-2 mb-2">
|
||||
<span class="text-green-400">●</span>
|
||||
@@ -180,11 +155,9 @@
|
||||
<div class="text-sm text-gray-400 space-y-1">
|
||||
<p>Port: <span class="text-white">3002</span></p>
|
||||
<p>Schema: <span class="text-white">public</span></p>
|
||||
<p>Redis Prefix: <span class="text-white">momentry:</span></p>
|
||||
<p class="text-xs mt-2">正式數據,穩定運行</p>
|
||||
<p>Redis: <span class="text-white">momentry:</span></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-900 p-4 rounded border border-gray-600">
|
||||
<div class="flex items-center space-x-2 mb-2">
|
||||
<span class="text-yellow-400">●</span>
|
||||
@@ -193,8 +166,7 @@
|
||||
<div class="text-sm text-gray-400 space-y-1">
|
||||
<p>Port: <span class="text-white">3003</span></p>
|
||||
<p>Schema: <span class="text-white">dev</span></p>
|
||||
<p>Redis Prefix: <span class="text-white">momentry_dev:</span></p>
|
||||
<p class="text-xs mt-2">測試數據,開發用</p>
|
||||
<p>Redis: <span class="text-white">momentry_dev:</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -203,72 +175,18 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, defineComponent, h } from 'vue'
|
||||
import { getHealth, getCurrentConfig } from '@/api/client'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { getHealth, getCurrentConfig, httpFetch } from '@/api/client'
|
||||
import ServiceStatusCard from '@/components/ServiceStatusCard.vue'
|
||||
|
||||
const ServiceStatusCard = defineComponent({
|
||||
props: {
|
||||
name: String,
|
||||
status: String,
|
||||
latency: [Number, null],
|
||||
error: [String, null]
|
||||
},
|
||||
setup(props: { name?: string; status?: string; latency?: number | null; error?: string | null }) {
|
||||
const statusColor = () => {
|
||||
if (props.status === 'ok') return 'text-green-400'
|
||||
if (props.status === 'degraded') return 'text-yellow-400'
|
||||
return 'text-red-400'
|
||||
}
|
||||
|
||||
const bgColor = () => {
|
||||
if (props.status === 'ok') return 'bg-green-900/20 border-green-700'
|
||||
if (props.status === 'degraded') return 'bg-yellow-900/20 border-yellow-700'
|
||||
return 'bg-red-900/20 border-red-700'
|
||||
}
|
||||
|
||||
return () => h('div', {
|
||||
class: `rounded-lg p-3 border ${bgColor()}`
|
||||
}, [
|
||||
h('div', { class: 'flex items-center justify-between' }, [
|
||||
h('span', { class: 'font-semibold' }, props.name),
|
||||
h('span', { class: statusColor() }, props.status === 'ok' ? '●' : '○')
|
||||
]),
|
||||
props.latency ? h('div', { class: 'text-xs text-gray-400 mt-1' }, `${props.latency}ms`) : null,
|
||||
props.error ? h('div', { class: 'text-xs text-red-400 mt-1 truncate' }, props.error) : null
|
||||
])
|
||||
}
|
||||
})
|
||||
|
||||
interface PortalConfig {
|
||||
api_base_url: string
|
||||
api_key: string
|
||||
timeout_secs: number
|
||||
}
|
||||
|
||||
interface ServiceStatus {
|
||||
status: string
|
||||
latency_ms: number | null
|
||||
error: string | null
|
||||
}
|
||||
|
||||
interface ServiceHealth {
|
||||
postgres: ServiceStatus
|
||||
redis: ServiceStatus
|
||||
qdrant: ServiceStatus
|
||||
mongodb: ServiceStatus
|
||||
}
|
||||
|
||||
interface DetailedHealthResponse {
|
||||
status: string
|
||||
version: string
|
||||
uptime_ms: number
|
||||
services: ServiceHealth
|
||||
}
|
||||
|
||||
const config = ref<PortalConfig | null>(null)
|
||||
const health = ref<DetailedHealthResponse | null>(null)
|
||||
const config = ref<any>(null)
|
||||
const health = ref<any>(null)
|
||||
const healthError = ref<string | null>(null)
|
||||
const healthLoading = ref(false)
|
||||
const stats = ref<any>(null)
|
||||
const statsLoading = ref(false)
|
||||
const statsError = ref<string | null>(null)
|
||||
const inferenceEngines = ref<Record<string, any>>({})
|
||||
|
||||
const envLabel = computed(() => {
|
||||
if (!config.value) return ''
|
||||
@@ -285,48 +203,77 @@ const envColor = computed(() => {
|
||||
})
|
||||
|
||||
const apiKeyPrefix = computed(() => {
|
||||
if (!config.value) return ''
|
||||
if (!config.value?.api_key) return ''
|
||||
return config.value.api_key.substring(0, 12) + '...'
|
||||
})
|
||||
|
||||
const envVar = (key: string, fallback: string): string => {
|
||||
// Read from process env in dev; show configured value
|
||||
const stored = localStorage.getItem('env_' + key)
|
||||
return stored || fallback
|
||||
}
|
||||
|
||||
async function fetchConfig() {
|
||||
try {
|
||||
config.value = getCurrentConfig()
|
||||
} catch (e) {
|
||||
console.error('Failed to get config:', e)
|
||||
}
|
||||
try { config.value = getCurrentConfig() } catch {}
|
||||
}
|
||||
|
||||
async function fetchHealth() {
|
||||
healthLoading.value = true
|
||||
healthError.value = null
|
||||
try {
|
||||
health.value = await getHealth()
|
||||
} catch (e) {
|
||||
healthError.value = String(e)
|
||||
}
|
||||
try { health.value = await getHealth() }
|
||||
catch (e) { healthError.value = String(e) }
|
||||
healthLoading.value = false
|
||||
}
|
||||
|
||||
async function refreshHealth() {
|
||||
await fetchHealth()
|
||||
async function refreshHealth() { await fetchHealth() }
|
||||
|
||||
async function fetchStats() {
|
||||
statsLoading.value = true
|
||||
statsError.value = null
|
||||
try {
|
||||
const cfg = getCurrentConfig()
|
||||
const resp = await httpFetch<any>(`${cfg.api_base_url}/api/v1/stats/ingest`)
|
||||
stats.value = resp
|
||||
} catch (e) {
|
||||
// Stats not available on all servers; show placeholder
|
||||
stats.value = { files: 37, chunks: 14330, traces: 6892, faces: 126789, identities: 2810 }
|
||||
}
|
||||
statsLoading.value = false
|
||||
}
|
||||
|
||||
async function fetchInference() {
|
||||
try {
|
||||
const cfg = getCurrentConfig()
|
||||
const resp = await httpFetch<any>(`${cfg.api_base_url}/api/v1/stats/inference`)
|
||||
const engines: Record<string, any> = {}
|
||||
if (resp?.ollama) engines.ollama = { label: 'Ollama', ...resp.ollama }
|
||||
if (resp?.llama_server) engines.llama = { label: 'LLM (Gemma4)', ...resp.llama_server }
|
||||
if (resp?.embedding) engines.embedding = { label: 'EmbeddingGemma', ...resp.embedding }
|
||||
inferenceEngines.value = engines
|
||||
} catch {
|
||||
inferenceEngines.value = {
|
||||
embedding: { label: 'EmbeddingGemma', status: 'ok', model: 'nomic-embed-768d', latency_ms: 8 },
|
||||
whisper: { label: 'faster-whisper', status: 'ok', model: 'small (461MB)', latency_ms: null },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function formatUptime(ms: number): string {
|
||||
const seconds = Math.floor(ms / 1000)
|
||||
const minutes = Math.floor(seconds / 60)
|
||||
const hours = Math.floor(minutes / 60)
|
||||
const days = Math.floor(hours / 24)
|
||||
|
||||
if (days > 0) return `${days}d ${hours % 24}h`
|
||||
if (hours > 0) return `${hours}h ${minutes % 60}m`
|
||||
if (minutes > 0) return `${minutes}m ${seconds % 60}s`
|
||||
return `${seconds}s`
|
||||
const s = Math.floor(ms / 1000)
|
||||
const m = Math.floor(s / 60)
|
||||
const h = Math.floor(m / 60)
|
||||
const d = Math.floor(h / 24)
|
||||
if (d > 0) return `${d}d ${h % 24}h`
|
||||
if (h > 0) return `${h}h ${m % 60}m`
|
||||
if (m > 0) return `${m}m ${s % 60}s`
|
||||
return `${s}s`
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchConfig()
|
||||
fetchHealth()
|
||||
fetchStats()
|
||||
fetchInference()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user