75 lines
2.3 KiB
TypeScript
75 lines
2.3 KiB
TypeScript
/**
|
||
* AI 故事分析 Composable
|
||
* 用於分析故事並提取資產
|
||
*/
|
||
|
||
import { useAIProvider } from './useAIProvider'
|
||
import { buildStoryAnalysisPrompt, type StoryAnalysisInput } from '~/utils/ai/prompts'
|
||
import { getTextModel } from '~/utils/storage'
|
||
import { DEFAULT_TEXT_MODEL } from '~/utils/clients/gemini-models'
|
||
import { getVisionModel } from '~/utils/storage'
|
||
import { DEFAULT_VISION_MODEL } from '~/utils/clients/gemini-models'
|
||
import type { StoryAnalysis } from '~/types/storyboard'
|
||
|
||
export function useAIStoryboardAnalysis() {
|
||
const isLoading = ref(false)
|
||
const error = ref<string | null>(null)
|
||
|
||
async function analyze(input: StoryAnalysisInput): Promise<StoryAnalysis> {
|
||
isLoading.value = true
|
||
error.value = null
|
||
|
||
try {
|
||
// 如果有圖片,使用視覺模型;否則使用文字模型
|
||
const modelName = input.styleImage
|
||
? (getVisionModel() || DEFAULT_VISION_MODEL)
|
||
: (getTextModel() || DEFAULT_TEXT_MODEL)
|
||
const provider = useAIProvider(modelName)
|
||
|
||
let prompt = buildStoryAnalysisPrompt(input)
|
||
|
||
// 如果有風格圖片,需要傳遞圖片給 API
|
||
if (input.styleImage) {
|
||
// 這裡需要根據 provider 類型處理圖片
|
||
// 暫時先傳遞文字 prompt,實際實作時需要處理圖片
|
||
const result = await provider.analyzeCamera({
|
||
prompt,
|
||
image: {
|
||
mimeType: 'image/jpeg',
|
||
data: input.styleImage.replace(/^data:image\/\w+;base64,/, '')
|
||
}
|
||
})
|
||
|
||
// 解析 JSON 結果
|
||
const jsonMatch = result.match(/\{[\s\S]*\}/)
|
||
if (jsonMatch) {
|
||
return JSON.parse(jsonMatch[0]) as StoryAnalysis
|
||
}
|
||
throw new Error('無法解析分析結果')
|
||
} else {
|
||
const result = await provider.generateStoryboard(prompt)
|
||
|
||
// 解析 JSON 結果
|
||
const jsonMatch = result.match(/\{[\s\S]*\}/)
|
||
if (jsonMatch) {
|
||
return JSON.parse(jsonMatch[0]) as StoryAnalysis
|
||
}
|
||
throw new Error('無法解析分析結果')
|
||
}
|
||
} catch (err) {
|
||
const errorMessage = err instanceof Error ? err.message : '未知錯誤'
|
||
error.value = errorMessage
|
||
throw err
|
||
} finally {
|
||
isLoading.value = false
|
||
}
|
||
}
|
||
|
||
return {
|
||
analyze,
|
||
isLoading: readonly(isLoading),
|
||
error: readonly(error)
|
||
}
|
||
}
|
||
|