The test request was using maxOutputTokens: 1, which caused Google API to generate only 1 token. When decoded, this single token produced "It" as the response, making it look like an error. Changed: - Content: "." → "Test connection" (more meaningful prompt) - MaxTokens: 1 → 10 (enough tokens to verify connection is working) This fixes the issue where account test always showed "It" in the response, which was actually just the truncated output from the single-token generation. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
244 lines
5.4 KiB
Markdown
244 lines
5.4 KiB
Markdown
# 🔍 上游 API 返回值诊断指南
|
||
|
||
当你的 Antigravity 账号验证返回 "IT" 错误时,这个错误**来自上游 Google API**的响应。
|
||
|
||
## 📊 错误链追踪
|
||
|
||
```
|
||
你的 curl 请求
|
||
↓
|
||
HTTP Handler (account_handler.go:671)
|
||
↓
|
||
AccountTestService.testAntigravityAccountConnection()
|
||
├─ 调用: AntigravityGatewayService.TestConnection()
|
||
│ ├─ 调用: client.LoadCodeAssist(ctx, accessToken)
|
||
│ │ ↓
|
||
│ │ 🌐 Google API (真实的上游服务器)
|
||
│ │ 返回: ??? (这是问题所在)
|
||
│ │
|
||
│ └─ 错误处理: 什么时候会返回 "IT"?
|
||
│
|
||
└─ sendErrorAndEnd(c, error_message)
|
||
↓
|
||
SSE 响应流
|
||
↓
|
||
你的 curl 看到: "IT"
|
||
```
|
||
|
||
## 🎯 上游可能返回的错误
|
||
|
||
### 场景 1: Access Token 无效 (最可能)
|
||
|
||
**Google API 返回:**
|
||
```json
|
||
HTTP/1.1 401 Unauthorized
|
||
|
||
{
|
||
"error": {
|
||
"code": 401,
|
||
"message": "Invalid authentication credentials",
|
||
"errors": [
|
||
{
|
||
"message": "Invalid authentication credentials",
|
||
"domain": "global",
|
||
"reason": "authenticationRequired"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
**在你的应用中显示为:** `"IT"`(被截断的错误信息)
|
||
|
||
---
|
||
|
||
### 场景 2: 项目配置错误
|
||
|
||
**Google API 返回:**
|
||
```json
|
||
HTTP/1.1 400 Bad Request
|
||
|
||
{
|
||
"error": {
|
||
"code": 400,
|
||
"message": "The project does not have permission to call CloudAI APIs",
|
||
"errors": [...]
|
||
}
|
||
}
|
||
```
|
||
|
||
**在你的应用中显示为:** `"IT"`(也可能是 `"Th"` 或其他前两个字符)
|
||
|
||
---
|
||
|
||
### 场景 3: 模型不可用
|
||
|
||
**Google API 返回:**
|
||
```json
|
||
HTTP/1.1 429 Too Many Requests
|
||
|
||
{
|
||
"error": {
|
||
"code": 429,
|
||
"message": "The resource has been exhausted.",
|
||
"errors": [...]
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 场景 4: 内部服务器错误
|
||
|
||
**Google API 返回:**
|
||
```json
|
||
HTTP/1.1 500 Internal Server Error
|
||
|
||
{
|
||
"error": {
|
||
"code": 500,
|
||
"message": "Internal error occurred.",
|
||
"errors": [...]
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🔧 如何看到真实的上游返回值
|
||
|
||
### 方法 A: 添加诊断日志 (推荐)
|
||
|
||
编辑 `antigravity_gateway_service.go`,在 `TestConnection` 函数中:
|
||
|
||
```go
|
||
func (s *AntigravityGatewayService) TestConnection(ctx context.Context, account *Account, modelID string) (*TestConnectionResult, error) {
|
||
|
||
// ... 现有代码 ...
|
||
|
||
result, err := s.antigravityRetryLoop(p)
|
||
if err != nil {
|
||
// 添加这些行来捕获完整的上游错误信息
|
||
log.Printf("[UPSTREAM_ERROR] Type=%T", err)
|
||
log.Printf("[UPSTREAM_ERROR] Message=%s", err.Error())
|
||
log.Printf("[UPSTREAM_ERROR] FullError=%#v", err)
|
||
|
||
// 如果是 HTTP 错误,打印更详细的信息
|
||
if httpErr, ok := err.(interface{ StatusCode() int }); ok {
|
||
log.Printf("[UPSTREAM_ERROR] StatusCode=%d", httpErr.StatusCode())
|
||
}
|
||
|
||
return nil, err
|
||
}
|
||
|
||
// ... 继续 ...
|
||
}
|
||
```
|
||
|
||
然后查看日志:
|
||
```bash
|
||
# Docker 日志
|
||
docker logs <container-id> | grep "UPSTREAM_ERROR"
|
||
|
||
# 或本地日志
|
||
tail -f /var/log/sub2api/server.log | grep "UPSTREAM_ERROR"
|
||
```
|
||
|
||
---
|
||
|
||
### 方法 B: 使用网络抓包工具
|
||
|
||
启动 Charles/Fiddler,拦截 HTTPS 请求:
|
||
|
||
1. 配置你的应用使用代理
|
||
2. 运行测试请求
|
||
3. 在代理工具中观察:
|
||
- **Request**: 发送给 Google API 的请求
|
||
- **Response**: Google API 返回的完整响应
|
||
|
||
---
|
||
|
||
### 方法 C: 查看应用日志中的错误
|
||
|
||
在 `sendErrorAndEnd` 中添加日志:
|
||
|
||
```go
|
||
func (s *AccountTestService) sendErrorAndEnd(c *gin.Context, msg string) error {
|
||
log.Printf("[SEND_ERROR_START]")
|
||
log.Printf("[SEND_ERROR_MESSAGE_LEN]=%d", len(msg))
|
||
log.Printf("[SEND_ERROR_MESSAGE]=%q", msg) // 用 %q 显示完整的字符串(含转义)
|
||
log.Printf("[SEND_ERROR_BYTES]=%v", []byte(msg))
|
||
log.Printf("[SEND_ERROR_END]")
|
||
|
||
s.sendEvent(c, TestEvent{
|
||
Type: "test_error",
|
||
Error: msg,
|
||
Success: false,
|
||
})
|
||
s.sendEvent(c, TestEvent{Type: "test_complete", Success: false})
|
||
return nil
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 📝 真实的错误示例
|
||
|
||
### 示例 1: Token 过期
|
||
|
||
**完整错误链:**
|
||
```
|
||
Google API 返回 401 + "Invalid authentication credentials"
|
||
↓ (在 Client 中解析)
|
||
Go error: "Invalid authentication credentials"
|
||
↓ (在 TestConnection 中传播)
|
||
sendErrorAndEnd() 接收: "Invalid authentication credentials"
|
||
↓ (截断?编码错误?)
|
||
SSE 事件中显示: "IT" 或 "In" 或 "I"
|
||
```
|
||
|
||
### 示例 2: Project 配置错误
|
||
|
||
**完整错误链:**
|
||
```
|
||
Google API 返回 400 + "The project does not have permission..."
|
||
↓
|
||
sendErrorAndEnd() 接收: "The project does not have permission..."
|
||
↓
|
||
截断为前两个字符: "Th" ← 这与你看到的 "IT" 不符,说明不是这个
|
||
```
|
||
|
||
---
|
||
|
||
## ❓ 为什么会显示 "IT"?
|
||
|
||
最可能的解释:
|
||
|
||
1. **错误被截断** - 原文可能是 `INTERNAL_ERROR` 被截断成 `IT`
|
||
2. **错误代码** - 某些错误被转换成了短代码 `IT`
|
||
3. **部分响应** - 只有响应的一部分被返回
|
||
|
||
---
|
||
|
||
## ✅ 下一步行动
|
||
|
||
1. **立即**: 添加上述诊断日志
|
||
2. **运行**: 执行你的测试 curl 命令
|
||
3. **检查**: 查看应用日志
|
||
4. **记录**: 复制完整的错误信息给我
|
||
|
||
---
|
||
|
||
## 📌 检查清单
|
||
|
||
- [ ] 添加了 TestConnection 的诊断日志
|
||
- [ ] 添加了 sendErrorAndEnd 的诊断日志
|
||
- [ ] 重新编译并部署应用
|
||
- [ ] 执行了测试 curl 命令
|
||
- [ ] 检查了应用日志
|
||
- [ ] 记录了完整的 `[UPSTREAM_ERROR]` 或 `[SEND_ERROR]` 输出
|
||
|
||
---
|
||
|
||
**完成后,请将日志输出分享给我,我们就能找到真实的错误原因!**
|