Skip to content

Commit

Permalink
Merge pull request xtekky#2378 from hlohaus/info
Browse files Browse the repository at this point in the history
Add image upload to Copilot provider
  • Loading branch information
hlohaus authored Nov 19, 2024
2 parents b937115 + c310316 commit 23b3458
Show file tree
Hide file tree
Showing 8 changed files with 199 additions and 165 deletions.
41 changes: 29 additions & 12 deletions g4f/Provider/Copilot.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@

from .base_provider import AbstractProvider, BaseConversation
from .helper import format_prompt
from ..typing import CreateResult, Messages
from ..typing import CreateResult, Messages, ImageType
from ..errors import MissingRequirementsError
from ..requests.raise_for_status import raise_for_status
from ..image import to_bytes, is_accepted_format
from .. import debug

class Conversation(BaseConversation):
Expand All @@ -43,6 +44,7 @@ class Copilot(AbstractProvider):
url = "https://copilot.microsoft.com"
working = True
supports_stream = True
default_model = "Copilot"

websocket_url = "wss://copilot.microsoft.com/c/api/chat?api-version=2"
conversation_url = f"{url}/c/api/conversations"
Expand All @@ -55,6 +57,7 @@ def create_completion(
stream: bool = False,
proxy: str = None,
timeout: int = 900,
image: ImageType = None,
conversation: Conversation = None,
return_conversation: bool = False,
**kwargs
Expand All @@ -66,7 +69,7 @@ def create_completion(
access_token = None
headers = None
cookies = conversation.cookie_jar if conversation is not None else None
if cls.needs_auth:
if cls.needs_auth or image is not None:
if conversation is None or conversation.access_token is None:
access_token, cookies = asyncio.run(cls.get_access_token_and_cookies(proxy))
else:
Expand Down Expand Up @@ -98,42 +101,57 @@ def create_completion(
if debug.logging:
print(f"Copilot: Use conversation: {conversation_id}")

images = []
if image is not None:
data = to_bytes(image)
response = session.post(
"https://copilot.microsoft.com/c/api/attachments",
headers={"content-type": is_accepted_format(data)},
data=data
)
raise_for_status(response)
images.append({"type":"image", "url": response.json().get("url")})

wss = session.ws_connect(cls.websocket_url)
wss.send(json.dumps({
"event": "send",
"conversationId": conversation_id,
"content": [{
"content": [*images, {
"type": "text",
"text": prompt,
}],
"mode": "chat"
}).encode(), CurlWsFlag.TEXT)

is_started = False
msg = None
while True:
try:
msg = json.loads(wss.recv()[0])
msg = wss.recv()[0]
msg = json.loads(msg)
except:
break
if msg.get("event") == "appendText":
yield msg.get("text")
elif msg.get("event") in ["done", "partCompleted"]:
break
if not is_started:
raise RuntimeError(f"Last message: {msg}")

@classmethod
async def get_access_token_and_cookies(cls, proxy: str = None):
if not has_nodriver:
raise MissingRequirementsError('Install "nodriver" package | pip install -U nodriver')
if has_platformdirs:
user_data_dir = user_config_dir("g4f-nodriver")
else:
user_data_dir = None
user_data_dir = user_config_dir("g4f-nodriver") if has_platformdirs else None
if debug.logging:
print(f"Copilot: Open nodriver with user_dir: {user_data_dir}")
browser = await nodriver.start(
user_data_dir=user_data_dir,
browser_args=None if proxy is None else [f"--proxy-server={proxy}"],
)
page = await browser.get(cls.url)
while True:
access_token = None
while access_token is None:
access_token = await page.evaluate("""
(() => {
for (var i = 0; i < localStorage.length; i++) {
Expand All @@ -146,9 +164,8 @@ async def get_access_token_and_cookies(cls, proxy: str = None):
}
})()
""")
if access_token:
break
asyncio.sleep(1)
if access_token is None:
asyncio.sleep(1)
cookies = {}
for c in await page.send(nodriver.cdp.network.get_cookies([cls.url])):
cookies[c.name] = c.value
Expand Down
11 changes: 8 additions & 3 deletions g4f/Provider/airforce/AirforceChat.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import requests
from aiohttp import ClientSession
from typing import List
import logging

from ...typing import AsyncResult, Messages
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
Expand Down Expand Up @@ -54,9 +55,13 @@ class AirforceChat(AsyncGeneratorProvider, ProviderModelMixin):
@classmethod
def get_models(cls) -> list:
if not cls.models:
response = requests.get('https://api.airforce/models')
data = response.json()
cls.models = [model['id'] for model in data['data']]
try:
response = requests.get('https://api.airforce/models', verify=False)
data = response.json()
cls.models = [model['id'] for model in data['data']]
except Exception as e:
logging.exception(e)
cls.models = [cls.default_model]

model_aliases = {
# openchat
Expand Down
32 changes: 15 additions & 17 deletions g4f/Provider/airforce/AirforceImage.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,37 @@
from urllib.parse import urlencode
import random
import requests
import logging

from ...typing import AsyncResult, Messages
from ...image import ImageResponse
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin


class AirforceImage(AsyncGeneratorProvider, ProviderModelMixin):
label = "Airforce Image"
#url = "https://api.airforce"
url = "https://api.airforce"
api_endpoint = "https://api.airforce/imagine2"
#working = True
working = False

default_model = 'flux'

response = requests.get('https://api.airforce/imagine/models')
data = response.json()

image_models = data

models = [*image_models, "stable-diffusion-xl-base", "stable-diffusion-xl-lightning", "Flux-1.1-Pro"]

additional_models = ["stable-diffusion-xl-base", "stable-diffusion-xl-lightning", "Flux-1.1-Pro"]
model_aliases = {
"sdxl": "stable-diffusion-xl-base",
"sdxl": "stable-diffusion-xl-lightning",
"flux-pro": "Flux-1.1-Pro",
}

@classmethod
def get_model(cls, model: str) -> str:
if model in cls.models:
return model
else:
return cls.default_model
def get_models(cls) -> list:
if not cls.models:
try:
response = requests.get('https://api.airforce/imagine/models', verify=False)
response.raise_for_status()
cls.models = [*response.json(), *cls.additional_models]
except Exception as e:
logging.exception(e)
cls.models = [cls.default_model]
return cls.models

@classmethod
async def create_async_generator(
Expand Down
3 changes: 2 additions & 1 deletion g4f/Provider/needs_auth/CopilotAccount.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
class CopilotAccount(Copilot):
needs_auth = True
parent = "Copilot"
default_model = ""
default_model = "Copilot"
default_vision_model = default_model
2 changes: 1 addition & 1 deletion g4f/Provider/needs_auth/OpenaiChat.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ async def create_async_generator(
f"{cls.url}/backend-anon/sentinel/chat-requirements"
if cls._api_key is None else
f"{cls.url}/backend-api/sentinel/chat-requirements",
json={"p": get_requirements_token(RequestConfig.proof_token)},
json={"p": get_requirements_token(RequestConfig.proof_token) if RequestConfig.proof_token else None},
headers=cls._headers
) as response:
cls._update_request_args(session)
Expand Down
9 changes: 4 additions & 5 deletions g4f/gui/client/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,12 @@
</button>
<div class="info">
<i class="fa-brands fa-discord"></i>
<span class="convo-title">discord ~ <a href="https://discord.gghttps://discord.gg/6yrm7H4B">discord.gg/6yrm7H4B</a>
<span class="convo-title">discord ~ <a href="https://discord.gg/6yrm7H4B" target="_blank">discord.gg/6yrm7H4B</a>
</span>
</div>
<div class="info">
<i class="fa-brands fa-github"></i>
<span class="convo-title">github ~ <a href="https://github.com/xtekky/gpt4free">@xtekky/gpt4free</a>
<span class="convo-title">github ~ <a href="https://github.com/xtekky/gpt4free" target="_blank">@xtekky/gpt4free</a>
</span>
</div>
<div class="info">
Expand All @@ -87,7 +87,6 @@
</div>
</div>
<div class="images hidden">

</div>
<div class="settings hidden">
<div class="paper">
Expand Down Expand Up @@ -192,7 +191,7 @@ <h3>Settings</h3>
<i class="fa-regular fa-stop"></i>
</button>
</div>
<div class="regenerate regenerate-hidden">
<div class="regenerate">
<button id="regenerateButton">
<span>Regenerate</span>
<i class="fa-solid fa-rotate"></i>
Expand Down Expand Up @@ -263,4 +262,4 @@ <h3>Settings</h3>
<i class="fa-solid fa-bars"></i>
</div>
</body>
</html>
</html>
32 changes: 17 additions & 15 deletions g4f/gui/client/static/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -472,21 +472,18 @@ body {
padding: 0 4px;
}

.stop_generating-hidden, .regenerate-hidden {
animation: hide_popup 0.4s;
display: none;
}

.stop_generating, .toolbar .regenerate {
position: absolute;
z-index: 1000000;
top: 0;
right: 0;
}

@media only screen and (min-width: 40em) {
.stop_generating, .toolbar .regenerate {
left: 50%;
transform: translateX(-50%);
right: auto;
}
}

.stop_generating button, .toolbar .regenerate button{
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
Expand All @@ -503,6 +500,17 @@ body {
animation: show_popup 0.4s;
}

@media only screen and (min-width: 40em) {
.stop_generating {
left: 50%;
transform: translateX(-50%);
right: auto;
}
.toolbar .regenerate {
right: 5px;
}
}

.toolbar .hide-input {
background: transparent;
border: none;
Expand All @@ -524,11 +532,6 @@ body {
}
}

.stop_generating-hidden #cancelButton, .regenerate-hidden #regenerateButton {
animation: hide_popup 0.4s;
display: none;
}

.typing {
position: absolute;
top: -25px;
Expand Down Expand Up @@ -779,8 +782,7 @@ select {
overflow: auto;
}


#cursor {
.cursor {
line-height: 17px;
margin-left: 3px;
-webkit-animation: blink 0.8s infinite;
Expand Down
Loading

0 comments on commit 23b3458

Please sign in to comment.