feat(antigravity): 同步 Antigravity-Manager 的请求逻辑
- System Prompt: 改为简短版,添加 OpenCode 过滤、MCP XML 协议注入、SYSTEM_PROMPT_END 标记 - HTTP Headers: 只保留 Content-Type/Authorization/User-Agent,移除 Accept 和 Host - User-Agent: 改为 antigravity/1.11.9 windows/amd64 - requestType: 动态判断 (agent/web_search/image_gen) - BaseURLs: 添加 daily sandbox 备用 URL - Fallback: 扩展触发条件 (429/408/404/5xx)
This commit is contained in:
@@ -16,15 +16,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// resolveHost 从 URL 解析 host
|
|
||||||
func resolveHost(urlStr string) string {
|
|
||||||
parsed, err := url.Parse(urlStr)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return parsed.Host
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewAPIRequestWithURL 使用指定的 base URL 创建 Antigravity API 请求(v1internal 端点)
|
// NewAPIRequestWithURL 使用指定的 base URL 创建 Antigravity API 请求(v1internal 端点)
|
||||||
func NewAPIRequestWithURL(ctx context.Context, baseURL, action, accessToken string, body []byte) (*http.Request, error) {
|
func NewAPIRequestWithURL(ctx context.Context, baseURL, action, accessToken string, body []byte) (*http.Request, error) {
|
||||||
// 构建 URL,流式请求添加 ?alt=sse 参数
|
// 构建 URL,流式请求添加 ?alt=sse 参数
|
||||||
@@ -39,23 +30,11 @@ func NewAPIRequestWithURL(ctx context.Context, baseURL, action, accessToken stri
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 基础 Headers
|
// 基础 Headers(与 Antigravity-Manager 保持一致,只设置这 3 个)
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
req.Header.Set("Authorization", "Bearer "+accessToken)
|
req.Header.Set("Authorization", "Bearer "+accessToken)
|
||||||
req.Header.Set("User-Agent", UserAgent)
|
req.Header.Set("User-Agent", UserAgent)
|
||||||
|
|
||||||
// Accept Header 根据请求类型设置
|
|
||||||
if isStream {
|
|
||||||
req.Header.Set("Accept", "text/event-stream")
|
|
||||||
} else {
|
|
||||||
req.Header.Set("Accept", "application/json")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 显式设置 Host Header
|
|
||||||
if host := resolveHost(apiURL); host != "" {
|
|
||||||
req.Host = host
|
|
||||||
}
|
|
||||||
|
|
||||||
return req, nil
|
return req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,12 +174,15 @@ func isConnectionError(err error) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// shouldFallbackToNextURL 判断是否应切换到下一个 URL
|
// shouldFallbackToNextURL 判断是否应切换到下一个 URL
|
||||||
// 仅连接错误和 HTTP 429 触发 URL 降级
|
// 与 Antigravity-Manager 保持一致:连接错误、429、408、404、5xx 触发 URL 降级
|
||||||
func shouldFallbackToNextURL(err error, statusCode int) bool {
|
func shouldFallbackToNextURL(err error, statusCode int) bool {
|
||||||
if isConnectionError(err) {
|
if isConnectionError(err) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return statusCode == http.StatusTooManyRequests
|
return statusCode == http.StatusTooManyRequests ||
|
||||||
|
statusCode == http.StatusRequestTimeout ||
|
||||||
|
statusCode == http.StatusNotFound ||
|
||||||
|
statusCode >= 500
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExchangeCode 用 authorization code 交换 token
|
// ExchangeCode 用 authorization code 交换 token
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ const (
|
|||||||
"https://www.googleapis.com/auth/cclog " +
|
"https://www.googleapis.com/auth/cclog " +
|
||||||
"https://www.googleapis.com/auth/experimentsandconfigs"
|
"https://www.googleapis.com/auth/experimentsandconfigs"
|
||||||
|
|
||||||
// User-Agent(模拟官方客户端)
|
// User-Agent(与 Antigravity-Manager 保持一致)
|
||||||
UserAgent = "antigravity/1.104.0 darwin/arm64"
|
UserAgent = "antigravity/1.11.9 windows/amd64"
|
||||||
|
|
||||||
// Session 过期时间
|
// Session 过期时间
|
||||||
SessionTTL = 30 * time.Minute
|
SessionTTL = 30 * time.Minute
|
||||||
@@ -42,9 +42,10 @@ const (
|
|||||||
URLAvailabilityTTL = 5 * time.Minute
|
URLAvailabilityTTL = 5 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
// BaseURLs 定义 Antigravity API 端点
|
// BaseURLs 定义 Antigravity API 端点(与 Antigravity-Manager 保持一致)
|
||||||
var BaseURLs = []string{
|
var BaseURLs = []string{
|
||||||
"https://cloudcode-pa.googleapis.com", // prod
|
"https://cloudcode-pa.googleapis.com", // prod (优先)
|
||||||
|
"https://daily-cloudcode-pa.sandbox.googleapis.com", // daily sandbox (备用)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BaseURL 默认 URL(保持向后兼容)
|
// BaseURL 默认 URL(保持向后兼容)
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ func TransformClaudeToGeminiWithOptions(claudeReq *ClaudeRequest, projectID, map
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. 构建 systemInstruction
|
// 2. 构建 systemInstruction
|
||||||
systemInstruction := buildSystemInstruction(claudeReq.System, claudeReq.Model, opts)
|
systemInstruction := buildSystemInstruction(claudeReq.System, claudeReq.Model, opts, claudeReq.Tools)
|
||||||
|
|
||||||
// 3. 构建 generationConfig
|
// 3. 构建 generationConfig
|
||||||
reqForConfig := claudeReq
|
reqForConfig := claudeReq
|
||||||
@@ -154,8 +154,40 @@ func GetDefaultIdentityPatch() string {
|
|||||||
return antigravityIdentity
|
return antigravityIdentity
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildSystemInstruction 构建 systemInstruction
|
// mcpXMLProtocol MCP XML 工具调用协议(与 Antigravity-Manager 保持一致)
|
||||||
func buildSystemInstruction(system json.RawMessage, modelName string, opts TransformOptions) *GeminiContent {
|
const mcpXMLProtocol = `
|
||||||
|
==== MCP XML 工具调用协议 (Workaround) ====
|
||||||
|
当你需要调用名称以 ` + "`mcp__`" + ` 开头的 MCP 工具时:
|
||||||
|
1) 优先尝试 XML 格式调用:输出 ` + "`<mcp__tool_name>{\"arg\":\"value\"}</mcp__tool_name>`" + `。
|
||||||
|
2) 必须直接输出 XML 块,无需 markdown 包装,内容为 JSON 格式的入参。
|
||||||
|
3) 这种方式具有更高的连通性和容错性,适用于大型结果返回场景。
|
||||||
|
===========================================`
|
||||||
|
|
||||||
|
// hasMCPTools 检测是否有 mcp__ 前缀的工具
|
||||||
|
func hasMCPTools(tools []ClaudeTool) bool {
|
||||||
|
for _, tool := range tools {
|
||||||
|
if strings.HasPrefix(tool.Name, "mcp__") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// filterOpenCodePrompt 过滤 OpenCode 默认提示词,只保留用户自定义指令
|
||||||
|
func filterOpenCodePrompt(text string) string {
|
||||||
|
if !strings.Contains(text, "You are an interactive CLI tool") {
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
// 提取 "Instructions from:" 及之后的部分
|
||||||
|
if idx := strings.Index(text, "Instructions from:"); idx >= 0 {
|
||||||
|
return text[idx:]
|
||||||
|
}
|
||||||
|
// 如果没有自定义指令,返回空
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildSystemInstruction 构建 systemInstruction(与 Antigravity-Manager 保持一致)
|
||||||
|
func buildSystemInstruction(system json.RawMessage, modelName string, opts TransformOptions, tools []ClaudeTool) *GeminiContent {
|
||||||
var parts []GeminiPart
|
var parts []GeminiPart
|
||||||
|
|
||||||
// 先解析用户的 system prompt,检测是否已包含 Antigravity identity
|
// 先解析用户的 system prompt,检测是否已包含 Antigravity identity
|
||||||
@@ -167,10 +199,14 @@ func buildSystemInstruction(system json.RawMessage, modelName string, opts Trans
|
|||||||
var sysStr string
|
var sysStr string
|
||||||
if err := json.Unmarshal(system, &sysStr); err == nil {
|
if err := json.Unmarshal(system, &sysStr); err == nil {
|
||||||
if strings.TrimSpace(sysStr) != "" {
|
if strings.TrimSpace(sysStr) != "" {
|
||||||
userSystemParts = append(userSystemParts, GeminiPart{Text: sysStr})
|
|
||||||
if strings.Contains(sysStr, "You are Antigravity") {
|
if strings.Contains(sysStr, "You are Antigravity") {
|
||||||
userHasAntigravityIdentity = true
|
userHasAntigravityIdentity = true
|
||||||
}
|
}
|
||||||
|
// 过滤 OpenCode 默认提示词
|
||||||
|
filtered := filterOpenCodePrompt(sysStr)
|
||||||
|
if filtered != "" {
|
||||||
|
userSystemParts = append(userSystemParts, GeminiPart{Text: filtered})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 尝试解析为数组
|
// 尝试解析为数组
|
||||||
@@ -178,10 +214,14 @@ func buildSystemInstruction(system json.RawMessage, modelName string, opts Trans
|
|||||||
if err := json.Unmarshal(system, &sysBlocks); err == nil {
|
if err := json.Unmarshal(system, &sysBlocks); err == nil {
|
||||||
for _, block := range sysBlocks {
|
for _, block := range sysBlocks {
|
||||||
if block.Type == "text" && strings.TrimSpace(block.Text) != "" {
|
if block.Type == "text" && strings.TrimSpace(block.Text) != "" {
|
||||||
userSystemParts = append(userSystemParts, GeminiPart{Text: block.Text})
|
|
||||||
if strings.Contains(block.Text, "You are Antigravity") {
|
if strings.Contains(block.Text, "You are Antigravity") {
|
||||||
userHasAntigravityIdentity = true
|
userHasAntigravityIdentity = true
|
||||||
}
|
}
|
||||||
|
// 过滤 OpenCode 默认提示词
|
||||||
|
filtered := filterOpenCodePrompt(block.Text)
|
||||||
|
if filtered != "" {
|
||||||
|
userSystemParts = append(userSystemParts, GeminiPart{Text: filtered})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -200,6 +240,16 @@ func buildSystemInstruction(system json.RawMessage, modelName string, opts Trans
|
|||||||
// 添加用户的 system prompt
|
// 添加用户的 system prompt
|
||||||
parts = append(parts, userSystemParts...)
|
parts = append(parts, userSystemParts...)
|
||||||
|
|
||||||
|
// 检测是否有 MCP 工具,如有则注入 XML 调用协议
|
||||||
|
if hasMCPTools(tools) {
|
||||||
|
parts = append(parts, GeminiPart{Text: mcpXMLProtocol})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果用户没有提供 Antigravity 身份,添加结束标记
|
||||||
|
if !userHasAntigravityIdentity {
|
||||||
|
parts = append(parts, GeminiPart{Text: "\n--- [SYSTEM_PROMPT_END] ---"})
|
||||||
|
}
|
||||||
|
|
||||||
if len(parts) == 0 {
|
if len(parts) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user