Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Codeium AI autocomplete integration#343

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Open
1egoman wants to merge18 commits intomain
base:main
Choose a base branch
Loading
fromcodeium-integration

Conversation

1egoman
Copy link
Contributor

@1egoman1egoman commentedOct 9, 2024
edited
Loading

Introduces codeium ai autocomplete into the srcbook editor.

Screenshot 2024-10-10 at 3 22 20 PM

A few high level notes:

  • There is no way to enable / disable this feature currently.
  • The other cells are used as context when performing code generation, leading to more contextually relevant suggestions.
  • By default, theEDITOR_API_KEY constant token is used. If a user authenticates with codeium, a token linked to their user account is used instead.

Comment on lines 1 to 85
import protobuf from 'protobufjs';
import Long from 'long';

import languageServerProto from './language-server-proto';

// NOTE: this EDITOR_API_KEY value was just included as a raw string in
// @codeium/react-code-editor. This seems to not be a secret? See here:
// https://github.com/Exafunction/codeium-react-code-editor/blob/768e1b231c00e078c86bc19c8ede697a1e37ec75/src/components/CodeiumEditor/CompletionProvider.ts#L48
const EDITOR_API_KEY = 'd49954eb-cfba-4992-980f-d8fb37f0e942';

// NOTE: The below logic has been adapted from codeium's `@codeium/react-code-editor package. See here:
// https://github.com/Exafunction/codeium-react-code-editor/blob/768e1b231c00e078c86bc19c8ede697a1e37ec75/src/components/CodeiumEditor/CompletionProvider.ts#L147-L159
export async function runCodiumAiAutocomplete(
optionalApiKey: string | null,
source: string,
sourceLanguage: 'javascript' | 'typescript',
cursorOffset: number,
): Promise<CodiumCompletionResult> {
const protos = protobuf.Root.fromJSON(languageServerProto as protobuf.INamespace);
const GetCompletionsRequest = protos.lookupType('exa.language_server_pb.GetCompletionsRequest');
const Metadata = protos.lookupType('exa.codeium_common_pb.Metadata');
const DocumentInfo = protos.lookupType('exa.language_server_pb.Document');
const EditorOptions = protos.lookupType('exa.codeium_common_pb.EditorOptions');
const Language = protos.lookupEnum('exa.codeium_common_pb.Language');
const GetCompletionsResponse = protos.lookupType('exa.language_server_pb.GetCompletionsResponse');

const sessionId = `react-editor-${crypto.randomUUID()}`;
const apiKey = optionalApiKey ?? EDITOR_API_KEY;

const payload = {
otherDocuments: [],
metadata: Metadata.create({
ideName: 'web',
extensionVersion: '1.0.12',
apiKey,
ideVersion: 'unknown',
extensionName: '@codeium/react-code-editor',
sessionId,
}),
document: DocumentInfo.create({
text: source,
editorLanguage: sourceLanguage,
language: Language.getOption(sourceLanguage === 'javascript' ? 'JAVASCRIPT' : 'TYPESCRIPT'),
cursorOffset: Long.fromValue(cursorOffset),
lineEnding: '\n',
}),
editorOptions: EditorOptions.create({
tabSize: Long.fromValue(4),
insertSpaces: true,
}),
};

const requestData = GetCompletionsRequest.create(payload);
const buffer = GetCompletionsRequest.encode(requestData).finish();

const response = await fetch(
'https://web-backend.codeium.com/exa.language_server_pb.LanguageServerService/GetCompletions',
{
method: 'POST',
body: buffer,
headers: {
'Connect-Protocol-Version': '1',
'Content-Type': 'application/proto',
Authorization: `Basic ${apiKey}-${sessionId}`,
},
},
);

const responseBodyBytes = new Uint8Array(await response.arrayBuffer());
const responseBody = GetCompletionsResponse.decode(responseBodyBytes);

return responseBody.toJSON() as CodiumCompletionResult;
}
Copy link
ContributorAuthor

@1egoman1egomanOct 10, 2024
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Here's what the logic to kick off the codeium autocomplete request looks like. While complex, the protobuf serialization / deserialization logic is unfortunately required the query codeium.

Comment on lines +1 to +10
// NOTE: to generate a new set of json definitions, run:
// npm run generate-codeium-proto-json

// Copyright Exafunction, Inc.

syntax = "proto3";

package exa.language_server_pb;

import "codeium_common.proto";
Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Here's the underlying protobuf definitions that are used to generate the codeium autocomplete request. These came from here:https://github.com/Exafunction/codeium-react-code-editor/blob/main/exa/language_server_pb/language_server.proto.

Note that to avoid including this raw text in the bundle that is sent to the client, I converted the protobuf file to json descriptiors (more about these here). These are a fair bit smaller and work just as well.

Another option I considered but ultimately decided against given the complexity of introducing was an alternate protobuf library that does code generation at build time, likebuf. If protobufs were going to be used everywhere and definitions were going to be changing often, I think it would be a lot of sense, but given this is only used here that seemed like overkill.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

@1egoman are we okay with the copyright on this?

Comment on lines +216 to +289

<h3 className="text-md pb-2">Codeium AI Autocomplete</h3>
<div className="flex flex-col">
<div className="opacity-70 text-sm pb-1">
By default, Codeium uses a public api token with limited capabilities. Optionally,
sign in to remove rate limits:
</div>

{codeiumApiKey ? (
<div
className="flex flex-col p-3 border rounded-sm"
onMouseEnter={() => setCodeiumApiKeyHovering(true)}
onMouseLeave={() => setCodeiumApiKeyHovering(false)}
>
<div className="opacity-70 text-sm pb-2">Signed in! Codeium API Key:</div>
<div className="flex justify-between items-center gap-2">
<div className="text-left align-middle relative w-full">
<Input
name="codeiumApiKey"
type={codeiumApiKeyVisible ? 'text' : 'password'}
value={codeiumApiKey}
disabled
className="disabled:opacity-100 disabled:cursor-text group-hover:border-border group-focus-within:border-border pr-8"
/>
{codeiumApiKeyVisible ? (
<EyeOffIcon
size={14}
className={cn(
'absolute right-3 top-2.5 cursor-pointer opacity-80 bg-background',
!codeiumApiKeyHovering && 'hidden',
)}
onClick={() => setCodeiumApiKeyVisible(false)}
/>
) : (
<EyeIcon
size={14}
className={cn(
'absolute right-3 top-2.5 cursor-pointer opacity-80 bg-background',
!codeiumApiKeyHovering && 'hidden',
)}
onClick={() => setCodeiumApiKeyVisible(true)}
/>
)}
</div>

<Button
variant="secondary"
onClick={() => {
updateConfigContext({ codeiumApiKey: null })
.then(() => {
toast.success('Detached Codeium API key.');
})
.catch((err) => {
console.error('Error detaching Codeium API key:', err);
toast.error('Error detaching Codeium API key!');
});
}}
>
Detach
</Button>
</div>
</div>
) : (
<div className="flex justify-center items-center p-3 h-[64px] border rounded-sm">
<Button asChild variant="secondary">
<Link
to={`https://www.codeium.com/profile?response_type=token&redirect_uri=${codeiumCallbackUrl}&state=a&scope=openid%20profile%20email&redirect_parameters_type=query`}
>
Authenticate with Codeium
</Link>
</Button>
</div>
)}
</div>
Copy link
ContributorAuthor

@1egoman1egomanOct 10, 2024
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I added a new interface element to the settings page to facilitate a user authenticating with codeium:

Screenshot 2024-10-10 at 3 07 50 PM

Note that even if a user does not sign in, the autocomplete logic still works, because it falls back to thatEDITOR_API_KEY value.

versecafe and deeeeone reacted with thumbs up emoji
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Nit: Could add a link to their sign in page

@@ -10,23 +10,28 @@
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"format": "prettier --write .",
"preview": "vite preview",
"check-types": "tsc"
"check-types": "tsc",
"generate-codeium-proto-json": "node --eval 'console.log(\"export default\", JSON.stringify(require(\"protobufjs\").loadSync(\"src/lib/ai-autocomplete/language_server.proto\").toJSON(), null, 2));' > src/lib/ai-autocomplete/languageServerProto.ts"
Copy link
ContributorAuthor

@1egoman1egomanOct 10, 2024
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I added this npm task to take thelanguage_server.proto file and convert it into the json descriptors data that is required by the client.

Note that currently this generated json descriptors file is committed. I could potentially set this up to generate fresh when starting up the dev server / building for release, but as this in practice will rarely / more likely never change, it seemed like setting up more complex build infrastructure here would be less useful than it otherwise would be.

@1egoman1egoman marked this pull request as ready for reviewOctober 10, 2024 19:51
As part of this, I had to build the protobuf file into a set of "jsondefinitions" that I could more easily include in a javascript bundle vsthe raw bytes of a proto file.
@1egoman1egoman changed the titleCodeium AI authcomplete integrationCodeium AI autocomplete integrationOct 11, 2024
Copy link
Contributor

@benjreinhartbenjreinhart left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Nit: I would prefer the if the protos we're in their own folder named something like "generated"

@@ -0,0 +1,166 @@
// Copyright Exafunction, Inc.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

@1egoman do you know if we're able to pull this in given the copyright?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

1egoman reacted with thumbs up emoji
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@nichocharnichocharnichochar left review comments

@versecafeversecafeversecafe left review comments

@benjreinhartbenjreinhartbenjreinhart approved these changes

Assignees
No one assigned
Labels
None yet
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

4 participants
@1egoman@benjreinhart@nichochar@versecafe

[8]ページ先頭

©2009-2025 Movatter.jp