Skip to content

Commit

Permalink
Move to lazyvim
Browse files Browse the repository at this point in the history
  • Loading branch information
tdickman committed Aug 29, 2024
1 parent 7210770 commit 847ff27
Show file tree
Hide file tree
Showing 24 changed files with 1,241 additions and 4 deletions.
5 changes: 5 additions & 0 deletions nvim-old/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# CopilotChat

Had to install manually for some reason. Followed guide in
[here](https://github.com/jellydn/CopilotChat.nvim) instead of using Plug.
Otherwise I installed it, and the command didn't seem to exist.
File renamed without changes.
File renamed without changes.
196 changes: 196 additions & 0 deletions nvim-old/rplugin/python3/copilot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
import requests
import dotenv
import os
import uuid
import time
import json

from prompt_toolkit import PromptSession
from prompt_toolkit.history import InMemoryHistory
import utilities
import typings
import prompts
from typing import List, Dict

LOGIN_HEADERS = {
"accept": "application/json",
"content-type": "application/json",
"editor-version": "Neovim/0.9.2",
"editor-plugin-version": "copilot.lua/1.11.4",
"user-agent": "GithubCopilot/1.133.0",
}


class Copilot:
def __init__(self, token: str = None):
if token is None:
token = utilities.get_cached_token()
self.github_token = token
self.token: Dict[str, any] = None
self.chat_history: List[typings.Message] = []
self.vscode_sessionid: str = None
self.machineid = utilities.random_hex()

self.session = requests.Session()

def request_auth(self):
url = "https://github.com/login/device/code"

response = self.session.post(
url,
headers=LOGIN_HEADERS,
data=json.dumps(
{"client_id": "Iv1.b507a08c87ecfe98", "scope": "read:user"}
),
).json()
return response

def poll_auth(self, device_code: str) -> bool:
url = "https://github.com/login/oauth/access_token"

response = self.session.post(
url,
headers=LOGIN_HEADERS,
data=json.dumps(
{
"client_id": "Iv1.b507a08c87ecfe98",
"device_code": device_code,
"grant_type": "urn:ietf:params:oauth:grant-type:device_code",
}
),
).json()
if "access_token" in response:
access_token, token_type = response["access_token"], response["token_type"]
url = "https://api.github.com/user"
headers = {
"authorization": f"{token_type} {access_token}",
"user-agent": "GithubCopilot/1.133.0",
"accept": "application/json",
}
response = self.session.get(url, headers=headers).json()
utilities.cache_token(response["login"], access_token)
self.github_token = access_token
return True
return False

def authenticate(self):
if self.github_token is None:
raise Exception("No token found")
self.vscode_sessionid = str(uuid.uuid4()) + str(round(time.time() * 1000))
url = "https://api.github.com/copilot_internal/v2/token"
headers = {
"authorization": f"token {self.github_token}",
"editor-version": "vscode/1.85.1",
"editor-plugin-version": "copilot-chat/0.12.2023120701",
"user-agent": "GitHubCopilotChat/0.12.2023120701",
}

self.token = self.session.get(url, headers=headers).json()

def ask(self, prompt: str, code: str, language: str = ""):
# If expired, reauthenticate
if self.token.get("expires_at") <= round(time.time()):
self.authenticate()
url = "https://api.githubcopilot.com/chat/completions"
self.chat_history.append(typings.Message(prompt, "user"))
system_prompt = prompts.COPILOT_INSTRUCTIONS
if prompt == prompts.FIX_SHORTCUT:
system_prompt = prompts.COPILOT_FIX
elif prompt == prompts.TEST_SHORTCUT:
system_prompt = prompts.COPILOT_TESTS
elif prompt == prompts.EXPLAIN_SHORTCUT:
system_prompt = prompts.COPILOT_EXPLAIN
data = utilities.generate_request(
self.chat_history, code, language, system_prompt=system_prompt
)

full_response = ""

response = self.session.post(
url, headers=self._headers(), json=data, stream=True
)
for line in response.iter_lines():
line = line.decode("utf-8").replace("data: ", "").strip()
if line.startswith("[DONE]"):
break
elif line == "":
continue
try:
line = json.loads(line)
if "choices" not in line:
print("Error:", line)
raise Exception(f"No choices on {line}")
if len(line["choices"]) == 0:
continue
content = line["choices"][0]["delta"]["content"]
if content is None:
continue
full_response += content
yield content
except json.decoder.JSONDecodeError:
print("Error:", line)
continue

self.chat_history.append(typings.Message(full_response, "system"))

def _get_embeddings(self, inputs: list[typings.FileExtract]):
embeddings = []
url = "https://api.githubcopilot.com/embeddings"
# If we have more than 18 files, we need to split them into multiple requests
for i in range(0, len(inputs), 18):
if i + 18 > len(inputs):
data = utilities.generate_embedding_request(inputs[i:])
else:
data = utilities.generate_embedding_request(inputs[i: i + 18])
response = self.session.post(url, headers=self._headers(), json=data).json()
if "data" not in response:
raise Exception(f"Error fetching embeddings: {response}")
for embedding in response["data"]:
embeddings.append(embedding["embedding"])
return embeddings

def _headers(self):
return {
"authorization": f"Bearer {self.token['token']}",
"x-request-id": str(uuid.uuid4()),
"vscode-sessionid": self.vscode_sessionid,
"machineid": self.machineid,
"editor-version": "vscode/1.85.1",
"editor-plugin-version": "copilot-chat/0.12.2023120701",
"openai-organization": "github-copilot",
"openai-intent": "conversation-panel",
"content-type": "application/json",
"user-agent": "GitHubCopilotChat/0.12.2023120701",
}


def get_input(session: PromptSession, text: str = ""):
print(text, end="", flush=True)
return session.prompt(multiline=True)


def main():
dotenv.load_dotenv()
token = os.getenv("COPILOT_TOKEN")
copilot = Copilot(token)
if copilot.github_token is None:
req = copilot.request_auth()
print("Please visit", req["verification_uri"], "and enter", req["user_code"])
while not copilot.poll_auth(req["device_code"]):
time.sleep(req["interval"])
print("Successfully authenticated")
copilot.authenticate()
session = PromptSession(history=InMemoryHistory())
while True:
user_prompt = get_input(session, "\n\nPrompt: \n")
if user_prompt == "!exit":
break
code = get_input(session, "\n\nCode: \n")

print("\n\nAI Response:")
for response in copilot.ask(user_prompt, code):
print(response, end="", flush=True)


if __name__ == "__main__":
main()
89 changes: 89 additions & 0 deletions nvim-old/rplugin/python3/plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import os
import time

import copilot
import prompts
import dotenv
import pynvim

dotenv.load_dotenv()


@pynvim.plugin
class CopilotChatPlugin(object):
def __init__(self, nvim: pynvim.Nvim):
self.nvim = nvim
self.copilot = copilot.Copilot(os.getenv("COPILOT_TOKEN"))
if self.copilot.github_token is None:
req = self.copilot.request_auth()
self.nvim.out_write(
f"Please visit {req['verification_uri']} and enter the code {req['user_code']}\n"
)
current_time = time.time()
wait_until = current_time + req["expires_in"]
while self.copilot.github_token is None:
self.copilot.poll_auth(req["device_code"])
time.sleep(req["interval"])
if time.time() > wait_until:
self.nvim.out_write("Timed out waiting for authentication\n")
return
self.nvim.out_write("Successfully authenticated with Copilot\n")
self.copilot.authenticate()

@pynvim.command("CopilotChat", nargs="1")
def copilotChat(self, args: list[str]):
if self.copilot.github_token is None:
self.nvim.out_write("Please authenticate with Copilot first\n")
return
prompt = " ".join(args)

if prompt == "/fix":
prompt = prompts.FIX_SHORTCUT
elif prompt == "/test":
prompt = prompts.TEST_SHORTCUT
elif prompt == "/explain":
prompt = prompts.EXPLAIN_SHORTCUT

# Get code from the unnamed register
code = self.nvim.eval("getreg('\"')")
file_type = self.nvim.eval("expand('%')").split(".")[-1]
# Check if we're already in a chat buffer
if self.nvim.eval("getbufvar(bufnr(), '&buftype')") != "nofile":
# Create a new scratch buffer to hold the chat
self.nvim.command("enew")
self.nvim.command("setlocal buftype=nofile bufhidden=hide noswapfile")
# Set filetype as markdown and wrap with linebreaks
self.nvim.command("setlocal filetype=markdown wrap linebreak")

# Get the current buffer
buf = self.nvim.current.buffer
self.nvim.api.buf_set_option(buf, "fileencoding", "utf-8")

# Add start separator
start_separator = f"""### User
{prompt}
### Copilot
"""
buf.append(start_separator.split("\n"), -1)

# Add chat messages
for token in self.copilot.ask(prompt, code, language=file_type):
buffer_lines = self.nvim.api.buf_get_lines(buf, 0, -1, 0)
last_line_row = len(buffer_lines) - 1
last_line = buffer_lines[-1]
last_line_col = len(last_line.encode('utf-8'))

self.nvim.api.buf_set_text(
buf,
last_line_row,
last_line_col,
last_line_row,
last_line_col,
token.split("\n"),
)

# Add end separator
end_separator = "\n---\n"
buf.append(end_separator.split("\n"), -1)
Loading

0 comments on commit 847ff27

Please sign in to comment.