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
@@ -0,0 +1,90 @@
|
||||
"""形仪域_真形锚点_主仪化仪定相_声音档案与克隆授权
|
||||
|
||||
Revision ID: 008
|
||||
Revises: 007
|
||||
Create Date: 2026-08-27 20:15:34.665285
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '008'
|
||||
down_revision: Union[str, Sequence[str], None] = '007'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Upgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('form_anchors',
|
||||
sa.Column('id', sa.String(length=80), nullable=False),
|
||||
sa.Column('character_id', sa.String(length=80), nullable=False),
|
||||
sa.Column('features_json', sa.Text(), nullable=False),
|
||||
sa.Column('age_presentation', sa.String(length=40), nullable=False),
|
||||
sa.Column('permissions_json', sa.Text(), nullable=False),
|
||||
sa.Column('mutable_scope_json', sa.Text(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(['character_id'], ['characters.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_form_anchors_character_id'), 'form_anchors', ['character_id'], unique=False)
|
||||
op.create_table('forms',
|
||||
sa.Column('id', sa.String(length=80), nullable=False),
|
||||
sa.Column('character_id', sa.String(length=80), nullable=False),
|
||||
sa.Column('kind', sa.String(length=20), nullable=False),
|
||||
sa.Column('status', sa.String(length=20), nullable=False),
|
||||
sa.Column('source', sa.String(length=20), nullable=False),
|
||||
sa.Column('description', sa.Text(), nullable=False),
|
||||
sa.Column('media_ref', sa.String(length=500), nullable=True),
|
||||
sa.Column('live2d_json', sa.Text(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column('adopted_at', sa.DateTime(timezone=True), nullable=True),
|
||||
sa.ForeignKeyConstraint(['character_id'], ['characters.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_forms_character_id'), 'forms', ['character_id'], unique=False)
|
||||
op.create_index(op.f('ix_forms_kind'), 'forms', ['kind'], unique=False)
|
||||
op.create_index(op.f('ix_forms_status'), 'forms', ['status'], unique=False)
|
||||
op.create_table('voice_profiles',
|
||||
sa.Column('id', sa.String(length=80), nullable=False),
|
||||
sa.Column('character_id', sa.String(length=80), nullable=False),
|
||||
sa.Column('source', sa.String(length=30), nullable=False),
|
||||
sa.Column('name', sa.String(length=120), nullable=False),
|
||||
sa.Column('params_json', sa.Text(), nullable=False),
|
||||
sa.Column('grant_purpose', sa.String(length=200), nullable=True),
|
||||
sa.Column('grant_expires_at', sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column('grant_distribution', sa.String(length=200), nullable=True),
|
||||
sa.Column('grant_revoked_at', sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column('is_active', sa.Boolean(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(['character_id'], ['characters.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_voice_profiles_character_id'), 'voice_profiles', ['character_id'], unique=False)
|
||||
op.create_index(op.f('ix_voice_profiles_is_active'), 'voice_profiles', ['is_active'], unique=False)
|
||||
op.add_column('pending_confirmations', sa.Column('expires_at', sa.DateTime(timezone=True), nullable=True))
|
||||
op.add_column('pending_confirmations', sa.Column('brief_json', sa.Text(), server_default='{}', nullable=False))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Downgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('pending_confirmations', 'brief_json')
|
||||
op.drop_column('pending_confirmations', 'expires_at')
|
||||
op.drop_index(op.f('ix_voice_profiles_is_active'), table_name='voice_profiles')
|
||||
op.drop_index(op.f('ix_voice_profiles_character_id'), table_name='voice_profiles')
|
||||
op.drop_table('voice_profiles')
|
||||
op.drop_index(op.f('ix_forms_status'), table_name='forms')
|
||||
op.drop_index(op.f('ix_forms_kind'), table_name='forms')
|
||||
op.drop_index(op.f('ix_forms_character_id'), table_name='forms')
|
||||
op.drop_table('forms')
|
||||
op.drop_index(op.f('ix_form_anchors_character_id'), table_name='form_anchors')
|
||||
op.drop_table('form_anchors')
|
||||
# ### end Alembic commands ###
|
||||
Reference in new issue
Block a user