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

XFEstudio/gpt4free

Improve Liabots provider, Add image api support

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

代码差异

11 个文件 +288 -116
Modified etc/examples/api.py +1 -1
@@ -3,7 +3,7 @@ import json
3 3 url = "http://localhost:1337/v1/chat/completions"
4 4 body = {
5 5 "model": "",
6 "provider": "MetaAI",
6 "provider": "",
7 7 "stream": True,
8 8 "messages": [
9 9 {"role": "assistant", "content": "What can you do? Who are you?"}
Modified g4f/Provider/Liaobots.py +56 -23
@@ -10,6 +10,15 @@ from .helper import get_connector
10 10 from ..requests import raise_for_status
11 11
12 12 models = {
13 "gpt-4o": {
14 "context": "8K",
15 "id": "gpt-4o-free",
16 "maxLength": 31200,
17 "model": "ChatGPT",
18 "name": "GPT-4o-free",
19 "provider": "OpenAI",
20 "tokenLimit": 7800,
21 },
13 22 "gpt-3.5-turbo": {
14 23 "id": "gpt-3.5-turbo",
15 24 "name": "GPT-3.5-Turbo",
@@ -95,7 +104,7 @@ class Liaobots(AsyncGeneratorProvider, ProviderModelMixin):
95 104 model_aliases = {
96 105 "claude-v2": "claude-2"
97 106 }
98 _auth_code = None
107 _auth_code = ""
99 108 _cookie_jar = None
100 109
101 110 @classmethod
@@ -120,7 +129,13 @@ class Liaobots(AsyncGeneratorProvider, ProviderModelMixin):
120 129 cookie_jar=cls._cookie_jar,
121 130 connector=get_connector(connector, proxy, True)
122 131 ) as session:
123 cls._auth_code = auth if isinstance(auth, str) else cls._auth_code
132 data = {
133 "conversationId": str(uuid.uuid4()),
134 "model": models[cls.get_model(model)],
135 "messages": messages,
136 "key": "",
137 "prompt": kwargs.get("system_message", "You are a helpful assistant."),
138 }
124 139 if not cls._auth_code:
125 140 async with session.post(
126 141 "https://liaobots.work/recaptcha/api/login",
@@ -128,31 +143,49 @@ class Liaobots(AsyncGeneratorProvider, ProviderModelMixin):
128 143 verify_ssl=False
129 144 ) as response:
130 145 await raise_for_status(response)
146 try:
131 147 async with session.post(
132 148 "https://liaobots.work/api/user",
133 json={"authcode": ""},
149 json={"authcode": cls._auth_code},
134 150 verify_ssl=False
135 151 ) as response:
136 152 await raise_for_status(response)
137 153 cls._auth_code = (await response.json(content_type=None))["authCode"]
154 if not cls._auth_code:
155 raise RuntimeError("Empty auth code")
138 156 cls._cookie_jar = session.cookie_jar
139
140 data = {
141 "conversationId": str(uuid.uuid4()),
142 "model": models[cls.get_model(model)],
143 "messages": messages,
144 "key": "",
145 "prompt": kwargs.get("system_message", "You are a helpful assistant."),
146 }
147 async with session.post(
148 "https://liaobots.work/api/chat",
149 json=data,
150 headers={"x-auth-code": cls._auth_code},
151 verify_ssl=False
152 ) as response:
153 await raise_for_status(response)
154 async for chunk in response.content.iter_any():
155 if b"<html coupert-item=" in chunk:
156 raise RuntimeError("Invalid session")
157 if chunk:
158 yield chunk.decode(errors="ignore")
157 async with session.post(
158 "https://liaobots.work/api/chat",
159 json=data,
160 headers={"x-auth-code": cls._auth_code},
161 verify_ssl=False
162 ) as response:
163 await raise_for_status(response)
164 async for chunk in response.content.iter_any():
165 if b"<html coupert-item=" in chunk:
166 raise RuntimeError("Invalid session")
167 if chunk:
168 yield chunk.decode(errors="ignore")
169 except:
170 async with session.post(
171 "https://liaobots.work/api/user",
172 json={"authcode": "pTIQr4FTnVRfr"},
173 verify_ssl=False
174 ) as response:
175 await raise_for_status(response)
176 cls._auth_code = (await response.json(content_type=None))["authCode"]
177 if not cls._auth_code:
178 raise RuntimeError("Empty auth code")
179 cls._cookie_jar = session.cookie_jar
180 async with session.post(
181 "https://liaobots.work/api/chat",
182 json=data,
183 headers={"x-auth-code": cls._auth_code},
184 verify_ssl=False
185 ) as response:
186 await raise_for_status(response)
187 async for chunk in response.content.iter_any():
188 if b"<html coupert-item=" in chunk:
189 raise RuntimeError("Invalid session")
190 if chunk:
191 yield chunk.decode(errors="ignore")
Modified g4f/Provider/needs_auth/Gemini.py +19 -17
@@ -4,6 +4,7 @@ import os
4 4 import json
5 5 import random
6 6 import re
7 import base64
7 8
8 9 from aiohttp import ClientSession, BaseConnector
9 10
@@ -22,7 +23,7 @@ from ..base_provider import AsyncGeneratorProvider
22 23 from ..helper import format_prompt, get_cookies
23 24 from ...requests.raise_for_status import raise_for_status
24 25 from ...errors import MissingAuthError, MissingRequirementsError
25 from ...image import to_bytes, to_data_uri, ImageResponse
26 from ...image import to_bytes, ImageResponse, ImageDataResponse
26 27 from ...webdriver import get_browser, get_driver_cookies
27 28
28 29 REQUEST_HEADERS = {
@@ -122,6 +123,7 @@ class Gemini(AsyncGeneratorProvider):
122 123 connector: BaseConnector = None,
123 124 image: ImageType = None,
124 125 image_name: str = None,
126 response_format: str = None,
125 127 **kwargs
126 128 ) -> AsyncResult:
127 129 prompt = format_prompt(messages)
@@ -192,22 +194,22 @@ class Gemini(AsyncGeneratorProvider):
192 194 if image_prompt:
193 195 images = [image[0][3][3] for image in response_part[4][0][12][7][0]]
194 196 resolved_images = []
195 preview = []
196 for image in images:
197 async with client.get(image, allow_redirects=False) as fetch:
198 image = fetch.headers["location"]
199 async with client.get(image, allow_redirects=False) as fetch:
200 image = fetch.headers["location"]
201 resolved_images.append(image)
202 preview.append(image.replace('=s512', '=s200'))
203 # preview_url = image.replace('=s512', '=s200')
204 # async with client.get(preview_url) as fetch:
205 # preview_data = to_data_uri(await fetch.content.read())
206 # async with client.get(image) as fetch:
207 # data = to_data_uri(await fetch.content.read())
208 # preview.append(preview_data)
209 # resolved_images.append(data)
210 yield ImageResponse(resolved_images, image_prompt, {"orginal_links": images, "preview": preview})
197 if response_format == "b64_json":
198 for image in images:
199 async with client.get(image) as response:
200 data = base64.b64encode(await response.content.read()).decode()
201 resolved_images.append(data)
202 yield ImageDataResponse(resolved_images, image_prompt)
203 else:
204 preview = []
205 for image in images:
206 async with client.get(image, allow_redirects=False) as fetch:
207 image = fetch.headers["location"]
208 async with client.get(image, allow_redirects=False) as fetch:
209 image = fetch.headers["location"]
210 resolved_images.append(image)
211 preview.append(image.replace('=s512', '=s200'))
212 yield ImageResponse(resolved_images, image_prompt, {"orginal_links": images, "preview": preview})
211 213
212 214 def build_request(
213 215 prompt: str,
Modified g4f/Provider/needs_auth/OpenaiChat.py +6 -6
@@ -61,7 +61,7 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
61 61 supports_system_message = True
62 62 default_model = None
63 63 default_vision_model = "gpt-4o"
64 models = ["gpt-3.5-turbo", "gpt-4", "gpt-4-gizmo", "gpt-4o"]
64 models = ["gpt-3.5-turbo", "gpt-4", "gpt-4-gizmo", "gpt-4o", "auto"]
65 65 model_aliases = {
66 66 "text-davinci-002-render-sha": "gpt-3.5-turbo",
67 67 "": "gpt-3.5-turbo",
@@ -394,10 +394,11 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
394 394 print(f"{e.__class__.__name__}: {e}")
395 395
396 396 arkose_token = None
397 proofTokens = None
397 398 if cls.default_model is None:
398 399 error = None
399 400 try:
400 arkose_token, api_key, cookies, headers = await getArkoseAndAccessToken(proxy)
401 arkose_token, api_key, cookies, headers, proofTokens = await getArkoseAndAccessToken(proxy)
401 402 cls._create_request_args(cookies, headers)
402 403 cls._set_api_key(api_key)
403 404 except NoValidHarFileError as e:
@@ -413,17 +414,17 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
413 414 if cls._api_key is None else
414 415 f"{cls.url}/backend-api/sentinel/chat-requirements",
415 416 json={"conversation_mode_kind": "primary_assistant"},
417 #json={"p": generate_proof_token(True, user_agent=cls._headers["user-agent"], proofTokens=proofTokens)},
416 418 headers=cls._headers
417 419 ) as response:
418 420 cls._update_request_args(session)
419 421 await raise_for_status(response)
420 422 data = await response.json()
421 blob = data["arkose"]["dx"]
422 need_arkose = data["arkose"]["required"]
423 need_arkose = data.get("arkose", {}).get("required")
423 424 chat_token = data["token"]
424 425 proofofwork = ""
425 426 if "proofofwork" in data:
426 proofofwork = generate_proof_token(**data["proofofwork"], user_agent=cls._headers["user-agent"])
427 proofofwork = generate_proof_token(**data["proofofwork"], user_agent=cls._headers["user-agent"], proofTokens=proofTokens)
427 428
428 429 if need_arkose and arkose_token is None:
429 430 arkose_token, api_key, cookies, headers = await getArkoseAndAccessToken(proxy)
@@ -435,7 +436,6 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
435 436 if debug.logging:
436 437 print(
437 438 'Arkose:', False if not need_arkose else arkose_token[:12]+"...",
438 'Turnstile:', data["turnstile"]["required"],
439 439 'Proofofwork:', False if proofofwork is None else proofofwork[:12]+"...",
440 440 )
441 441
Modified g4f/Provider/openai/har_file.py +21 -9
@@ -12,6 +12,7 @@ from copy import deepcopy
12 12
13 13 from .crypt import decrypt, encrypt
14 14 from ...requests import StreamSession
15 from ... import debug
15 16
16 17 class NoValidHarFileError(Exception):
17 18 ...
@@ -31,6 +32,7 @@ chatArk: arkReq = None
31 32 accessToken: str = None
32 33 cookies: dict = None
33 34 headers: dict = None
35 proofTokens: list = []
34 36
35 37 def readHAR():
36 38 dirPath = "./"
@@ -54,6 +56,15 @@ def readHAR():
54 56 # Error: not a HAR file!
55 57 continue
56 58 for v in harFile['log']['entries']:
59 v_headers = get_headers(v)
60 try:
61 if "openai-sentinel-proof-token" in v_headers:
62 proofTokens.append(json.loads(base64.b64decode(
63 v_headers["openai-sentinel-proof-token"].split("gAAAAAB", 1)[-1].encode()
64 ).decode()))
65 except Exception as e:
66 if debug.logging:
67 print(f"Read proof token: {e}")
57 68 if arkPreURL in v['request']['url']:
58 69 chatArks.append(parseHAREntry(v))
59 70 elif v['request']['url'] == sessionUrl:
@@ -61,13 +72,13 @@ def readHAR():
61 72 accessToken = json.loads(v["response"]["content"]["text"]).get("accessToken")
62 73 except KeyError:
63 74 continue
64 cookies = {c['name']: c['value'] for c in v['request']['cookies']}
65 headers = get_headers(v)
75 cookies = {c['name']: c['value'] for c in v['request']['cookies'] if c['name'] != "oai-did"}
76 headers = v_headers
66 77 if not accessToken:
67 78 raise NoValidHarFileError("No accessToken found in .har files")
68 79 if not chatArks:
69 return None, accessToken, cookies, headers
70 return chatArks.pop(), accessToken, cookies, headers
80 return None, accessToken, cookies, headers, proofTokens
81 return chatArks.pop(), accessToken, cookies, headers, proofTokens
71 82
72 83 def get_headers(entry) -> dict:
73 84 return {h['name'].lower(): h['value'] for h in entry['request']['headers'] if h['name'].lower() not in ['content-length', 'cookie'] and not h['name'].startswith(':')}
@@ -101,7 +112,8 @@ def genArkReq(chatArk: arkReq) -> arkReq:
101 112 async def sendRequest(tmpArk: arkReq, proxy: str = None):
102 113 async with StreamSession(headers=tmpArk.arkHeader, cookies=tmpArk.arkCookies, proxies={"https": proxy}) as session:
103 114 async with session.post(tmpArk.arkURL, data=tmpArk.arkBody) as response:
104 arkose = (await response.json()).get("token")
115 data = await response.json()
116 arkose = data.get("token")
105 117 if "sup=1|rid=" not in arkose:
106 118 return RuntimeError("No valid arkose token generated")
107 119 return arkose
@@ -131,10 +143,10 @@ def getN() -> str:
131 143 return base64.b64encode(timestamp.encode()).decode()
132 144
133 145 async def getArkoseAndAccessToken(proxy: str) -> tuple[str, str, dict, dict]:
134 global chatArk, accessToken, cookies, headers
146 global chatArk, accessToken, cookies, headers, proofTokens
135 147 if chatArk is None or accessToken is None:
136 chatArk, accessToken, cookies, headers = readHAR()
148 chatArk, accessToken, cookies, headers, proofTokens = readHAR()
137 149 if chatArk is None:
138 return None, accessToken, cookies, headers
150 return None, accessToken, cookies, headers, proofTokens
139 151 newReq = genArkReq(chatArk)
140 return await sendRequest(newReq, proxy), accessToken, cookies, headers
152 return await sendRequest(newReq, proxy), accessToken, cookies, headers, proofTokens
Modified g4f/Provider/openai/proofofwork.py +32 -16
@@ -2,35 +2,51 @@ import random
2 2 import hashlib
3 3 import json
4 4 import base64
5 from datetime import datetime, timedelta, timezone
5 from datetime import datetime, timezone
6 6
7 def generate_proof_token(required: bool, seed: str, difficulty: str, user_agent: str):
7 proof_token_cache: dict = {}
8
9 def generate_proof_token(required: bool, seed: str = None, difficulty: str = None, user_agent: str = None, proofTokens: list = None):
8 10 if not required:
9 11 return
10
11 cores = [8, 12, 16, 24]
12 screens = [3000, 4000, 6000]
13
14 core = random.choice(cores)
15 screen = random.choice(screens)
12 if seed is not None and seed in proof_token_cache:
13 return proof_token_cache[seed]
16 14
17 15 # Get current UTC time
18 16 now_utc = datetime.now(timezone.utc)
19 17 parse_time = now_utc.strftime('%a, %d %b %Y %H:%M:%S GMT')
20 18
21 config = [core + screen, parse_time, None, 0, user_agent, "https://tcr9i.chat.openai.com/v2/35536E1E-65B4-4D96-9D97-6ADB7EFF8147/api.js","dpl=53d243de46ff04dadd88d293f088c2dd728f126f","en","en-US",442,"plugins−[object PluginArray]","","alert"]
22
23 diff_len = len(difficulty) // 2
24
19 if proofTokens:
20 config = random.choice(proofTokens)
21 else:
22 screen = random.choice([3008, 4010, 6000]) * random.choice([1, 2, 4])
23 config = [
24 screen, parse_time,
25 None, 0, user_agent,
26 "https://tcr9i.chat.openai.com/v2/35536E1E-65B4-4D96-9D97-6ADB7EFF8147/api.js",
27 "dpl=1440a687921de39ff5ee56b92807faaadce73f13","en","en-US",
28 None,
29 "plugins−[object PluginArray]",
30 random.choice(["_reactListeningcfilawjnerp", "_reactListening9ne2dfo1i47", "_reactListening410nzwhan2a"]),
31 random.choice(["alert", "ontransitionend", "onprogress"])
32 ]
33
34 config[1] = parse_time
35 config[4] = user_agent
36 config[7] = random.randint(101, 2100)
37
38 diff_len = None if difficulty is None else len(difficulty)
25 39 for i in range(100000):
26 40 config[3] = i
27 41 json_data = json.dumps(config)
28 42 base = base64.b64encode(json_data.encode()).decode()
29 hash_value = hashlib.sha3_512((seed + base).encode()).digest()
43 hash_value = hashlib.sha3_512((seed or "" + base).encode()).digest()
30 44
31 if hash_value.hex()[:diff_len] <= difficulty:
32 result = "gAAAAAB" + base
33 return result
45 if difficulty is None or hash_value.hex()[:diff_len] <= difficulty:
46 if seed is None:
47 return "gAAAAAC" + base
48 proof_token_cache[seed] = "gAAAAAB" + base
49 return proof_token_cache[seed]
34 50
35 51 fallback_base = base64.b64encode(f'"{seed}"'.encode()).decode()
36 52 return "gAAAAABwQ8Lk5FbGpA2NcR9dShT6gYjU7VxZ4D" + fallback_base
Modified g4f/api/__init__.py +45 -21
@@ -47,6 +47,14 @@ class ChatCompletionsForm(BaseModel):
47 47 web_search: Optional[bool] = None
48 48 proxy: Optional[str] = None
49 49
50 class ImagesGenerateForm(BaseModel):
51 model: Optional[str] = None
52 provider: Optional[str] = None
53 prompt: str
54 response_format: Optional[str] = None
55 api_key: Optional[str] = None
56 proxy: Optional[str] = None
57
50 58 class AppConfig():
51 59 list_ignored_providers: Optional[list[str]] = None
52 60 g4f_api_key: Optional[str] = None
@@ -149,37 +157,53 @@ class Api:
149 157 if auth_header and auth_header != "Bearer":
150 158 config.api_key = auth_header
151 159 response = self.client.chat.completions.create(
152 **{
153 **AppConfig.defaults,
154 **config.dict(exclude_none=True),
155 },
156
160 **{
161 **AppConfig.defaults,
162 **config.dict(exclude_none=True),
163 },
157 164 ignored=AppConfig.list_ignored_providers
158 165 )
166 if not config.stream:
167 return JSONResponse((await response).to_json())
168
169 async def streaming():
170 try:
171 async for chunk in response:
172 yield f"data: {json.dumps(chunk.to_json())}\n\n"
173 except GeneratorExit:
174 pass
175 except Exception as e:
176 logging.exception(e)
177 yield f'data: {format_exception(e, config)}\n\n'
178 yield "data: [DONE]\n\n"
179 return StreamingResponse(streaming(), media_type="text/event-stream")
180
159 181 except Exception as e:
160 182 logging.exception(e)
161 183 return Response(content=format_exception(e, config), status_code=500, media_type="application/json")
162 184
163 if not config.stream:
164 return JSONResponse((await response).to_json())
165
166 async def streaming():
167 try:
168 async for chunk in response:
169 yield f"data: {json.dumps(chunk.to_json())}\n\n"
170 except GeneratorExit:
171 pass
172 except Exception as e:
173 logging.exception(e)
174 yield f'data: {format_exception(e, config)}\n\n'
175 yield "data: [DONE]\n\n"
176
177 return StreamingResponse(streaming(), media_type="text/event-stream")
178
179 185 @self.app.post("/v1/completions")
180 186 async def completions():
181 187 return Response(content=json.dumps({'info': 'Not working yet.'}, indent=4), media_type="application/json")
182 188
189 @self.app.post("/v1/images/generations")
190 async def images_generate(config: ImagesGenerateForm, request: Request = None, provider: str = None):
191 try:
192 config.provider = provider if config.provider is None else config.provider
193 if config.api_key is None and request is not None:
194 auth_header = request.headers.get("Authorization")
195 if auth_header is not None:
196 auth_header = auth_header.split(None, 1)[-1]
197 if auth_header and auth_header != "Bearer":
198 config.api_key = auth_header
199 response = self.client.images.generate(
200 **config.dict(exclude_none=True),
201 )
202 return JSONResponse((await response).to_json())
203 except Exception as e:
204 logging.exception(e)
205 return Response(content=format_exception(e, config), status_code=500, media_type="application/json")
206
183 207 def format_exception(e: Exception, config: ChatCompletionsForm) -> str:
184 208 last_provider = g4f.get_last_provider(True)
185 209 return json.dumps({
Modified g4f/client/async_client.py +75 -15
@@ -3,6 +3,9 @@ from __future__ import annotations
3 3 import time
4 4 import random
5 5 import string
6 import asyncio
7 import base64
8 from aiohttp import ClientSession, BaseConnector
6 9
7 10 from .types import Client as BaseClient
8 11 from .types import ProviderType, FinishReason
@@ -11,9 +14,11 @@ from .types import AsyncIterResponse, ImageProvider
11 14 from .image_models import ImageModels
12 15 from .helper import filter_json, find_stop, filter_none, cast_iter_async
13 16 from .service import get_last_provider, get_model_and_provider
17 from ..Provider import ProviderUtils
14 18 from ..typing import Union, Messages, AsyncIterator, ImageType
15 from ..errors import NoImageResponseError
16 from ..image import ImageResponse as ImageProviderResponse
19 from ..errors import NoImageResponseError, ProviderNotFoundError
20 from ..requests.aiohttp import get_connector
21 from ..image import ImageResponse as ImageProviderResponse, ImageDataResponse
17 22
18 23 try:
19 24 anext
@@ -156,12 +161,28 @@ class Chat():
156 161 def __init__(self, client: AsyncClient, provider: ProviderType = None):
157 162 self.completions = Completions(client, provider)
158 163
159 async def iter_image_response(response: AsyncIterator) -> Union[ImagesResponse, None]:
164 async def iter_image_response(
165 response: AsyncIterator,
166 response_format: str = None,
167 connector: BaseConnector = None,
168 proxy: str = None
169 ) -> Union[ImagesResponse, None]:
160 170 async for chunk in response:
161 171 if isinstance(chunk, ImageProviderResponse):
162 return ImagesResponse([Image(image) for image in chunk.get_list()])
172 if response_format == "b64_json":
173 async with ClientSession(
174 connector=get_connector(connector, proxy)
175 ) as session:
176 async def fetch_image(image):
177 async with session.get(image) as response:
178 return base64.b64encode(await response.content.read()).decode()
179 images = await asyncio.gather(*[fetch_image(image) for image in chunk.get_list()])
180 return ImagesResponse([Image(None, image, chunk.alt) for image in images], int(time.time()))
181 return ImagesResponse([Image(image, None, chunk.alt) for image in chunk.get_list()], int(time.time()))
182 elif isinstance(chunk, ImageDataResponse):
183 return ImagesResponse([Image(None, image, chunk.alt) for image in chunk.get_list()], int(time.time()))
163 184
164 def create_image(client: AsyncClient, provider: ProviderType, prompt: str, model: str = "", **kwargs) -> AsyncIterator:
185 def create_image(provider: ProviderType, prompt: str, model: str = "", **kwargs) -> AsyncIterator:
165 186 prompt = f"create a image with: {prompt}"
166 187 if provider.__name__ == "You":
167 188 kwargs["chat_mode"] = "create"
@@ -169,7 +190,6 @@ def create_image(client: AsyncClient, provider: ProviderType, prompt: str, model
169 190 model,
170 191 [{"role": "user", "content": prompt}],
171 192 stream=True,
172 proxy=client.get_proxy(),
173 193 **kwargs
174 194 )
175 195
@@ -179,31 +199,71 @@ class Images():
179 199 self.provider: ImageProvider = provider
180 200 self.models: ImageModels = ImageModels(client)
181 201
182 async def generate(self, prompt, model: str = "", **kwargs) -> ImagesResponse:
183 provider = self.models.get(model, self.provider)
202 def get_provider(self, model: str, provider: ProviderType = None):
203 if isinstance(provider, str):
204 if provider in ProviderUtils.convert:
205 provider = ProviderUtils.convert[provider]
206 else:
207 raise ProviderNotFoundError(f'Provider not found: {provider}')
208 else:
209 provider = self.models.get(model, self.provider)
210 return provider
211
212 async def generate(
213 self,
214 prompt,
215 model: str = "",
216 provider: ProviderType = None,
217 response_format: str = None,
218 connector: BaseConnector = None,
219 proxy: str = None,
220 **kwargs
221 ) -> ImagesResponse:
222 provider = self.get_provider(model, provider)
184 223 if hasattr(provider, "create_async_generator"):
185 response = create_image(self.client, provider, prompt, **kwargs)
224 response = create_image(
225 provider,
226 prompt,
227 **filter_none(
228 response_format=response_format,
229 connector=connector,
230 proxy=self.client.get_proxy() if proxy is None else proxy,
231 ),
232 **kwargs
233 )
186 234 else:
187 235 response = await provider.create_async(prompt)
188 236 return ImagesResponse([Image(image) for image in response.get_list()])
189 image = await iter_image_response(response)
237 image = await iter_image_response(response, response_format, connector, proxy)
190 238 if image is None:
191 239 raise NoImageResponseError()
192 240 return image
193 241
194 async def create_variation(self, image: ImageType, model: str = None, **kwargs):
195 provider = self.models.get(model, self.provider)
242 async def create_variation(
243 self,
244 image: ImageType,
245 model: str = None,
246 response_format: str = None,
247 connector: BaseConnector = None,
248 proxy: str = None,
249 **kwargs
250 ):
251 provider = self.get_provider(model, provider)
196 252 result = None
197 253 if hasattr(provider, "create_async_generator"):
198 254 response = provider.create_async_generator(
199 255 "",
200 256 [{"role": "user", "content": "create a image like this"}],
201 True,
257 stream=True,
202 258 image=image,
203 proxy=self.client.get_proxy(),
259 **filter_none(
260 response_format=response_format,
261 connector=connector,
262 proxy=self.client.get_proxy() if proxy is None else proxy,
263 ),
204 264 **kwargs
205 265 )
206 result = iter_image_response(response)
266 result = iter_image_response(response, response_format, connector, proxy)
207 267 if result is None:
208 268 raise NoImageResponseError()
209 269 return result
Modified g4f/client/service.py +4 -2
@@ -4,7 +4,7 @@ from typing import Union
4 4
5 5 from .. import debug, version
6 6 from ..errors import ProviderNotFoundError, ModelNotFoundError, ProviderNotWorkingError, StreamNotSupportedError
7 from ..models import Model, ModelUtils
7 from ..models import Model, ModelUtils, default
8 8 from ..Provider import ProviderUtils
9 9 from ..providers.types import BaseRetryProvider, ProviderType
10 10 from ..providers.retry_provider import IterProvider
@@ -60,7 +60,9 @@ def get_model_and_provider(model : Union[Model, str],
60 60 model = ModelUtils.convert[model]
61 61
62 62 if not provider:
63 if isinstance(model, str):
63 if not model:
64 model = default
65 elif isinstance(model, str):
64 66 raise ModelNotFoundError(f'Model not found: {model}')
65 67 provider = model.best_provider
66 68
Modified g4f/client/stubs.py +17 -6
@@ -96,13 +96,24 @@ class ChatCompletionDeltaChoice(Model):
96 96 }
97 97
98 98 class Image(Model):
99 url: str
99 def __init__(self, url: str = None, b64_json: str = None, revised_prompt: str = None) -> None:
100 if url is not None:
101 self.url = url
102 if b64_json is not None:
103 self.b64_json = b64_json
104 if revised_prompt is not None:
105 self.revised_prompt = revised_prompt
100 106
101 def __init__(self, url: str) -> None:
102 self.url = url
107 def to_json(self):
108 return self.__dict__
103 109
104 110 class ImagesResponse(Model):
105 data: list[Image]
106
107 def __init__(self, data: list) -> None:
111 def __init__(self, data: list[Image], created: int = 0) -> None:
108 112 self.data = data
113 self.created = created
114
115 def to_json(self):
116 return {
117 **self.__dict__,
118 "data": [image.to_json() for image in self.data]
119 }
Modified g4f/image.py +12 -0
@@ -275,6 +275,18 @@ class ImagePreview(ImageResponse):
275 275 def to_string(self):
276 276 return super().__str__()
277 277
278 class ImageDataResponse():
279 def __init__(
280 self,
281 images: Union[str, list],
282 alt: str,
283 ):
284 self.images = images
285 self.alt = alt
286
287 def get_list(self) -> list[str]:
288 return [self.images] if isinstance(self.images, str) else self.images
289
278 290 class ImageRequest:
279 291 def __init__(
280 292 self,