Add system memory monitoring, Bedrock support, and Discord integrations
- Add CPU/GPU memory monitoring endpoint and frontend widget - Add Amazon Bedrock model support (API key / inference profiles) plus model selector entries - Add Discord weather/laundry notification job (JMA forecast + LLM-generated message) - Add Discord bot that replies to mentions via the configured LLM, with weather-aware context - Add scheduled "useless tips" job with separate webhook and quiet-hours skip - Switch FastAPI startup to lifespan handler, log viewer, markdown rendering, and toast UI additions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,14 @@
|
||||
export interface ApiLog {
|
||||
id: number
|
||||
session_id: string | null
|
||||
model: string
|
||||
request_json: string
|
||||
response_text: string | null
|
||||
error: string | null
|
||||
latency_ms: number | null
|
||||
created_at: string
|
||||
}
|
||||
|
||||
export interface SessionInfo {
|
||||
id: string
|
||||
title: string
|
||||
@@ -6,22 +17,64 @@ export interface SessionInfo {
|
||||
}
|
||||
|
||||
export interface SSEChunk {
|
||||
type: 'session_id' | 'chunk' | 'done'
|
||||
type: 'session_id' | 'chunk' | 'done' | 'error'
|
||||
value?: string
|
||||
}
|
||||
|
||||
const BASE = ''
|
||||
|
||||
async function parseJson<T>(res: Response, label: string): Promise<T> {
|
||||
const ct = res.headers.get('content-type') ?? ''
|
||||
if (!ct.includes('application/json')) {
|
||||
throw new Error(`${label}: バックエンドに接続できません (${res.status} ${res.statusText})`)
|
||||
}
|
||||
if (!res.ok) {
|
||||
const body = await res.json().catch(() => ({}))
|
||||
throw new Error(`${label}: ${body.detail ?? res.statusText}`)
|
||||
}
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function fetchSessions(): Promise<SessionInfo[]> {
|
||||
const res = await fetch(`${BASE}/chat/sessions`)
|
||||
if (!res.ok) throw new Error('Failed to fetch sessions')
|
||||
return res.json()
|
||||
return parseJson<SessionInfo[]>(res, 'セッション取得')
|
||||
}
|
||||
|
||||
export interface MessageRecord {
|
||||
id: number
|
||||
role: 'user' | 'assistant' | 'system'
|
||||
content: string
|
||||
created_at: string
|
||||
}
|
||||
|
||||
export async function fetchMessages(sessionId: string): Promise<MessageRecord[]> {
|
||||
const res = await fetch(`${BASE}/chat/sessions/${sessionId}/messages`)
|
||||
return parseJson<MessageRecord[]>(res, 'メッセージ取得')
|
||||
}
|
||||
|
||||
export async function deleteSession(sessionId: string): Promise<void> {
|
||||
await fetch(`${BASE}/chat/sessions/${sessionId}`, { method: 'DELETE' })
|
||||
}
|
||||
|
||||
export async function fetchLogs(params: {
|
||||
session_id?: string
|
||||
status?: 'ok' | 'error'
|
||||
limit?: number
|
||||
offset?: number
|
||||
} = {}): Promise<ApiLog[]> {
|
||||
const q = new URLSearchParams()
|
||||
if (params.session_id) q.set('session_id', params.session_id)
|
||||
if (params.status) q.set('status', params.status)
|
||||
if (params.limit != null) q.set('limit', String(params.limit))
|
||||
if (params.offset != null) q.set('offset', String(params.offset))
|
||||
const res = await fetch(`${BASE}/logs?${q}`)
|
||||
return parseJson<ApiLog[]>(res, 'ログ取得')
|
||||
}
|
||||
|
||||
export async function deleteLog(logId: number): Promise<void> {
|
||||
await fetch(`${BASE}/logs/${logId}`, { method: 'DELETE' })
|
||||
}
|
||||
|
||||
export function streamChat(
|
||||
message: string,
|
||||
sessionId: string | null,
|
||||
@@ -54,8 +107,8 @@ export function streamChat(
|
||||
try {
|
||||
const parsed: SSEChunk = JSON.parse(line.slice(6))
|
||||
onChunk(parsed)
|
||||
} catch {
|
||||
// ignore malformed
|
||||
} catch (e) {
|
||||
console.error('[SSE] JSON parse error:', e, '| raw:', line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user