Skip to content
This repository has been archived by the owner on Sep 30, 2024. It is now read-only.

Commit

Permalink
add basic completions
Browse files Browse the repository at this point in the history
  • Loading branch information
beyang committed Mar 22, 2023
1 parent 9ea2598 commit 6d1f531
Show file tree
Hide file tree
Showing 5 changed files with 448 additions and 0 deletions.
12 changes: 12 additions & 0 deletions client/cody/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@
{
"command": "cody.delete-access-token",
"title": "Cody: Delete Access Token"
},
{
"command": "cody.experimental.suggest",
"title": "Ask Cody: View Suggestions"
}
],
"keybindings": [
Expand All @@ -109,6 +113,11 @@
"key": "ctrl+alt+c",
"mac": "ctrl+alt+c",
"when": "cody.activated"
},
{
"command": "cody.experimental.suggest",
"key": "alt+\\",
"when": "sourcegraph.cody.activated"
}
],
"submenus": [
Expand Down Expand Up @@ -212,6 +221,9 @@
"blended"
],
"default": "embeddings"
},
"cody.keys.openai": {
"type": "string"
}
}
}
Expand Down
22 changes: 22 additions & 0 deletions client/cody/src/command/CommandsProvider.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import * as openai from 'openai'
import * as vscode from 'vscode'

import { ChatViewProvider } from '../chat/ChatViewProvider'
import { CodyCompletionItemProvider } from '../completions'
import { CompletionsDocumentProvider } from '../completions/docprovider'
import { getConfiguration } from '../configuration'
import { ExtensionApi } from '../extension-api'

Expand Down Expand Up @@ -96,6 +99,25 @@ export const CommandsProvider = async (context: vscode.ExtensionContext): Promis
)
)

if (config.experimentalSuggest && config.openaiKey) {
const configuration = new openai.Configuration({
apiKey: config.openaiKey,
})
const openaiApi = new openai.OpenAIApi(configuration)
const docprovider = new CompletionsDocumentProvider()
vscode.workspace.registerTextDocumentContentProvider('cody', docprovider)

const completionsProvider = new CodyCompletionItemProvider(openaiApi, docprovider)
context.subscriptions.push(
vscode.commands.registerCommand('cody.experimental.suggest', async () => {
await completionsProvider.fetchAndShowCompletions()
})
)
context.subscriptions.push(
vscode.languages.registerInlineCompletionItemProvider({ scheme: 'file' }, completionsProvider)
)
}

// Watch all relevant configuration and secrets for changes.
context.subscriptions.push(
vscode.workspace.onDidChangeConfiguration(async event => {
Expand Down
104 changes: 104 additions & 0 deletions client/cody/src/completions/docprovider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import * as openai from 'openai'
import * as vscode from 'vscode'

// FIXME: When OpenAI's logit_bias uses a more precise type than 'object',
// specify JSON-able objects as { [prop: string]: JSONSerialiable | undefined }
export type JSONSerializable = null | string | number | boolean | object | JSONSerializable[]

interface Meta {
elapsedMillis: number
prompt: string
suffix: string
llmOptions: JSONSerializable
}

export interface CompletionGroup {
lang: string
prefixText: string
completions: openai.CreateChatCompletionResponse
meta?: Meta
}

export class CompletionsDocumentProvider implements vscode.TextDocumentContentProvider {
private completionsByUri: { [uri: string]: CompletionGroup[] } = {}

private isDebug(): boolean {
return vscode.workspace.getConfiguration().get<boolean>('cody.debug') === true
}

private fireDocumentChanged(uri: vscode.Uri): void {
this.onDidChangeEmitter.fire(uri)
}

public clearCompletions(uri: vscode.Uri): void {
delete this.completionsByUri[uri.toString()]
this.fireDocumentChanged(uri)
}

public addCompletions(
uri: vscode.Uri,
lang: string,
prefixText: string,
completions: openai.CreateChatCompletionResponse,
debug?: Meta
): void {
if (!this.completionsByUri[uri.toString()]) {
this.completionsByUri[uri.toString()] = []
}

this.completionsByUri[uri.toString()].push({
lang,
prefixText,
completions,
meta: debug,
})
this.fireDocumentChanged(uri)
}

public onDidChangeEmitter = new vscode.EventEmitter<vscode.Uri>()
public onDidChange = this.onDidChangeEmitter.event

public provideTextDocumentContent(uri: vscode.Uri): string {
const completionGroups = this.completionsByUri[uri.toString()]
if (!completionGroups) {
return 'Loading...'
}

return completionGroups
.map(({ completions, lang, prefixText, meta }) =>
completions.choices
.map(({ message, finish_reason }, index) => {
if (!message?.content) {
return undefined
}

let completionText = `\`\`\`${lang}\n${prefixText}${message.content}\n\`\`\``
if (this.isDebug() && meta) {
completionText =
`\`\`\`\n${meta.prompt}\n\`\`\`` +
'\n' +
completionText +
'\n' +
`\`\`\`\n${meta.suffix}\n\`\`\``
}
const headerComponents = [`${index + 1} / ${completions.choices.length}`]
if (finish_reason) {
headerComponents.push(`finish_reason:${finish_reason}`)
}
return headerize(headerComponents.join(', '), 80) + '\n' + completionText
})
.filter(t => t)
.join('\n\n')
)
.join('\n\n')
}
}

function headerize(label: string, width: number): string {
const prefix = '# ======= '
let buffer = width - label.length - prefix.length - 1
if (buffer < 0) {
buffer = 0
}
return `${prefix}${label} ${'='.repeat(buffer)}`
}
Loading

0 comments on commit 6d1f531

Please sign in to comment.