269 lines
6.5 KiB
TypeScript
269 lines
6.5 KiB
TypeScript
import type { FamilyMember } from "@/types/family"
|
||
|
||
/**
|
||
* AI 辅助功能配置
|
||
*/
|
||
export interface AIConfig {
|
||
provider: "openai" | "custom" | "browser"
|
||
apiKey?: string
|
||
apiEndpoint?: string
|
||
model?: string
|
||
}
|
||
|
||
/**
|
||
* 获取 AI 配置
|
||
*/
|
||
export function getAIConfig(): AIConfig | null {
|
||
if (typeof window === "undefined") return null
|
||
|
||
const config = localStorage.getItem("ai-config")
|
||
return config ? JSON.parse(config) : null
|
||
}
|
||
|
||
/**
|
||
* 保存 AI 配置
|
||
*/
|
||
export function saveAIConfig(config: AIConfig): void {
|
||
if (typeof window === "undefined") return
|
||
|
||
localStorage.setItem("ai-config", JSON.stringify(config))
|
||
}
|
||
|
||
/**
|
||
* 生成成员简介
|
||
*/
|
||
export async function generateBio(member: Partial<FamilyMember>): Promise<string> {
|
||
const config = getAIConfig()
|
||
|
||
if (!config) {
|
||
throw new Error("请先配置 AI 服务")
|
||
}
|
||
|
||
const prompt = `请根据以下信息生成一段简洁的人物简介(100字以内):
|
||
|
||
姓名:${member.fullName || "未知"}
|
||
性别:${member.gender === "MALE" ? "男" : member.gender === "FEMALE" ? "女" : "未知"}
|
||
出生日期:${member.birthDate || "未知"}
|
||
${member.deathDate ? `逝世日期:${member.deathDate}` : ""}
|
||
籍贯:${member.ancestralHome || "未知"}
|
||
${member.courtesyName ? `字:${member.courtesyName}` : ""}
|
||
${member.artName ? `号:${member.artName}` : ""}
|
||
|
||
请用第三人称,简洁专业的语言描述。`
|
||
|
||
return await callAI(prompt, config)
|
||
}
|
||
|
||
/**
|
||
* 分析家族关系
|
||
*/
|
||
export async function analyzeRelationship(
|
||
member1: FamilyMember,
|
||
member2: FamilyMember,
|
||
allMembers: FamilyMember[]
|
||
): Promise<string> {
|
||
const config = getAIConfig()
|
||
|
||
if (!config) {
|
||
throw new Error("请先配置 AI 服务")
|
||
}
|
||
|
||
const prompt = `请分析以下两位家族成员的关系:
|
||
|
||
成员A:${member1.fullName},第${member1.generation}世
|
||
成员B:${member2.fullName},第${member2.generation}世
|
||
|
||
家族成员总数:${allMembers.length}人
|
||
|
||
请分析他们可能的关系(如:兄弟、堂兄弟、叔侄等),并给出理由。`
|
||
|
||
return await callAI(prompt, config)
|
||
}
|
||
|
||
/**
|
||
* 智能推荐字辈
|
||
*/
|
||
export async function suggestGenerationName(
|
||
generation: number,
|
||
existingNames: string[]
|
||
): Promise<string[]> {
|
||
const config = getAIConfig()
|
||
|
||
if (!config) {
|
||
throw new Error("请先配置 AI 服务")
|
||
}
|
||
|
||
const prompt = `请为家族第${generation}世推荐5个合适的字辈用字。
|
||
|
||
已使用的字辈:${existingNames.join("、")}
|
||
|
||
要求:
|
||
1. 符合中国传统字辈命名习惯
|
||
2. 寓意吉祥、积极向上
|
||
3. 避免与已有字辈重复
|
||
4. 只返回5个汉字,用逗号分隔`
|
||
|
||
const response = await callAI(prompt, config)
|
||
return response.split(/[,,、]/).map(s => s.trim()).filter(Boolean).slice(0, 5)
|
||
}
|
||
|
||
/**
|
||
* 生成家族故事标题
|
||
*/
|
||
export async function generateStoryTitle(content: string): Promise<string> {
|
||
const config = getAIConfig()
|
||
|
||
if (!config) {
|
||
throw new Error("请先配置 AI 服务")
|
||
}
|
||
|
||
const prompt = `请为以下家族故事生成一个简洁有吸引力的标题(10字以内):
|
||
|
||
${content.substring(0, 200)}...
|
||
|
||
只返回标题,不要其他内容。`
|
||
|
||
return await callAI(prompt, config)
|
||
}
|
||
|
||
/**
|
||
* 智能数据校验
|
||
*/
|
||
export async function validateMemberData(member: Partial<FamilyMember>): Promise<{
|
||
valid: boolean
|
||
issues: string[]
|
||
suggestions: string[]
|
||
}> {
|
||
const config = getAIConfig()
|
||
|
||
if (!config) {
|
||
return {
|
||
valid: true,
|
||
issues: [],
|
||
suggestions: []
|
||
}
|
||
}
|
||
|
||
const prompt = `请检查以下家族成员信息是否合理:
|
||
|
||
姓名:${member.fullName || "未填写"}
|
||
性别:${member.gender || "未填写"}
|
||
出生日期:${member.birthDate || "未填写"}
|
||
逝世日期:${member.deathDate || "未填写"}
|
||
世代:${member.generation || "未填写"}
|
||
|
||
请指出可能存在的问题和改进建议。以JSON格式返回:
|
||
{
|
||
"valid": true/false,
|
||
"issues": ["问题1", "问题2"],
|
||
"suggestions": ["建议1", "建议2"]
|
||
}`
|
||
|
||
try {
|
||
const response = await callAI(prompt, config)
|
||
return JSON.parse(response)
|
||
} catch (error) {
|
||
return {
|
||
valid: true,
|
||
issues: [],
|
||
suggestions: []
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 调用 AI API
|
||
*/
|
||
async function callAI(prompt: string, config: AIConfig): Promise<string> {
|
||
if (config.provider === "browser") {
|
||
// 使用浏览器内置 AI(如果支持)
|
||
if ("ai" in window && "languageModel" in (window as any).ai) {
|
||
try {
|
||
const session = await (window as any).ai.languageModel.create()
|
||
const result = await session.prompt(prompt)
|
||
return result
|
||
} catch (error) {
|
||
throw new Error("浏览器 AI 不可用")
|
||
}
|
||
} else {
|
||
throw new Error("浏览器不支持内置 AI")
|
||
}
|
||
}
|
||
|
||
if (config.provider === "openai") {
|
||
// 调用 OpenAI API
|
||
const response = await fetch("https://api.openai.com/v1/chat/completions", {
|
||
method: "POST",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
"Authorization": `Bearer ${config.apiKey}`,
|
||
},
|
||
body: JSON.stringify({
|
||
model: config.model || "gpt-3.5-turbo",
|
||
messages: [
|
||
{
|
||
role: "system",
|
||
content: "你是一个专业的家谱管理助手,帮助用户整理和分析家族信息。"
|
||
},
|
||
{
|
||
role: "user",
|
||
content: prompt
|
||
}
|
||
],
|
||
temperature: 0.7,
|
||
max_tokens: 500,
|
||
}),
|
||
})
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`API 调用失败: ${response.statusText}`)
|
||
}
|
||
|
||
const data = await response.json()
|
||
return data.choices[0].message.content.trim()
|
||
}
|
||
|
||
if (config.provider === "custom") {
|
||
// 调用自定义 API
|
||
const response = await fetch(config.apiEndpoint!, {
|
||
method: "POST",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
...(config.apiKey ? { "Authorization": `Bearer ${config.apiKey}` } : {}),
|
||
},
|
||
body: JSON.stringify({ prompt }),
|
||
})
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`API 调用失败: ${response.statusText}`)
|
||
}
|
||
|
||
const data = await response.json()
|
||
return data.response || data.result || data.text || ""
|
||
}
|
||
|
||
throw new Error("不支持的 AI 提供商")
|
||
}
|
||
|
||
/**
|
||
* 检查 AI 是否可用
|
||
*/
|
||
export function isAIAvailable(): boolean {
|
||
const config = getAIConfig()
|
||
if (!config) return false
|
||
|
||
if (config.provider === "browser") {
|
||
return typeof window !== "undefined" && "ai" in window
|
||
}
|
||
|
||
if (config.provider === "openai") {
|
||
return !!config.apiKey
|
||
}
|
||
|
||
if (config.provider === "custom") {
|
||
return !!config.apiEndpoint
|
||
}
|
||
|
||
return false
|
||
}
|