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

XFEstudio/gpt4free

Update Perplexity provider to improve model selection and response handling

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

代码差异

2 个文件 +145 -127
Modified g4f/Provider/Perplexity.py +144 -126
@@ -1,39 +1,51 @@
1 1 from __future__ import annotations
2 2
3 import json
3 import random
4 4 import uuid
5 5
6 from ..typing import AsyncResult, Messages
7 from ..requests import StreamSession, raise_for_status
8 from ..errors import ResponseError
9 from ..providers.response import FinishReason, Sources
6 from ..typing import AsyncResult, Messages, Cookies
7 from ..requests import StreamSession, raise_for_status, sse_stream
8 from ..cookies import get_cookies
9 from ..providers.response import ProviderInfo
10 10 from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
11 from .. import debug
11 12
12 13 class Perplexity(AsyncGeneratorProvider, ProviderModelMixin):
13 14 label = "Perplexity"
14 15 url = "https://www.perplexity.ai"
16 cookie_domain = ".perplexity.ai"
15 17 working = True
16 supports_stream = True
17 supports_system_message = True
18 supports_message_history = True
18 active_by_default = True
19 19
20 default_model = "turbo"
20 default_model = "auto"
21 21 models = [
22 22 default_model,
23 "sonar",
24 "sonar-pro",
23 "turbo",
24 "pplx_pro",
25 "gpt-5",
25 26 ]
27 model_aliases = {
28 "gpt-5": "gpt5",
29 }
30
31 _user_id = None
26 32
27 33 @classmethod
28 34 async def create_async_generator(
29 35 cls,
30 36 model: str,
31 37 messages: Messages,
38 cookies: Cookies = None,
32 39 proxy: str = None,
33 40 **kwargs
34 41 ) -> AsyncResult:
35 model = cls.get_model(model)
36
42 if not model:
43 model = cls.default_model
44 if cookies is None:
45 cookies = get_cookies(cls.cookie_domain, False)
46 else:
47 cls._user_id = None
48
37 49 # Generate UUIDs for request tracking
38 50 frontend_uuid = str(uuid.uuid4())
39 51 frontend_context_uuid = str(uuid.uuid4())
@@ -58,121 +70,127 @@ class Perplexity(AsyncGeneratorProvider, ProviderModelMixin):
58 70 if message["role"] == "user":
59 71 query = message["content"]
60 72 break
61
62 # Prepare the request payload
63 data = {
64 "params": {
65 "attachments": [],
66 "language": "en-US",
67 "timezone": "America/New_York",
68 "search_focus": "internet",
69 "sources": ["web"],
70 "search_recency_filter": None,
71 "frontend_uuid": frontend_uuid,
72 "mode": "concise",
73 "model_preference": model,
74 "is_related_query": False,
75 "is_sponsored": False,
76 "visitor_id": visitor_id,
77 "frontend_context_uuid": frontend_context_uuid,
78 "prompt_source": "user",
79 "query_source": "home",
80 "is_incognito": False,
81 "time_from_first_type": 0,
82 "local_search_enabled": False,
83 "use_schematized_api": True,
84 "send_back_text_in_streaming_api": False,
85 "supported_block_use_cases": [
86 "answer_modes",
87 "media_items",
88 "knowledge_cards",
89 "inline_entity_cards",
90 "place_widgets",
91 "finance_widgets",
92 "sports_widgets",
93 "shopping_widgets",
94 "jobs_widgets",
95 "search_result_widgets",
96 "clarification_responses",
97 "inline_images",
98 "inline_assets",
99 "inline_finance_widgets",
100 "placeholder_cards",
101 "diff_blocks",
102 "inline_knowledge_cards",
103 "entity_group_v2",
104 "refinement_filters",
105 "canvas_mode"
106 ],
107 "client_coordinates": None,
108 "mentions": [],
109 "dsl_query": query,
110 "skip_search_enabled": False,
111 "is_nav_suggestions_disabled": False,
112 "always_search_override": False,
113 "override_no_search": False,
114 "comet_max_assistant_enabled": False,
115 "version": "2.18"
116 },
117 "query_str": query
118 }
119
120 async with StreamSession(headers=headers, proxy=proxy, impersonate="chrome") as session:
73
74 async with StreamSession(headers=headers, cookies=cookies, proxy=proxy, impersonate="chrome") as session:
75 if cls._user_id is None:
76 async with session.get(f"{cls.url}/api/auth/session") as response:
77 await raise_for_status(response)
78 user = await response.json()
79 cls._user_id = user.get("user", {}).get("id")
80 debug.log(f"Perplexity user id: {cls._user_id}")
81 if model == "auto":
82 model = "pplx_pro" if cls._user_id else "turbo"
83 yield ProviderInfo(**cls.get_dict(), model=model)
84 if model in cls.model_aliases:
85 model = cls.model_aliases[model]
86 if cls._user_id is None:
87 data = {
88 "params": {
89 "attachments": [],
90 "language": "en-US",
91 "timezone": "America/New_York",
92 "search_focus": "internet",
93 "sources": ["web"],
94 "search_recency_filter": None,
95 "frontend_uuid": frontend_uuid,
96 "mode": "concise",
97 "model_preference": model,
98 "is_related_query": False,
99 "is_sponsored": False,
100 "visitor_id": visitor_id,
101 "frontend_context_uuid": frontend_context_uuid,
102 "prompt_source": "user",
103 "query_source": "home",
104 "is_incognito": False,
105 "time_from_first_type": 0,
106 "local_search_enabled": False,
107 "use_schematized_api": True,
108 "send_back_text_in_streaming_api": False,
109 "supported_block_use_cases": [
110 "answer_modes",
111 "media_items",
112 "knowledge_cards",
113 "inline_entity_cards",
114 "place_widgets",
115 "finance_widgets",
116 "sports_widgets",
117 "shopping_widgets",
118 "jobs_widgets",
119 "search_result_widgets",
120 "clarification_responses",
121 "inline_images",
122 "inline_assets",
123 "inline_finance_widgets",
124 "placeholder_cards",
125 "diff_blocks",
126 "inline_knowledge_cards",
127 "entity_group_v2",
128 "refinement_filters",
129 "canvas_mode"
130 ],
131 "client_coordinates": None,
132 "mentions": [],
133 "dsl_query": query,
134 "skip_search_enabled": False,
135 "is_nav_suggestions_disabled": False,
136 "always_search_override": False,
137 "override_no_search": False,
138 "comet_max_assistant_enabled": False,
139 "version": "2.18"
140 },
141 "query_str": query
142 }
143 else:
144 data = {
145 "params": {
146 "last_backend_uuid": None,
147 "read_write_token": "457a2d3d-c53f-4065-8554-7645a36fc220",
148 "attachments": [],
149 "language": "en-US",
150 "timezone": "America/New_York",
151 "search_focus": "internet",
152 "sources": ["web"],
153 "frontend_uuid": frontend_uuid,
154 "mode": "copilot",
155 "model_preference": "gpt5",
156 "is_related_query": False,
157 "is_sponsored": False,
158 "visitor_id": visitor_id,
159 "user_nextauth_id": cls._user_id,
160 "prompt_source": "user",
161 "query_source":"followup",
162 "is_incognito": False,
163 "time_from_first_type": random.randint(0, 1000),
164 "local_search_enabled": False,
165 "use_schematized_api": True,
166 "send_back_text_in_streaming_api": False,
167 "supported_block_use_cases": ["answer_modes", "media_items", "knowledge_cards", "inline_entity_cards", "place_widgets", "finance_widgets", "sports_widgets", "shopping_widgets", "jobs_widgets", "search_result_widgets", "clarification_responses", "inline_images", "inline_assets", "inline_finance_widgets", "placeholder_cards", "diff_blocks", "inline_knowledge_cards", "entity_group_v2", "refinement_filters", "canvas_mode"],
168 "client_coordinates": None,
169 "mentions": [],
170 "skip_search_enabled": True,
171 "is_nav_suggestions_disabled": False,
172 "followup_source": "link",
173 "version": "2.18"
174 },
175 "query_str": query
176 }
121 177 async with session.post(
122 178 f"{cls.url}/rest/sse/perplexity_ask",
123 179 json=data,
124 180 ) as response:
125 181 await raise_for_status(response)
126
127 # Track last message position for incremental updates
128 last_message = 0
129
130 # Parse SSE stream
131 async for line in response.content:
132 if line:
133 line = line.decode('utf-8').strip()
134 if line.startswith("data: "):
135 data_str = line[6:]
136 if data_str == "[DONE]":
137 break
138 try:
139 json_data = json.loads(data_str)
140
141 # Handle error responses
142 if json_data.get("status") == "failed":
143 error_message = json_data.get("text", "Unknown API error")
144 raise ResponseError(f"API Error: {error_message}")
145
146 # Handle normal responses with 'output' field (like PerplexityLabs)
147 if "output" in json_data:
148 # Yield incremental output
149 output = json_data["output"]
150 if len(output) > last_message:
151 yield output[last_message:]
152 last_message = len(output)
153
154 # Check if response is final
155 if json_data.get("final", False):
156 # Yield citations if available
157 if json_data.get("citations"):
158 yield Sources(json_data["citations"])
159 yield FinishReason("stop")
160 break
161
162 # Handle alternative response format with 'text' field
163 elif "text" in json_data:
164 text = json_data["text"]
165 if text:
166 yield text
167
168 # Check if response is final
169 if json_data.get("final", False):
170 yield FinishReason("stop")
171 break
172
173 except json.JSONDecodeError:
174 # Skip malformed JSON
175 continue
176 except ResponseError:
177 # Re-raise ResponseError directly
178 raise
182 full_response = ""
183 last_response = ""
184 async for json_data in sse_stream(response):
185 for block in json_data.get("blocks", []):
186 for patch in block.get("diff_block", {}).get("patches", []):
187 value = patch.get("value", "")
188 value = value.get("answer", "") if isinstance(value, dict) else value
189 if value:
190 if value.startswith(full_response):
191 value = value[len(full_response):]
192 if value.startswith(last_response):
193 value = value[len(last_response):]
194 last_response = value
195 full_response += value
196 yield value
Modified g4f/Provider/PerplexityLabs.py +1 -1
@@ -15,7 +15,7 @@ WS_URL = "wss://www.perplexity.ai/socket.io/"
15 15 class PerplexityLabs(AsyncGeneratorProvider, ProviderModelMixin):
16 16 label = "Perplexity Labs"
17 17 url = "https://labs.perplexity.ai"
18 working = True
18 working = False
19 19 active_by_default = True
20 20
21 21 default_model = "r1-1776"