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

XFEstudio/gpt4free

Add conversation and continue support in DDG

64e6e47c
Heiner Lohaus <hlohaus@users.noreply.github.com>
提交于

代码差异

12 个文件 +104 -134
Modified g4f/Provider/AutonomousAI.py +6 -9
@@ -6,8 +6,8 @@ import json
6 6
7 7 from ..typing import AsyncResult, Messages
8 8 from ..requests.raise_for_status import raise_for_status
9 from ..providers.response import FinishReason
9 10 from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
10 from .helper import format_prompt
11 11
12 12 class AutonomousAI(AsyncGeneratorProvider, ProviderModelMixin):
13 13 url = "https://www.autonomous.ai/anon/"
@@ -32,7 +32,6 @@ class AutonomousAI(AsyncGeneratorProvider, ProviderModelMixin):
32 32 "qwen-2.5-coder-32b": "qwen_coder",
33 33 "hermes-3": "hermes",
34 34 "llama-3.2-90b": "vision",
35 "llama-3.3-70b": "summary"
36 35 }
37 36
38 37 @classmethod
@@ -57,12 +56,8 @@ class AutonomousAI(AsyncGeneratorProvider, ProviderModelMixin):
57 56 }
58 57
59 58 async with ClientSession(headers=headers) as session:
60 prompt = format_prompt(messages)
61
62 # Encode message
63 message = [{"role": "user", "content": prompt}]
64 message_json = json.dumps(message)
65 encoded_message = base64.b64encode(message_json.encode('utf-8')).decode('utf-8')
59 message_json = json.dumps(messages)
60 encoded_message = base64.b64encode(message_json.encode()).decode(errors="ignore")
66 61
67 62 data = {
68 63 "messages": encoded_message,
@@ -84,7 +79,9 @@ class AutonomousAI(AsyncGeneratorProvider, ProviderModelMixin):
84 79 chunk_data = json.loads(chunk_str.replace("data: ", ""))
85 80 if "choices" in chunk_data and chunk_data["choices"]:
86 81 delta = chunk_data["choices"][0].get("delta", {})
87 if "content" in delta:
82 if "content" in delta and delta["content"]:
88 83 yield delta["content"]
84 if "finish_reason" in chunk_data and chunk_data["finish_reason"]:
85 yield FinishReason(chunk_data["finish_reason"])
89 86 except json.JSONDecodeError:
90 87 continue
Modified g4f/Provider/CablyAI.py +12 -46
@@ -1,38 +1,26 @@
1 1 from __future__ import annotations
2 2
3 from aiohttp import ClientSession
4 import json
5
6 3 from ..typing import AsyncResult, Messages
7 from ..requests.raise_for_status import raise_for_status
8 from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
9 from .helper import format_prompt
10
4 from .needs_auth import OpenaiAPI
11 5
12 class CablyAI(AsyncGeneratorProvider, ProviderModelMixin):
6 class CablyAI(OpenaiAPI):
13 7 url = "https://cablyai.com"
14 api_endpoint = "https://cablyai.com/v1/chat/completions"
15
8 login_url = None
9 needs_auth = False
10 api_base = "https://cablyai.com/v1"
16 11 working = True
17 supports_stream = True
18 supports_system_message = True
19 supports_message_history = True
20 12
21 13 default_model = "Cably-80B"
22 14 models = [default_model]
23
24 15 model_aliases = {"cably-80b": default_model}
25 16
26 17 @classmethod
27 async def create_async_generator(
18 def create_async_generator(
28 19 cls,
29 20 model: str,
30 21 messages: Messages,
31 stream: bool = False,
32 proxy: str = None,
33 22 **kwargs
34 23 ) -> AsyncResult:
35 model = cls.get_model(model)
36 24 headers = {
37 25 'Accept': '*/*',
38 26 'Accept-Language': 'en-US,en;q=0.9',
@@ -41,31 +29,9 @@ class CablyAI(AsyncGeneratorProvider, ProviderModelMixin):
41 29 'Referer': 'https://cablyai.com/chat',
42 30 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
43 31 }
44
45 async with ClientSession(headers=headers) as session:
46 data = {
47 "model": model,
48 "messages": messages,
49 "stream": stream
50 }
51
52 async with session.post(cls.api_endpoint, json=data, proxy=proxy) as response:
53 await raise_for_status(response)
54 buffer = ""
55 async for chunk in response.content:
56 if chunk:
57 buffer += chunk.decode()
58 while "\n\n" in buffer:
59 chunk_data, buffer = buffer.split("\n\n", 1)
60 if chunk_data.startswith("data: "):
61 try:
62 json_data = json.loads(chunk_data[6:])
63 if "choices" in json_data and json_data["choices"]:
64 content = json_data["choices"][0]["delta"].get("content", "")
65 if content:
66 yield content
67 except json.JSONDecodeError:
68 # Skip invalid JSON
69 pass
70 elif chunk_data.strip() == "data: [DONE]":
71 return
32 return super().create_async_generator(
33 model=model,
34 messages=messages,
35 headers=headers,
36 **kwargs
37 )
Modified g4f/Provider/ChatGLM.py +7 -6
@@ -8,7 +8,6 @@ from aiohttp import ClientSession
8 8 from ..typing import AsyncResult, Messages
9 9 from ..requests.raise_for_status import raise_for_status
10 10 from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
11 from .helper import format_prompt
12 11
13 12 class ChatGLM(AsyncGeneratorProvider, ProviderModelMixin):
14 13 url = "https://chatglm.cn"
@@ -17,7 +16,7 @@ class ChatGLM(AsyncGeneratorProvider, ProviderModelMixin):
17 16 working = True
18 17 supports_stream = True
19 18 supports_system_message = False
20 supports_message_history = True
19 supports_message_history = False
21 20
22 21 default_model = "all-tools-230b"
23 22 models = [default_model]
@@ -47,7 +46,6 @@ class ChatGLM(AsyncGeneratorProvider, ProviderModelMixin):
47 46 }
48 47
49 48 async with ClientSession(headers=headers) as session:
50 prompt = format_prompt(messages)
51 49 data = {
52 50 "assistant_id": "65940acff94777010aa6b796",
53 51 "conversation_id": "",
@@ -62,17 +60,19 @@ class ChatGLM(AsyncGeneratorProvider, ProviderModelMixin):
62 60 },
63 61 "messages": [
64 62 {
65 "role": "user",
63 "role": message["role"],
66 64 "content": [
67 65 {
68 66 "type": "text",
69 "text": prompt
67 "text": message["content"]
70 68 }
71 69 ]
72 70 }
71 for message in messages
73 72 ]
74 73 }
75 74
75 yield_text = 0
76 76 async with session.post(cls.api_endpoint, json=data, proxy=proxy) as response:
77 77 await raise_for_status(response)
78 78 async for chunk in response.content:
@@ -85,8 +85,9 @@ class ChatGLM(AsyncGeneratorProvider, ProviderModelMixin):
85 85 if parts:
86 86 content = parts[0].get('content', [])
87 87 if content:
88 text = content[0].get('text', '')
88 text = content[0].get('text', '')[yield_text:]
89 89 if text:
90 90 yield text
91 yield_text += len(text)
91 92 except json.JSONDecodeError:
92 93 pass
Modified g4f/Provider/ChatGptEs.py +3 -11
@@ -2,7 +2,6 @@ from __future__ import annotations
2 2
3 3 import os
4 4 import re
5 import json
6 5
7 6 from aiohttp import ClientSession
8 7
@@ -17,20 +16,14 @@ class ChatGptEs(AsyncGeneratorProvider, ProviderModelMixin):
17 16
18 17 working = True
19 18 supports_stream = True
20 supports_system_message = True
21 supports_message_history = True
19 supports_system_message = False
20 supports_message_history = False
22 21
23 22 default_model = 'gpt-4o'
24 23 models = ['gpt-4', default_model, 'gpt-4o-mini']
25 24
26 25 SYSTEM_PROMPT = "Your default language is English. Always respond in English unless the user's message is in a different language. If the user's message is not in English, respond in the language of the user's message. Maintain this language behavior throughout the conversation unless explicitly instructed otherwise. User input:"
27 26
28 @classmethod
29 def get_model(cls, model: str) -> str:
30 if model in cls.models:
31 return model
32 return cls.model_aliases[model]
33
34 27 @classmethod
35 28 async def create_async_generator(
36 29 cls,
@@ -68,10 +61,9 @@ class ChatGptEs(AsyncGeneratorProvider, ProviderModelMixin):
68 61 'wpaicg_chat_client_id': os.urandom(5).hex(),
69 62 'wpaicg_chat_history': None
70 63 }
71 print(payload['message'])
72 64
73 65 async with session.post(cls.api_endpoint, headers=headers, data=payload) as response:
74 response.raise_for_status()
66 await raise_for_status(response)
75 67 result = await response.json()
76 68 if "Du musst das Kästchen anklicken!" in result['data']:
77 69 raise ValueError(result['data'])
Modified g4f/Provider/ChatGptt.py +2 -9
@@ -2,7 +2,6 @@ from __future__ import annotations
2 2
3 3 import os
4 4 import re
5 import json
6 5
7 6 from aiohttp import ClientSession
8 7
@@ -23,12 +22,6 @@ class ChatGptt(AsyncGeneratorProvider, ProviderModelMixin):
23 22 default_model = 'gpt-4o'
24 23 models = ['gpt-4', default_model, 'gpt-4o-mini']
25 24
26 @classmethod
27 def get_model(cls, model: str) -> str:
28 if model in cls.models:
29 return model
30 return cls.model_aliases[model]
31
32 25 @classmethod
33 26 async def create_async_generator(
34 27 cls,
@@ -64,7 +57,7 @@ class ChatGptt(AsyncGeneratorProvider, ProviderModelMixin):
64 57 'wpaicg_chat_history': None
65 58 }
66 59
67 async with session.post(cls.api_endpoint, headers=headers, data=payload) as response:
60 async with session.post(cls.api_endpoint, headers=headers, data=payload, proxy=proxy) as response:
68 61 await raise_for_status(response)
69 62 result = await response.json()
70 yield result['data']
63 yield result['data']
Modified g4f/Provider/DDG.py +57 -37
@@ -1,15 +1,23 @@
1 1 from __future__ import annotations
2 2
3 from aiohttp import ClientSession, ClientTimeout, ClientError
3 from aiohttp import ClientSession, ClientTimeout
4 4 import json
5 5 import asyncio
6 6 import random
7 7
8 from ..typing import AsyncResult, Messages
8 from ..typing import AsyncResult, Messages, Cookies
9 9 from ..requests.raise_for_status import raise_for_status
10 10 from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
11 11 from .helper import format_prompt
12 from ..providers.response import FinishReason, JsonConversation
12 13
14 class Conversation(JsonConversation):
15 vqd: str = None
16 message_history: Messages = []
17 cookies: dict = {}
18
19 def __init__(self, model: str):
20 self.model = model
13 21
14 22 class DDG(AsyncGeneratorProvider, ProviderModelMixin):
15 23 label = "DuckDuckGo AI Chat"
@@ -74,42 +82,54 @@ class DDG(AsyncGeneratorProvider, ProviderModelMixin):
74 82 messages: Messages,
75 83 proxy: str = None,
76 84 timeout: int = 30,
85 cookies: Cookies = None,
86 conversation: Conversation = None,
87 return_conversation: bool = False,
77 88 **kwargs
78 89 ) -> AsyncResult:
79 90 model = cls.get_model(model)
80
81 async with ClientSession(timeout=ClientTimeout(total=timeout)) as session:
82 try:
83 # Fetch VQD token with retries
84 vqd = await cls.fetch_vqd(session)
85
86 headers = {
87 "accept": "text/event-stream",
88 "content-type": "application/json",
89 "x-vqd-4": vqd,
90 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
91 }
92
93 data = {
94 "model": model,
95 "messages": [{"role": "user", "content": format_prompt(messages)}],
96 }
97
98 async with session.post(cls.api_endpoint, json=data, headers=headers, proxy=proxy) as response:
99 await raise_for_status(response)
100 async for line in response.content:
101 line = line.decode("utf-8").strip()
102 if line.startswith("data:"):
103 try:
104 message = json.loads(line[5:].strip())
105 if "message" in message:
91 if cookies is None and conversation is not None:
92 cookies = conversation.cookies
93 async with ClientSession(timeout=ClientTimeout(total=timeout), cookies=cookies) as session:
94 # Fetch VQD token
95 if conversation is None:
96 conversation = Conversation(model)
97 conversation.vqd = await cls.fetch_vqd(session)
98 conversation.message_history = [{"role": "user", "content": format_prompt(messages)}]
99 else:
100 conversation.message_history.append(messages[-1])
101 headers = {
102 "accept": "text/event-stream",
103 "content-type": "application/json",
104 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36",
105 "x-vqd-4": conversation.vqd,
106 }
107 data = {
108 "model": model,
109 "messages": conversation.message_history,
110 }
111 async with session.post(cls.api_endpoint, json=data, headers=headers, proxy=proxy) as response:
112 await raise_for_status(response)
113 reason = None
114 full_message = ""
115 async for line in response.content:
116 line = line.decode("utf-8").strip()
117 if line.startswith("data:"):
118 try:
119 message = json.loads(line[5:].strip())
120 if "message" in message:
121 if message["message"]:
106 122 yield message["message"]
107 except json.JSONDecodeError:
108 continue
109
110 except ClientError as e:
111 raise Exception(f"HTTP ClientError occurred: {e}")
112 except asyncio.TimeoutError:
113 raise Exception("Request timed out.")
114 except Exception as e:
115 raise Exception(f"An error occurred: {str(e)}")
123 full_message += message["message"]
124 reason = "length"
125 else:
126 reason = "stop"
127 except json.JSONDecodeError:
128 continue
129 if return_conversation:
130 conversation.message_history.append({"role": "assistant", "content": full_message})
131 conversation.vqd = response.headers.get("x-vqd-4", conversation.vqd)
132 conversation.cookies = {n: c.value for n, c in session.cookie_jar.filter_cookies(cls.url).items()}
133 yield conversation
134 if reason is not None:
135 yield FinishReason(reason)
Modified g4f/Provider/ImageLabs.py +1 -1
@@ -48,7 +48,7 @@ class ImageLabs(AsyncGeneratorProvider, ProviderModelMixin):
48 48 }
49 49
50 50 async with ClientSession(headers=headers) as session:
51 prompt = messages[-1]["content"]
51 prompt = messages[-1]["content"] if prompt is None else prompt
52 52
53 53 # Generate image
54 54 payload = {
Modified g4f/Provider/Pizzagpt.py +2 -0
@@ -46,4 +46,6 @@ class Pizzagpt(AsyncGeneratorProvider, ProviderModelMixin):
46 46 response_json = await response.json()
47 47 content = response_json.get("answer", response_json).get("content")
48 48 if content:
49 if "misuse detected. please get in touch" in content:
50 raise ValueError(content)
49 51 yield content
Modified g4f/Provider/PollinationsAI.py +3 -4
@@ -11,14 +11,13 @@ from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
11 11 from ..requests.raise_for_status import raise_for_status
12 12 from ..typing import AsyncResult, Messages
13 13 from ..image import ImageResponse
14 from .helper import format_prompt
15 14
16 15 class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
17 16 label = "Pollinations AI"
18 17 url = "https://pollinations.ai"
19 18
20 19 working = True
21 supports_stream = True
20 supports_stream = False
22 21 supports_system_message = True
23 22 supports_message_history = True
24 23
@@ -172,9 +171,9 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
172 171 params = {k: v for k, v in params.items() if v is not None}
173 172
174 173 async with ClientSession(headers=headers) as session:
175 prompt = quote(messages[-1]["content"] if prompt is None else prompt)
174 prompt = messages[-1]["content"] if prompt is None else prompt
176 175 param_string = "&".join(f"{k}={v}" for k, v in params.items())
177 url = f"{cls.image_api_endpoint}/prompt/{prompt}?{param_string}"
176 url = f"{cls.image_api_endpoint}/prompt/{quote(prompt)}?{param_string}"
178 177
179 178 async with session.head(url, proxy=proxy) as response:
180 179 if response.status == 200:
Modified g4f/Provider/hf_space/Qwen_Qwen_2_72B_Instruct.py +9 -10
@@ -1,6 +1,5 @@
1 1 from __future__ import annotations
2 2
3 import asyncio
4 3 import aiohttp
5 4 import json
6 5 import uuid
@@ -9,7 +8,7 @@ import re
9 8 from ...typing import AsyncResult, Messages
10 9 from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
11 10 from ..helper import format_prompt
12
11 from ... import debug
13 12
14 13 class Qwen_Qwen_2_72B_Instruct(AsyncGeneratorProvider, ProviderModelMixin):
15 14 url = "https://qwen-qwen2-72b-instruct.hf.space"
@@ -49,10 +48,12 @@ class Qwen_Qwen_2_72B_Instruct(AsyncGeneratorProvider, ProviderModelMixin):
49 48 }
50 49
51 50 # Prepare the prompt
51 system_prompt = "\n".join([message["content"] for message in messages if message["role"] == "system"])
52 messages = [message for message in messages if message["role"] != "system"]
52 53 prompt = format_prompt(messages)
53 54
54 55 payload_join = {
55 "data": [prompt, [], ""],
56 "data": [prompt, [], system_prompt],
56 57 "event_data": None,
57 58 "fn_index": 0,
58 59 "trigger_id": 11,
@@ -87,7 +88,7 @@ class Qwen_Qwen_2_72B_Instruct(AsyncGeneratorProvider, ProviderModelMixin):
87 88 if decoded_line.startswith('data: '):
88 89 try:
89 90 json_data = json.loads(decoded_line[6:])
90
91
91 92 # Look for generation stages
92 93 if json_data.get('msg') == 'process_generating':
93 94 if 'output' in json_data and 'data' in json_data['output']:
@@ -97,10 +98,10 @@ class Qwen_Qwen_2_72B_Instruct(AsyncGeneratorProvider, ProviderModelMixin):
97 98 if isinstance(item, list) and len(item) > 1:
98 99 fragment = str(item[1])
99 100 # Ignore [0, 1] type fragments and duplicates
100 if not re.match(r'^\[.*\]$', fragment) and fragment not in full_response:
101 if not re.match(r'^\[.*\]$', fragment) and not full_response.endswith(fragment):
101 102 full_response += fragment
102 103 yield fragment
103
104
104 105 # Check for completion
105 106 if json_data.get('msg') == 'process_completed':
106 107 # Final check to ensure we get the complete response
@@ -117,8 +118,6 @@ class Qwen_Qwen_2_72B_Instruct(AsyncGeneratorProvider, ProviderModelMixin):
117 118 if final_full_response:
118 119 yield final_full_response
119 120 break
120
121
121 122 except json.JSONDecodeError:
122 print("Could not parse JSON:", decoded_line)
123 except Exception as e:
124 print(f"Error processing response: {e}")
123 debug.log("Could not parse JSON:", decoded_line)
Modified g4f/Provider/needs_auth/DeepSeek.py +1 -1
@@ -12,4 +12,4 @@ class DeepSeek(OpenaiAPI):
12 12 supports_stream = True
13 13 supports_message_history = True
14 14 default_model = "deepseek-chat"
15 models = [default_model]
15 fallback_models = [default_model]
Modified g4f/Provider/needs_auth/OpenaiAPI.py +1 -0
@@ -136,6 +136,7 @@ class OpenaiAPI(AsyncGeneratorProvider, ProviderModelMixin, RaiseErrorMixin):
136 136 finish = cls.read_finish_reason(choice)
137 137 if finish is not None:
138 138 yield finish
139 break
139 140
140 141 @staticmethod
141 142 def read_finish_reason(choice: dict) -> Optional[FinishReason]: