1
This commit is contained in:
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 作为编号占位符"`
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user