feat: Add AI quality features - character memory, content filter, style guard, quality gate

This commit is contained in:
empty
2026-01-06 17:40:55 +08:00
parent af5140b142
commit 29b6cdf709
9 changed files with 891 additions and 0 deletions

View File

@@ -0,0 +1,208 @@
/**
* Quality API Client
*
* Provides client methods for AI quality features:
* - Character memory management
* - Content filtering
* - Style guard
* - Quality gate evaluation
*/
const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000/api'
export interface Character {
id: string
name: string
appearance_description: string
clothing_description: string
distinctive_features: string[]
character_type: string
reference_image?: string
}
export interface ContentCheckResult {
passed: boolean
category: 'safe' | 'sensitive' | 'blocked'
flagged_items: string[]
reason?: string
}
export interface StyleAnchor {
color_palette: string
art_style: string
composition_style: string
texture: string
lighting: string
style_prefix: string
reference_image?: string
}
export interface QualityScore {
overall_score: number
aesthetic_score: number
alignment_score: number
technical_score: number
passed: boolean
issues: string[]
}
class QualityApiClient {
private baseUrl = API_BASE
// ============================================================
// Character Memory
// ============================================================
async getCharacters(storyboardId: string): Promise<Character[]> {
const response = await fetch(
`${this.baseUrl}/quality/characters/${storyboardId}`
)
if (!response.ok) {
throw new Error('Failed to get characters')
}
return response.json()
}
async createCharacter(
storyboardId: string,
data: Omit<Character, 'id'>
): Promise<Character> {
const response = await fetch(
`${this.baseUrl}/quality/characters/${storyboardId}`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
}
)
if (!response.ok) {
throw new Error('Failed to create character')
}
return response.json()
}
async updateCharacter(
storyboardId: string,
charId: string,
data: Omit<Character, 'id'>
): Promise<Character> {
const response = await fetch(
`${this.baseUrl}/quality/characters/${storyboardId}/${charId}`,
{
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
}
)
if (!response.ok) {
throw new Error('Failed to update character')
}
return response.json()
}
async deleteCharacter(storyboardId: string, charId: string): Promise<void> {
const response = await fetch(
`${this.baseUrl}/quality/characters/${storyboardId}/${charId}`,
{ method: 'DELETE' }
)
if (!response.ok) {
throw new Error('Failed to delete character')
}
}
// ============================================================
// Content Filter
// ============================================================
async checkContent(text: string): Promise<ContentCheckResult> {
const response = await fetch(`${this.baseUrl}/quality/check-content`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text }),
})
if (!response.ok) {
throw new Error('Failed to check content')
}
return response.json()
}
// ============================================================
// Style Guard
// ============================================================
async extractStyle(
storyboardId: string,
imagePath: string
): Promise<StyleAnchor> {
const response = await fetch(
`${this.baseUrl}/quality/extract-style/${storyboardId}`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ image_path: imagePath }),
}
)
if (!response.ok) {
throw new Error('Failed to extract style')
}
return response.json()
}
async getStyle(storyboardId: string): Promise<StyleAnchor | null> {
const response = await fetch(
`${this.baseUrl}/quality/style/${storyboardId}`
)
if (response.status === 404) {
return null
}
if (!response.ok) {
throw new Error('Failed to get style')
}
return response.json()
}
async applyStyle(
storyboardId: string,
prompt: string
): Promise<{ styled_prompt: string }> {
const response = await fetch(
`${this.baseUrl}/quality/apply-style/${storyboardId}`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ prompt }),
}
)
if (!response.ok) {
throw new Error('Failed to apply style')
}
return response.json()
}
// ============================================================
// Quality Gate
// ============================================================
async evaluateImage(
imagePath: string,
prompt: string,
narration?: string
): Promise<QualityScore> {
const response = await fetch(`${this.baseUrl}/quality/evaluate-image`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
image_path: imagePath,
prompt,
narration,
}),
})
if (!response.ok) {
throw new Error('Failed to evaluate image')
}
return response.json()
}
}
// Export singleton instance
export const qualityApi = new QualityApiClient()