返回提交历史
Modified
g4f/Provider/PollinationsAI.py
+1
-1
Modified
g4f/integration/pydantic_ai.py
+14
-37
Modified
g4f/models.py
+0
-6
Modified
g4f/providers/any_provider.py
+1
-1
XFEstudio/gpt4free
Refactor PollinationsAI model selection logic; update default system in AIModel; clean up unused provider imports
1dac52a1
代码差异
4 个文件
+16
-45
@@ -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]
@@ -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)
@@ -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,
@@ -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: