Files

220 lines
6.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Context
仓库当前已经为 Suno 音乐生成铺好了主路径:
- `runtime-model-discovery` 会把 `lyrics``music``suno` 等标识统归到 `audio`
- `provider-routing` 会为 Suno 推断 `tuzi.suno.music` 绑定
- `audio-api-service` 负责提交 `/suno/submit/music`、轮询 `/suno/fetch/{taskId}` 并提取音频 URL
- 任务队列、自动插入和任务面板会把 `TaskType.AUDIO` 默认当成“可播放音频资产”
这条链路在音乐生成场景下是成立的,但在歌词场景下不成立:
- 歌词任务仍然属于 Suno 的异步任务
- 但 fetch 结果里的核心字段是:
- `data.data.text`
- `data.data.title`
- `data.data.tags`
- 没有 `audio_url`
因此,当前代码里“路由类型 = 结果类型 = 画布落地方式”的隐含绑定需要被拆开。
## Goals / Non-Goals
- Goals:
- 在不新增独立模态的前提下支持 Suno `lyrics` 动作
- 保持 `audio` 路由、默认预设和主入口不变
- 显式区分 Suno 的提交动作和最终结果类型
- 让歌词任务进入现有任务队列、恢复、重试和画布插入链路
- 最大化复用现有文本插入能力,而不是新造歌词节点系统
- Non-Goals:
- 不把歌词能力改造成独立 `lyrics` 模态
- 不在本次中实现歌词编辑器、版本对比或歌词协作能力
- 不覆盖所有 Suno 高级动作
- 不重构整个任务系统为严格的 discriminated union 体系
## Decisions
- Decision: 歌词能力继续归属 `audio` 路由族
- 预设配置仍然只维护 `audio` 路由
- AI 输入栏也仍然通过 `音频` 模式进入
- 通过 `sunoAction = 'music' | 'lyrics'` 区分当前执行动作
- Decision: 显式拆分“提交动作”和“结果类型”
- `music``lyrics` 决定 submit path、请求字段和结果提取器
- `resultKind` 决定任务队列展示和画布落地方式
- `TaskType` 首批保持为 `AUDIO`,避免把歌词任务拆成新的队列大类
- Decision: `lyrics` 走独立 submit但继续共用 fetch
- `music -> POST /suno/submit/music`
- `lyrics -> POST /suno/submit/lyrics`
- 两者都通过 `GET /suno/fetch/{task_id}` 获取状态与结果
- Decision: 任务结果模型以增量字段扩展为主
- 保持现有图片、视频、音频路径影响最小
- 为任务结果新增:
- `resultKind`
- `lyricsText`
- `lyricsTitle`
- `lyricsTags`
- `url` 对歌词结果不再视为必填
- Decision: 歌词落画布复用现有文本插入链
- 任务面板点击“插入”时,把歌词结果格式化成 markdown / text 内容
- 自动插入同样走文本插入能力
- 不创建音频节点,也不伪造音频卡片
- Decision: UI 参数按动作分域
- `music` 保留现有参数:
- `mv`
- `title`
- `tags`
- `continueSource`
- `continueClipId`
- `continueAt`
- `lyrics` 只需要:
- `prompt`
- 可选的内部 `notifyHook`
- 音乐专属参数在歌词动作下不显示、不提交
## Proposed Data Shape
```ts
type SunoAction = 'music' | 'lyrics';
type SunoResultKind = 'audio' | 'lyrics';
interface SunoGenerationRequest {
prompt: string;
model?: string;
modelRef?: ModelRef | null;
sunoAction?: SunoAction;
notifyHook?: string;
title?: string;
tags?: string;
mv?: string;
continueClipId?: string;
continueAt?: number;
params?: Record<string, unknown>;
}
interface TaskResult {
resultKind?: 'image' | 'video' | 'audio' | 'lyrics' | 'chat';
url?: string;
urls?: string[];
format: string;
size: number;
title?: string;
previewImageUrl?: string;
providerTaskId?: string;
primaryClipId?: string;
clipIds?: string[];
clips?: AudioClipResult[];
lyricsText?: string;
lyricsTitle?: string;
lyricsTags?: string[];
}
```
## Provider Binding Direction
首批不建议为 `lyrics` 新增独立模态 binding而是在现有 audio binding metadata 上增加动作表:
```ts
interface ProviderAudioBindingMetadata {
defaultAction?: 'music';
supportedActions?: Array<'music' | 'lyrics' | string>;
actions?: Record<
string,
{
submitPath: string;
resultKind: 'audio' | 'lyrics';
requestFields: string[];
}
>;
versionField?: string;
versionOptions?: string[];
defaultVersion?: string;
supportsContinuation?: boolean;
supportsUploadContinuation?: boolean;
supportsTags?: boolean;
supportsTitle?: boolean;
}
```
对 Suno 的首批映射可收敛为:
```ts
{
operation: 'audio',
protocol: 'tuzi.suno.music',
pollPathTemplate: '/suno/fetch/{taskId}',
metadata: {
audio: {
defaultAction: 'music',
supportedActions: ['music', 'lyrics'],
actions: {
music: {
submitPath: '/suno/submit/music',
resultKind: 'audio',
requestFields: ['prompt', 'mv', 'title', 'tags', 'continue_clip_id', 'continue_at']
},
lyrics: {
submitPath: '/suno/submit/lyrics',
resultKind: 'lyrics',
requestFields: ['prompt', 'notify_hook']
}
}
}
}
}
```
## Canvas Formatting Strategy
歌词插入画布时优先生成语义化文本,而不是直接丢一大段纯文本:
```md
# 战斗进行时
标签: EDM, 激烈的
[Chorus]
...
```
这样可以直接复用现有:
- markdown 解析为卡片
- 纯文本回退为文本元素
两条路径。
## Risks / Trade-offs
- 风险: `TaskType.AUDIO` 继续承载歌词任务会让部分旧代码仍然误判为音频资产
- Mitigation: 统一改为优先判断 `result.resultKind`
- 风险: 把 `url` 改为非必填会触发较多类型修正
- Mitigation: 先用增量字段方式扩展,聚焦实际消费点改造
- 风险: 任务队列过滤仍归入“音频”类,用户可能会觉得歌词不是音频
- Mitigation: 在 item 级别增加明确的动作标签,如“歌词”
- 风险: 画布文本插入格式不稳定,影响可读性
- Mitigation: 在服务层统一生成 markdown 模板,不把格式拼装分散到多个组件里
## Implementation Outline
1. 扩展 Suno 请求和 binding metadata加入 `lyrics` 动作
2. 抽出共享 fetch 轮询与 action-specific 结果提取器
3. 扩展任务结果模型与 IndexedDB 读写结构
4. 让 AI 输入栏按动作切换参数面板
5. 让任务队列、恢复与重试按 `resultKind` 分支
6. 让手动插入与自动插入把歌词结果落到文本插入链路