Skip to content
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

configure autocomplete provider based on cody LLM settings in site config #1035

1 change: 1 addition & 0 deletions lib/shared/src/sourcegraph-api/graphql/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ export interface CodyLLMSiteConfiguration {
fastChatModelMaxTokens?: number
completionModel?: string
completionModelMaxTokens?: number
provider?: string
}

interface IsContextRequiredForChatQueryResponse {
Expand Down
1 change: 1 addition & 0 deletions lib/shared/src/sourcegraph-api/graphql/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ query CurrentSiteCodyLlmConfiguration {
fastChatModelMaxTokens
completionModel
completionModelMaxTokens
provider
Copy link
Contributor

Choose a reason for hiding this comment

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

I assume this doesn't cause issues if the provider field does not exist?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great catch! How did I miss that?! Thank you, @chwarwick!
Addressed in a16c760
Honestly, I don't like the implementation. Do you know of a more robust way of getting a potentially non-existing field?

cc: @philipp-spiess

}
}
}`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@ export async function createInlineCompletionItemProvider({

const disposables: vscode.Disposable[] = []

const providerConfig = await createProviderConfig(config, client, featureFlagProvider)
const providerConfig = await createProviderConfig(
config,
client,
featureFlagProvider,
authProvider.getAuthStatus().configOverwrites
)
if (providerConfig) {
const history = new VSCodeDocumentHistory()
const sectionObserver = config.autocompleteExperimentalGraphContext
Expand Down
197 changes: 149 additions & 48 deletions vscode/src/completions/providers/createProvider.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Configuration } from '@sourcegraph/cody-shared/src/configuration'
import { FeatureFlag, FeatureFlagProvider } from '@sourcegraph/cody-shared/src/experimentation/FeatureFlagProvider'
import { CodyLLMSiteConfiguration } from '@sourcegraph/cody-shared/src/sourcegraph-api/graphql/client'

import { logError } from '../../log'
import { CodeCompletionsClient } from '../client'
Expand All @@ -14,75 +15,135 @@ import { createProviderConfig as createUnstableOpenAIProviderConfig } from './un
export async function createProviderConfig(
config: Configuration,
client: CodeCompletionsClient,
featureFlagProvider?: FeatureFlagProvider
featureFlagProvider: FeatureFlagProvider,
codyLLMSiteConfig?: CodyLLMSiteConfiguration
): Promise<ProviderConfig | null> {
const { provider, model } = await resolveDefaultProvider(config.autocompleteAdvancedProvider, featureFlagProvider)
switch (provider) {
case 'unstable-codegen': {
if (config.autocompleteAdvancedServerEndpoint !== null) {
return createUnstableCodeGenProviderConfig(config.autocompleteAdvancedServerEndpoint)
}
const defaultAnthropicProviderConfig = createAnthropicProviderConfig({
client,
contextWindowTokens: 2048,
mode: config.autocompleteAdvancedModel === 'claude-instant-infill' ? 'infill' : 'default',
})

/**
* Look for the autocomplete provider in VSCode settings and return matching provider config.
*/
const providerAndModelFromVSCodeConfig = await resolveDefaultProviderFromVSCodeConfigOrFeatureFlags(
config.autocompleteAdvancedProvider,
featureFlagProvider
)
if (providerAndModelFromVSCodeConfig) {
const { provider, model } = providerAndModelFromVSCodeConfig

switch (provider) {
case 'unstable-codegen': {
if (config.autocompleteAdvancedServerEndpoint !== null) {
return createUnstableCodeGenProviderConfig(config.autocompleteAdvancedServerEndpoint)
}

logError(
'createProviderConfig',
'Provider `unstable-codegen` can not be used without configuring `cody.autocomplete.advanced.serverEndpoint`.'
)
return null
}
case 'unstable-azure-openai': {
if (config.autocompleteAdvancedServerEndpoint === null) {
logError(
'createProviderConfig',
'Provider `unstable-azure-openai` can not be used without configuring `cody.autocomplete.advanced.serverEndpoint`.'
'Provider `unstable-codegen` can not be used without configuring `cody.autocomplete.advanced.serverEndpoint`.'
)
return null
}
case 'unstable-azure-openai': {
if (config.autocompleteAdvancedServerEndpoint === null) {
logError(
'createProviderConfig',
'Provider `unstable-azure-openai` can not be used without configuring `cody.autocomplete.advanced.serverEndpoint`.'
)
return null
}

if (config.autocompleteAdvancedAccessToken === null) {
if (config.autocompleteAdvancedAccessToken === null) {
logError(
'createProviderConfig',
'Provider `unstable-azure-openai` can not be used without configuring `cody.autocomplete.advanced.accessToken`.'
)
return null
}

return createUnstableAzureOpenAiProviderConfig({
serverEndpoint: config.autocompleteAdvancedServerEndpoint,
accessToken: config.autocompleteAdvancedAccessToken,
})
}
case 'unstable-openai': {
return createUnstableOpenAIProviderConfig({
client,
contextWindowTokens: 2048,
})
}
case 'unstable-fireworks': {
return createUnstableFireworksProviderConfig({
client,
model: config.autocompleteAdvancedModel ?? model ?? null,
})
}
case 'anthropic': {
return defaultAnthropicProviderConfig
}
default:
logError(
'createProviderConfig',
'Provider `unstable-azure-openai` can not be used without configuring `cody.autocomplete.advanced.accessToken`.'
`Unrecognized provider '${config.autocompleteAdvancedProvider}' configured.`
)
return null
}

return createUnstableAzureOpenAiProviderConfig({
serverEndpoint: config.autocompleteAdvancedServerEndpoint,
accessToken: config.autocompleteAdvancedAccessToken,
})
}
case 'unstable-openai': {
return createUnstableOpenAIProviderConfig({
client,
contextWindowTokens: 2048,
})
}
case 'unstable-fireworks': {
return createUnstableFireworksProviderConfig({
client,
model: config.autocompleteAdvancedModel ?? model ?? null,
})
}
case 'anthropic': {
return createAnthropicProviderConfig({
client,
contextWindowTokens: 2048,
mode: config.autocompleteAdvancedModel === 'claude-instant-infill' ? 'infill' : 'default',
})
}
default:
}

/**
* If autocomplete provider is not defined in the VSCode settings,
* check the completions provider in the connected Sourcegraph instance site config
* and return the matching provider config.
*/
if (codyLLMSiteConfig?.provider) {
const parsed = parseProviderAndModel({
provider: codyLLMSiteConfig.provider,
model: codyLLMSiteConfig.completionModel,
})
if (!parsed) {
logError(
'createProviderConfig',
`Unrecognized provider '${config.autocompleteAdvancedProvider}' configured.`
`Failed to parse the model name for '${codyLLMSiteConfig.provider}' completions provider.`
)
return null
}
const { provider, model } = parsed
switch (provider) {
case 'openai':
case 'azure-openai':
return createUnstableOpenAIProviderConfig({
client,
contextWindowTokens: 2048,
model,
})

case 'fireworks':
return createUnstableFireworksProviderConfig({
client,
model: model ?? null,
})
case 'aws-bedrock':
case 'anthropic':
return defaultAnthropicProviderConfig
default:
logError('createProviderConfig', `Unrecognized provider '${provider}' configured.`)
return null
}
}

/**
* If autocomplete provider is not defined neither in VSCode nor in Sourcegraph instance site config,
* use the default provider config ("anthropic").
*/
return defaultAnthropicProviderConfig
}

async function resolveDefaultProvider(
async function resolveDefaultProviderFromVSCodeConfigOrFeatureFlags(
configuredProvider: string | null,
featureFlagProvider?: FeatureFlagProvider
): Promise<{ provider: string; model?: 'starcoder-7b' | 'starcoder-16b' | 'claude-instant-infill' }> {
): Promise<{ provider: string; model?: 'starcoder-7b' | 'starcoder-16b' | 'claude-instant-infill' } | null> {
if (configuredProvider) {
return { provider: configuredProvider }
}
Expand All @@ -101,5 +162,45 @@ async function resolveDefaultProvider(
return { provider: 'anthropic', model: 'claude-instant-infill' }
}

return { provider: 'anthropic' }
return null
}

const delimeters: Record<string, string> = {
sourcegraph: '/',
'aws-bedrock': '.',
chwarwick marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* For certain completions providers configured in the Sourcegraph instance site config
* the model name consists MODEL_PROVIDER and MODEL_NAME separated by a specific delimeter (see {@link delimeters}).
*
* This function checks if the given provider has a specific model naming format and:
* - if it does, parses the model name and returns the parsed provider and model names;
* - if it doesn't, returns the original provider and model names.
*
* E.g. for "sourcegraph" provider the completions model name consists of model provider and model name separated by "/".
* So when received `{ provider: "sourcegraph", model: "anthropic/claude-instant-1" }` the expected output would be `{ provider: "anthropic", model: "claude-instant-1" }`.
*/
function parseProviderAndModel({
provider,
model,
}: {
provider: string
model?: string
}): { provider: string; model?: string } | null {
const delimeter = delimeters[provider]
if (!delimeter) {
return { provider, model }
}

if (model) {
const index = model.indexOf(delimeter)
const parsedProvider = model.slice(0, index)
const parsedModel = model.slice(index + 1)
if (parsedProvider && parsedModel) {
return { provider: parsedProvider, model: parsedModel }
}
}

return null
}
7 changes: 5 additions & 2 deletions vscode/src/completions/providers/unstable-openai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,14 +153,17 @@ export class UnstableOpenAIProvider extends Provider {
}
}

export function createProviderConfig(unstableAzureOpenAIOptions: UnstableOpenAIOptions): ProviderConfig {
export function createProviderConfig({
model,
...unstableAzureOpenAIOptions
}: UnstableOpenAIOptions & { model?: string }): ProviderConfig {
return {
create(options: ProviderOptions) {
return new UnstableOpenAIProvider(options, { ...unstableAzureOpenAIOptions })
},
maximumContextCharacters: tokensToChars(unstableAzureOpenAIOptions.contextWindowTokens),
enableExtendedMultilineTriggers: false,
identifier: PROVIDER_IDENTIFIER,
model: 'gpt-35-turbo',
model: model || 'gpt-35-turbo',
taras-yemets marked this conversation as resolved.
Show resolved Hide resolved
}
}