This commit is contained in:
2026-07-30 09:58:34 +08:00
parent 1511af1774
commit 20ae86bbe4
5 changed files with 90 additions and 28 deletions
BIN
View File
Binary file not shown.
@@ -1,11 +1,13 @@
package domain
type VideoGen struct {
MinDuration string `json:"min_duration" dc:"模型支持的最小视频时长(秒)"`
MaxDuration string `json:"max_duration" dc:"模型支持的最大视频时长(秒)"`
FirstFrame string `json:"first_frame" dc:"视频的首帧/初始画面,传入一张图片作为视频第一帧画面"`
ReferenceImage string `json:"reference_image" dc:"参考图片,用于生成角色形象和风格一致性参考"`
ReferenceVideo string `json:"reference_video" dc:"参考视频"`
MaxMediaItems string `json:"max_media_items" dc:"模型允许传入的最大参考媒体数量"`
ReferenceTemplate string `json:"reference_template" dc:"prompt 中引用参考素材的标签格式,用 %d 作为编号占位符"`
MinDuration string `json:"min_duration" dc:"模型支持的最小视频时长(秒)"`
MaxDuration string `json:"max_duration" dc:"模型支持的最大视频时长(秒)"`
FirstFrame string `json:"first_frame" dc:"视频的首帧/初始画面,传入一张图片作为视频第一帧画面"`
ReferenceImage string `json:"reference_image" dc:"参考图片,用于生成角色形象和风格一致性参考"`
ReferenceVideo string `json:"reference_video" dc:"参考视频"`
MaxMediaItems string `json:"max_media_items" dc:"模型允许传入的最大参考媒体数量"`
ImgReferenceTemplate string `json:"img_reference_template" dc:"prompt 中引用参考图片的标签格式,用 %d 作为编号占位符"`
VideoReferenceTemplate string `json:"video_reference_template" dc:"prompt 中引用参考视频的标签格式,用 %d 作为编号占位符"`
AudioReferenceTemplate string `json:"audio_reference_template" dc:"prompt 中引用参考音频的标签格式,用 %d 作为编号占位符"`
}
+10 -5
View File
@@ -850,14 +850,19 @@ func createPendingTasks(ctx context.Context, dramaId, epId int64, script string,
var nameLabels []_nameLabel
var refURLs []string
// 根据 schema 中的 media.max_items 读取 media 数组长度上限
// schema 读取 media 数组长度上限
maxRefItems := 5
if modelCfg.SchemaMapping != "" && strings.HasPrefix(modelCfg.SchemaMapping, "{") {
if modelCfg.SchemaMapping != "" && strings.HasPrefix(modelCfg.SchemaMapping, "{") && modelCfg.Schema != "" {
var m map[string]any
if err := json.Unmarshal([]byte(modelCfg.SchemaMapping), &m); err == nil {
if v, ok := m["max_media_items"]; ok {
if f, ok := v.(float64); ok {
maxRefItems = int(f)
if path, ok := m["max_media_items"].(string); ok && path != "" {
var sMap map[string]any
if err := json.Unmarshal([]byte(modelCfg.Schema), &sMap); err == nil {
if v := schemaValueByPath(sMap, path); v != nil {
if f, ok := v.(float64); ok {
maxRefItems = int(f)
}
}
}
}
}
+33 -7
View File
@@ -2441,25 +2441,51 @@ func parseMediaDef(val string) *mediaDef {
return nil
}
// schemaDurationBounds 从 schema_mapping 中取 duration 在 body 中的点号路径,自动加 body 前缀后从 schema 获取实际约束值
// schemaDurationBounds 从 schema_mapping 中min_duration/max_duration 的路径,
// 再按路径从 schema JSON 中获取实际约束值。
func schemaDurationBounds(schema, schemaMapping string) (minDur, maxDur int) {
var m map[string]any
if err := json.Unmarshal([]byte(schemaMapping), &m); err != nil {
return 0, 0
}
if v, ok := m["min_duration"]; ok {
if f, ok := v.(float64); ok {
minDur = int(f)
var sMap map[string]any
json.Unmarshal([]byte(schema), &sMap)
if path, ok := m["min_duration"].(string); ok && path != "" {
if v := schemaValueByPath(sMap, path); v != nil {
if f, ok := v.(float64); ok {
minDur = int(f)
}
}
}
if v, ok := m["max_duration"]; ok {
if f, ok := v.(float64); ok {
maxDur = int(f)
if path, ok := m["max_duration"].(string); ok && path != "" {
if v := schemaValueByPath(sMap, path); v != nil {
if f, ok := v.(float64); ok {
maxDur = int(f)
}
}
}
return
}
// schemaValueByPath 按点号路径在嵌套 map 中查找值。
// 例如 path="body.parameters.duration.min" 会依次导航。
func schemaValueByPath(doc map[string]any, path string) any {
parts := strings.Split(path, ".")
current := any(doc)
for _, p := range parts {
m, ok := current.(map[string]any)
if !ok {
return nil
}
current = m[p]
if current == nil {
return nil
}
}
return current
}
// convertBodyMediaToBase64 将 body 中媒体文件的本地路径转为 base64(原地修改)
// 覆盖 reference_urls(字符串数组)和 media(对象数组的 urlField 字段)
func convertBodyMediaToBase64(body map[string]any, urlField string) {
+38 -9
View File
@@ -114,9 +114,11 @@ func buildSchemaMapping(ctx context.Context, schema string, existingMapping stri
## 输出格式
输出的 JSON 对象键是 json 字段名。每个字段的值由你根据 Schema 中的信息分析得出
- 若该概念在 Schema 中有对应的具体字段,输出字段的点号路径
- 若该概念没有直接对应的字段(如需要从字段的 description 描述中推断),输出推导出的内容
输出的 JSON 对象键是 json 字段名。每个字段的值有两类
第一类(Schema 路径):若该概念在 Schema 中有直接定义位置(约束值或字段定义),输出定位到该位置的完整点号路径。若定位的是对象数组的特定元素及其属性,在路径后追加 ?实际筛选字段名=筛选值&实际值字段名=# 格式,其中 =# 标记的目标值字段名替换为 schema 中的实际字段名。
第二类(推导字符串):若该概念在 Schema 中没有直接对应的定义位置,输出根据 Schema 信息推导出的内容字符串。
## 重要规则
@@ -150,12 +152,7 @@ func buildSchemaMapping(ctx context.Context, schema string, existingMapping stri
return existingMapping
}
content := strings.TrimSpace(result.Content)
// 去掉可能的 markdown 代码块标记
content = strings.TrimPrefix(content, "```json")
content = strings.TrimPrefix(content, "```")
content = strings.TrimSuffix(content, "```")
content = strings.TrimSpace(content)
content := extractJSONObject(result.Content)
var mapping map[string]any
if err := json.Unmarshal([]byte(content), &mapping); err != nil {
@@ -217,6 +214,38 @@ func schemaMaxTokens(schemaStr string) int {
return 4096
}
// extractJSONObject 从字符串中提取第一个完整的 JSON 对象({...}),
// 丢弃前后的非 JSON 文本(如 LLM 返回的分析说明)。
func extractJSONObject(s string) string {
start := strings.Index(s, "{")
if start < 0 {
return s
}
// 跳过可能出现的 markdown 代码块标记
for start > 0 {
ch := s[start-1]
if ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' {
start--
} else {
break
}
}
end := strings.LastIndex(s, "}")
if end <= start {
return s
}
// 去掉可能的 markdown 代码块后缀
snippet := s[start : end+1]
// 去掉可能的 ```json 或 ``` 前缀(已经在 { 之前被截掉了,但以防万一还留有残余)
snippet = strings.TrimPrefix(snippet, "```json")
snippet = strings.TrimPrefix(snippet, "```")
snippet = strings.TrimSuffix(snippet, "```")
snippet = strings.TrimSpace(snippet)
return snippet
}
// buildFieldDescriptions 从 domain.VideoGen 结构体中反射读取字段定义,构建提示词中的目标字段说明
func buildFieldDescriptions() string {
t := reflect.TypeOf((*domain.VideoGen)(nil)).Elem()