diff --git a/docs/how-to-guides/deploy-a-flow/deploy-using-dev-server.md b/docs/how-to-guides/deploy-a-flow/deploy-using-dev-server.md index a569ae81cd1..2cc2d591d9d 100644 --- a/docs/how-to-guides/deploy-a-flow/deploy-using-dev-server.md +++ b/docs/how-to-guides/deploy-a-flow/deploy-using-dev-server.md @@ -14,6 +14,13 @@ an example to show how to deploy a flow. Please ensure you have [create the connection](../manage-connections.md#create-a-connection) required by flow, if not, you could refer to [Setup connection for web-classification](https://github.com/microsoft/promptflow/tree/main/examples/flows/standard/web-classification). +Note: We will use relevant environment variable ({connection_name}_{key_name}) to override connection configurations in +serving mode, white space in connection name will be removed directly from environment variable name. For instance, +if there is a custom connection named 'custom_connection' with a configuration key called 'chat_deployment_name,' the +function will attempt to retrieve 'chat_deployment_name' from the environment variable +'CUSTOM_CONNECTION_CHAT_DEPLOYMENT_NAME' by default. If the environment variable is not set, it will use the original +value as a fallback. + The following CLI commands allows you serve a flow folder as an endpoint. By running this command, a [flask](https://flask.palletsprojects.com/en/) app will start in the environment where command is executed, please ensure all prerequisites required by flow have been installed. ```bash diff --git a/src/promptflow/promptflow/_sdk/_serving/flow_invoker.py b/src/promptflow/promptflow/_sdk/_serving/flow_invoker.py index 002c218dce2..62760ca15f0 100644 --- a/src/promptflow/promptflow/_sdk/_serving/flow_invoker.py +++ b/src/promptflow/promptflow/_sdk/_serving/flow_invoker.py @@ -12,6 +12,7 @@ get_local_connections_from_executable, resolve_connections_environment_variable_reference, update_environment_variables_with_connections, + override_connection_config_with_environment_variable, ) from promptflow._sdk.entities._connection import _Connection from promptflow.executor import FlowExecutor @@ -61,6 +62,7 @@ def _init_connections(self, connection_provider): else: raise UnsupportedConnectionProvider(connection_provider) + override_connection_config_with_environment_variable(self.connections) resolve_connections_environment_variable_reference(self.connections) update_environment_variables_with_connections(self.connections) logger.info(f"Promptflow get connections successfully. keys: {self.connections.keys()}") diff --git a/src/promptflow/promptflow/_sdk/_utils.py b/src/promptflow/promptflow/_sdk/_utils.py index 475b3006f7f..38adb9f5b68 100644 --- a/src/promptflow/promptflow/_sdk/_utils.py +++ b/src/promptflow/promptflow/_sdk/_utils.py @@ -317,6 +317,29 @@ def _match_env_reference(val: str): return name +def override_connection_config_with_environment_variable(connections: Dict[str, dict]): + """ + The function will use relevant environment variable to override connection configurations. For instance, if there + is a custom connection named 'custom_connection' with a configuration key called 'chat_deployment_name,' the + function will attempt to retrieve 'chat_deployment_name' from the environment variable + 'CUSTOM_CONNECTION_CHAT_DEPLOYMENT_NAME' by default. If the environment variable is not set, it will use the + original value as a fallback. + """ + logger = logging.getLogger(LOGGER_NAME) + for connection_name, connection in connections.items(): + values = connection.get("value", {}) + for key, val in values.items(): + connection_name = connection_name.replace(" ", "_") + env_name = f"{connection_name}_{key}".upper() + if env_name not in os.environ: + continue + values[key] = os.environ[env_name] + logger.info( + f"Connection {connection_name}'s {key} is overridden with environment variable {env_name}" + ) + return connections + + def resolve_connections_environment_variable_reference(connections: Dict[str, dict]): """The function will resolve connection secrets env var reference like api_key: ${env:KEY}""" for connection in connections.values(): diff --git a/src/promptflow/tests/sdk_cli_test/unittests/test_utils.py b/src/promptflow/tests/sdk_cli_test/unittests/test_utils.py index c3104b06330..c20ad2bca97 100644 --- a/src/promptflow/tests/sdk_cli_test/unittests/test_utils.py +++ b/src/promptflow/tests/sdk_cli_test/unittests/test_utils.py @@ -30,6 +30,7 @@ generate_flow_tools_json, refresh_connections_dir, resolve_connections_environment_variable_reference, + override_connection_config_with_environment_variable, snake_to_camel, ) @@ -87,6 +88,29 @@ def test_resolve_connections_environment_variable_reference(self): assert connections["test_connection"]["value"]["api_base"] == "BASE" assert connections["test_custom_connection"]["value"]["key"] == "CUSTOM_VALUE" + def test_override_connection_config_with_environment_variable(self): + connections = { + "test_connection": { + "type": "AzureOpenAIConnection", + "value": { + "api_key": "KEY", + "api_base": "https://gpt-test-eus.openai.azure.com/", + }, + }, + "test_custom_connection": { + "type": "CustomConnection", + "value": {"key": "value1", "key2": "value2"}, + }, + } + with mock.patch.dict( + os.environ, {"TEST_CONNECTION_API_BASE": "BASE", "TEST_CUSTOM_CONNECTION_KEY": "CUSTOM_VALUE"} + ): + override_connection_config_with_environment_variable(connections) + assert connections["test_connection"]["value"]["api_key"] == "KEY" + assert connections["test_connection"]["value"]["api_base"] == "BASE" + assert connections["test_custom_connection"]["value"]["key"] == "CUSTOM_VALUE" + assert connections["test_custom_connection"]["value"]["key2"] == "value2" + def test_generate_flow_tools_json(self) -> None: # call twice to ensure system path won't be affected during generation for _ in range(2):