|
from __future__ import annotations |
|
|
|
import json |
|
|
|
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin, BaseConversation |
|
from ...typing import AsyncResult, Messages, Cookies |
|
from ...requests.raise_for_status import raise_for_status |
|
from ...requests import StreamSession |
|
from ...providers.helper import format_prompt |
|
from ...cookies import get_cookies |
|
|
|
class Conversation(BaseConversation): |
|
conversation_id: str |
|
|
|
def __init__(self, conversation_id: str): |
|
self.conversation_id = conversation_id |
|
|
|
class GithubCopilot(AsyncGeneratorProvider, ProviderModelMixin): |
|
url = "https://copilot.microsoft.com" |
|
working = True |
|
needs_auth = True |
|
supports_stream = True |
|
default_model = "gpt-4o" |
|
models = [default_model, "o1-mini", "o1-preview", "claude-3.5-sonnet"] |
|
|
|
@classmethod |
|
async def create_async_generator( |
|
cls, |
|
model: str, |
|
messages: Messages, |
|
stream: bool = False, |
|
api_key: str = None, |
|
proxy: str = None, |
|
cookies: Cookies = None, |
|
conversation_id: str = None, |
|
conversation: Conversation = None, |
|
return_conversation: bool = False, |
|
**kwargs |
|
) -> AsyncResult: |
|
if not model: |
|
model = cls.default_model |
|
if cookies is None: |
|
cookies = get_cookies(".github.com") |
|
async with StreamSession( |
|
proxy=proxy, |
|
impersonate="chrome", |
|
cookies=cookies, |
|
headers={ |
|
"GitHub-Verified-Fetch": "true", |
|
} |
|
) as session: |
|
headers = {} |
|
if api_key is None: |
|
async with session.post("https://github.com/github-copilot/chat/token") as response: |
|
await raise_for_status(response, "Get token") |
|
api_key = (await response.json()).get("token") |
|
headers = { |
|
"Authorization": f"GitHub-Bearer {api_key}", |
|
} |
|
if conversation is not None: |
|
conversation_id = conversation.conversation_id |
|
if conversation_id is None: |
|
print(headers) |
|
async with session.post("https://api.individual.githubcopilot.com/github/chat/threads", headers=headers) as response: |
|
await raise_for_status(response) |
|
conversation_id = (await response.json()).get("thread_id") |
|
if return_conversation: |
|
yield Conversation(conversation_id) |
|
content = messages[-1]["content"] |
|
else: |
|
content = format_prompt(messages) |
|
json_data = { |
|
"content": content, |
|
"intent": "conversation", |
|
"references":[], |
|
"context": [], |
|
"currentURL": f"https://github.com/copilot/c/{conversation_id}", |
|
"streaming": True, |
|
"confirmations": [], |
|
"customInstructions": [], |
|
"model": model, |
|
"mode": "immersive" |
|
} |
|
async with session.post( |
|
f"https://api.individual.githubcopilot.com/github/chat/threads/{conversation_id}/messages", |
|
json=json_data, |
|
headers=headers |
|
) as response: |
|
async for line in response.iter_lines(): |
|
if line.startswith(b"data: "): |
|
data = json.loads(line[6:]) |
|
if data.get("type") == "content": |
|
yield data.get("body") |