From 3e14a8fecfc8192d9e67bf29f158650ac8f236c3 Mon Sep 17 00:00:00 2001 From: "Ankush Pala ankush@lastmileai.dev" <> Date: Mon, 4 Mar 2024 19:35:47 -0500 Subject: [PATCH 1/3] [server] 5/n Update AIConfig edit|start commands to pass env Summary: This diff modifies the AIConfig start and edit commands to take in an dot env path. Due to core utils logic of handling args, the help messages automatically get updated too. Also Removes the loadenv() on server init. This is okay because loadenv is invoked at the run step. [see 4/n](https://github.com/lastmile-ai/aiconfig/pull/1384) Test Plan: ``` ankush@ap-mbp aiconfig % aiconfig start -h /opt/homebrew/lib/python3.11/site-packages/pydantic/_internal/_fields.py:151: UserWarning: Field "model_parsers" has conflict with protected namespace "model_". You may be able to resolve this warning by setting `model_config['protected_namespaces'] = ()`. warnings.warn( usage: aiconfig start [-h] [--server-port SERVER_PORT] [--log-level LOG_LEVEL] [--server-mode {debug_servers,debug_backend,prod}] [--parsers-module-path PARSERS_MODULE_PATH] [--env-file-path ENV_FILE_PATH] options: -h, --help show this help message and exit --server-port SERVER_PORT --log-level LOG_LEVEL --server-mode {debug_servers,debug_backend,prod} --parsers-module-path PARSERS_MODULE_PATH **--env-file-path ENV_FILE_PATH** ``` --- python/src/aiconfig/editor/server/server_utils.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/python/src/aiconfig/editor/server/server_utils.py b/python/src/aiconfig/editor/server/server_utils.py index 0788f45da..753a069c9 100644 --- a/python/src/aiconfig/editor/server/server_utils.py +++ b/python/src/aiconfig/editor/server/server_utils.py @@ -10,7 +10,7 @@ from textwrap import dedent from threading import Event from types import ModuleType -from typing import Any, Callable, NewType, Type, TypeVar, cast +from typing import Any, Callable, NewType, Type, TypeVar, cast, Optional import lastmile_utils.lib.core.api as core_utils import result @@ -66,6 +66,7 @@ class EditServerConfig(core_utils.Record): log_level: str | int = "INFO" server_mode: ServerMode = ServerMode.PROD parsers_module_path: str = "aiconfig_model_registry.py" + env_file_path: Optional[str] = None @field_validator("server_mode", mode="before") def convert_to_mode( @@ -84,6 +85,7 @@ class StartServerConfig(core_utils.Record): log_level: str | int = "INFO" server_mode: ServerMode = ServerMode.PROD parsers_module_path: str = "aiconfig_model_registry.py" + env_file_path: Optional[str] = None @field_validator("server_mode", mode="before") def convert_to_mode( @@ -294,13 +296,12 @@ def init_server_state( aiconfigrc_path: str, ) -> Result[None, str]: LOGGER.info("Initializing server state for 'edit' command") - # TODO: saqadri - load specific .env file if specified - dotenv.load_dotenv() _load_user_parser_module_if_exists( initialization_settings.parsers_module_path ) state = get_server_state(app) state.aiconfigrc_path = aiconfigrc_path + state.env_file_path = initialization_settings.env_file_path if isinstance(initialization_settings, StartServerConfig): # The aiconfig will be loaded later, when the editor sends the payload. From d4782eb437e628887fe99d5c4f9854282104ba96 Mon Sep 17 00:00:00 2001 From: "Ankush Pala ankush@lastmileai.dev" <> Date: Mon, 4 Mar 2024 19:35:47 -0500 Subject: [PATCH 2/3] [vscode] 6/n update Route Table with env api Summary: Stack contains changes that updates overall environment setup flow for vscode extension. This diff adds support to call "SET_ENV_FILE_PATH" implemented in the previous diff. No functional changes. Test Plan: --- vscode-extension/src/util.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vscode-extension/src/util.ts b/vscode-extension/src/util.ts index dd6ee61f3..ebbfcbd06 100644 --- a/vscode-extension/src/util.ts +++ b/vscode-extension/src/util.ts @@ -45,6 +45,8 @@ export const EDITOR_SERVER_ROUTE_TABLE = { urlJoin(hostUrl, EDITOR_SERVER_API_ENDPOINT, "/load_content"), LOAD_MODEL_PARSER_MODULE: (hostUrl: string) => urlJoin(hostUrl, EDITOR_SERVER_API_ENDPOINT, "/load_model_parser_module"), + SET_ENV_FILE_PATH: (hostUrl: string) => + urlJoin(hostUrl, EDITOR_SERVER_API_ENDPOINT, "/set_env_file_path"), }; export async function isServerReady(serverUrl: string) { From a74e8a9e686d32829792ad744bc6a0215c79d72b Mon Sep 17 00:00:00 2001 From: "Ankush Pala ankush@lastmileai.dev" <> Date: Mon, 4 Mar 2024 19:35:47 -0500 Subject: [PATCH 3/3] [vscode] 7/n listeners and .env Summary: When the .env file path is set, vscode will invoke api/set_env on each AIConfig server. Defines a listener to a newly introduced setting for the env_file_path. This ensures that users do not need to restart vscode to pickup env changes. Test Plan: 1. set env file path 2. changes picked up 3. changes picked up on restart 4. Changes picked up when manually removing vscode setting and re-setting env file path and api key https://github.com/lastmile-ai/aiconfig/assets/141073967/07ac1d43-28ef-439f-98e7-94a4e73e11c9 --- vscode-extension/package.json | 5 +++++ vscode-extension/src/constants.ts | 2 ++ .../src/editor_server/editorServer.ts | 4 ++++ vscode-extension/src/extension.ts | 21 +++++++++++++++++++ vscode-extension/src/util.ts | 17 +++++++++++++++ 5 files changed, 49 insertions(+) diff --git a/vscode-extension/package.json b/vscode-extension/package.json index b2d13c4a8..09bdb06b9 100644 --- a/vscode-extension/package.json +++ b/vscode-extension/package.json @@ -128,6 +128,11 @@ "type": "string", "default": "", "description": "Version of the AIConfig Editor extension last time it was activated. We use this value to check if the extension has been updated to prompt users to reload VS Code" + }, + "vscode-aiconfig.env_file_path": { + "type": "string", + "default": "", + "description": "File path to your .env file containing your API keys" } } }, diff --git a/vscode-extension/src/constants.ts b/vscode-extension/src/constants.ts index a2033eb9b..0741b4b25 100644 --- a/vscode-extension/src/constants.ts +++ b/vscode-extension/src/constants.ts @@ -3,3 +3,5 @@ export const PYTHON_INTERPRETER_CACHE_KEY_NAME = "pythonInterpreter"; // Used for prompting user to reload VS Code window on update, and ensuring // that walkthrough gets shown on first install export const VERSION_KEY_NAME = "version"; + +export const ENV_FILE_PATH = "env_file_path"; \ No newline at end of file diff --git a/vscode-extension/src/editor_server/editorServer.ts b/vscode-extension/src/editor_server/editorServer.ts index c548d9898..0ae6710b2 100644 --- a/vscode-extension/src/editor_server/editorServer.ts +++ b/vscode-extension/src/editor_server/editorServer.ts @@ -3,6 +3,7 @@ import { EXTENSION_NAME } from "../util"; import { getPythonPath } from "../utilities/pythonSetupUtils"; import { ChildProcessWithoutNullStreams, spawn } from "child_process"; import serverPortManager from "./serverPortManager"; +import { ENV_FILE_PATH } from "../constants"; export enum EditorServerState { Starting = "Starting", @@ -73,6 +74,8 @@ export class EditorServer { const modelRegistryPathArgs = modelRegistryPath ? ["--parsers-module-path", modelRegistryPath] : []; + const filePath = config.get(ENV_FILE_PATH) as string; + const envFilePathArgs = filePath ? ["--env-file-path", filePath] : []; this.port = await serverPortManager.getPort(); @@ -89,6 +92,7 @@ export class EditorServer { "--server-port", this.port.toString(), ...modelRegistryPathArgs, + ...envFilePathArgs, ], { cwd: this.cwd, diff --git a/vscode-extension/src/extension.ts b/vscode-extension/src/extension.ts index 84602c42e..e3a577d3d 100644 --- a/vscode-extension/src/extension.ts +++ b/vscode-extension/src/extension.ts @@ -27,6 +27,7 @@ import { setupEnvironmentVariables, getConfigurationTarget, getModeFromDocument, + updateServerEnv } from "./util"; import { initialize, @@ -34,6 +35,7 @@ import { savePythonInterpreterToCache, } from "./utilities/pythonSetupUtils"; import { performVersionInstallAndUpdateActionsIfNeeded } from "./utilities/versionUpdateUtils"; +import { ENV_FILE_PATH } from "./constants"; // This method is called when your extension is activated // Your extension is activated the very first time a command is executed @@ -208,6 +210,25 @@ export async function activate(context: vscode.ExtensionContext) { } ) ); + + // Handle changes to the .env path + context.subscriptions.push(vscode.workspace.onDidChangeConfiguration( async (event) => { + if (event.affectsConfiguration("vscode-aiconfig" + "." + ENV_FILE_PATH)) { + // Get new env + const envPath = vscode.workspace.getConfiguration("vscode-aiconfig").get(ENV_FILE_PATH) as string; + console.log(`New .env path set: ${envPath}`); + // set env on all AIConfig Servers + const editors: Array = Array.from( + aiconfigEditorManager.getRegisteredEditors() + ); + if (editors.length > 0) { + editors.forEach(async (editor) => { + await updateServerEnv(editor.editorServer.url, envPath); + }); + + } + } + })); } // This method is called when your extension is deactivated diff --git a/vscode-extension/src/util.ts b/vscode-extension/src/util.ts index ebbfcbd06..9d9ad0e4e 100644 --- a/vscode-extension/src/util.ts +++ b/vscode-extension/src/util.ts @@ -8,6 +8,8 @@ import fs from "fs"; import path from "path"; import os from "os"; +import { ENV_FILE_PATH } from "./constants"; + export const EXTENSION_NAME = "vscode-aiconfig"; export const COMMANDS = { INIT: `${EXTENSION_NAME}.init`, @@ -100,6 +102,15 @@ export async function updateServerState( }); } +export async function updateServerEnv(serverUrl: string, filePath: string) { + return await ufetch.post( + EDITOR_SERVER_ROUTE_TABLE.SET_ENV_FILE_PATH(serverUrl), + { + [ENV_FILE_PATH]: filePath, + } + ); +} + // Figure out what kind of AIConfig this is that we are loading export function getModeFromDocument( document: vscode.TextDocument @@ -383,6 +394,12 @@ export async function setupEnvironmentVariables( "Please define your environment variables." ); } + + // Update Server Env FLow + // Set the .env file path in the settings + // vscode Extension has a listener for changes defined at activation. + const config = vscode.workspace.getConfiguration(EXTENSION_NAME); + await config.update(ENV_FILE_PATH, envPath, getConfigurationTarget()); } /**