diff --git a/src/app/(dashboard)/dashboard/audit/A2aAuditTab.tsx b/src/app/(dashboard)/dashboard/audit/A2aAuditTab.tsx index 1cd2f7c2dc..2c390c68cd 100644 --- a/src/app/(dashboard)/dashboard/audit/A2aAuditTab.tsx +++ b/src/app/(dashboard)/dashboard/audit/A2aAuditTab.tsx @@ -182,7 +182,7 @@ export default function A2aAuditTab() { - {task.state} + {t(`a2aState${task.state.charAt(0).toUpperCase()}${task.state.slice(1)}`)} {taskDuration(task)} diff --git a/src/app/(dashboard)/dashboard/audit/ComplianceTab.tsx b/src/app/(dashboard)/dashboard/audit/ComplianceTab.tsx index f2a0dc4329..a8f86190cc 100644 --- a/src/app/(dashboard)/dashboard/audit/ComplianceTab.tsx +++ b/src/app/(dashboard)/dashboard/audit/ComplianceTab.tsx @@ -330,7 +330,7 @@ export default function ComplianceTab() { - {entry.action} + {t.has(`eventTypes.${entry.action}`) ? t(`eventTypes.${entry.action}`) : entry.action} diff --git a/src/app/(dashboard)/dashboard/costs/quota-share/components/PoolCard.tsx b/src/app/(dashboard)/dashboard/costs/quota-share/components/PoolCard.tsx index 6bae71c651..bd131e0841 100644 --- a/src/app/(dashboard)/dashboard/costs/quota-share/components/PoolCard.tsx +++ b/src/app/(dashboard)/dashboard/costs/quota-share/components/PoolCard.tsx @@ -24,8 +24,9 @@ export interface PoolCardProps { } function computeStatus(usage: PoolUsageSnapshot | null): "green" | "amber" | "red" { - if (!usage || usage.dimensions.length === 0) return "green"; - const utilizations = usage.dimensions.map((d) => + const dims = usage?.dimensions ?? []; + if (dims.length === 0) return "green"; + const utilizations = dims.map((d) => d.limit > 0 ? (d.consumedTotal / d.limit) * 100 : 0 ); const avg = utilizations.reduce((s, u) => s + u, 0) / utilizations.length; @@ -54,7 +55,7 @@ export default function PoolCard({ const { icon: statusIcon, cls: statusCls } = STATUS_ICONS[status]; // Check for plan dimensions from usage - const hasDimensions = usage && usage.dimensions.length > 0; + const hasDimensions = !!usage?.dimensions?.length; return ( @@ -103,10 +104,10 @@ export default function PoolCard({
- {usage.dimensions.map((dim, i) => ( + {(usage?.dimensions ?? []).map((dim, i) => ( 0) { totalUtil += (dim.consumedTotal / dim.limit) * 100; utilCount += 1; } - for (const key of dim.perKey) { + for (const key of dim.perKey ?? []) { if (key.borrowing) borrowing += 1; } } diff --git a/src/app/(dashboard)/dashboard/logs/page.tsx b/src/app/(dashboard)/dashboard/logs/page.tsx index 5ef549d357..40b1271958 100644 --- a/src/app/(dashboard)/dashboard/logs/page.tsx +++ b/src/app/(dashboard)/dashboard/logs/page.tsx @@ -1,9 +1,7 @@ "use client"; import { useState, useRef, useEffect } from "react"; -import { useSearchParams } from "next/navigation"; -import { ConfirmModal, RequestLoggerV2, ProxyLogger, SegmentedControl } from "@/shared/components"; -import ConsoleLogViewer from "@/shared/components/ConsoleLogViewer"; +import { ConfirmModal, RequestLoggerV2 } from "@/shared/components"; import EmailPrivacyToggle from "@/shared/components/EmailPrivacyToggle"; import ActiveRequestsPanel from "@/shared/components/ActiveRequestsPanel"; import { useTranslations } from "next-intl"; @@ -15,18 +13,7 @@ const TIME_RANGES = [ { label: "24h", hours: 24 }, ]; -const TAB_TO_LOG_TYPE: Record = { - "request-logs": "request-logs", - "proxy-logs": "proxy-logs", - console: "call-logs", -}; - export default function LogsPage() { - const searchParams = useSearchParams(); - const requestedTab = searchParams.get("tab"); - const [activeTab, setActiveTab] = useState( - requestedTab && TAB_TO_LOG_TYPE[requestedTab] ? requestedTab : "request-logs" - ); const [showExport, setShowExport] = useState(false); const [exporting, setExporting] = useState(false); const [showCleanHistory, setShowCleanHistory] = useState(false); @@ -36,12 +23,6 @@ export default function LogsPage() { const dropdownRef = useRef(null); const t = useTranslations("logs"); - useEffect(() => { - if (requestedTab && TAB_TO_LOG_TYPE[requestedTab] && requestedTab !== activeTab) { - setActiveTab(requestedTab); - } - }, [activeTab, requestedTab]); - useEffect(() => { function handleClickOutside(e: MouseEvent) { if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) { @@ -56,7 +37,7 @@ export default function LogsPage() { setExporting(true); setShowExport(false); try { - const logType = TAB_TO_LOG_TYPE[activeTab] || "call-logs"; + const logType = "request-logs"; const res = await fetch(`/api/logs/export?hours=${hours}&type=${logType}`); if (!res.ok) throw new Error(t("exportFailed")); const blob = await res.blob(); @@ -108,15 +89,7 @@ export default function LogsPage() { return (
- +

{t("requestLogs")}

@@ -211,14 +184,10 @@ export default function LogsPage() {
)} - {activeTab === "request-logs" && ( -
- - -
- )} - {activeTab === "proxy-logs" && } - {activeTab === "console" && } +
+ + +
0 ? ( + status.vectorStore.backend === "none" ? ( + + terminal + {t("engine.vectorStoreInstallHint")} + + ) : status.vectorStore.needsReindex > 0 ? ( warning {t("engine.needsReindex", { count: status.vectorStore.needsReindex })} diff --git a/src/app/(dashboard)/dashboard/memory/components/tabs/MemoriesTab.tsx b/src/app/(dashboard)/dashboard/memory/components/tabs/MemoriesTab.tsx index 458c45c453..5df03c94b2 100644 --- a/src/app/(dashboard)/dashboard/memory/components/tabs/MemoriesTab.tsx +++ b/src/app/(dashboard)/dashboard/memory/components/tabs/MemoriesTab.tsx @@ -236,6 +236,16 @@ export default function MemoriesTab() { } }; + // Auto-run health check on mount + poll every 30s, so the indicator reflects + // engine health without requiring a manual click. + useEffect(() => { + void checkHealth(); + const id = setInterval(() => { + void checkHealth(); + }, 30_000); + return () => clearInterval(id); + }, []); + const openEdit = (m: Memory) => { setEditTarget(m); setEditOpen(true); diff --git a/src/app/(dashboard)/dashboard/memory/page.tsx b/src/app/(dashboard)/dashboard/memory/page.tsx index 6d1c382e47..cf228ede40 100644 --- a/src/app/(dashboard)/dashboard/memory/page.tsx +++ b/src/app/(dashboard)/dashboard/memory/page.tsx @@ -7,15 +7,18 @@ import MemoryConceptCard from "./components/MemoryConceptCard"; import MemoriesTab from "./components/tabs/MemoriesTab"; import PlaygroundTab from "./components/tabs/PlaygroundTab"; import EngineTab from "./components/tabs/EngineTab"; +import { useMemorySettings } from "./hooks/useMemorySettings"; type TabId = "memories" | "playground" | "engine"; -const TABS: TabId[] = ["memories", "playground", "engine"]; +const TABS: TabId[] = ["memories", "engine", "playground"]; function MemoryPageContent() { const t = useTranslations("memory"); const searchParams = useSearchParams(); const router = useRouter(); + const { settings, save } = useMemorySettings(); + const memoryEnabled = settings?.enabled ?? true; const rawTab = searchParams.get("tab") ?? ""; const activeTab: TabId = TABS.includes(rawTab as TabId) ? (rawTab as TabId) : "memories"; @@ -31,23 +34,44 @@ function MemoryPageContent() { {/* Concept card */} - {/* Tab navigation */} -
- {TABS.map((tab) => ( + {/* Tab navigation + memory enable toggle */} +
+
+ {TABS.map((tab) => ( + + ))} +
+
{/* Tab content */} diff --git a/src/app/(dashboard)/dashboard/playground/components/StudioConfigPane.tsx b/src/app/(dashboard)/dashboard/playground/components/StudioConfigPane.tsx index decb51ed73..20440a705a 100644 --- a/src/app/(dashboard)/dashboard/playground/components/StudioConfigPane.tsx +++ b/src/app/(dashboard)/dashboard/playground/components/StudioConfigPane.tsx @@ -8,11 +8,14 @@ import type { PlaygroundEndpoint } from "@/lib/playground/codeExport"; import { endpointToPath } from "@/lib/playground/codeExport"; import PresetPicker from "./PresetPicker"; import ImprovePromptButton from "./ImprovePromptButton"; +import { useProviderOptions } from "@/app/(dashboard)/dashboard/translator/hooks/useProviderOptions"; +import { useAvailableModels } from "@/app/(dashboard)/dashboard/translator/hooks/useAvailableModels"; export interface ConfigState { endpoint: PlaygroundEndpoint; baseUrl: string; model: string; + provider?: string; systemPrompt: string; params: PlaygroundParams; } @@ -46,6 +49,10 @@ const ENDPOINT_OPTIONS: Array<{ value: PlaygroundEndpoint; label: string }> = [ */ export default function StudioConfigPane({ configState, setConfigState }: StudioConfigPaneProps) { const [collapsed, setCollapsed] = useState(false); + const { provider, setProvider, providerOptions, loading: loadingProviders } = useProviderOptions( + configState.provider ?? "" + ); + const { availableModels, loading: loadingModels } = useAvailableModels(); function update(key: K, value: ConfigState[K]) { setConfigState({ ...configState, [key]: value }); @@ -108,18 +115,56 @@ export default function StudioConfigPane({ configState, setConfigState }: Studio
+ {/* Provider */} +
+ + +
+ {/* Model */}
- update("model", e.target.value)} - placeholder="e.g. openai/gpt-4o" - className="w-full text-xs bg-surface border border-border rounded px-2 py-1.5 focus:outline-none focus:ring-1 focus:ring-primary text-text-main" - /> + {availableModels.length > 0 ? ( + + ) : ( + update("model", e.target.value)} + placeholder="e.g. openai/gpt-4o" + className="w-full text-xs bg-surface border border-border rounded px-2 py-1.5 focus:outline-none focus:ring-1 focus:ring-primary text-text-main" + /> + )}
{/* System prompt */} diff --git a/src/app/(dashboard)/dashboard/playground/components/tabs/BuildTab.tsx b/src/app/(dashboard)/dashboard/playground/components/tabs/BuildTab.tsx index 40bfb78c98..a3a5c521a1 100644 --- a/src/app/(dashboard)/dashboard/playground/components/tabs/BuildTab.tsx +++ b/src/app/(dashboard)/dashboard/playground/components/tabs/BuildTab.tsx @@ -6,9 +6,8 @@ import { useRef, useState } from "react"; import { useTranslations } from "next-intl"; import { useToolsBuilder } from "../../hooks/useToolsBuilder"; import { useStructuredOutput } from "../../hooks/useStructuredOutput"; -import ToolsBuilder from "../ToolsBuilder"; -import StructuredOutputEditor from "../StructuredOutputEditor"; import MarkdownMessage from "../MarkdownMessage"; +import BuildWizard from "./build/BuildWizard"; import type { ConfigState } from "../StudioConfigPane"; interface BuildTabProps { @@ -225,177 +224,104 @@ export default function BuildTab({ configState }: BuildTabProps) { await runRequest(newMessages); } - function clearConversation() { - setMessages([]); - setToolCalls([]); - setToolResultDrafts([]); - setValidationResult(null); - setPrompt(""); - } - - return ( -
- {/* Left panel: conversation + run */} -
- {/* Toolbar */} -
- - - {messages.length > 0 && ( - - )} - -
- {toolsBuilder.tools.length > 0 && ( - - {toolsBuilder.tools.length} tool{toolsBuilder.tools.length !== 1 ? "s" : ""} - - )} - {structuredOutput.enabled && ( - - JSON mode - + {msg.role === "user" ? ( + {msg.content} + ) : ( + )}
+ ))} - {/* Conversation history */} -
- {messages.map((msg, idx) => ( -
+ {/* Tool call UI */} + {toolCalls.length > 0 && ( +
+ {toolCalls.map((tc) => { + const draft = toolResultDrafts.find((d) => d.toolCallId === tc.id); + return (
- {msg.role === "user" ? ( - {msg.content} - ) : ( - - )} -
-
- ))} - - {/* Tool call UI */} - {toolCalls.length > 0 && ( -
- {toolCalls.map((tc) => { - const draft = toolResultDrafts.find((d) => d.toolCallId === tc.id); - return ( -
+ + function + + + {tc.function.name} + +
+
+                  {tc.function.arguments}
+                
+
+ +