XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet
公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/gpt4free

fix: enhance retry logic and parameter handling in commit and provider code

- Added `--max-retries` argument to `parse_arguments()` in `commit.py` with default `MAX_RETRIES` - Updated `generate_commit_message()` to accept a `max_retries` parameter and iterate accordingly - Included check to raise immediately if `max_retries` is 1 within `generate_commit_message()` - Passed `args.max_retries` when calling `generate_commit_message()` in `main()` - In `g4f/Provider/har/__init__.py`, imported `ResponseError` and added check for network error to raise `ResponseError` - In `g4f/Provider/hf_space/Qwen_Qwen_3.py`, changed default model string and updated system prompt handling to use `get_system_prompt()` - In `g4f/Provider/needs_auth/LMArenaBeta.py`, modified callback to wait for cookie and turnstile response - In `g4f/Provider/needs_auth/PuterJS.py`, adjusted `get_models()` to filter out certain models - In `g4f/gui/server/api.py`, adjusted `get_model_data()` to handle models starting with "openrouter:" - In `g4f/providers/any_provider.py`, imported and used `ResponseError`; added logic to process `model_aliases` with updated model name resolution - Refined model name cleaning logic to handle additional patterns and replaced multiple regex patterns to better match version strings - Updated list of providers `PROVIERS_LIST_1`, `PROVIERS_LIST_2`, `PROVIERS_LIST_3`, and their usage to include new providers and adjust filtering - In `g4f/version.py`, added `get_git_version()` function, retrieved version with `git describe` command, instead of only relying on `get_github_version()`, increasing robustness

c12227a1
hlohaus <983577+hlohaus@users.noreply.github.com>
提交于

代码差异

11 个文件 +124 -121
Modified etc/tool/commit.py +7 -4
@@ -128,6 +128,8 @@ def parse_arguments():
128 128 help="List available AI models and exit")
129 129 parser.add_argument("--repo", type=str, default=".",
130 130 help="Git repository path (default: current directory)")
131 parser.add_argument("--max-retries", type=int, default=MAX_RETRIES,
132 help="Maximum number of retries for AI generation (default: 3)")
131 133
132 134 return parser.parse_args()
133 135
@@ -288,7 +290,7 @@ def show_spinner(duration: int = None):
288 290 stop_spinner.set()
289 291 raise
290 292
291 def generate_commit_message(diff_text: str, model: str = DEFAULT_MODEL) -> Optional[str]:
293 def generate_commit_message(diff_text: str, model: str = DEFAULT_MODEL, max_retries: int = MAX_RETRIES) -> Optional[str]:
292 294 """Generate a commit message based on the git diff"""
293 295 if not diff_text or diff_text.strip() == "":
294 296 return "No changes staged for commit"
@@ -324,7 +326,7 @@ def generate_commit_message(diff_text: str, model: str = DEFAULT_MODEL) -> Optio
324 326 IMPORTANT: Be 100% factual. Only mention code that was actually changed. Never invent or assume changes not shown in the diff. If unsure about a change's purpose, describe what changed rather than why. Output nothing except for the commit message, and don't surround it in quotes.
325 327 """
326 328
327 for attempt in range(MAX_RETRIES):
329 for attempt in range(max_retries):
328 330 try:
329 331 # Start spinner
330 332 spinner = show_spinner()
@@ -352,7 +354,8 @@ def generate_commit_message(diff_text: str, model: str = DEFAULT_MODEL) -> Optio
352 354 spinner.set()
353 355 sys.stdout.write("\r" + " " * 50 + "\r")
354 356 sys.stdout.flush()
355
357 if max_retries == 1:
358 raise e # If no retries, raise immediately
356 359 print(f"Error generating commit message (attempt {attempt+1}/{MAX_RETRIES}): {e}")
357 360 if attempt < MAX_RETRIES - 1:
358 361 print(f"Retrying in {RETRY_DELAY} seconds...")
@@ -464,7 +467,7 @@ def main():
464 467 sys.exit(0)
465 468
466 469 print(f"Using model: {args.model}")
467 commit_message = generate_commit_message(diff, args.model)
470 commit_message = generate_commit_message(diff, args.model, args.max_retries)
468 471
469 472 if not commit_message:
470 473 print("Failed to generate commit message after multiple attempts.")
Modified g4f/Provider/har/__init__.py +3 -0
@@ -10,6 +10,7 @@ from ...requests import DEFAULT_HEADERS, StreamSession, StreamResponse, FormData
10 10 from ...providers.response import JsonConversation
11 11 from ...tools.media import merge_media
12 12 from ...image import to_bytes, is_accepted_format
13 from ...errors import ResponseError
13 14 from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
14 15 from ..helper import get_last_user_message
15 16 from ..openai.har_file import get_headers
@@ -139,6 +140,8 @@ class HarProvider(AsyncGeneratorProvider, ProviderModelMixin):
139 140 if not line.startswith(b"data: "):
140 141 continue
141 142 for content in find_str(json.loads(line[6:]), 3):
143 if "**NETWORK ERROR DUE TO HIGH TRAFFIC." in content:
144 raise ResponseError(content)
142 145 if content == '<span class="cursor"></span> ' or content == 'update':
143 146 continue
144 147 if content.endswith("▌"):
Modified g4f/Provider/hf_space/Qwen_Qwen_3.py +13 -13
@@ -9,7 +9,7 @@ from ...providers.response import Reasoning, JsonConversation
9 9 from ...requests.raise_for_status import raise_for_status
10 10 from ...errors import ModelNotFoundError
11 11 from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
12 from ..helper import get_last_user_message
12 from ..helper import get_last_user_message, get_system_prompt
13 13 from ... import debug
14 14
15 15
@@ -22,19 +22,19 @@ class Qwen_Qwen_3(AsyncGeneratorProvider, ProviderModelMixin):
22 22 supports_stream = True
23 23 supports_system_message = True
24 24
25 default_model = "qwen3-235b-a22b"
25 default_model = "qwen-3-235b"
26 26 models = {
27 27 default_model,
28 "qwen3-32b",
29 "qwen3-30b-a3b",
30 "qwen3-14b",
31 "qwen3-8b",
32 "qwen3-4b",
33 "qwen3-1.7b",
34 "qwen3-0.6b",
28 "qwen-3-32b",
29 "qwen-3-30b-a3b",
30 "qwen-3-14b",
31 "qwen-3-8b",
32 "qwen-3-4b",
33 "qwen-3-1.7b",
34 "qwen-3-0.6b",
35 35 }
36 36 model_aliases = {
37 "qwen-3-235b": default_model,
37 "qwen-3-235b": "qwen3-235b-a22b",
38 38 "qwen-3-30b": "qwen3-30b-a3b",
39 39 "qwen-3-32b": "qwen3-32b",
40 40 "qwen-3-14b": "qwen3-14b",
@@ -76,12 +76,12 @@ class Qwen_Qwen_3(AsyncGeneratorProvider, ProviderModelMixin):
76 76 'Cache-Control': 'no-cache',
77 77 }
78 78
79 sys_prompt = "\n".join([message['content'] for message in messages if message['role'] == 'system'])
80 sys_prompt = sys_prompt if sys_prompt else "You are a helpful and harmless assistant."
79 system_prompt = get_system_prompt(messages)
80 system_prompt = system_prompt if system_prompt else "You are a helpful and harmless assistant."
81 81
82 82 payload_join = {"data": [
83 83 get_last_user_message(messages),
84 {"thinking_budget": thinking_budget, "model": cls.get_model(model), "sys_prompt": sys_prompt}, None, None],
84 {"thinking_budget": thinking_budget, "model": cls.get_model(model), "sys_prompt": system_prompt}, None, None],
85 85 "event_data": None, "fn_index": 13, "trigger_id": 31, "session_hash": conversation.session_hash
86 86 }
87 87
Modified g4f/Provider/needs_auth/LMArenaBeta.py +3 -2
@@ -78,7 +78,7 @@ class LMArenaBeta(AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin):
78 78 label = "LMArena Beta"
79 79 url = "https://beta.lmarena.ai"
80 80 api_endpoint = "https://beta.lmarena.ai/api/stream/create-evaluation"
81 working = True
81 working = has_nodriver
82 82
83 83 default_model = list(text_models.keys())[0]
84 84 models = list(text_models) + list(image_models)
@@ -102,7 +102,8 @@ class LMArenaBeta(AsyncGeneratorProvider, ProviderModelMixin, AuthFileMixin):
102 102 async def callback(page):
103 103 while not await page.evaluate('document.cookie.indexOf("arena-auth-prod-v1") >= 0'):
104 104 await asyncio.sleep(1)
105 await asyncio.sleep(5)
105 while await page.evaluate('document.querySelector(\'[name="cf-turnstile-response"]\').length > 0') :
106 await asyncio.sleep(1)
106 107 args = await get_args_from_nodriver(cls.url, proxy=proxy, callback=callback)
107 108 except (RuntimeError, FileNotFoundError) as e:
108 109 debug.log(f"Nodriver is not available: {type(e).__name__}: {e}")
Modified g4f/Provider/needs_auth/PuterJS.py +3 -2
@@ -257,14 +257,15 @@ class PuterJS(AsyncGeneratorProvider, ProviderModelMixin):
257 257 "lfm-7b": "openrouter:liquid/lfm-7b",
258 258 "lfm-3b": "openrouter:liquid/lfm-3b",
259 259 "lfm-40b": "openrouter:liquid/lfm-40b",
260 }
260 261
261 }
262 262 @classmethod
263 def get_models(cls) -> list[str]:
263 def get_models(cls, api_key: str = None) -> list[str]:
264 264 if not cls.models:
265 265 try:
266 266 url = "https://api.puter.com/puterai/chat/models/"
267 267 cls.models = requests.get(url).json().get("models", [])
268 cls.models = [model for model in cls.models if model not in ["abuse", "costly", "fake"]]
268 269 except Exception as e:
269 270 debug.log(f"PuterJS: Failed to fetch models from API: {e}")
270 271 cls.models = list(cls.model_aliases.keys())
Modified g4f/Provider/needs_auth/hf/HuggingChat.py +1 -8
@@ -51,14 +51,7 @@ class HuggingChat(AsyncAuthedProvider, ProviderModelMixin):
51 51 def get_models(cls):
52 52 if not cls.models:
53 53 try:
54 text = requests.get(cls.url).text
55 text = re.search(r'models:(\[.+?\]),oldModels:', text).group(1)
56 text = re.sub(r',parameters:{[^}]+?}', '', text)
57 text = text.replace('void 0', 'null')
58 def add_quotation_mark(match):
59 return f'{match.group(1)}"{match.group(2)}":'
60 text = re.sub(r'([{,])([A-Za-z0-9_]+?):', add_quotation_mark, text)
61 models = json.loads(text)
54 models = requests.get(f"{cls.url}/api/v2/models").json().get("json")
62 55 cls.text_models = [model["id"] for model in models]
63 56 cls.models = cls.text_models + cls.image_models
64 57 cls.vision_models = [model["id"] for model in models if model["multimodal"]]
Modified g4f/gui/server/api.py +1 -1
@@ -43,7 +43,7 @@ class Api:
43 43 def get_model_data(provider: ProviderModelMixin, model: str):
44 44 return {
45 45 "model": model,
46 "label": model.split(":")[-1] if provider.__name__ == "AnyProvider" else model,
46 "label": model.split(":")[-1] if provider.__name__ == "AnyProvider" and not model.startswith("openrouter:") else model,
47 47 "default": model == provider.default_model,
48 48 "vision": model in provider.vision_models,
49 49 "audio": False if provider.audio_models is None else model in provider.audio_models,
Modified g4f/providers/any_provider.py +77 -75
@@ -10,22 +10,29 @@ from ..Provider.needs_auth import OpenaiChat, CopilotAccount
10 10 from ..Provider.hf_space import HuggingSpace
11 11 from ..Provider import __map__
12 12 from ..Provider import Cloudflare, Gemini, Grok, DeepSeekAPI, PerplexityLabs, LambdaChat, PollinationsAI, PuterJS
13 from ..Provider import Microsoft_Phi_4_Multimodal, DeepInfraChat, Blackbox, OIVSCodeSer2, OIVSCodeSer0501, TeachAnything, Together, WeWordle, Yqcloud, Chatai, Free2GPT, ARTA, ImageLabs, LegacyLMArena
13 from ..Provider import Microsoft_Phi_4_Multimodal, DeepInfraChat, Blackbox, OIVSCodeSer2, OIVSCodeSer0501, TeachAnything
14 from ..Provider import Together, WeWordle, Yqcloud, Chatai, Free2GPT, ARTA, ImageLabs, LegacyLMArena, LMArenaBeta
14 15 from ..Provider import EdgeTTS, gTTS, MarkItDown, OpenAIFM
15 16 from ..Provider import HarProvider, HuggingFace, HuggingFaceMedia
16 17 from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
17 18 from .. import Provider
18 19 from .. import models
19 20
20 MAIN_PROVIERS = [
21 OpenaiChat, Cloudflare, HarProvider, PerplexityLabs, Gemini, Grok, DeepSeekAPI, Blackbox,
22 OIVSCodeSer2, OIVSCodeSer0501, TeachAnything, Together, WeWordle, Yqcloud, Chatai, Free2GPT, ARTA, ImageLabs, LegacyLMArena,
23 HuggingSpace, LambdaChat, CopilotAccount, PollinationsAI, DeepInfraChat, HuggingFace, HuggingFaceMedia
21 PROVIERS_LIST_1 = [
22 OpenaiChat, PollinationsAI, Cloudflare, PerplexityLabs, Gemini, Grok, DeepSeekAPI, Blackbox, OpenAIFM,
23 OIVSCodeSer2, OIVSCodeSer0501, TeachAnything, Together, WeWordle, Yqcloud, Chatai, Free2GPT, ARTA, ImageLabs,
24 HarProvider, LegacyLMArena, LMArenaBeta, LambdaChat, CopilotAccount, DeepInfraChat,
25 HuggingSpace, HuggingFace, HuggingFaceMedia, PuterJS, Together
24 26 ]
25 27
26 SPECIAL_PROVIDERS = [OpenaiChat, CopilotAccount, PollinationsAI, HuggingSpace, Cloudflare, PerplexityLabs, Gemini, Grok, LegacyLMArena, ARTA]
28 PROVIERS_LIST_2 = [
29 OpenaiChat, CopilotAccount, PollinationsAI, PerplexityLabs, Gemini, Grok, ARTA
30 ]
27 31
28 SPECIAL_PROVIDERS2 = [HarProvider, LambdaChat, DeepInfraChat, HuggingFace, HuggingFaceMedia, PuterJS]
32 PROVIERS_LIST_3 = [
33 HarProvider, LambdaChat, DeepInfraChat, HuggingFace, HuggingFaceMedia, LegacyLMArena, LMArenaBeta,
34 PuterJS, Together, Cloudflare, HuggingSpace
35 ]
29 36
30 37 LABELS = {
31 38 "default": "Default",
@@ -33,15 +40,21 @@ LABELS = {
33 40 "llama": "Meta: LLaMA",
34 41 "deepseek": "DeepSeek",
35 42 "qwen": "Alibaba: Qwen",
36 "google": "Google: Gemini / Gemma / Bard",
43 "google": "Google: Gemini / Gemma",
37 44 "grok": "xAI: Grok",
38 45 "claude": "Anthropic: Claude",
39 46 "command": "Cohere: Command",
40 47 "phi": "Microsoft: Phi / WizardLM",
41 48 "mistral": "Mistral",
42 49 "PollinationsAI": "Pollinations AI",
50 "ARTA": "ARTA",
51 "voices": "Voices",
43 52 "perplexity": "Perplexity Labs",
44 53 "openrouter": "OpenRouter",
54 "glm": "GLM",
55 "tulu": "Tulu",
56 "reka": "Reka",
57 "hermes": "Hermes",
45 58 "video": "Video Generation",
46 59 "image": "Image Generation",
47 60 "other": "Other Models",
@@ -65,31 +78,35 @@ class AnyProvider(AsyncGeneratorProvider, ProviderModelMixin):
65 78 continue # Already added
66 79
67 80 added = False
68
69 # Check for PollinationsAI models (with prefix)
70 if model.startswith("PollinationsAI:"):
71 groups["PollinationsAI"].append(model)
81 # Check for models with prefix
82 start = model.split(":")[0]
83 if start in ("PollinationsAI", "ARTA", "openrouter"):
84 submodel = model.split(":", maxsplit=1)[1]
85 if submodel in OpenAIFM.voices or submodel in PollinationsAI.audio_models[PollinationsAI.default_audio_model]:
86 groups["voices"].append(submodel)
87 else:
88 groups[start].append(model)
72 89 added = True
73 90 # Check for Mistral company models specifically
74 91 elif model.startswith("mistral") and not any(x in model for x in ["dolphin", "nous", "openhermes"]):
75 92 groups["mistral"].append(model)
76 93 added = True
77 elif model.startswith(("mistralai/", "mixtral-", "pixtral-", "ministral-", "codestral-")):
94 elif model.startswith(("pixtral-", "ministral-", "codestral")) or "mistral" in model or "mixtral" in model:
78 95 groups["mistral"].append(model)
79 96 added = True
80 97 # Check for Qwen models
81 elif model.startswith(("qwen", "Qwen/", "qwq", "qvq")):
98 elif model.startswith(("qwen", "Qwen", "qwq", "qvq")):
82 99 groups["qwen"].append(model)
83 100 added = True
84 101 # Check for Microsoft Phi models
85 elif model.startswith(("phi-", "microsoft/")):
102 elif model.startswith(("phi-", "microsoft/")) or "wizardlm" in model.lower():
86 103 groups["phi"].append(model)
87 104 added = True
88 105 # Check for Meta LLaMA models
89 106 elif model.startswith(("llama-", "meta-llama/", "llama2-", "llama3")):
90 107 groups["llama"].append(model)
91 108 added = True
92 elif model == "meta-ai":
109 elif model == "meta-ai" or model.startswith("codellama-"):
93 110 groups["llama"].append(model)
94 111 added = True
95 112 # Check for Google models
@@ -100,14 +117,6 @@ class AnyProvider(AsyncGeneratorProvider, ProviderModelMixin):
100 117 elif model.startswith(("command-", "CohereForAI/", "c4ai-command")):
101 118 groups["command"].append(model)
102 119 added = True
103 # Check for Claude models
104 elif model.startswith("claude-"):
105 groups["claude"].append(model)
106 added = True
107 # Check for Grok models
108 elif model.startswith("grok-"):
109 groups["grok"].append(model)
110 added = True
111 120 # Check for DeepSeek models
112 121 elif model.startswith(("deepseek-", "janus-")):
113 122 groups["deepseek"].append(model)
@@ -116,34 +125,27 @@ class AnyProvider(AsyncGeneratorProvider, ProviderModelMixin):
116 125 elif model.startswith(("sonar", "sonar-", "pplx-")) or model == "r1-1776":
117 126 groups["perplexity"].append(model)
118 127 added = True
128 # Check for image models - UPDATED to include flux check
129 elif model in cls.image_models:
130 groups["image"].append(model)
131 added = True
119 132 # Check for OpenAI models
120 elif model.startswith(("gpt-", "chatgpt-", "o1", "o1-", "o3-", "o4-")) or model in ("auto", "dall-e-3", "searchgpt"):
133 elif model.startswith(("gpt-", "chatgpt-", "o1", "o1-", "o3-", "o4-")) or model in ("auto", "searchgpt"):
121 134 groups["openai"].append(model)
122 135 added = True
123 # Check for openrouter models
124 elif model.startswith(("openrouter:")):
125 groups["openrouter"].append(model)
126 added = True
127 136 # Check for video models
128 137 elif model in cls.video_models:
129 138 groups["video"].append(model)
130 139 added = True
131 # Check for image models - UPDATED to include flux check
132 elif model in cls.image_models or "flux" in model.lower() or "stable-diffusion" in model.lower() or "sdxl" in model.lower() or "gpt-image" in model.lower():
133 groups["image"].append(model)
134 added = True
135
140 if not added:
141 for group in LABELS.keys():
142 if model == group or group in model:
143 groups[group].append(model)
144 added = True
145 break
136 146 # If not categorized, check for special cases then put in other
137 147 if not added:
138 # CodeLlama is Meta's model
139 if model.startswith("codellama-"):
140 groups["llama"].append(model)
141 # WizardLM is Microsoft's
142 elif "wizardlm" in model.lower():
143 groups["phi"].append(model)
144 else:
145 groups["other"].append(model)
146
148 groups["other"].append(model)
147 149 return [
148 150 {"group": LABELS[group], "models": names} for group, names in groups.items()
149 151 ]
@@ -174,31 +176,19 @@ class AnyProvider(AsyncGeneratorProvider, ProviderModelMixin):
174 176 all_models = [cls.default_model] + list(model_with_providers.keys())
175 177
176 178 # Process special providers
177 for provider in SPECIAL_PROVIDERS:
179 for provider in PROVIERS_LIST_2:
178 180 provider: ProviderType = provider
179 181 if not provider.working or provider.get_parent() in ignored:
180 182 continue
181 183 if provider == CopilotAccount:
182 184 all_models.extend(list(provider.model_aliases.keys()))
183 elif provider == PollinationsAI:
185 elif provider in [PollinationsAI, ARTA]:
184 186 all_models.extend([f"{provider.__name__}:{model}" for model in provider.get_models() if model not in all_models])
185 187 cls.audio_models.update({f"{provider.__name__}:{model}": [] for model in provider.get_models() if model in provider.audio_models})
186 188 cls.image_models.extend([f"{provider.__name__}:{model}" for model in provider.get_models() if model in provider.image_models])
187 189 cls.vision_models.extend([f"{provider.__name__}:{model}" for model in provider.get_models() if model in provider.vision_models])
188 all_models.extend(list(provider.model_aliases.keys()))
189 elif provider == LegacyLMArena:
190 # Add models from LegacyLMArena
191 provider_models = provider.get_models()
192 all_models.extend(provider_models)
193 # Also add model aliases
194 all_models.extend(list(provider.model_aliases.keys()))
195 # Add vision models
196 cls.vision_models.extend(provider.vision_models)
197 elif provider == ARTA:
198 # Add all ARTA models as image models
199 arta_models = provider.get_models()
200 all_models.extend(arta_models)
201 cls.image_models.extend(arta_models)
190 if provider == PollinationsAI:
191 all_models.extend(list(provider.model_aliases.keys()))
202 192 else:
203 193 all_models.extend(provider.get_models())
204 194
@@ -215,9 +205,9 @@ class AnyProvider(AsyncGeneratorProvider, ProviderModelMixin):
215 205 name = name.split("/")[-1].split(":")[0].lower()
216 206 # Date patterns
217 207 name = re.sub(r'-\d{4}-\d{2}-\d{2}', '', name)
218 name = re.sub(r'-\d{8}', '', name)
219 name = re.sub(r'-\d{4}', '', name)
208 name = re.sub(r'-\d{3,8}', '', name)
220 209 name = re.sub(r'-\d{2}-\d{2}', '', name)
210 name = re.sub(r'-[0-9a-f]{8}$', '', name)
221 211 # Version patterns
222 212 name = re.sub(r'-(instruct|chat|preview|experimental|v\d+|fp8|bf16|hf)$', '', name)
223 213 # Other replacements
@@ -226,23 +216,28 @@ class AnyProvider(AsyncGeneratorProvider, ProviderModelMixin):
226 216 name = name.replace("meta-llama-", "llama-")
227 217 name = name.replace("llama3", "llama-3")
228 218 name = name.replace("flux.1-", "flux-")
219 name = name.replace("-free", "")
220 name = name.replace("qwen1-", "qwen-1")
221 name = name.replace("qwen2-", "qwen-2")
222 name = name.replace("qwen3-", "qwen-3")
223 name = name.replace("stable-diffusion-3.5-large", "sd-3.5-large")
229 224 return name
230 225
231 226 # Process HAR providers
232 for provider in SPECIAL_PROVIDERS2:
227 for provider in PROVIERS_LIST_3:
233 228 if not provider.working or provider.get_parent() in ignored:
234 229 continue
235 230 new_models = provider.get_models()
236 231 if provider == HuggingFaceMedia:
237 232 new_models = provider.video_models
238
239 # Add original models too, not just cleaned names
240 all_models.extend(new_models)
241
242 model_map = {model if model.startswith("openrouter:") else clean_name(model): model for model in new_models}
243 if not provider.model_aliases:
244 provider.model_aliases = {}
245 provider.model_aliases.update(model_map)
233 model_map = {}
234 for model in new_models:
235 clean_value = model if model.startswith("openrouter:") else clean_name(model)
236 if clean_value not in model_map:
237 model_map[clean_value] = model
238 if provider.model_aliases:
239 model_map.update(provider.model_aliases)
240 provider.model_aliases = model_map
246 241 all_models.extend(list(model_map.keys()))
247 242
248 243 # Update special model lists with both original and cleaned names
@@ -262,7 +257,10 @@ class AnyProvider(AsyncGeneratorProvider, ProviderModelMixin):
262 257 cls.audio_models.update(provider.audio_models)
263 258
264 259 # Update model counts
265 cls.models_count.update({model: all_models.count(model) for model in all_models if all_models.count(model) > cls.models_count.get(model, 0)})
260 for model in all_models:
261 count = all_models.count(model)
262 if count > cls.models_count.get(model, 0):
263 cls.models_count.update({model: count})
266 264
267 265 # Deduplicate and store
268 266 cls.models_storage[ignored_key] = list(dict.fromkeys([model if model else cls.default_model for model in all_models]))
@@ -320,11 +318,15 @@ class AnyProvider(AsyncGeneratorProvider, ProviderModelMixin):
320 318 if isinstance(api_key, dict):
321 319 for provider in api_key:
322 320 if api_key.get(provider):
323 if provider in __map__ and __map__[provider] not in MAIN_PROVIERS:
321 if provider in __map__ and __map__[provider] not in PROVIERS_LIST_1:
324 322 extra_providers.append(__map__[provider])
325 for provider in MAIN_PROVIERS + extra_providers:
323 for provider in PROVIERS_LIST_1 + extra_providers:
326 324 if provider.working:
327 if not model or model in provider.get_models() or model in provider.model_aliases:
325 provider_api_key = api_key
326 if isinstance(api_key, dict):
327 provider_api_key = api_key.get(provider.get_parent())
328 provider_models = provider.get_models(api_key=provider_api_key) if provider_api_key else provider.get_models()
329 if not model or model in provider_models or provider.model_aliases and model in provider.model_aliases:
328 330 providers.append(provider)
329 331 if model in models.__models__:
330 332 for provider in models.__models__[model][1]:
@@ -334,7 +336,7 @@ class AnyProvider(AsyncGeneratorProvider, ProviderModelMixin):
334 336
335 337 if len(providers) == 0:
336 338 raise ModelNotFoundError(f"AnyProvider: Model {model} not found in any provider.")
337
339
338 340 async for chunk in IterListProvider(providers).create_async_generator(
339 341 model,
340 342 messages,
Modified g4f/providers/base_provider.py +3 -4
@@ -367,20 +367,19 @@ class ProviderModelMixin:
367 367 @classmethod
368 368 def get_models(cls, **kwargs) -> list[str]:
369 369 if not cls.models and cls.default_model is not None:
370 return [cls.default_model]
370 cls.models = [cls.default_model]
371 371 return cls.models
372 372
373 373 @classmethod
374 374 def get_model(cls, model: str, **kwargs) -> str:
375 375 if not model and cls.default_model is not None:
376 376 model = cls.default_model
377 elif model in cls.model_aliases:
377 if model in cls.model_aliases:
378 378 model = cls.model_aliases[model]
379 379 else:
380 380 if model not in cls.get_models(**kwargs) and cls.models:
381 raise ModelNotFoundError(f"Model is not supported: {model} in: {cls.__name__} Valid models: {cls.models}")
381 raise ModelNotFoundError(f"Model not found: {model} in: {cls.__name__} Valid models: {cls.models}")
382 382 cls.last_model = model
383 debug.last_model = model
384 383 return model
385 384
386 385 class RaiseErrorMixin():
Modified g4f/providers/retry_provider.py +4 -4
@@ -55,16 +55,16 @@ class IterListProvider(BaseRetryProvider):
55 55 self.last_provider = provider
56 56 if not model:
57 57 model = getattr(provider, "default_model", None)
58 model = provider.model_aliases.get(model, model) if hasattr(provider, "model_aliases") else model
59 debug.log(f"Using {provider.__name__} provider with model {model}")
60 yield ProviderInfo(**provider.get_dict(), model=model)
58 alias = provider.model_aliases.get(model, model) if hasattr(provider, "model_aliases") else model
59 debug.log(f"Using {provider.__name__} provider with model {alias}")
60 yield ProviderInfo(**provider.get_dict(), model=alias)
61 61 extra_body = kwargs.copy()
62 62 if isinstance(api_key, dict):
63 63 api_key = api_key.get(provider.get_parent())
64 64 if api_key:
65 65 extra_body["api_key"] = api_key
66 66 try:
67 response = provider.create_function(model, messages, stream=stream, **extra_body)
67 response = provider.create_function(alias, messages, stream=stream, **extra_body)
68 68 for chunk in response:
69 69 if chunk:
70 70 yield chunk
Modified g4f/version.py +9 -8
@@ -48,6 +48,14 @@ def get_github_version(repo: str) -> str:
48 48 except requests.RequestException as e:
49 49 raise VersionNotFoundError(f"Failed to get GitHub release version: {e}")
50 50
51 def get_git_version() -> str:
52 # Read from git repository
53 try:
54 command = ["git", "describe", "--tags", "--abbrev=0"]
55 return check_output(command, text=True, stderr=PIPE).strip()
56 except CalledProcessError:
57 return None
58
51 59 class VersionUtils:
52 60 """
53 61 Utility class for managing and comparing package versions of 'g4f'.
@@ -78,14 +86,7 @@ class VersionUtils:
78 86 if version:
79 87 return version
80 88
81 # Read from git repository
82 try:
83 command = ["git", "describe", "--tags", "--abbrev=0"]
84 return check_output(command, text=True, stderr=PIPE).strip()
85 except CalledProcessError:
86 pass
87
88 return None
89 return get_git_version()
89 90
90 91 @property
91 92 def latest_version(self) -> str: