Add projects
This commit is contained in:
parent
2d3a9ad623
commit
8b607dd700
1802 changed files with 503346 additions and 2 deletions
|
|
@ -0,0 +1,793 @@
|
|||
package interactions
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
translatorcommon "github.com/router-for-me/CLIProxyAPI/v7/internal/translator/common"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/util"
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/sjson"
|
||||
)
|
||||
|
||||
func ConvertInteractionsRequestToAntigravity(modelName string, inputRawJSON []byte, stream bool) []byte {
|
||||
root := gjson.ParseBytes(inputRawJSON)
|
||||
functionNameMap := util.SanitizedFunctionNameMap(inputRawJSON)
|
||||
out := []byte(`{"project":"","request":{"contents":[]},"model":""}`)
|
||||
out, _ = sjson.SetBytes(out, "model", modelName)
|
||||
if stream || root.Get("stream").Bool() {
|
||||
out, _ = sjson.SetBytes(out, "request.stream", true)
|
||||
}
|
||||
out = copyInteractionsSystemToAntigravity(out, root)
|
||||
out = copyInteractionsGenerationConfigToAntigravity(out, root)
|
||||
contentItems := translatorcommon.NewRawArrayItems(root.Get("input.#").Int())
|
||||
appendInteractionsInputToAntigravity(&contentItems, root.Get("input"))
|
||||
out = translatorcommon.SetRawArrayItems(out, "request.contents", contentItems)
|
||||
out = copyInteractionsToolsToAntigravity(out, root, functionNameMap)
|
||||
out = rewriteInteractionsFunctionNames(out, functionNameMap)
|
||||
out = attachDefaultAntigravitySafetySettings(out)
|
||||
return out
|
||||
}
|
||||
|
||||
func rewriteInteractionsFunctionNames(out []byte, functionNameMap map[string]string) []byte {
|
||||
contents := gjson.GetBytes(out, "request.contents")
|
||||
canBatchContents := contents.IsArray()
|
||||
if canBatchContents {
|
||||
contents.ForEach(func(_, content gjson.Result) bool {
|
||||
parts := content.Get("parts")
|
||||
if parts.Exists() && !parts.IsArray() {
|
||||
canBatchContents = false
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
if canBatchContents {
|
||||
contentsChanged := false
|
||||
contentItems := translatorcommon.NewRawArrayItems(contents.Get("#").Int())
|
||||
contents.ForEach(func(_, content gjson.Result) bool {
|
||||
contentJSON := []byte(content.Raw)
|
||||
partsChanged := false
|
||||
partItems := make([][]byte, 0, 4)
|
||||
content.Get("parts").ForEach(func(_, part gjson.Result) bool {
|
||||
partJSON := []byte(part.Raw)
|
||||
for _, field := range []string{"functionCall", "functionResponse"} {
|
||||
nameResult := part.Get(field + ".name")
|
||||
name := nameResult.String()
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
mappedName := util.MapSanitizedFunctionName(functionNameMap, name)
|
||||
if nameResult.Type == gjson.String && mappedName == name {
|
||||
continue
|
||||
}
|
||||
partJSON, _ = sjson.SetBytes(partJSON, field+".name", mappedName)
|
||||
partsChanged = true
|
||||
}
|
||||
partItems = append(partItems, partJSON)
|
||||
return true
|
||||
})
|
||||
if partsChanged {
|
||||
contentJSON, _ = sjson.SetRawBytes(contentJSON, "parts", translatorcommon.JoinRawArray(partItems))
|
||||
contentsChanged = true
|
||||
}
|
||||
contentItems = append(contentItems, contentJSON)
|
||||
return true
|
||||
})
|
||||
if contentsChanged {
|
||||
out, _ = sjson.SetRawBytes(out, "request.contents", translatorcommon.JoinRawArray(contentItems))
|
||||
}
|
||||
} else {
|
||||
for contentIndex, content := range contents.Array() {
|
||||
for partIndex, part := range content.Get("parts").Array() {
|
||||
for _, field := range []string{"functionCall", "functionResponse"} {
|
||||
nameResult := part.Get(field + ".name")
|
||||
name := nameResult.String()
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
mappedName := util.MapSanitizedFunctionName(functionNameMap, name)
|
||||
if nameResult.Type == gjson.String && mappedName == name {
|
||||
continue
|
||||
}
|
||||
path := fmt.Sprintf("request.contents.%d.parts.%d.%s.name", contentIndex, partIndex, field)
|
||||
out, _ = sjson.SetBytes(out, path, mappedName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allowedPath := "request.toolConfig.functionCallingConfig.allowedFunctionNames"
|
||||
allowedNames := gjson.GetBytes(out, allowedPath)
|
||||
if allowedNames.IsArray() {
|
||||
namesChanged := false
|
||||
nameItems := make([][]byte, 0, 4)
|
||||
allowedNames.ForEach(func(_, name gjson.Result) bool {
|
||||
mappedName := util.MapSanitizedFunctionName(functionNameMap, name.String())
|
||||
namesChanged = namesChanged || name.Type != gjson.String || mappedName != name.String()
|
||||
mappedNameJSON, _ := json.Marshal(mappedName)
|
||||
nameItems = append(nameItems, mappedNameJSON)
|
||||
return true
|
||||
})
|
||||
if namesChanged {
|
||||
out, _ = sjson.SetRawBytes(out, allowedPath, translatorcommon.JoinRawArray(nameItems))
|
||||
}
|
||||
} else {
|
||||
for index, name := range allowedNames.Array() {
|
||||
mappedName := util.MapSanitizedFunctionName(functionNameMap, name.String())
|
||||
if name.Type == gjson.String && mappedName == name.String() {
|
||||
continue
|
||||
}
|
||||
path := fmt.Sprintf("%s.%d", allowedPath, index)
|
||||
out, _ = sjson.SetBytes(out, path, mappedName)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func copyInteractionsSystemToAntigravity(out []byte, root gjson.Result) []byte {
|
||||
sys := root.Get("system_instruction")
|
||||
if !sys.Exists() {
|
||||
return out
|
||||
}
|
||||
if sys.Type == gjson.String {
|
||||
instr := []byte(`{"parts":[{"text":""}]}`)
|
||||
instr, _ = sjson.SetBytes(instr, "parts.0.text", sys.String())
|
||||
out, _ = sjson.SetRawBytes(out, "request.systemInstruction", instr)
|
||||
return out
|
||||
}
|
||||
if text := sys.Get("text"); text.Exists() && !sys.Get("parts").Exists() {
|
||||
instr := []byte(`{"parts":[{"text":""}]}`)
|
||||
instr, _ = sjson.SetBytes(instr, "parts.0.text", text.String())
|
||||
out, _ = sjson.SetRawBytes(out, "request.systemInstruction", instr)
|
||||
return out
|
||||
}
|
||||
out, _ = sjson.SetRawBytes(out, "request.systemInstruction", []byte(sys.Raw))
|
||||
return out
|
||||
}
|
||||
|
||||
func copyInteractionsGenerationConfigToAntigravity(out []byte, root gjson.Result) []byte {
|
||||
if cfg := root.Get("generation_config"); cfg.Exists() {
|
||||
out, _ = sjson.SetRawBytes(out, "request.generationConfig", convertSnakeCaseKeysToCamelCaseForAntigravity([]byte(cfg.Raw)))
|
||||
} else if cfg := root.Get("generationConfig"); cfg.Exists() {
|
||||
out, _ = sjson.SetRawBytes(out, "request.generationConfig", []byte(cfg.Raw))
|
||||
}
|
||||
out = normalizeInteractionsGenerationConfigForAntigravity(out)
|
||||
out = copyInteractionsReasoningToAntigravity(out, root)
|
||||
out = copyInteractionsResponseModalitiesToAntigravity(out, root)
|
||||
out = copyInteractionsToolChoiceToAntigravity(out, root)
|
||||
return out
|
||||
}
|
||||
|
||||
func normalizeInteractionsGenerationConfigForAntigravity(out []byte) []byte {
|
||||
if thinkingLevel := gjson.GetBytes(out, "request.generationConfig.thinkingLevel"); thinkingLevel.Exists() {
|
||||
out, _ = sjson.SetRawBytes(out, "request.generationConfig.thinkingConfig.thinkingLevel", []byte(thinkingLevel.Raw))
|
||||
out, _ = sjson.DeleteBytes(out, "request.generationConfig.thinkingLevel")
|
||||
}
|
||||
if thinkingBudget := gjson.GetBytes(out, "request.generationConfig.thinkingBudget"); thinkingBudget.Exists() {
|
||||
out, _ = sjson.SetRawBytes(out, "request.generationConfig.thinkingConfig.thinkingBudget", []byte(thinkingBudget.Raw))
|
||||
out, _ = sjson.DeleteBytes(out, "request.generationConfig.thinkingBudget")
|
||||
}
|
||||
if includeThoughts := gjson.GetBytes(out, "request.generationConfig.includeThoughts"); includeThoughts.Exists() {
|
||||
out, _ = sjson.SetRawBytes(out, "request.generationConfig.thinkingConfig.includeThoughts", []byte(includeThoughts.Raw))
|
||||
out, _ = sjson.DeleteBytes(out, "request.generationConfig.includeThoughts")
|
||||
}
|
||||
if summaries := gjson.GetBytes(out, "request.generationConfig.thinkingSummaries"); summaries.Exists() {
|
||||
if includeThoughts, ok := antigravityThinkingSummariesIncludeThoughts(summaries); ok {
|
||||
out, _ = sjson.SetBytes(out, "request.generationConfig.thinkingConfig.includeThoughts", includeThoughts)
|
||||
}
|
||||
out, _ = sjson.DeleteBytes(out, "request.generationConfig.thinkingSummaries")
|
||||
}
|
||||
if toolChoice := gjson.GetBytes(out, "request.generationConfig.toolChoice"); toolChoice.Exists() {
|
||||
out, _ = sjson.DeleteBytes(out, "request.generationConfig.toolChoice")
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func copyInteractionsReasoningToAntigravity(out []byte, root gjson.Result) []byte {
|
||||
reasoning := root.Get("reasoning")
|
||||
if !reasoning.Exists() {
|
||||
return out
|
||||
}
|
||||
effort := strings.ToLower(strings.TrimSpace(reasoning.Get("effort").String()))
|
||||
if effort == "" {
|
||||
effort = strings.ToLower(strings.TrimSpace(reasoning.Get("thinking_level").String()))
|
||||
}
|
||||
if effort != "" {
|
||||
// Thinking amount and summary visibility are independent. This OpenAI-style
|
||||
// compatibility alias controls only the amount; includeThoughts is written
|
||||
// below only for an explicit Interactions summary selector.
|
||||
if effort == "auto" {
|
||||
out, _ = sjson.SetBytes(out, "request.generationConfig.thinkingConfig.thinkingBudget", -1)
|
||||
} else {
|
||||
out, _ = sjson.SetBytes(out, "request.generationConfig.thinkingConfig.thinkingLevel", effort)
|
||||
}
|
||||
}
|
||||
if summary := reasoning.Get("summary"); summary.Exists() {
|
||||
if includeThoughts, ok := antigravityThinkingSummariesIncludeThoughts(summary); ok {
|
||||
out, _ = sjson.SetBytes(out, "request.generationConfig.thinkingConfig.includeThoughts", includeThoughts)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func copyInteractionsResponseModalitiesToAntigravity(out []byte, root gjson.Result) []byte {
|
||||
mods := root.Get("response_modalities")
|
||||
if !mods.Exists() {
|
||||
mods = root.Get("responseModalities")
|
||||
}
|
||||
if !mods.Exists() || !mods.IsArray() {
|
||||
return out
|
||||
}
|
||||
var responseMods []string
|
||||
mods.ForEach(func(_, mod gjson.Result) bool {
|
||||
switch strings.ToLower(strings.TrimSpace(mod.String())) {
|
||||
case "text":
|
||||
responseMods = append(responseMods, "TEXT")
|
||||
case "image":
|
||||
responseMods = append(responseMods, "IMAGE")
|
||||
case "audio":
|
||||
responseMods = append(responseMods, "AUDIO")
|
||||
}
|
||||
return true
|
||||
})
|
||||
if len(responseMods) > 0 {
|
||||
out, _ = sjson.SetBytes(out, "request.generationConfig.responseModalities", responseMods)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func copyInteractionsToolChoiceToAntigravity(out []byte, root gjson.Result) []byte {
|
||||
toolChoice := root.Get("tool_choice")
|
||||
if !toolChoice.Exists() {
|
||||
toolChoice = root.Get("generation_config.tool_choice")
|
||||
}
|
||||
if !toolChoice.Exists() {
|
||||
toolChoice = root.Get("generationConfig.toolChoice")
|
||||
}
|
||||
if !toolChoice.Exists() {
|
||||
return out
|
||||
}
|
||||
mode := ""
|
||||
var allowedNames []string
|
||||
if toolChoice.Type == gjson.String {
|
||||
switch strings.ToLower(strings.TrimSpace(toolChoice.String())) {
|
||||
case "none":
|
||||
mode = "NONE"
|
||||
case "auto":
|
||||
mode = "AUTO"
|
||||
case "required", "any":
|
||||
mode = "ANY"
|
||||
}
|
||||
} else if toolChoice.IsObject() {
|
||||
switch strings.ToLower(strings.TrimSpace(toolChoice.Get("type").String())) {
|
||||
case "none":
|
||||
mode = "NONE"
|
||||
case "auto":
|
||||
mode = "AUTO"
|
||||
case "required", "any":
|
||||
mode = "ANY"
|
||||
case "function":
|
||||
mode = "ANY"
|
||||
if name := toolChoice.Get("function.name").String(); strings.TrimSpace(name) != "" {
|
||||
allowedNames = append(allowedNames, name)
|
||||
}
|
||||
case "tool":
|
||||
mode = "ANY"
|
||||
if name := toolChoice.Get("name").String(); strings.TrimSpace(name) != "" {
|
||||
allowedNames = append(allowedNames, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if mode == "" {
|
||||
return out
|
||||
}
|
||||
out, _ = sjson.SetBytes(out, "request.toolConfig.functionCallingConfig.mode", mode)
|
||||
if len(allowedNames) > 0 {
|
||||
out, _ = sjson.SetBytes(out, "request.toolConfig.functionCallingConfig.allowedFunctionNames", allowedNames)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func appendInteractionsInputToAntigravity(items *[][]byte, input gjson.Result) {
|
||||
if !input.Exists() {
|
||||
return
|
||||
}
|
||||
if input.Type == gjson.String {
|
||||
appendAntigravityTextContent(items, "user", input.String())
|
||||
return
|
||||
}
|
||||
if input.IsArray() {
|
||||
input.ForEach(func(_, item gjson.Result) bool {
|
||||
appendInteractionsStepToAntigravity(items, item, "user")
|
||||
return true
|
||||
})
|
||||
return
|
||||
}
|
||||
if steps := input.Get("steps"); steps.Exists() && steps.IsArray() {
|
||||
defaultRole := "user"
|
||||
if role := input.Get("role").String(); role == "model" || role == "assistant" {
|
||||
defaultRole = "model"
|
||||
}
|
||||
steps.ForEach(func(_, step gjson.Result) bool {
|
||||
appendInteractionsStepToAntigravity(items, step, defaultRole)
|
||||
return true
|
||||
})
|
||||
return
|
||||
}
|
||||
appendInteractionsStepToAntigravity(items, input, "user")
|
||||
}
|
||||
|
||||
func appendInteractionsStepToAntigravity(items *[][]byte, step gjson.Result, defaultRole string) {
|
||||
if step.Type == gjson.String {
|
||||
appendAntigravityTextContent(items, defaultRole, step.String())
|
||||
return
|
||||
}
|
||||
if steps := step.Get("steps"); steps.Exists() && steps.IsArray() {
|
||||
role := defaultRole
|
||||
if itemRole := step.Get("role").String(); itemRole == "model" || itemRole == "assistant" {
|
||||
role = "model"
|
||||
} else if itemRole == "user" {
|
||||
role = "user"
|
||||
}
|
||||
steps.ForEach(func(_, child gjson.Result) bool {
|
||||
appendInteractionsStepToAntigravity(items, child, role)
|
||||
return true
|
||||
})
|
||||
return
|
||||
}
|
||||
switch step.Get("type").String() {
|
||||
case "model_output":
|
||||
appendInteractionsStepContentToAntigravity(items, "model", step, false)
|
||||
case "thought":
|
||||
appendInteractionsStepContentToAntigravity(items, "model", step, true)
|
||||
case "function_call":
|
||||
appendInteractionsFunctionCallToAntigravity(items, step)
|
||||
case "function_result":
|
||||
appendInteractionsFunctionResultToAntigravity(items, step)
|
||||
case "user_input", "":
|
||||
if step.Get("parts").Exists() {
|
||||
appendInteractionsNativeContentToAntigravity(items, step, defaultRole)
|
||||
} else {
|
||||
appendInteractionsContentListToAntigravity(items, defaultRole, step.Get("content"))
|
||||
}
|
||||
default:
|
||||
if step.Get("parts").Exists() {
|
||||
appendInteractionsNativeContentToAntigravity(items, step, defaultRole)
|
||||
} else if step.Get("content").Exists() {
|
||||
appendInteractionsContentListToAntigravity(items, defaultRole, step.Get("content"))
|
||||
} else if text := step.Get("text"); text.Exists() {
|
||||
appendAntigravityTextContent(items, defaultRole, text.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func appendInteractionsNativeContentToAntigravity(items *[][]byte, step gjson.Result, defaultRole string) {
|
||||
parts := step.Get("parts")
|
||||
if !parts.Exists() || !parts.IsArray() {
|
||||
return
|
||||
}
|
||||
partItems := make([][]byte, 0, 4)
|
||||
parts.ForEach(func(_, part gjson.Result) bool {
|
||||
if partJSON := interactionsNativeAntigravityPart(part); len(partJSON) > 0 {
|
||||
partItems = append(partItems, partJSON)
|
||||
}
|
||||
return true
|
||||
})
|
||||
if len(partItems) > 0 {
|
||||
role := antigravityContentRole(step.Get("role").String(), defaultRole)
|
||||
*items = append(*items, antigravityContent(role, partItems))
|
||||
}
|
||||
}
|
||||
|
||||
func appendInteractionsStepContentToAntigravity(items *[][]byte, role string, step gjson.Result, thought bool) {
|
||||
content := step.Get("content")
|
||||
if !content.Exists() {
|
||||
return
|
||||
}
|
||||
partItems := make([][]byte, 0, 4)
|
||||
if content.IsArray() {
|
||||
content.ForEach(func(_, part gjson.Result) bool {
|
||||
if partJSON := appendInteractionsContentToAntigravityPart(nil, part, thought); len(partJSON) > 0 {
|
||||
partItems = append(partItems, partJSON)
|
||||
}
|
||||
return true
|
||||
})
|
||||
} else if content.IsObject() {
|
||||
if partJSON := appendInteractionsContentToAntigravityPart(nil, content, thought); len(partJSON) > 0 {
|
||||
partItems = append(partItems, partJSON)
|
||||
}
|
||||
} else if content.Type == gjson.String {
|
||||
partItems = append(partItems, antigravityTextPartJSON(content.String(), thought))
|
||||
}
|
||||
if len(partItems) > 0 {
|
||||
*items = append(*items, antigravityContent(role, partItems))
|
||||
}
|
||||
}
|
||||
|
||||
func appendInteractionsContentListToAntigravity(items *[][]byte, role string, content gjson.Result) {
|
||||
if !content.Exists() {
|
||||
return
|
||||
}
|
||||
if content.IsArray() {
|
||||
content.ForEach(func(_, part gjson.Result) bool {
|
||||
appendInteractionsContentPartToAntigravity(items, role, part)
|
||||
return true
|
||||
})
|
||||
return
|
||||
}
|
||||
if content.IsObject() {
|
||||
appendInteractionsContentPartToAntigravity(items, role, content)
|
||||
} else if content.Type == gjson.String {
|
||||
appendAntigravityTextContent(items, role, content.String())
|
||||
}
|
||||
}
|
||||
|
||||
func appendInteractionsContentPartToAntigravity(items *[][]byte, role string, part gjson.Result) {
|
||||
partJSON := appendInteractionsContentToAntigravityPart(nil, part, false)
|
||||
if len(partJSON) > 0 {
|
||||
*items = append(*items, antigravityContent(role, [][]byte{partJSON}))
|
||||
}
|
||||
}
|
||||
|
||||
func appendInteractionsContentToAntigravityPart(_ []byte, content gjson.Result, thought bool) []byte {
|
||||
if text := content.Get("text"); text.Exists() {
|
||||
return antigravityTextPartJSON(text.String(), thought)
|
||||
}
|
||||
if inline := content.Get("inline_data"); inline.Exists() {
|
||||
return antigravityInlineDataPartJSON(inline)
|
||||
}
|
||||
if inline := content.Get("inlineData"); inline.Exists() {
|
||||
return antigravityInlineDataPartJSON(inline)
|
||||
}
|
||||
switch strings.ToLower(strings.TrimSpace(content.Get("type").String())) {
|
||||
case "text":
|
||||
if text := content.Get("text"); text.Exists() {
|
||||
return antigravityTextPartJSON(text.String(), thought)
|
||||
}
|
||||
case "image", "audio", "video", "document":
|
||||
if mime := content.Get("mime_type"); mime.Exists() || content.Get("mimeType").Exists() {
|
||||
mimeType := mime.String()
|
||||
if mimeType == "" {
|
||||
mimeType = content.Get("mimeType").String()
|
||||
}
|
||||
if data := content.Get("data").String(); data != "" {
|
||||
return antigravityInlineDataPartJSON(gjson.Parse(fmt.Sprintf(`{"mime_type":%q,"data":%q}`, mimeType, data)))
|
||||
}
|
||||
}
|
||||
if uri := content.Get("file_uri"); uri.Exists() || content.Get("fileUri").Exists() {
|
||||
fileURI := uri.String()
|
||||
if fileURI == "" {
|
||||
fileURI = content.Get("fileUri").String()
|
||||
}
|
||||
mimeType := content.Get("mime_type").String()
|
||||
if mimeType == "" {
|
||||
mimeType = content.Get("mimeType").String()
|
||||
}
|
||||
return antigravityFileDataPartJSON(gjson.Parse(fmt.Sprintf(`{"mimeType":%q,"fileUri":%q}`, mimeType, fileURI)))
|
||||
}
|
||||
if url := content.Get("url"); url.Exists() {
|
||||
return antigravityInlineDataPartFromDataURL(url.String())
|
||||
}
|
||||
case "image_url":
|
||||
return antigravityInlineDataPartFromDataURL(content.Get("image_url.url").String())
|
||||
case "input_audio":
|
||||
mimeType := antigravityInputAudioMimeType(content.Get("input_audio.format").String())
|
||||
return antigravityInlineDataPartJSON(gjson.Parse(fmt.Sprintf(`{"mime_type":%q,"data":%q}`, mimeType, content.Get("input_audio.data").String())))
|
||||
case "file":
|
||||
filename := content.Get("file.filename").String()
|
||||
fileData := content.Get("file.file_data").String()
|
||||
if mimeType, data, ok := translatorcommon.NormalizeOpenAIFileData(filename, "", fileData); ok {
|
||||
return antigravityInlineDataPartJSON(gjson.Parse(fmt.Sprintf(`{"mime_type":%q,"data":%q}`, mimeType, data)))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func appendInteractionsFunctionCallToAntigravity(items *[][]byte, step gjson.Result) {
|
||||
part := []byte(`{"functionCall":{"name":"","args":{}}}`)
|
||||
part, _ = sjson.SetBytes(part, "functionCall.name", step.Get("name").String())
|
||||
if callID := step.Get("call_id"); callID.Exists() {
|
||||
part, _ = sjson.SetBytes(part, "functionCall.id", callID.String())
|
||||
} else if id := step.Get("id"); id.Exists() {
|
||||
part, _ = sjson.SetBytes(part, "functionCall.id", id.String())
|
||||
}
|
||||
if args := step.Get("arguments"); args.Exists() {
|
||||
part, _ = sjson.SetRawBytes(part, "functionCall.args", []byte(args.Raw))
|
||||
}
|
||||
*items = append(*items, antigravityContent("model", [][]byte{part}))
|
||||
}
|
||||
|
||||
func appendInteractionsFunctionResultToAntigravity(items *[][]byte, step gjson.Result) {
|
||||
part := []byte(`{"functionResponse":{"name":"","response":{}}}`)
|
||||
part, _ = sjson.SetBytes(part, "functionResponse.name", step.Get("name").String())
|
||||
if callID := step.Get("call_id"); callID.Exists() {
|
||||
part, _ = sjson.SetBytes(part, "functionResponse.id", callID.String())
|
||||
} else if id := step.Get("id"); id.Exists() {
|
||||
part, _ = sjson.SetBytes(part, "functionResponse.id", id.String())
|
||||
}
|
||||
if result := step.Get("result"); result.Exists() {
|
||||
part, _ = sjson.SetRawBytes(part, "functionResponse.response", []byte(result.Raw))
|
||||
}
|
||||
*items = append(*items, antigravityContent("user", [][]byte{part}))
|
||||
}
|
||||
|
||||
func copyInteractionsToolsToAntigravity(out []byte, root gjson.Result, functionNameMap map[string]string) []byte {
|
||||
tools := root.Get("tools")
|
||||
if !tools.Exists() {
|
||||
return out
|
||||
}
|
||||
if !tools.IsArray() {
|
||||
out, _ = sjson.SetRawBytes(out, "request.tools", []byte(tools.Raw))
|
||||
return out
|
||||
}
|
||||
var functionDeclarations [][]byte
|
||||
var otherTools [][]byte
|
||||
tools.ForEach(func(_, tool gjson.Result) bool {
|
||||
if decls := tool.Get("functionDeclarations"); decls.Exists() && decls.IsArray() {
|
||||
decls.ForEach(func(_, decl gjson.Result) bool {
|
||||
if converted := antigravityFunctionDeclarationJSON(decl, functionNameMap); len(converted) > 0 {
|
||||
functionDeclarations = append(functionDeclarations, converted)
|
||||
}
|
||||
return true
|
||||
})
|
||||
return true
|
||||
}
|
||||
if decls := tool.Get("function_declarations"); decls.Exists() && decls.IsArray() {
|
||||
decls.ForEach(func(_, decl gjson.Result) bool {
|
||||
if converted := antigravityFunctionDeclarationJSON(decl, functionNameMap); len(converted) > 0 {
|
||||
functionDeclarations = append(functionDeclarations, converted)
|
||||
}
|
||||
return true
|
||||
})
|
||||
return true
|
||||
}
|
||||
if tool.Get("type").String() == "function" || tool.Get("name").Exists() {
|
||||
if converted := antigravityFunctionDeclarationJSON(tool, functionNameMap); len(converted) > 0 {
|
||||
functionDeclarations = append(functionDeclarations, converted)
|
||||
}
|
||||
return true
|
||||
}
|
||||
otherTools = append(otherTools, []byte(tool.Raw))
|
||||
return true
|
||||
})
|
||||
deduplicated := util.DeduplicateFunctionDeclarations(translatorcommon.JoinRawArray(functionDeclarations))
|
||||
hasFunction := len(deduplicated) > 2
|
||||
if hasFunction || len(otherTools) > 0 {
|
||||
toolItems := make([][]byte, 0, 1+len(otherTools))
|
||||
if hasFunction {
|
||||
functionToolNode := []byte(`{"functionDeclarations":[]}`)
|
||||
functionToolNode, _ = sjson.SetRawBytes(functionToolNode, "functionDeclarations", deduplicated)
|
||||
toolItems = append(toolItems, functionToolNode)
|
||||
}
|
||||
toolItems = append(toolItems, otherTools...)
|
||||
out, _ = sjson.SetRawBytes(out, "request.tools", translatorcommon.JoinRawArray(toolItems))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func antigravityFunctionDeclarationJSON(decl gjson.Result, functionNameMap map[string]string) []byte {
|
||||
fn := decl
|
||||
if nested := decl.Get("function"); nested.Exists() && nested.IsObject() {
|
||||
fn = nested
|
||||
}
|
||||
name := fn.Get("name").String()
|
||||
if strings.TrimSpace(name) == "" {
|
||||
return nil
|
||||
}
|
||||
out := []byte(`{"name":"","parametersJsonSchema":{"type":"object","properties":{}}}`)
|
||||
out, _ = sjson.SetBytes(out, "name", util.MapSanitizedFunctionName(functionNameMap, name))
|
||||
if desc := fn.Get("description"); desc.Exists() {
|
||||
out, _ = sjson.SetBytes(out, "description", desc.String())
|
||||
}
|
||||
if params := fn.Get("parametersJsonSchema"); params.Exists() {
|
||||
out, _ = sjson.SetRawBytes(out, "parametersJsonSchema", []byte(params.Raw))
|
||||
} else if params := fn.Get("parameters"); params.Exists() {
|
||||
out, _ = sjson.SetRawBytes(out, "parametersJsonSchema", []byte(params.Raw))
|
||||
}
|
||||
if response := fn.Get("response"); response.Exists() {
|
||||
out, _ = sjson.SetRawBytes(out, "response", []byte(response.Raw))
|
||||
}
|
||||
if responseSchema := fn.Get("responseJsonSchema"); responseSchema.Exists() {
|
||||
out, _ = sjson.SetRawBytes(out, "responseJsonSchema", []byte(responseSchema.Raw))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func interactionsNativeAntigravityPart(part gjson.Result) []byte {
|
||||
switch {
|
||||
case part.Get("text").Exists(), part.Get("functionCall").Exists(), part.Get("functionResponse").Exists():
|
||||
return []byte(part.Raw)
|
||||
case part.Get("inlineData").Exists():
|
||||
return antigravityInlineDataPartJSON(part.Get("inlineData"))
|
||||
case part.Get("fileData").Exists():
|
||||
return antigravityFileDataPartJSON(part.Get("fileData"))
|
||||
case part.Get("inline_data").Exists():
|
||||
return antigravityInlineDataPartJSON(part.Get("inline_data"))
|
||||
case part.Get("file_data").Exists():
|
||||
return antigravityFileDataPartJSON(part.Get("file_data"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func antigravityTextPartJSON(text string, thought bool) []byte {
|
||||
partJSON := []byte(`{"text":""}`)
|
||||
partJSON, _ = sjson.SetBytes(partJSON, "text", text)
|
||||
if thought {
|
||||
partJSON, _ = sjson.SetBytes(partJSON, "thought", true)
|
||||
}
|
||||
return partJSON
|
||||
}
|
||||
|
||||
func antigravityInlineDataPartJSON(inline gjson.Result) []byte {
|
||||
mimeType := inline.Get("mimeType").String()
|
||||
if mimeType == "" {
|
||||
mimeType = inline.Get("mime_type").String()
|
||||
}
|
||||
data := inline.Get("data").String()
|
||||
if mimeType == "" || data == "" {
|
||||
return nil
|
||||
}
|
||||
partJSON := []byte(`{"inlineData":{"mimeType":"","data":""}}`)
|
||||
partJSON, _ = sjson.SetBytes(partJSON, "inlineData.mimeType", mimeType)
|
||||
partJSON, _ = sjson.SetBytes(partJSON, "inlineData.data", data)
|
||||
return partJSON
|
||||
}
|
||||
|
||||
func antigravityFileDataPartJSON(fileData gjson.Result) []byte {
|
||||
mimeType := fileData.Get("mimeType").String()
|
||||
if mimeType == "" {
|
||||
mimeType = fileData.Get("mime_type").String()
|
||||
}
|
||||
fileURI := fileData.Get("fileUri").String()
|
||||
if fileURI == "" {
|
||||
fileURI = fileData.Get("file_uri").String()
|
||||
}
|
||||
if mimeType == "" || fileURI == "" {
|
||||
return nil
|
||||
}
|
||||
partJSON := []byte(`{"fileData":{"mimeType":"","fileUri":""}}`)
|
||||
partJSON, _ = sjson.SetBytes(partJSON, "fileData.mimeType", mimeType)
|
||||
partJSON, _ = sjson.SetBytes(partJSON, "fileData.fileUri", fileURI)
|
||||
return partJSON
|
||||
}
|
||||
|
||||
func antigravityInlineDataPartFromDataURL(dataURL string) []byte {
|
||||
if !strings.HasPrefix(dataURL, "data:") {
|
||||
return nil
|
||||
}
|
||||
payload := dataURL[5:]
|
||||
pieces := strings.SplitN(payload, ";", 2)
|
||||
if len(pieces) != 2 || !strings.HasPrefix(pieces[1], "base64,") {
|
||||
return nil
|
||||
}
|
||||
return antigravityInlineDataPartJSON(gjson.Parse(fmt.Sprintf(`{"mime_type":%q,"data":%q}`, pieces[0], pieces[1][7:])))
|
||||
}
|
||||
|
||||
func appendAntigravityTextContent(items *[][]byte, role, text string) {
|
||||
part := antigravityTextPartJSON(text, false)
|
||||
*items = append(*items, antigravityContent(antigravityContentRole(role, "user"), [][]byte{part}))
|
||||
}
|
||||
|
||||
func antigravityContent(role string, parts [][]byte) []byte {
|
||||
content := []byte(`{"role":"","parts":[]}`)
|
||||
content, _ = sjson.SetBytes(content, "role", role)
|
||||
content, _ = sjson.SetRawBytes(content, "parts", translatorcommon.JoinRawArray(parts))
|
||||
return content
|
||||
}
|
||||
|
||||
func antigravityContentRole(role, defaultRole string) string {
|
||||
switch strings.ToLower(strings.TrimSpace(role)) {
|
||||
case "model", "assistant":
|
||||
return "model"
|
||||
case "user":
|
||||
return "user"
|
||||
}
|
||||
if defaultRole == "model" {
|
||||
return "model"
|
||||
}
|
||||
return "user"
|
||||
}
|
||||
|
||||
func antigravityInputAudioMimeType(format string) string {
|
||||
switch strings.ToLower(strings.TrimSpace(format)) {
|
||||
case "wav":
|
||||
return "audio/wav"
|
||||
case "mp3":
|
||||
return "audio/mpeg"
|
||||
case "flac":
|
||||
return "audio/flac"
|
||||
case "opus":
|
||||
return "audio/opus"
|
||||
case "pcm16":
|
||||
return "audio/pcm"
|
||||
default:
|
||||
return "audio/mpeg"
|
||||
}
|
||||
}
|
||||
|
||||
func antigravityThinkingSummariesIncludeThoughts(summary gjson.Result) (bool, bool) {
|
||||
if summary.Type != gjson.String {
|
||||
return false, false
|
||||
}
|
||||
switch strings.ToLower(strings.TrimSpace(summary.String())) {
|
||||
case "auto":
|
||||
return true, true
|
||||
case "none":
|
||||
return false, true
|
||||
default:
|
||||
return false, false
|
||||
}
|
||||
}
|
||||
|
||||
func convertSnakeCaseKeysToCamelCaseForAntigravity(raw []byte) []byte {
|
||||
root := gjson.ParseBytes(raw)
|
||||
if !root.Exists() {
|
||||
return raw
|
||||
}
|
||||
out := []byte(`{}`)
|
||||
out = copySnakeCaseValueToCamelCaseForAntigravity(out, "", root)
|
||||
return out
|
||||
}
|
||||
|
||||
func copySnakeCaseValueToCamelCaseForAntigravity(out []byte, path string, node gjson.Result) []byte {
|
||||
if node.IsObject() {
|
||||
node.ForEach(func(key, value gjson.Result) bool {
|
||||
childPath := joinAntigravityJSONPath(path, toAntigravityCamelCase(key.String()))
|
||||
out = copySnakeCaseValueToCamelCaseForAntigravity(out, childPath, value)
|
||||
return true
|
||||
})
|
||||
return out
|
||||
}
|
||||
if node.IsArray() {
|
||||
node.ForEach(func(_, value gjson.Result) bool {
|
||||
out = copySnakeCaseValueToCamelCaseForAntigravity(out, path+".-1", value)
|
||||
return true
|
||||
})
|
||||
return out
|
||||
}
|
||||
out, _ = sjson.SetRawBytes(out, path, []byte(node.Raw))
|
||||
return out
|
||||
}
|
||||
|
||||
func joinAntigravityJSONPath(path, key string) string {
|
||||
if path == "" {
|
||||
return key
|
||||
}
|
||||
return path + "." + key
|
||||
}
|
||||
|
||||
func toAntigravityCamelCase(s string) string {
|
||||
parts := strings.Split(s, "_")
|
||||
if len(parts) == 0 {
|
||||
return s
|
||||
}
|
||||
out := parts[0]
|
||||
for _, part := range parts[1:] {
|
||||
if part == "" {
|
||||
continue
|
||||
}
|
||||
out += strings.ToUpper(part[:1]) + part[1:]
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func attachDefaultAntigravitySafetySettings(out []byte) []byte {
|
||||
if gjson.GetBytes(out, "request.safetySettings").Exists() {
|
||||
return out
|
||||
}
|
||||
settings := []map[string]string{
|
||||
{"category": "HARM_CATEGORY_HARASSMENT", "threshold": "OFF"},
|
||||
{"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "OFF"},
|
||||
{"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold": "OFF"},
|
||||
{"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "OFF"},
|
||||
{"category": "HARM_CATEGORY_CIVIC_INTEGRITY", "threshold": "BLOCK_NONE"},
|
||||
}
|
||||
raw, errMarshal := json.Marshal(settings)
|
||||
if errMarshal != nil {
|
||||
return out
|
||||
}
|
||||
out, _ = sjson.SetRawBytes(out, "request.safetySettings", raw)
|
||||
return out
|
||||
}
|
||||
Loading…
Reference in a new issue