Files
19904408334 76c55fbb73 feat: 新增业务字段路径读写工具
新增 TakeBusinessFields、WriteBusinessFields、SetByPath 与 GetByPath 等工具,支持按映射路径写入请求体与解析响应,并更新相关依赖。
2026-08-18 10:11:09 +08:00

102 lines
2.7 KiB
Go

package service
import (
"sort"
"strings"
modelUtils "model-gateway/service/utils"
"github.com/gogf/gf/v2/util/gconv"
)
// streamToolCallAcc 流式 tool_call 按 index 累加的碎片(OpenAI 兼容 delta 格式)
type streamToolCallAcc struct {
id string
typ string
fnName string
fnArgs strings.Builder
}
// streamToolCallDelta OpenAI 兼容流式 tool_call 增量片段,字段名集中于此
type streamToolCallDelta struct {
Index int `json:"index"`
Id string `json:"id"`
Type string `json:"type"`
Function struct {
Name string `json:"name"`
Arguments string `json:"arguments"`
} `json:"function"`
}
// toStreamToolCallDeltas 把 JSON 反序列化的 any 数组在边界转成强类型片段
func toStreamToolCallDeltas(rawCalls []any) []streamToolCallDelta {
var deltas []streamToolCallDelta
if err := gconv.Structs(rawCalls, &deltas); err != nil {
return nil
}
return deltas
}
// accumulateStreamToolCallsByPath 按配置路径从 chunk 读取 tool_calls 数组并累加。
// 路径未命中或值非数组时无副作用(不创建任何槽位)。
func accumulateStreamToolCallsByPath(chunk map[string]any, toolsPath string, acc map[int]*streamToolCallAcc) {
raw := modelUtils.GetByPathValue(chunk, modelUtils.CleanFieldPath(toolsPath))
rawCalls, _ := raw.([]any)
accumulateToolCallFragments(toStreamToolCallDeltas(rawCalls), acc)
}
// accumulateToolCallFragments 按 index 累加 tool_calls 增量片段:
// id/type/function.name 首片段补齐,function.arguments 为字符串片段需按 index 拼接。
func accumulateToolCallFragments(rawCalls []streamToolCallDelta, acc map[int]*streamToolCallAcc) {
for _, d := range rawCalls {
slot, ok := acc[d.Index]
if !ok {
slot = &streamToolCallAcc{}
acc[d.Index] = slot
}
if d.Id != "" {
slot.id = d.Id
}
if d.Type != "" {
slot.typ = d.Type
}
if d.Function.Name != "" {
slot.fnName = d.Function.Name
}
slot.fnArgs.WriteString(d.Function.Arguments)
}
}
// finalizeStreamToolCalls 把累加结果按 index 升序转为 []map[string]any,形状对齐 dto.ModelTool。
// 无有效工具返回 nil。
func finalizeStreamToolCalls(acc map[int]*streamToolCallAcc) []map[string]any {
if len(acc) == 0 {
return nil
}
idx := make([]int, 0, len(acc))
for i := range acc {
idx = append(idx, i)
}
sort.Ints(idx)
tools := make([]map[string]any, 0, len(idx))
for _, i := range idx {
s := acc[i]
fn := map[string]any{}
if s.fnName != "" {
fn["name"] = s.fnName
}
if s.fnArgs.Len() > 0 {
fn["arguments"] = s.fnArgs.String()
}
tool := map[string]any{"function": fn}
if s.id != "" {
tool["id"] = s.id
}
if s.typ != "" {
tool["type"] = s.typ
}
tools = append(tools, tool)
}
return tools
}