GithubCopilot.py 3.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. from __future__ import annotations
  2. import json
  3. from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin, BaseConversation
  4. from ...typing import AsyncResult, Messages, Cookies
  5. from ...requests.raise_for_status import raise_for_status
  6. from ...requests import StreamSession
  7. from ...providers.helper import format_prompt
  8. from ...cookies import get_cookies
  9. class Conversation(BaseConversation):
  10. conversation_id: str
  11. def __init__(self, conversation_id: str):
  12. self.conversation_id = conversation_id
  13. class GithubCopilot(AsyncGeneratorProvider, ProviderModelMixin):
  14. url = "https://github.com/copilot"
  15. working = True
  16. needs_auth = True
  17. supports_stream = True
  18. default_model = "gpt-4o"
  19. models = [default_model, "o1-mini", "o1-preview", "claude-3.5-sonnet"]
  20. @classmethod
  21. async def create_async_generator(
  22. cls,
  23. model: str,
  24. messages: Messages,
  25. stream: bool = False,
  26. api_key: str = None,
  27. proxy: str = None,
  28. cookies: Cookies = None,
  29. conversation_id: str = None,
  30. conversation: Conversation = None,
  31. return_conversation: bool = False,
  32. **kwargs
  33. ) -> AsyncResult:
  34. if not model:
  35. model = cls.default_model
  36. if cookies is None:
  37. cookies = get_cookies(".github.com")
  38. async with StreamSession(
  39. proxy=proxy,
  40. impersonate="chrome",
  41. cookies=cookies,
  42. headers={
  43. "GitHub-Verified-Fetch": "true",
  44. }
  45. ) as session:
  46. headers = {}
  47. if api_key is None:
  48. async with session.post("https://github.com/github-copilot/chat/token") as response:
  49. await raise_for_status(response, "Get token")
  50. api_key = (await response.json()).get("token")
  51. headers = {
  52. "Authorization": f"GitHub-Bearer {api_key}",
  53. }
  54. if conversation is not None:
  55. conversation_id = conversation.conversation_id
  56. if conversation_id is None:
  57. async with session.post("https://api.individual.githubcopilot.com/github/chat/threads", headers=headers) as response:
  58. await raise_for_status(response)
  59. conversation_id = (await response.json()).get("thread_id")
  60. if return_conversation:
  61. yield Conversation(conversation_id)
  62. content = messages[-1]["content"]
  63. else:
  64. content = format_prompt(messages)
  65. json_data = {
  66. "content": content,
  67. "intent": "conversation",
  68. "references":[],
  69. "context": [],
  70. "currentURL": f"https://github.com/copilot/c/{conversation_id}",
  71. "streaming": True,
  72. "confirmations": [],
  73. "customInstructions": [],
  74. "model": model,
  75. "mode": "immersive"
  76. }
  77. async with session.post(
  78. f"https://api.individual.githubcopilot.com/github/chat/threads/{conversation_id}/messages",
  79. json=json_data,
  80. headers=headers
  81. ) as response:
  82. async for line in response.iter_lines():
  83. if line.startswith(b"data: "):
  84. data = json.loads(line[6:])
  85. if data.get("type") == "content":
  86. yield data.get("body")