Files
momentry_studio/src/composables/useVLM.ts
Momentry Studio e2612dde72 feat: add VLM composables for Vue 3 integration
- useVLMScenes(fileUuid): load scenes for a specific file with thumbnails
- useVLMSearch(): search VLM scenes and traces with bilingual support
- useSceneThumbnail(fileUuid, sceneIndex): load scene thumbnail URLs
- TypeScript interfaces: VLMTag, VLMScene, VLMTrace, FileScene
- Uses direct HTTP calls via getApiBase() (no Tauri IPC needed)
- Follows Vue 3 composable best practices (ref, loading, error states)
2026-10-23 20:21:00 +08:00

157 lines
4.0 KiB
TypeScript

import { ref } from 'vue'
import { getApiBase } from '@/api/config'
export interface VLMTag {
en: string
local: string
}
export interface VLMScene {
file_uuid: string
file_name: string
scene_index: number
timestamp_sec: number
frame_number: number
thumbnail_url: string
vlm_description: string
vlm_description_localized?: string
vlm_objects: string[]
vlm_tags: VLMTag[]
score: number
source: string
}
export interface VLMTrace {
file_uuid: string
file_name: string
trace_id: number
start_frame: number
end_frame: number
start_time: number
end_time: number
timestamp_sec: number
thumbnail_url: string
vlm_description: string
vlm_description_localized?: string
vlm_objects: string[]
vlm_tags: VLMTag[]
score: number
identity_name?: string
}
export interface FileScene {
index: number
filename: string
timestamp_sec: number
vlm_description: string
vlm_description_localized?: string
vlm_objects: string[]
vlm_tags: VLMTag[]
vlm_status: string
}
export function useVLMScenes(fileUuid: string) {
const scenes = ref<FileScene[]>([])
const loading = ref(false)
const error = ref<string | null>(null)
async function loadScenes() {
loading.value = true
error.value = null
try {
const base = getApiBase()
const resp = await fetch(`${base}/api/v1/file/${fileUuid}/scenes`)
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
const data = await resp.json()
scenes.value = Array.isArray(data) ? data : (data.scenes || [])
} catch (e: any) {
error.value = e.message || 'Failed to load scenes'
scenes.value = []
} finally {
loading.value = false
}
}
function getThumbnailUrl(sceneIndex: number): string {
const base = getApiBase()
const paddedIndex = String(sceneIndex).padStart(3, '0')
return `${base}/api/v1/file/${fileUuid}/scenes/scene_${paddedIndex}.jpg`
}
return { scenes, loading, error, loadScenes, getThumbnailUrl }
}
export function useVLMSearch() {
const scenes = ref<VLMScene[]>([])
const traces = ref<VLMTrace[]>([])
const loading = ref(false)
const error = ref<string | null>(null)
const total = ref(0)
async function search(query: string, options?: {
page_size?: number
min_score?: number
output_language?: string
offset?: number
}) {
loading.value = true
error.value = null
const base = getApiBase()
const params = {
query,
page_size: options?.page_size || 20,
min_score: options?.min_score ?? 0.0,
output_language: options?.output_language || 'en',
...(options?.offset != null ? { offset: options.offset } : {})
}
try {
const [scenesRes, tracesRes] = await Promise.all([
fetch(`${base}/api/v1/search/vlm/scenes`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(params)
}).then(r => r.json()),
fetch(`${base}/api/v1/search/vlm/traces`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(params)
}).then(r => r.json())
])
scenes.value = scenesRes?.results || []
traces.value = tracesRes?.results || []
total.value = scenesRes?.total || 0
} catch (e: any) {
error.value = e.message || 'Search failed'
scenes.value = []
traces.value = []
} finally {
loading.value = false
}
}
function clearResults() {
scenes.value = []
traces.value = []
total.value = 0
}
return { scenes, traces, loading, error, total, search, clearResults }
}
export function useSceneThumbnail(fileUuid: string, sceneIndex: number) {
const url = ref<string | null>(null)
const loading = ref(false)
function loadThumbnail() {
loading.value = true
const base = getApiBase()
const paddedIndex = String(sceneIndex).padStart(3, '0')
url.value = `${base}/api/v1/file/${fileUuid}/scenes/scene_${paddedIndex}.jpg`
loading.value = false
}
return { url, loading, loadThumbnail }
}