diff --git a/extensions/positron-assistant/src/anthropic.ts b/extensions/positron-assistant/src/anthropic.ts index d8e19cd0ade2..e08524c47813 100644 --- a/extensions/positron-assistant/src/anthropic.ts +++ b/extensions/positron-assistant/src/anthropic.ts @@ -62,12 +62,12 @@ export class AnthropicLanguageModel implements positron.ai.LanguageModelChatProv id: 'anthropic-api', displayName: 'Anthropic' }, - supportedOptions: ['apiKey', 'apiKeyEnvVar'], + supportedOptions: ['apiKey', 'autoconfigure'], defaults: { name: DEFAULT_ANTHROPIC_MODEL_NAME, model: DEFAULT_ANTHROPIC_MODEL_MATCH + '-latest', toolCalls: true, - apiKeyEnvVar: { key: 'ANTHROPIC_API_KEY', signedIn: false }, + autoconfigure: { type: positron.ai.LanguageModelAutoconfigureType.EnvVariable, key: 'ANTHROPIC_API_KEY', signedIn: false } }, }; diff --git a/extensions/positron-assistant/src/config.ts b/extensions/positron-assistant/src/config.ts index e1cf6d623d7f..3a1fc73997cd 100644 --- a/extensions/positron-assistant/src/config.ts +++ b/extensions/positron-assistant/src/config.ts @@ -7,7 +7,7 @@ import * as positron from 'positron'; import { randomUUID } from 'crypto'; import { getLanguageModels } from './models'; import { completionModels } from './completion'; -import { clearTokenUsage, disposeModels, log, registerModel } from './extension'; +import { clearTokenUsage, disposeModels, getAutoconfiguredModels, log, registerModel } from './extension'; import { CopilotService } from './copilot.js'; import { PositronAssistantApi } from './api.js'; import { PositLanguageModel } from './posit.js'; @@ -153,35 +153,51 @@ export async function showConfigurationDialog(context: vscode.ExtensionContext, // Gather model sources; ignore disabled providers const enabledProviders = await getEnabledProviders(); + // Models in persistent storage const registeredModels = context.globalState.get>('positron.assistant.models'); - const sources = [...getLanguageModels(), ...completionModels] + // Auto-configured models (e.g., env var based or managed credentials) stored in memory + const autoconfiguredModels = getAutoconfiguredModels(); + const sources: positron.ai.LanguageModelSource[] = [...getLanguageModels(), ...completionModels] .map((provider) => { - const isRegistered = registeredModels?.find((modelConfig) => modelConfig.provider === provider.source.provider.id); - return { + // Get model data from `registeredModels` (for manually configured models; stored in persistent storage) + // or `autoconfiguredModels` (for auto-configured models; e.g., env var based or managed credentials) + const isRegistered = registeredModels?.find((modelConfig) => modelConfig.provider === provider.source.provider.id) || autoconfiguredModels.find((modelConfig) => modelConfig.provider === provider.source.provider.id); + // Update source data with actual model configuration status if found + // Otherwise, use defaults from provider + const source: positron.ai.LanguageModelSource = { ...provider.source, signedIn: !!isRegistered, defaults: isRegistered ? { ...provider.source.defaults, ...isRegistered } : provider.source.defaults }; + return source; }) .filter((source) => { // If no specific set of providers was specified, include all return enabledProviders.length === 0 || enabledProviders.includes(source.provider.id); }) .map((source) => { - // Resolve environment variables in apiKeyEnvVar - if ('apiKeyEnvVar' in source.defaults && source.defaults.apiKeyEnvVar) { - const envVarName = (source.defaults as any).apiKeyEnvVar.key; - const envVarValue = process.env[envVarName]; - - return { - ...source, - defaults: { - ...source.defaults, - apiKeyEnvVar: { key: envVarName, signedIn: !!envVarValue } - }, - }; + // Handle autoconfigurable providers + if ('autoconfigure' in source.defaults && source.defaults.autoconfigure) { + // Resolve environment variables + if (source.defaults.autoconfigure.type === positron.ai.LanguageModelAutoconfigureType.EnvVariable) { + const envVarName = source.defaults.autoconfigure.key; + const envVarValue = process.env[envVarName]; + + return { + ...source, + defaults: { + ...source.defaults, + autoconfigure: { type: positron.ai.LanguageModelAutoconfigureType.EnvVariable, key: envVarName, signedIn: !!envVarValue } + }, + }; + } else if (source.defaults.autoconfigure.type === positron.ai.LanguageModelAutoconfigureType.Custom) { + // No special handling for custom autoconfiguration at this time + // The custom autoconfiguration logic should handle everything + // and is retrieved from `autoconfiguredModels` above + return source; + } } return source; }); diff --git a/extensions/positron-assistant/src/constants.ts b/extensions/positron-assistant/src/constants.ts index b0a417321ab4..af5eb59c2081 100644 --- a/extensions/positron-assistant/src/constants.ts +++ b/extensions/positron-assistant/src/constants.ts @@ -5,6 +5,7 @@ import * as path from 'path'; import { DocumentSelector } from 'vscode-languageclient'; +import * as vscode from 'vscode'; /** The extension root directory. */ export const EXTENSION_ROOT_DIR = path.join(__dirname, '..'); @@ -49,3 +50,8 @@ export const MAX_CONTEXT_VARIABLES = 400; /** Max number of models to attempt connecting to when checking auth for a provider */ export const DEFAULT_MAX_CONNECTION_ATTEMPTS = 3; + +/** + * Determines if the Posit Web environment is detected. + */ +export const IS_RUNNING_ON_PWB = !!process.env.RS_SERVER_URL && vscode.env.uiKind === vscode.UIKind.Web; diff --git a/extensions/positron-assistant/src/extension.ts b/extensions/positron-assistant/src/extension.ts index 2763bb37f2aa..2f86eae42146 100644 --- a/extensions/positron-assistant/src/extension.ts +++ b/extensions/positron-assistant/src/extension.ts @@ -6,7 +6,7 @@ import * as vscode from 'vscode'; import * as positron from 'positron'; import { EncryptedSecretStorage, expandConfigToSource, getEnabledProviders, getModelConfiguration, getModelConfigurations, getStoredModels, GlobalSecretStorage, logStoredModels, ModelConfig, SecretStorage, showConfigurationDialog, StoredModelConfig } from './config'; -import { createModelConfigsFromEnv, newLanguageModelChatProvider } from './models'; +import { createAutomaticModelConfigs, newLanguageModelChatProvider } from './models'; import { registerMappedEditsProvider } from './edits'; import { ParticipantService, registerParticipants } from './participants'; import { newCompletionProvider, registerHistoryTracking } from './completion'; @@ -32,6 +32,16 @@ let modelDisposables: ModelDisposable[] = []; let assistantEnabled = false; let tokenTracker: TokenTracker; +const autoconfiguredModels: ModelConfig[] = []; + +/** + * Get all models which were automatically configured (e.g., via environment variables or managed credentials). + * @returns A list of models that were automatically configured + */ +export function getAutoconfiguredModels(): ModelConfig[] { + return [...autoconfiguredModels]; +} + /** A chat or completion model provider disposable with associated configuration. */ class ModelDisposable implements vscode.Disposable { constructor( @@ -108,6 +118,7 @@ export async function registerModels(context: vscode.ExtensionContext, storage: // Dispose of existing models disposeModels(); + let autoModelConfigs: ModelConfig[]; let modelConfigs: ModelConfig[] = []; try { // Refresh the set of enabled providers @@ -123,10 +134,10 @@ export async function registerModels(context: vscode.ExtensionContext, storage: return enabled; }); - // Add any configs that should automatically work when the right environment variables are set - const modelConfigsFromEnv = createModelConfigsFromEnv(); + // Add any configs that should automatically work when the right conditions are met + autoModelConfigs = await createAutomaticModelConfigs(); // we add in the config if we don't already have it configured - for (const config of modelConfigsFromEnv) { + for (const config of autoModelConfigs) { if (!modelConfigs.find(c => c.provider === config.provider)) { modelConfigs.push(config); } @@ -143,6 +154,15 @@ export async function registerModels(context: vscode.ExtensionContext, storage: try { await registerModelWithAPI(config, context, storage); registeredModels.push(config); + if (autoModelConfigs.includes(config)) { + // In addition, track auto-configured models separately + // at a module level so that we can expose them via + // getAutoconfiguredModels() + // This is needed since auto-configured models are not + // stored in persistent storage like manually configured models + // are, and configuration data needs to be retrieved from memory. + autoconfiguredModels.push(config); + } } catch (e) { vscode.window.showErrorMessage(`${e}`); } diff --git a/extensions/positron-assistant/src/models.ts b/extensions/positron-assistant/src/models.ts index 7585d1a43350..07b9fd2d4c22 100644 --- a/extensions/positron-assistant/src/models.ts +++ b/extensions/positron-assistant/src/models.ts @@ -6,7 +6,7 @@ import * as vscode from 'vscode'; import * as positron from 'positron'; import * as ai from 'ai'; -import { getMaxConnectionAttempts, getProviderTimeoutMs, ModelConfig, SecretStorage } from './config'; +import { getMaxConnectionAttempts, getProviderTimeoutMs, getEnabledProviders, ModelConfig, SecretStorage } from './config'; import { AnthropicProvider, createAnthropic } from '@ai-sdk/anthropic'; import { AzureOpenAIProvider, createAzure } from '@ai-sdk/azure'; import { createVertex, GoogleVertexProvider } from '@ai-sdk/google-vertex'; @@ -19,12 +19,13 @@ import { processMessages, toAIMessage } from './utils'; import { AmazonBedrockProvider, createAmazonBedrock } from '@ai-sdk/amazon-bedrock'; import { fromNodeProviderChain } from '@aws-sdk/credential-providers'; import { AnthropicLanguageModel, DEFAULT_ANTHROPIC_MODEL_MATCH, DEFAULT_ANTHROPIC_MODEL_NAME } from './anthropic'; -import { DEFAULT_MAX_TOKEN_INPUT, DEFAULT_MAX_TOKEN_OUTPUT } from './constants.js'; +import { DEFAULT_MAX_TOKEN_INPUT, DEFAULT_MAX_TOKEN_OUTPUT, IS_RUNNING_ON_PWB } from './constants.js'; import { log, recordRequestTokenUsage, recordTokenUsage } from './extension.js'; import { TokenUsage } from './tokens.js'; import { BedrockClient, FoundationModelSummary, InferenceProfileSummary, ListFoundationModelsCommand, ListInferenceProfilesCommand } from '@aws-sdk/client-bedrock'; import { PositLanguageModel } from './posit.js'; import { applyModelFilters } from './modelFilters'; +import { autoconfigureWithManagedCredentials, AWS_MANAGED_CREDENTIALS } from './pwb'; /** * Models used by chat participants and for vscode.lm.* API functionality. @@ -263,7 +264,21 @@ class EchoLanguageModel implements positron.ai.LanguageModelChatProvider { //#endregion //#region Language Models +/** + * Result of an autoconfiguration attempt. + * - Signed in indicates whether the model is configured and ready to use. + * - Message provides additional information to be displayed to user in the configuration modal, if signed in. + */ +export type AutoconfigureResult = { + signedIn: false; +} | { + signedIn: true; + message: string; +}; + abstract class AILanguageModel implements positron.ai.LanguageModelChatProvider { + public static source: positron.ai.LanguageModelSource; + public readonly name; public readonly provider; public readonly id; @@ -669,6 +684,13 @@ abstract class AILanguageModel implements positron.ai.LanguageModelChatProvider } return this._config.model === id; } + + /** + * Autoconfigures the language model, if supported. + * May implement functionality such as checking for environment variables or assessing managed credentials. + * @returns A promise that resolves to the autoconfigure result. + */ + static autoconfigure?: () => Promise; } class AnthropicAILanguageModel extends AILanguageModel implements positron.ai.LanguageModelChatProvider { @@ -683,12 +705,12 @@ class AnthropicAILanguageModel extends AILanguageModel implements positron.ai.La id: 'anthropic-api', displayName: 'Anthropic' }, - supportedOptions: ['apiKey', 'apiKeyEnvVar'], + supportedOptions: ['apiKey', 'autoconfigure'], defaults: { name: DEFAULT_ANTHROPIC_MODEL_NAME, model: DEFAULT_ANTHROPIC_MODEL_MATCH + '-latest', toolCalls: true, - apiKeyEnvVar: { key: 'ANTHROPIC_API_KEY', signedIn: false }, + autoconfigure: { type: positron.ai.LanguageModelAutoconfigureType.EnvVariable, key: 'ANTHROPIC_API_KEY', signedIn: false }, }, }; @@ -1035,11 +1057,12 @@ export class AWSLanguageModel extends AILanguageModel implements positron.ai.Lan id: 'amazon-bedrock', displayName: 'Amazon Bedrock' }, - supportedOptions: ['toolCalls'], + supportedOptions: ['toolCalls', 'autoconfigure'], defaults: { name: 'Claude 4 Sonnet Bedrock', model: 'us.anthropic.claude-sonnet-4-20250514-v1:0', toolCalls: true, + autoconfigure: { type: positron.ai.LanguageModelAutoconfigureType.Custom, message: 'Automatically configured using AWS credentials', signedIn: false }, }, }; bedrockClient: BedrockClient; @@ -1245,6 +1268,14 @@ export class AWSLanguageModel extends AILanguageModel implements positron.ai.Lan return undefined; } + static override async autoconfigure(): Promise { + return autoconfigureWithManagedCredentials( + AWS_MANAGED_CREDENTIALS, + AWSLanguageModel.source.provider.id, + AWSLanguageModel.source.provider.displayName + ); + } + } //#endregion @@ -1282,31 +1313,62 @@ export function getLanguageModels() { * * @returns The model configurations that are configured by the environment. */ -export function createModelConfigsFromEnv(): ModelConfig[] { +export async function createAutomaticModelConfigs(): Promise { const models = getLanguageModels(); const modelConfigs: ModelConfig[] = []; - models.forEach(model => { - if ('apiKeyEnvVar' in model.source.defaults) { - const key = model.source.defaults.apiKeyEnvVar?.key; + for (const model of models) { + if (!('autoconfigure' in model.source.defaults)) { + // Not an autoconfigurable model + continue; + } + + if (model.source.defaults.autoconfigure.type === positron.ai.LanguageModelAutoconfigureType.EnvVariable) { + // Handle environment variable based auto-configuration + const key = model.source.defaults.autoconfigure.key; // pragma: allowlist nextline secret const apiKey = key ? process.env[key] : undefined; if (key && apiKey) { - const modelConfig = { + const modelConfig: ModelConfig = { id: `${model.source.provider.id}`, provider: model.source.provider.id, type: positron.PositronLanguageModelType.Chat, name: model.source.provider.displayName, model: model.source.defaults.model, apiKey: apiKey, - // pragma: allowlist nextline secret - apiKeyEnvVar: 'apiKeyEnvVar' in model.source.defaults ? model.source.defaults.apiKeyEnvVar : undefined, + autoconfigure: { + type: positron.ai.LanguageModelAutoconfigureType.EnvVariable, + key: key, + signedIn: true, + } }; modelConfigs.push(modelConfig); } + } else if (model.source.defaults.autoconfigure.type === positron.ai.LanguageModelAutoconfigureType.Custom) { + // Handle custom auto-configuration + if ('autoconfigure' in model && model.autoconfigure) { + const result = await model.autoconfigure(); + if (result.signedIn) { + const modelConfig: ModelConfig = { + id: `${model.source.provider.id}`, + provider: model.source.provider.id, + type: positron.PositronLanguageModelType.Chat, + name: model.source.provider.displayName, + model: model.source.defaults.model, + apiKey: undefined, + // pragma: allowlist nextline secret + autoconfigure: { + type: positron.ai.LanguageModelAutoconfigureType.Custom, + message: result.message, + signedIn: true + } + }; + modelConfigs.push(modelConfig); + } + } } - }); + } return modelConfigs; } diff --git a/extensions/positron-assistant/src/pwb.ts b/extensions/positron-assistant/src/pwb.ts new file mode 100644 index 000000000000..ce983a3ec568 --- /dev/null +++ b/extensions/positron-assistant/src/pwb.ts @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (C) 2025 Posit Software, PBC. All rights reserved. + * Licensed under the Elastic License 2.0. See LICENSE.txt for license information. + *--------------------------------------------------------------------------------------------*/ + +import { getEnabledProviders } from './config'; +import { IS_RUNNING_ON_PWB } from './constants'; +import { log } from './extension'; +import { AutoconfigureResult } from './models.js'; + +/** + * Configuration for managed credentials on Posit Workbench. + */ +export interface ManagedCredentialConfig { + /** Display name for the credential type shown to users */ + readonly displayName: string; + /** Environment variable name that indicates managed credentials are available */ + readonly envVar: string; + /** Validator function to confirm the env var value for managed credentials */ + readonly validator: (string) => boolean; +} + +/** + * AWS managed credentials configuration for Posit Workbench. + */ +export const AWS_MANAGED_CREDENTIALS: ManagedCredentialConfig = { + displayName: 'AWS managed credentials', + envVar: 'AWS_WEB_IDENTITY_TOKEN_FILE', + validator: (value: string) => value.includes('posit-workbench'), +}; + +/** + * Helper function to autoconfigure language models using managed credentials on Posit Workbench. + * + * @template T - The credential configuration type (e.g., typeof AWS_MANAGED_CREDENTIALS) + * @param credentialConfig - The credential configuration to check + * @param providerId - The provider ID to check if enabled + * @param displayName - The provider display name for logging + * @returns A promise that resolves to the autoconfigure result + */ +export async function autoconfigureWithManagedCredentials( + credentialConfig: T, + providerId: string, + displayName: string +): Promise { + // Configure automatically if: + // - We are on PWB, and + // - the provider is enabled in settings, and + // - managed credentials are available + + if (!IS_RUNNING_ON_PWB) { + return { signedIn: false }; + } + + const providerEnabled = await getEnabledProviders().then( + providers => providers.includes(providerId) + ); + if (!providerEnabled) { + return { signedIn: false }; + } + + // Check for managed credentials using the provided config + const tokenEnv = process.env[credentialConfig.envVar]; + if (!tokenEnv || !credentialConfig.validator(tokenEnv)) { + // PWB managed credentials not set + return { signedIn: false }; + } + + log.info(`[${displayName}] Auto-configuring with managed credentials.`); + return { + signedIn: true, + message: credentialConfig.displayName, + }; +} diff --git a/src/positron-dts/positron.d.ts b/src/positron-dts/positron.d.ts index 48047fd85caf..096e21ecf14b 100644 --- a/src/positron-dts/positron.d.ts +++ b/src/positron-dts/positron.d.ts @@ -2222,9 +2222,37 @@ declare module 'positron' { maxInputTokens?: number; maxOutputTokens?: number; completions?: boolean; - apiKeyEnvVar?: { key: string; signedIn: boolean }; // The environment variable name for the API key + autoconfigure?: LanguageModelAutoconfigure; } + /** + * Types of autoconfiguration support for language models. + */ + export enum LanguageModelAutoconfigureType { + // Autoconfigured using environment variables + EnvVariable = 0, + // Autoconfigured using a custom function on the language model provider + // E.g., for Workbench managed credentials + Custom = 1 + } + /** + * Language model autoconfiguration options. + */ + export type LanguageModelAutoconfigure = ( + { + type: LanguageModelAutoconfigureType.EnvVariable; + // Environment variable key used to retrieve API key, if set + key: string; + signedIn: boolean; + } | + { + type: LanguageModelAutoconfigureType.Custom; + // Message to show in the UI if autoconfiguration was successful + message: string; + signedIn: boolean; + } + ); + /** * Request the current plot data. */ diff --git a/src/vs/workbench/api/common/positron/extHost.positron.api.impl.ts b/src/vs/workbench/api/common/positron/extHost.positron.api.impl.ts index 53f72d5188f9..3103dd5aeb0f 100644 --- a/src/vs/workbench/api/common/positron/extHost.positron.api.impl.ts +++ b/src/vs/workbench/api/common/positron/extHost.positron.api.impl.ts @@ -358,6 +358,7 @@ export function createPositronApiFactoryAndRegisterActors(accessor: ServicesAcce setCurrentProvider(id: string): Promise { return extHostAiFeatures.setCurrentProvider(id); }, + LanguageModelAutoconfigureType: extHostTypes.LanguageModelAutoconfigureType }; const notebooks: typeof positron.notebooks = { diff --git a/src/vs/workbench/api/common/positron/extHostTypes.positron.ts b/src/vs/workbench/api/common/positron/extHostTypes.positron.ts index 4d2e5547b09c..fea4cb761c0f 100644 --- a/src/vs/workbench/api/common/positron/extHostTypes.positron.ts +++ b/src/vs/workbench/api/common/positron/extHostTypes.positron.ts @@ -394,6 +394,13 @@ export enum PositronLanguageModelType { Completion = 'completion', } +// Equivalent in positron.d.ts API: LanguageModelAutoconfigureType +export enum LanguageModelAutoconfigureType { + EnvVariable = 0, + Custom = 1 +} + + /** * The possible locations a Positron Assistant chat request can be invoked from. */ diff --git a/src/vs/workbench/contrib/positronAssistant/browser/components/languageModelConfigComponent.tsx b/src/vs/workbench/contrib/positronAssistant/browser/components/languageModelConfigComponent.tsx index 189e32c0b2ec..130e9741c654 100644 --- a/src/vs/workbench/contrib/positronAssistant/browser/components/languageModelConfigComponent.tsx +++ b/src/vs/workbench/contrib/positronAssistant/browser/components/languageModelConfigComponent.tsx @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as React from 'react' -import { IPositronLanguageModelConfig, IPositronLanguageModelSource } from '../../common/interfaces/positronAssistantService.js' +import { IPositronLanguageModelAutoconfigure, LanguageModelAutoconfigureType, IPositronLanguageModelConfig, IPositronLanguageModelSource } from '../../common/interfaces/positronAssistantService.js' import { localize } from '../../../../../nls.js' import { LabeledTextInput } from '../../../../browser/positronComponents/positronModalDialog/components/labeledTextInput.js' import { Button } from '../../../../../base/browser/ui/positronComponents/button/button.js' @@ -130,9 +130,10 @@ function interpolate(text: string, value: (key: string) => React.ReactNode | und export const LanguageModelConfigComponent = (props: LanguageModelConfigComponentProps) => { const { authMethod, authStatus, config, source } = props; const { apiKey } = config; - const hasEnvApiKey = !!source.defaults.apiKeyEnvVar && source.defaults.apiKeyEnvVar.signedIn; - const showApiKeyInput = authMethod === AuthMethod.API_KEY && authStatus !== AuthStatus.SIGNED_IN && !hasEnvApiKey; - const showCancelButton = authMethod === AuthMethod.OAUTH && authStatus === AuthStatus.SIGNING_IN && !hasEnvApiKey; + + const hasAutoconfigure = !!source.defaults.autoconfigure && source.defaults.autoconfigure.signedIn; + const showApiKeyInput = authMethod === AuthMethod.API_KEY && authStatus !== AuthStatus.SIGNED_IN && !hasAutoconfigure; + const showCancelButton = authMethod === AuthMethod.OAUTH && authStatus === AuthStatus.SIGNING_IN && !hasAutoconfigure; const showBaseUrl = authMethod === AuthMethod.API_KEY && source.supportedOptions?.includes('baseUrl'); // This currently only updates the API key for the provider, but in the future it may be extended to support @@ -142,7 +143,7 @@ export const LanguageModelConfigComponent = (props: LanguageModelConfigComponent }; return <> - {!hasEnvApiKey &&
+ {!hasAutoconfigure &&
{showApiKeyInput && } {showCancelButton && @@ -152,7 +153,7 @@ export const LanguageModelConfigComponent = (props: LanguageModelConfigComponent }
} {showBaseUrl && props.onChange({ ...config, baseUrl: newBaseUrl })} />} - + ; } @@ -241,17 +242,23 @@ const ExternalLink = (props: { href: string, children: React.ReactNode }) => { ; } -const ExternalAPIKey = (props: { provider: string, envKeyName?: { key: string; signedIn: boolean } }) => { - - return ( - props.envKeyName ? -
- { - props.envKeyName && props.envKeyName.signedIn ? -

{localize('positron.languageModelConfig.externalApiInUse', "The {0} environment variable is currently in use.", props.envKeyName?.key)}

- : -

{localize('positron.languageModelConfig.externalApiSetup', "You can also assign the {0} environment variable and restart Positron.", props.envKeyName.key)}

- } -
: null - ); +const AutoconfiguredModel = (props: { provider: string, displayName: string, details?: IPositronLanguageModelAutoconfigure }) => { + if (props.details?.type === LanguageModelAutoconfigureType.EnvVariable) { + return (
+ { + props.details.signedIn ? +

{localize('positron.languageModelConfig.externalApiInUse', "The {0} environment variable is currently in use.", props.details.key)}

+ : +

{localize('positron.languageModelConfig.externalApiSetup', "You can also assign the {0} environment variable and restart Positron.", props.details.key)}

+ } +
); + } else if (props.details?.type === LanguageModelAutoconfigureType.Custom && props.details.signedIn) { + return (
+ { +

{localize('positron.languageModelConfig.autoconfiguredModelInUse', "{0} has been automatically configured using {1}", props.displayName, props.details.message)}

+ } +
); + } else { + return null; + } } diff --git a/src/vs/workbench/contrib/positronAssistant/common/interfaces/positronAssistantService.ts b/src/vs/workbench/contrib/positronAssistant/common/interfaces/positronAssistantService.ts index 080f7a70a00a..a62c314190a9 100644 --- a/src/vs/workbench/contrib/positronAssistant/common/interfaces/positronAssistantService.ts +++ b/src/vs/workbench/contrib/positronAssistant/common/interfaces/positronAssistantService.ts @@ -50,6 +50,26 @@ export interface IPositronLanguageModelSource { authMethods?: string[]; } +// Equivalent in positron.d.ts API: LanguageModelAutoconfigureType +export enum LanguageModelAutoconfigureType { + EnvVariable = 0, + Custom = 1 +} + +// Equivalent in positron.d.ts API: LanguageModelAutoconfigure +export type IPositronLanguageModelAutoconfigure = ( + { + type: LanguageModelAutoconfigureType.EnvVariable; + key: string; + signedIn: boolean; + } | + { + type: LanguageModelAutoconfigureType.Custom; + message: string; + signedIn: boolean; + } +); + // Equivalent in positron.d.ts API: LanguageModelConfig export interface IPositronLanguageModelConfig { type: PositronLanguageModelType; @@ -67,7 +87,7 @@ export interface IPositronLanguageModelConfig { maxInputTokens?: number; maxOutputTokens?: number; completions?: boolean; - apiKeyEnvVar?: { key: string; signedIn: boolean }; + autoconfigure?: IPositronLanguageModelAutoconfigure; } //#endregion