返回提交历史
Modified
g4f/Provider/needs_auth/Antigravity.py
+13
-7
Modified
g4f/Provider/needs_auth/GeminiCLI.py
+24
-14
Modified
g4f/client/stubs.py
+1
-0
Modified
g4f/config.py
+3
-3
Modified
g4f/cookies.py
+1
-1
Modified
g4f/providers/base_provider.py
+2
-2
Modified
scripts/setup-openclaw.sh
+1
-1
XFEstudio/gpt4free
fix: Update thought signature handling and improve tool call processing in Antigravity and GeminiCLI providers
0217ca24
代码差异
7 个文件
+45
-28
@@ -992,9 +992,10 @@ class AntigravityProvider:
992
992
"name": tool_call["function"]["name"],
993
993
"args": json.loads(tool_call["function"]["arguments"]),
994
994
}
995
# Restore thought_signature required by Gemini thinking models
996
if "thought_signature" in tool_call:
997
func_call["thoughtSignature"] = tool_call["thought_signature"]
995
# Restore thoughtSignature for Gemini thinking models when available
996
thought_sig = tool_call.get("extra_content", {}).get("google", {}).get("thought_signature")
997
if thought_sig:
998
func_call["thoughtSignature"] = thought_sig
998
999
parts.append({"functionCall": func_call})
999
1000
1000
1001
# Handle string content
@@ -1256,7 +1257,7 @@ class AntigravityProvider:
1256
1257
1257
1258
# Function calls from Gemini
1258
1259
elif "functionCall" in part:
1259
tool_calls.append(part["functionCall"])
1260
tool_calls.append(part)
1260
1261
1261
1262
# Text content
1262
1263
elif "text" in part:
@@ -1275,7 +1276,8 @@ class AntigravityProvider:
1275
1276
if tool_calls:
1276
1277
# Convert Gemini tool calls to OpenAI format
1277
1278
openai_tool_calls = []
1278
for i, tc in enumerate(tool_calls):
1279
for i, part in enumerate(tool_calls):
1280
tc = part["functionCall"]
1279
1281
tool_call_obj = {
1280
1282
"id": f"call_{i}_{tc.get('name', 'unknown')}",
1281
1283
"type": "function",
@@ -1285,8 +1287,12 @@ class AntigravityProvider:
1285
1287
}
1286
1288
}
1287
1289
# Preserve thought_signature for thinking models (Gemini 2.5+)
1288
if "thoughtSignature" in tc:
1289
tool_call_obj["thought_signature"] = tc["thoughtSignature"]
1290
if "thoughtSignature" in part:
1291
tool_call_obj["extra_content"] = {
1292
"google": {
1293
"thought_signature": tc["thought_signature"]
1294
}
1295
}
1290
1296
openai_tool_calls.append(tool_call_obj)
1291
1297
yield ToolCalls(openai_tool_calls)
1292
1298
@@ -572,36 +572,40 @@ class GeminiCLIProvider():
572
572
573
573
# Handle tool role (OpenAI style)
574
574
if msg["role"] == "tool":
575
tool_result = msg.get("content", "")
575
576
parts = [
576
577
{
577
578
"functionResponse": {
578
579
"name": msg.get("tool_call_id", "unknown_function"),
579
580
"response": {
580
581
"result": (
581
msg["content"]
582
if isinstance(msg["content"], str)
583
else json.dumps(msg["content"])
582
tool_result
583
if isinstance(tool_result, str)
584
else json.dumps(tool_result)
584
585
)
585
586
},
586
587
}
587
588
}
588
],
589
]
589
590
590
591
# Handle assistant messages with tool calls
591
592
elif msg["role"] == "assistant" and msg.get("tool_calls"):
592
593
parts = []
593
if isinstance(msg["content"], str) and msg["content"].strip():
594
parts.append({"text": msg["content"]})
595
for tool_call in msg["tool_calls"]:
594
content = msg.get("content")
595
if isinstance(content, str) and content.strip():
596
parts.append({"text": content})
597
for idx, tool_call in enumerate(msg["tool_calls"]):
596
598
if tool_call.get("type") == "function":
597
599
func_call = {
598
600
"name": tool_call["function"]["name"],
599
601
"args": json.loads(tool_call["function"]["arguments"]),
600
602
}
601
603
# Restore thought_signature required by Gemini thinking models
602
if "thought_signature" in tool_call:
603
func_call["thoughtSignature"] = tool_call["thought_signature"]
604
parts.append({"functionCall": func_call})
604
thought_sig = tool_call.get("extra_content", {}).get("google", {}).get("thought_signature", "skip_thought_signature_validator")
605
if idx == 0: # Only add skip_thought_signature_validator for the first tool call if no signature is present
606
parts.append({"functionCall": func_call, "thoughtSignature": thought_sig})
607
else:
608
parts.append({"functionCall": func_call})
605
609
606
610
# Handle string content
607
611
elif isinstance(msg["content"], str):
@@ -609,6 +613,7 @@ class GeminiCLIProvider():
609
613
610
614
# Handle array content (possibly multimodal)
611
615
elif isinstance(msg["content"], list):
616
parts = []
612
617
for content in msg["content"]:
613
618
ctype = content.get("type")
614
619
if ctype == "text":
@@ -822,7 +827,7 @@ class GeminiCLIProvider():
822
827
823
828
# Function calls from Gemini
824
829
elif "functionCall" in part:
825
tool_calls.append(part["functionCall"])
830
tool_calls.append(part)
826
831
827
832
# Text content
828
833
elif "text" in part:
@@ -843,7 +848,8 @@ class GeminiCLIProvider():
843
848
if tool_calls:
844
849
# Convert Gemini tool calls to OpenAI format
845
850
openai_tool_calls = []
846
for i, tc in enumerate(tool_calls):
851
for i, part in enumerate(tool_calls):
852
tc = part["functionCall"]
847
853
tool_call_obj = {
848
854
"id": f"call_{i}_{tc.get('name', 'unknown')}",
849
855
"type": "function",
@@ -853,8 +859,12 @@ class GeminiCLIProvider():
853
859
}
854
860
}
855
861
# Preserve thought_signature for thinking models (Gemini 2.5+)
856
if "thoughtSignature" in tc:
857
tool_call_obj["thought_signature"] = tc["thoughtSignature"]
862
if "thoughtSignature" in part:
863
tool_call_obj["extra_content"] = {
864
"google": {
865
"thought_signature": part["thoughtSignature"]
866
}
867
}
858
868
openai_tool_calls.append(tool_call_obj)
859
869
yield ToolCalls(openai_tool_calls)
860
870
if usage_metadata:
@@ -70,6 +70,7 @@ class ToolCallModel(BaseModel):
70
70
id: str
71
71
type: str
72
72
function: ToolFunctionModel
73
extra_content: Optional[dict] = None
73
74
74
75
@classmethod
75
76
def model_construct(cls, function=None, index=0, **kwargs):
@@ -15,12 +15,12 @@ def get_config_dir() -> Path:
15
15
elif sys.platform == "darwin":
16
16
return Path.home() / "Library" / "Application Support"
17
17
return Path.home() / ".config"
18
config_dir = Path.home() / ".config"
18
config_dir = Path.home() / ".g4f"
19
19
if not config_dir.exists():
20
20
config_dir = get_fallback_config_dir()
21
21
if not config_dir.exists():
22
22
config_dir = Path.home() / ".g4f"
23
config_dir.mkdir(parents=True, exist_ok=True)
23
config_dir = config_dir / "g4f"
24
24
return config_dir
25
25
26
26
DEFAULT_PORT = 1337
@@ -28,7 +28,7 @@ DEFAULT_TIMEOUT = 600
28
28
DEFAULT_STREAM_TIMEOUT = 30
29
29
30
30
PACKAGE_NAME = "g4f"
31
CONFIG_DIR = get_config_dir() / PACKAGE_NAME
31
CONFIG_DIR = get_config_dir()
32
32
COOKIES_DIR = CONFIG_DIR / "cookies"
33
33
CUSTOM_COOKIES_DIR = "./har_and_cookies"
34
34
ORGANIZATION = "gpt4free"
@@ -226,7 +226,7 @@ def read_cookie_files(dir_path: Optional[str] = None, domains_filter: Optional[L
226
226
from dotenv import load_dotenv
227
227
env_path = os.path.join(dir_path, ".env")
228
228
load_dotenv(env_path, override=True)
229
debug.log(f"Read cookies: Loaded env vars from {env_path}")
229
debug.log(f"Loaded env vars from {env_path}: {os.path.exists(env_path)}")
230
230
except ImportError:
231
231
debug.error("Warning: 'python-dotenv' is not installed. Env vars not loaded.")
232
232
@@ -296,11 +296,11 @@ class AsyncGeneratorProvider(AbstractProvider):
296
296
"""Get the quota information for the API key."""
297
297
if cls.quota_url is None:
298
298
raise NotImplementedError(f"{cls.__name__} does not implement get_quota method")
299
if not api_key:
299
if not api_key and cls.needs_auth:
300
300
raise MissingAuthError("API key is required.")
301
301
headers = {
302
302
"authorization": f"Bearer {api_key}"
303
}
303
} if api_key else {}
304
304
async with ClientSession() as session:
305
305
async with session.get(cls.quota_url, headers=headers) as response:
306
306
await raise_for_status(response)
@@ -35,7 +35,7 @@ models:
35
35
providers:
36
36
- provider: "GeminiCLI"
37
37
model: "gemini-3-flash-preview"
38
condition: "quota.models.gemini-3-flash-preview.remaining > 0 and error_count < 3"
38
condition: "quota.models.gemini-3-flash-preview.remainingFraction > 0 and error_count < 3"
39
39
- provider: "Antigravity"
40
40
model: "gemini-3-flash"
41
41
condition: "quota.models.gemini-3-flash.quotaInfo.remainingFraction > 0 and error_count < 3"