M7:形仪与语音——真形锚点、候选定相、声音档案与克隆授权、ASR 接入
- 形仪域 domains/xingyi:form_anchors(稳定辨识特征/年龄表现/许可禁忌/可变范围——生成内容不可静默覆盖锚点);forms(主仪/化仪/文字形仪,candidate→adopt 定相治理:未定相不进呈现位、主仪唯一、新主仪自动退役旧主仪);voice_profiles(三来源,授权克隆必填用途与传播范围,revoke-grant 撤回立即生效,assert-usable 合成前置校验拒绝撤回/过期) - ASR:元炁 SenseVoice /v1/audio/transcriptions 透传(Whisper 兼容 multipart);YUANQI_BASE_URL 未配置时 503 不降级不伪造——多模态关闭纪律 - 前端形仪工坊(人物详情页):真形锚点编辑(特征逐行解析)/候选登记定相废弃/声音档案建立与授权撤回;头像呈现位改走定相主仪(forms/primary);xingyi api 库 + mock + 组件 4 用例 - 退役:旧 avatar 模块(services/endpoints/avatar、avatar_dir 配置、avatar_url 字段、前端直传组件与测试) - 验收(test_m7_xingyi 三用例):未定相不成为正式形仪(含主仪唯一与旧主仪 retired 断言);克隆授权可撤回(缺用途拒/撤回即拒/过期同理/预设不受约束);关闭全部多模态后文字相伴完整可用(ASR 503 时镜树缘契百工照常——回归) - 四套件:后端 376、前端 203、BFF 4、神识 5 包全绿(2 个预存环境失败不变);迁移 008 净库+±1 干净
This commit is contained in:
1 parent
7e23d6fa84
commit
b419e0afde
30 files changed
+1876
-468
No files matched your search
@@ -3,6 +3,7 @@ import { notFound } from "next/navigation";
|
||||
|
||||
import { CharacterIdentityCard } from "@/components/characters/CharacterIdentityCard";
|
||||
import { CovenantPanel } from "@/components/characters/CovenantPanel";
|
||||
import { FormStudio } from "@/components/xingyi/FormStudio";
|
||||
import { CharacterLifecycleBar } from "@/components/characters/CharacterLifecycleBar";
|
||||
import { ModelPolicyEditForm } from "@/components/characters/ModelPolicyEditForm";
|
||||
import { ProfileEditForm } from "@/components/characters/ProfileEditForm";
|
||||
@@ -14,6 +15,11 @@ import { serverApi } from "@/lib/api/server-client";
|
||||
import { describeError } from "@/lib/utils/error";
|
||||
import { formatDateTime } from "@/lib/utils/format";
|
||||
import type { Covenant } from "@/lib/api/covenants";
|
||||
import type {
|
||||
FormAnchorData,
|
||||
FormData,
|
||||
VoiceProfileData,
|
||||
} from "@/lib/api/xingyi";
|
||||
import type { Character } from "@/types/api";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
@@ -45,6 +51,29 @@ export default async function CharacterDetailPage({
|
||||
} catch {
|
||||
covenant = null;
|
||||
}
|
||||
// 形仪三区独立拉取,失败静默(无形仪是合法状态——启镜只需最小形仪)
|
||||
let anchor: FormAnchorData = {
|
||||
character_id: characterId,
|
||||
features: {},
|
||||
age_presentation: "adult",
|
||||
permissions: {},
|
||||
mutable_scope: {},
|
||||
};
|
||||
let forms: FormData[] = [];
|
||||
let voices: VoiceProfileData[] = [];
|
||||
let primary: FormData | null = null;
|
||||
try {
|
||||
[anchor, forms, voices, primary] = await Promise.all([
|
||||
serverApi.get<FormAnchorData>(`/characters/${characterId}/form-anchor`),
|
||||
serverApi.get<FormData[]>(`/characters/${characterId}/forms`),
|
||||
serverApi.get<VoiceProfileData[]>(`/characters/${characterId}/voices`),
|
||||
serverApi.get<FormData | null>(
|
||||
`/characters/${characterId}/forms/primary`,
|
||||
),
|
||||
]);
|
||||
} catch {
|
||||
/* 空态合法 */
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
@@ -71,12 +100,24 @@ export default async function CharacterDetailPage({
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<CharacterIdentityCard character={character} />
|
||||
<CharacterIdentityCard
|
||||
character={character}
|
||||
primaryMediaRef={primary?.media_ref ?? null}
|
||||
/>
|
||||
|
||||
<div className="mt-6">
|
||||
<CovenantPanel characterId={character.id} initialCovenant={covenant} />
|
||||
</div>
|
||||
|
||||
<div className="mt-6">
|
||||
<FormStudio
|
||||
characterId={character.id}
|
||||
initialAnchor={anchor}
|
||||
initialForms={forms}
|
||||
initialVoices={voices}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||||
<ProfileEditForm character={character} />
|
||||
<ModelPolicyEditForm character={character} />
|
||||
|
||||
Reference in new issue
Block a user