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

XFEstudio/gpt4free

Refactor PollinationsAI model selection logic; update default system in AIModel; clean up unused provider imports

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

代码差异

4 个文件 +16 -45
Modified g4f/Provider/PollinationsAI.py +1 -1
@@ -206,7 +206,7 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
206 206 if is_data_an_audio(media_data, filename):
207 207 has_audio = True
208 208 break
209 model = cls.default_audio_model if has_audio else model
209 model = cls.default_audio_model if has_audio else cls.default_model
210 210 elif cls._models_loaded or cls.get_models():
211 211 if model in cls.model_aliases:
212 212 model = cls.model_aliases[model]
Modified g4f/integration/pydantic_ai.py +14 -37
@@ -6,8 +6,9 @@ from dataclasses import dataclass, field
6 6
7 7 from pydantic_ai import ModelResponsePart, ThinkingPart, ToolCallPart
8 8 from pydantic_ai.models import Model, ModelResponse, KnownModelName, infer_model
9 from pydantic_ai.models.openai import OpenAIChatModel, UnexpectedModelBehavior
10 from pydantic_ai.models.openai import OpenAISystemPromptRole, _CHAT_FINISH_REASON_MAP, _map_usage, _now_utc, number_to_datetime, split_content_into_text_and_thinking, replace
9 from pydantic_ai.usage import RequestUsage
10 from pydantic_ai.models.openai import OpenAIChatModel
11 from pydantic_ai.models.openai import OpenAISystemPromptRole, _now_utc, split_content_into_text_and_thinking, replace
11 12
12 13 import pydantic_ai.models.openai
13 14 pydantic_ai.models.openai.NOT_GIVEN = None
@@ -31,7 +32,7 @@ class AIModel(OpenAIChatModel):
31 32 provider: str | None = None,
32 33 *,
33 34 system_prompt_role: OpenAISystemPromptRole | None = None,
34 system: str | None = 'openai',
35 system: str | None = 'g4f',
35 36 **kwargs
36 37 ):
37 38 """Initialize an AI model.
@@ -46,7 +47,7 @@ class AIModel(OpenAIChatModel):
46 47 customize the `base_url` and `api_key` to use a different provider.
47 48 """
48 49 self._model_name = model_name
49 self._provider = provider
50 self._provider = getattr(provider, '__name__', provider)
50 51 self.client = AsyncClient(provider=provider, **kwargs)
51 52 self.system_prompt_role = system_prompt_role
52 53 self._system = system
@@ -58,36 +59,12 @@ class AIModel(OpenAIChatModel):
58 59
59 60 def _process_response(self, response: ChatCompletion | str) -> ModelResponse:
60 61 """Process a non-streamed response, and prepare a message to return."""
61 # Although the OpenAI SDK claims to return a Pydantic model (`ChatCompletion`) from the chat completions function:
62 # * it hasn't actually performed validation (presumably they're creating the model with `model_construct` or something?!)
63 # * if the endpoint returns plain text, the return type is a string
64 # Thus we validate it fully here.
65 if not isinstance(response, ChatCompletion):
66 raise UnexpectedModelBehavior('Invalid response from OpenAI chat completions endpoint, expected JSON data')
67
68 if response.created:
69 timestamp = number_to_datetime(response.created)
70 else:
71 timestamp = _now_utc()
72 response.created = int(timestamp.timestamp())
73
74 # Workaround for local Ollama which sometimes returns a `None` finish reason.
75 if response.choices and (choice := response.choices[0]) and choice.finish_reason is None: # pyright: ignore[reportUnnecessaryComparison]
76 choice.finish_reason = 'stop'
77
78 62 choice = response.choices[0]
79 63 items: list[ModelResponsePart] = []
80 64
81 # The `reasoning` field is only present in gpt-oss via Ollama and OpenRouter.
82 # - https://cookbook.openai.com/articles/gpt-oss/handle-raw-cot#chat-completions-api
83 # - https://openrouter.ai/docs/use-cases/reasoning-tokens#basic-usage-with-reasoning-tokens
84 65 if reasoning := getattr(choice.message, 'reasoning', None):
85 66 items.append(ThinkingPart(id='reasoning', content=reasoning, provider_name=self.system))
86 67
87 # NOTE: We don't currently handle OpenRouter `reasoning_details`:
88 # - https://openrouter.ai/docs/use-cases/reasoning-tokens#preserving-reasoning-blocks
89 # If you need this, please file an issue.
90
91 68 if choice.message.content:
92 69 items.extend(
93 70 (replace(part, id='content', provider_name=self.system) if isinstance(part, ThinkingPart) else part)
@@ -95,20 +72,21 @@ class AIModel(OpenAIChatModel):
95 72 )
96 73 if choice.message.tool_calls is not None:
97 74 for c in choice.message.tool_calls:
98 items.append(ToolCallPart(c.get("function").get("name"), c.get("function").get("arguments"), tool_call_id=c.get("id")))
99
100 raw_finish_reason = choice.finish_reason
101 finish_reason = _CHAT_FINISH_REASON_MAP.get(raw_finish_reason)
75 items.append(ToolCallPart(c.function.name, c.function.arguments, tool_call_id=c.id))
76 usage = RequestUsage(
77 input_tokens=response.usage.prompt_tokens,
78 output_tokens=response.usage.completion_tokens,
79 )
102 80
103 81 return ModelResponse(
104 82 parts=items,
105 usage=_map_usage(response, self._provider, "", self._model_name),
83 usage=usage,
106 84 model_name=response.model,
107 timestamp=timestamp,
85 timestamp=_now_utc(),
108 86 provider_details=None,
109 87 provider_response_id=response.id,
110 88 provider_name=self._provider,
111 finish_reason=finish_reason,
89 finish_reason=choice.finish_reason,
112 90 )
113 91
114 92 def new_infer_model(model: Model | KnownModelName, api_key: str = None) -> Model:
@@ -125,5 +103,4 @@ def new_infer_model(model: Model | KnownModelName, api_key: str = None) -> Model
125 103 def patch_infer_model(api_key: str | None = None):
126 104 import pydantic_ai.models
127 105
128 pydantic_ai.models.infer_model = partial(new_infer_model, api_key=api_key)
129 pydantic_ai.models.OpenAIChatModel = AIModel
106 pydantic_ai.models.infer_model = partial(new_infer_model, api_key=api_key)
Modified g4f/models.py +0 -6
@@ -6,7 +6,6 @@ from typing import Dict, List, Optional
6 6 from .Provider import IterListProvider, ProviderType
7 7 from .Provider import (
8 8 ### No Auth Required ###
9 Blackbox,
10 9 Chatai,
11 10 Cloudflare,
12 11 Copilot,
@@ -17,7 +16,6 @@ from .Provider import (
17 16 GLM,
18 17 Kimi,
19 18 LambdaChat,
20 Mintlify,
21 19 OIVSCodeSer2,
22 20 OIVSCodeSer0501,
23 21 OperaAria,
@@ -27,7 +25,6 @@ from .Provider import (
27 25 PollinationsAI,
28 26 PollinationsImage,
29 27 Qwen,
30 StringableInference,
31 28 TeachAnything,
32 29 Together,
33 30 WeWordle,
@@ -155,7 +152,6 @@ default = Model(
155 152 name = "",
156 153 base_provider = "",
157 154 best_provider = IterListProvider([
158 StringableInference,
159 155 OIVSCodeSer0501,
160 156 OIVSCodeSer2,
161 157 Copilot,
@@ -168,7 +164,6 @@ default = Model(
168 164 Together,
169 165 Chatai,
170 166 WeWordle,
171 Mintlify,
172 167 TeachAnything,
173 168 OpenaiChat,
174 169 Cloudflare,
@@ -179,7 +174,6 @@ default_vision = VisionModel(
179 174 name = "",
180 175 base_provider = "",
181 176 best_provider = IterListProvider([
182 StringableInference,
183 177 DeepInfra,
184 178 OIVSCodeSer0501,
185 179 OIVSCodeSer2,
Modified g4f/providers/any_provider.py +1 -1
@@ -348,7 +348,7 @@ class AnyProvider(AsyncGeneratorProvider, AnyModelProviderMixin):
348 348 has_audio = True
349 349 break
350 350 has_image = True
351 if "tools" in kwargs:
351 if kwargs.get("tools", None):
352 352 providers = [PollinationsAI]
353 353 elif "audio" in kwargs or "audio" in kwargs.get("modalities", []):
354 354 if kwargs.get("audio", {}).get("language") is None: