Aider-AI / aider

aider is AI pair programming in your terminal
https://aider.chat/
Apache License 2.0
21.88k stars 2.03k forks source link

Feature: Add GitHub Copilot as model provider #2227

Open Jasmin68k opened 1 week ago

Jasmin68k commented 1 week ago

Issue

Hello!

Please add GitHub Copilot as model provider.

Should be possible like this: https://github.com/olimorris/codecompanion.nvim/blob/5c5a5c759b8c925e81f8584a0279eefc8a6c6643/lua/codecompanion/adapters/copilot.lua

Idea taken from: https://github.com/cline/cline/discussions/660

Thank you!

Version and model info

No response

dimfeld commented 1 week ago

Seems like it's a standard OpenAI-style API, with an token that is refreshed periodically using a long-lived refrehs token, OAuth style.

The tricky part seems to be actually getting the refresh token in the first place. All the Neovim plugins rely on authenticating through copilot.vim or copilot.lua, which use Github's closed-source Javascript language server. Then once the initial auth step is done, they copy the refresh token from the location in the filesystem where it gets saved and go from there.

The good news is that the refresh token doesn't really seem to ever expire. My token at ~/.config/github-copilot/hosts.json is two years old and still works even though my Copilot subscription lapsed for a time in between.

If we're ok with having Aider pull its refresh token in a similar way, then this should be pretty easy to implement, but the initial setup for the user may be a bit of a hassle.

For additional reference, here's Avante.nvim's implementation which is pretty similar to the one in CodeCompanion.

Jasmin68k commented 1 week ago

Great, that doesn't look too complicated, thanks.

Haven't looked into it, is the JS language server the only way to get the token?

Anything we can do with these: https://github.com/settings/tokens?

RodolfoCastanheira commented 1 week ago

Haven't looked into it, is the JS language server the only way to get the token?

This code can get the token from github api. Note that it register itself as copilot.vim, as I don't know if copilot allows for other third part clients.

 resp = requests.post('https://github.com/login/device/code', headers={
'accept': 'application/json',
'editor-version': 'Neovim/0.6.1',
'editor-plugin-version': 'copilot.vim/1.16.0',
'content-type': 'application/json',
'user-agent': 'GithubCopilot/1.155.0',
'accept-encoding': 'gzip,deflate,br'
}, data='{"client_id":"Iv1.b507a08c87ecfe98","scope":"read:user"}')

# Parse the response json, isolating the device_code, user_code, and verification_uri
resp_json = resp.json()
device_code = resp_json.get('device_code')
user_code = resp_json.get('user_code')
verification_uri = resp_json.get('verification_uri')

# Print the user code and verification uri
print(f'Please visit {verification_uri} and enter code {user_code} to authenticate.')

while True:
    time.sleep(5)

    resp = requests.post('https://github.com/login/oauth/access_token', headers={
        'accept': 'application/json',
        'editor-version': 'Neovim/0.6.1',
        'editor-plugin-version': 'copilot.vim/1.16.0',
        'content-type': 'application/json',
        'user-agent': 'GithubCopilot/1.155.0',
        'accept-encoding': 'gzip,deflate,br'
        }, data=f'{{"client_id":"Iv1.b507a08c87ecfe98","device_code":"{device_code}","grant_type":"urn:ietf:params:oauth:grant-type:device_code"}}')

    # Parse the response json, isolating the access_token
    resp_json = resp.json()
    access_token = resp_json.get('access_token')

    if access_token:
        break

print('Authentication success: '+access_token)
paul-gauthier commented 1 week ago

Thanks for trying aider and filing this issue.

This sounds like it would require using a undocumented API or abusing a documented API for an off-label purpose?

Jasmin68k commented 1 week ago

@RodolfoCastanheira Great, that's looks like a simple solution. We should have all the pieces then?

@paul-gauthier I don't know, whether it's undocumented or would be off-label. I mainly forwarded the idea from Cline (see op) and also made the same suggestion to LiteLLM (see mention above).

dimfeld commented 1 week ago

I think there's a decent argument to be made that it's acceptable, if not officially supported.

The main difference between the extensions and what Aider (and the other existing, unofficial integrations) would do is around the auth token management. For extensions the tokens are automatically managed for you and the short-lived access token is passed to each call to the extension code. But in both cases, it's using an OAuth style flow that authenticates as your Github user, and that token stops working if you stop paying.

I can understand the worry for a high-profile project like Aider doing an integration like this. Hopefully this helps clear it up some.

paul-gauthier commented 1 week ago

Aider relies on litellm for integrations to LLM API providers, so I think it would be best to focus on getting it implemented there.

That said, it seems unclear that Copilot would sanction this sort of use of their API. So I'm not sure it would be appropriate for litellm (or aider) to add this sort of support.

I'm happy to be shown something that would clarify that this would be a legitimate use of the copilot API.

RodolfoCastanheira commented 1 week ago

I've checked the usage policy[1] and the official forum, but couldn't find anything that says third-party integrations are either allowed or forbidden. When someone asks for help with an unofficial integration, the GitHub staff just lets the community respond (they don't jump in and say "no, you can't do that" or "we don't support that").

It seems like personal use for coding is okay, as long as you're not getting out of hand. And it looks like these unofficial integrations are tolerated, even if this usage is not officially endorsed. The silence on the matter seems like a deliberate choice.

[1] https://docs.github.com/en/site-policy/github-terms/github-terms-for-additional-products-and-features#github-copilot

gsoul commented 4 days ago

Here's the official documentation on how agents should use their API: https://docs.github.com/en/copilot/building-copilot-extensions/building-a-copilot-agent-for-your-copilot-extension/using-copilots-llm-for-your-agent

I think it's quite a strong sign that they allow such usage.