From f45d67ec997135e0ec630a889cc90d5aa60e91ab Mon Sep 17 00:00:00 2001 From: kylebunting Date: Mon, 29 Jul 2024 14:37:01 -0600 Subject: [PATCH 01/13] Adding default status message when creating a new operation --- src/dotnet/State/Services/StateService.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dotnet/State/Services/StateService.cs b/src/dotnet/State/Services/StateService.cs index 960c0780d8..cac38a368d 100644 --- a/src/dotnet/State/Services/StateService.cs +++ b/src/dotnet/State/Services/StateService.cs @@ -53,6 +53,7 @@ public async Task CreateLongRunningOperation(string operat var operation = new LongRunningOperation { Status = OperationStatus.Pending, + StatusMessage = "Operation was submitted and is pending execution.", OperationId = operationId }; return await cosmosDbService.UpsertLongRunningOperation(operation); From 2d0a848663048b1805c6a56ea7093c9212f53a0e Mon Sep 17 00:00:00 2001 From: kylebunting Date: Mon, 29 Jul 2024 14:37:30 -0600 Subject: [PATCH 02/13] Updating app config key names to conform to new convention --- .../agents/langchain_knowledge_management_agent.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/python/PythonSDK/foundationallm/langchain/agents/langchain_knowledge_management_agent.py b/src/python/PythonSDK/foundationallm/langchain/agents/langchain_knowledge_management_agent.py index 08704837d5..f676e2e8d1 100644 --- a/src/python/PythonSDK/foundationallm/langchain/agents/langchain_knowledge_management_agent.py +++ b/src/python/PythonSDK/foundationallm/langchain/agents/langchain_knowledge_management_agent.py @@ -48,9 +48,9 @@ def get_prediction_from_audio_file(self, attachments: Optional[List[str]] = None try: storage_manager = BlobStorageManager( - account_name=self.config.get_value('FoundationaLLM:Attachment:ResourceProviderService:Storage:AccountName'), + account_name=self.config.get_value('FoundationaLLM:ResourceProviders:Attachment:Storage:AccountName'), container_name=file.split('/')[0], - authentication_type=self.config.get_value('FoundationaLLM:Attachment:ResourceProviderService:Storage:AuthenticationType') + authentication_type=self.config.get_value('FoundationaLLM:ResourceProviders:Attachment:Storage:AuthenticationType') ) except Exception as e: raise e @@ -79,8 +79,8 @@ def get_prediction_from_audio_file(self, attachments: Optional[List[str]] = None audio_embeddings = clap_model.get_audio_embeddings(file_paths, resample=True) data = audio_embeddings.numpy().tolist() - base_url = self.config.get_value('FoundationaLLM:APIs:AudioClassificationAPI:APIUrl').rstrip('/') - endpoint = self.config.get_value('FoundationaLLM:APIs:AudioClassificationAPI:Classification:PredictionEndpoint') + base_url = self.config.get_value('FoundationaLLM:APIEndpoints:AudioClassificationAPI:APIUrl').rstrip('/') + endpoint = self.config.get_value('FoundationaLLM:APIEndpoints:AudioClassificationAPI:Classification:PredictionEndpoint') api_endpoint = f'{base_url}{endpoint}' # Create the embeddings payload. From e4e35c7dd6b759a8bfe50d97a66552ec27c9980a Mon Sep 17 00:00:00 2001 From: kylebunting Date: Mon, 29 Jul 2024 14:41:20 -0600 Subject: [PATCH 03/13] Updating app config keys to conform to new convention --- src/python/AgentHubAPI/app/dependencies.py | 2 +- src/python/DataSourceHubAPI/app/dependencies.py | 2 +- src/python/GatekeeperIntegrationAPI/app/dependencies.py | 2 +- src/python/LangChainAPI/app/dependencies.py | 2 +- src/python/PromptHubAPI/app/dependencies.py | 2 +- .../foundationallm/integration/config/configuration_tests.py | 4 ++-- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/python/AgentHubAPI/app/dependencies.py b/src/python/AgentHubAPI/app/dependencies.py index 4bae2d5dec..6153e2452a 100644 --- a/src/python/AgentHubAPI/app/dependencies.py +++ b/src/python/AgentHubAPI/app/dependencies.py @@ -45,7 +45,7 @@ def validate_api_key_header(x_api_key: str = Depends(APIKeyHeader(name='X-API-Ke Returns True of the X-API-Key value from the request header matches the expected value. Otherwise, returns False. """ - result = x_api_key == get_config().get_value(f'FoundationaLLM:APIs:{API_NAME}:APIKey') + result = x_api_key == get_config().get_value(f'FoundationaLLM:APIEndpoints:{API_NAME}:APIKey') if not result: logging.error('Invalid API key. You must provide a valid API key in the X-API-KEY header.') diff --git a/src/python/DataSourceHubAPI/app/dependencies.py b/src/python/DataSourceHubAPI/app/dependencies.py index 3d112c2c96..760784c8b2 100644 --- a/src/python/DataSourceHubAPI/app/dependencies.py +++ b/src/python/DataSourceHubAPI/app/dependencies.py @@ -46,7 +46,7 @@ def validate_api_key_header(x_api_key: str = Depends(APIKeyHeader(name='X-API-Ke Otherwise, returns False. """ - result = x_api_key == get_config().get_value(f'FoundationaLLM:APIs:{API_NAME}:APIKey') + result = x_api_key == get_config().get_value(f'FoundationaLLM:APIEndpoints:{API_NAME}:APIKey') if not result: logging.error('Invalid API key. You must provide a valid API key in the X-API-KEY header.') diff --git a/src/python/GatekeeperIntegrationAPI/app/dependencies.py b/src/python/GatekeeperIntegrationAPI/app/dependencies.py index 67c2108cd3..1568beb07b 100644 --- a/src/python/GatekeeperIntegrationAPI/app/dependencies.py +++ b/src/python/GatekeeperIntegrationAPI/app/dependencies.py @@ -46,7 +46,7 @@ def validate_api_key_header(x_api_key: str = Depends(APIKeyHeader(name='X-API-Ke Otherwise, returns False. """ - result = x_api_key == get_config().get_value(f'FoundationaLLM:APIs:{API_NAME}:APIKey') + result = x_api_key == get_config().get_value(f'FoundationaLLM:APIEndpoints:{API_NAME}:APIKey') if not result: logging.error('Invalid API key. You must provide a valid API key in the X-API-KEY header.') diff --git a/src/python/LangChainAPI/app/dependencies.py b/src/python/LangChainAPI/app/dependencies.py index bdc4d4e30c..72b8fc5fc0 100644 --- a/src/python/LangChainAPI/app/dependencies.py +++ b/src/python/LangChainAPI/app/dependencies.py @@ -45,7 +45,7 @@ async def validate_api_key_header(x_api_key: str = Depends(APIKeyHeader(name='X- Otherwise, returns False. """ - result = x_api_key == get_config().get_value(f'FoundationaLLM:APIs:{API_NAME}:APIKey') + result = x_api_key == get_config().get_value(f'FoundationaLLM:APIEndpoints:{API_NAME}:APIKey') if not result: logging.error('Invalid API key. You must provide a valid API key in the X-API-KEY header.') diff --git a/src/python/PromptHubAPI/app/dependencies.py b/src/python/PromptHubAPI/app/dependencies.py index a2bd235868..e8875ba35f 100644 --- a/src/python/PromptHubAPI/app/dependencies.py +++ b/src/python/PromptHubAPI/app/dependencies.py @@ -46,7 +46,7 @@ def validate_api_key_header(x_api_key: str = Depends(APIKeyHeader(name='X-API-Ke Otherwise, returns False. """ - result = x_api_key == get_config().get_value(f'FoundationaLLM:APIs:{API_NAME}:APIKey') + result = x_api_key == get_config().get_value(f'FoundationaLLM:APIEndpoints:{API_NAME}:APIKey') if not result: logging.error('Invalid API key. You must provide a valid API key in the X-API-KEY header.') diff --git a/tests/python/IntegrationSDK.Tests/foundationallm/integration/config/configuration_tests.py b/tests/python/IntegrationSDK.Tests/foundationallm/integration/config/configuration_tests.py index c8a27c3c75..373600db11 100644 --- a/tests/python/IntegrationSDK.Tests/foundationallm/integration/config/configuration_tests.py +++ b/tests/python/IntegrationSDK.Tests/foundationallm/integration/config/configuration_tests.py @@ -15,11 +15,11 @@ class ConfigurationTests: This test class also expects a valid Azure credential (DefaultAzureCredential) session. """ def test_configuration_retrieves_key(self, test_config): - setting = test_config.get_value("FoundationaLLM:APIs:GatekeeperIntegrationAPI:APIUrl") + setting = test_config.get_value("FoundationaLLM:APIEndpoints:GatekeeperIntegrationAPI:APIUrl") print(setting) assert setting is not None def test_configuration_retrieves_key_from_keyvault(self, test_config): - setting = test_config.get_value("FoundationaLLM:APIs:GatekeeperIntegrationAPI:APIKey") + setting = test_config.get_value("FoundationaLLM:APIEndpoints:GatekeeperIntegrationAPI:APIKey") print(setting) assert setting is not None From 5231f4d2e3e5048676d23ac71b8f91430a901e0a Mon Sep 17 00:00:00 2001 From: kylebunting Date: Mon, 29 Jul 2024 14:41:53 -0600 Subject: [PATCH 04/13] Fixes to LangChain completions endpoint based on testing --- .../LangChainAPI/app/routers/completions.py | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/src/python/LangChainAPI/app/routers/completions.py b/src/python/LangChainAPI/app/routers/completions.py index 8a7d65024b..6b0d4948a5 100644 --- a/src/python/LangChainAPI/app/routers/completions.py +++ b/src/python/LangChainAPI/app/routers/completions.py @@ -14,7 +14,7 @@ Response, status ) -from foundationallm.config import Configuration, Context +from foundationallm.config import Configuration from foundationallm.models.operations import ( LongRunningOperation, LongRunningOperationLogEntry, @@ -130,14 +130,11 @@ async def create_completion_response( span.set_attribute('user_identity', x_user_identity) # Change the operation status to 'InProgress' using an async task. - loop = asyncio.get_running_loop() - loop.create_task( - operations_manager.update_operation( - operation_id, - instance_id, - status = OperationStatus.INPROGRESS, - status_message = f'Operation state changed to {OperationStatus.INPROGRESS}.' - ) + await operations_manager.update_operation( + operation_id, + instance_id, + status = OperationStatus.INPROGRESS, + status_message = 'Operation state changed to in progress.' ) # Create an orchestration manager to process the completion request. @@ -162,15 +159,25 @@ async def create_completion_response( ) ) except Exception as e: - # TODO: Log the error and return an appropriate response to the caller. # Send the completion response to the State API and mark the operation as failed. print(f'Operation {operation_id} failed with error: {e}') - await operations_manager.update_operation( - operation_id, - instance_id, - status = OperationStatus.FAILED, - status_message = f'Operation failed with error: {e}' + completion = CompletionResponse( + operation_id = operation_id, + user_prompt=completion_request.user_prompt, + completion=f'Operation failed with error: {e}' ) + await asyncio.gather( + operations_manager.set_operation_result( + operation_id=operation_id, + instance_id=instance_id, + completion_response=completion), + operations_manager.update_operation( + operation_id=operation_id, + instance_id=instance_id, + status = OperationStatus.FAILED, + status_message = f'Operation failed with error: {e}' + ) + ) @router.get( '/async-completions/{operation_id}/status', @@ -192,8 +199,8 @@ async def get_operation_status( try: span.set_attribute('operation_id', operation_id) span.set_attribute('instance_id', instance_id) - - operation = operations_manager.get_operation_state( + + operation = await operations_manager.get_operation( operation_id, instance_id ) @@ -226,7 +233,7 @@ async def get_operation_result( span.set_attribute('operation_id', operation_id) span.set_attribute('instance_id', instance_id) - completion_response = operations_manager.get_operation_result( + completion_response = await operations_manager.get_operation_result( operation_id, instance_id ) @@ -239,7 +246,7 @@ async def get_operation_result( handle_exception(e) @router.get( - '/async-completions/{operation_id}/log', + '/async-completions/{operation_id}/logs', summary = 'Retrieve the log of operational steps for the specified operation ID.', responses = { 200: {'description': 'The operation log was retrieved successfully.'}, @@ -259,7 +266,7 @@ async def get_operation_log( span.set_attribute('operation_id', operation_id) span.set_attribute('instance_id', instance_id) - log = operations_manager.get_operation_log( + log = await operations_manager.get_operation_log( operation_id, instance_id ) From 1977404dfd2681dc527fa45c0df9b99e759d5a65 Mon Sep 17 00:00:00 2001 From: kylebunting Date: Mon, 29 Jul 2024 14:42:18 -0600 Subject: [PATCH 05/13] Making status message and last_updated optional --- .../models/operations/long_running_operation.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/python/PythonSDK/foundationallm/models/operations/long_running_operation.py b/src/python/PythonSDK/foundationallm/models/operations/long_running_operation.py index 02d9ec7cc3..6b326fad51 100644 --- a/src/python/PythonSDK/foundationallm/models/operations/long_running_operation.py +++ b/src/python/PythonSDK/foundationallm/models/operations/long_running_operation.py @@ -1,5 +1,6 @@ from datetime import datetime from pydantic import BaseModel, Field +from typing import Optional class LongRunningOperation(BaseModel): """ @@ -7,6 +8,6 @@ class LongRunningOperation(BaseModel): """ operation_id: str = Field(description='The unique identifier for the operation.') status: str = Field(description='The status of the operation.') - status_message: str = Field(description='The message associated with the operation status.') - last_updated: datetime = Field(default=datetime.now(), description='The timestamp of the last update to the operation.') + status_message: Optional[str] = Field(description='The message associated with the operation status.') + last_updated: Optional[datetime] = Field(default=None, description='The timestamp of the last update to the operation.') From 8f0a418f00eacdd7c7ac022398be04b906505136 Mon Sep 17 00:00:00 2001 From: kylebunting Date: Mon, 29 Jul 2024 14:42:32 -0600 Subject: [PATCH 06/13] Adding additional fields returned by the State API --- .../models/operations/long_running_operation_log_entry.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/python/PythonSDK/foundationallm/models/operations/long_running_operation_log_entry.py b/src/python/PythonSDK/foundationallm/models/operations/long_running_operation_log_entry.py index 5f333b3ed9..50911b84ee 100644 --- a/src/python/PythonSDK/foundationallm/models/operations/long_running_operation_log_entry.py +++ b/src/python/PythonSDK/foundationallm/models/operations/long_running_operation_log_entry.py @@ -4,6 +4,9 @@ class LongRunningOperationLogEntry(BaseModel): """ Represents an entry in the operation execution log. """ - timestamp: str = Field(description='The timestamp of the log entry.') + id: str = Field(description='The unique id of the log entry.') + type: str = Field(description='The type of log entry') + operation_id: str = Field(description='The unique id of the operation.') + time_stamp: str = Field(description='The timestamp of the log entry.') status: str = Field(description='The status of the operation at the time of the log entry.') status_message: str = Field(description='The status message of the log entry.') From fd08793333f69006778c85ed4978dae2cf70f7c8 Mon Sep 17 00:00:00 2001 From: kylebunting Date: Mon, 29 Jul 2024 14:42:57 -0600 Subject: [PATCH 07/13] Updating app config keys to conform to new convention --- .../PythonSDK/foundationallm/operations/operations_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/python/PythonSDK/foundationallm/operations/operations_manager.py b/src/python/PythonSDK/foundationallm/operations/operations_manager.py index 766b51ec4f..fae69ca37d 100644 --- a/src/python/PythonSDK/foundationallm/operations/operations_manager.py +++ b/src/python/PythonSDK/foundationallm/operations/operations_manager.py @@ -17,8 +17,8 @@ class OperationsManager(): def __init__(self, config: Configuration): self.config = config # Retrieve the State API configuration settings. - self.state_api_url = config.get_value('FoundationaLLM:APIs:StateAPI:APIUrl').rstrip('/') - self.state_api_key = config.get_value('FoundationaLLM:APIs:StateAPI:APIKey') + self.state_api_url = config.get_value('FoundationaLLM:APIEndpoints:StateAPI:APIUrl').rstrip('/') + self.state_api_key = config.get_value('FoundationaLLM:APIEndpoints:StateAPI:APIKey') async def create_operation( self, From 30285272a309134b8321a6276a3e7bc62a08cd4f Mon Sep 17 00:00:00 2001 From: kylebunting Date: Mon, 29 Jul 2024 14:43:43 -0600 Subject: [PATCH 08/13] Removing operation payload from create_operation call to State API --- .../operations/operations_manager.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/python/PythonSDK/foundationallm/operations/operations_manager.py b/src/python/PythonSDK/foundationallm/operations/operations_manager.py index fae69ca37d..4e311d46e6 100644 --- a/src/python/PythonSDK/foundationallm/operations/operations_manager.py +++ b/src/python/PythonSDK/foundationallm/operations/operations_manager.py @@ -41,13 +41,6 @@ async def create_operation( LongRunningOperation Object representing the operation. """ - operation = LongRunningOperation( - operation_id=operation_id, - status=OperationStatus.PENDING, - status_message='Operation was submitted and is pending execution.', - last_updated=datetime.now() - ) - try: headers = { "x-api-key": self.state_api_key, @@ -58,14 +51,14 @@ async def create_operation( # Call the State API to create a new operation. r = requests.post( f'{self.state_api_url}/instances/{instance_id}/operations/{operation_id}', - json=json.dumps(operation.__dict__, default=str), - headers=headers + headers=headers, + verify=False ) if r.status_code != 200: - raise Exception(f'An error occurred while retrieving the result of operation {operation_id}: ({r.status_code}) {r.text}') + raise Exception(f'An error occurred while creating the operation {operation_id}: ({r.status_code}) {r.text}') - return operation + return r.json() except Exception as e: raise e From 59486d756096b1d04e8dc275e4f14685a24b2740 Mon Sep 17 00:00:00 2001 From: kylebunting Date: Mon, 29 Jul 2024 14:45:54 -0600 Subject: [PATCH 09/13] Updating return values and error messages --- .../operations/operations_manager.py | 53 +++++++++---------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/src/python/PythonSDK/foundationallm/operations/operations_manager.py b/src/python/PythonSDK/foundationallm/operations/operations_manager.py index 4e311d46e6..4b1d2a598c 100644 --- a/src/python/PythonSDK/foundationallm/operations/operations_manager.py +++ b/src/python/PythonSDK/foundationallm/operations/operations_manager.py @@ -1,6 +1,5 @@ import json import requests -from datetime import datetime from typing import List from foundationallm.config import Configuration from foundationallm.models.operations import ( @@ -40,7 +39,7 @@ async def create_operation( ------- LongRunningOperation Object representing the operation. - """ + """ try: headers = { "x-api-key": self.state_api_key, @@ -91,10 +90,9 @@ async def update_operation(self, operation = LongRunningOperation( operation_id=operation_id, status=status, - status_message=status_message, - last_updated=datetime.now() + status_message=status_message ) - + try: # Call the State API to create a new operation. headers = { @@ -105,21 +103,22 @@ async def update_operation(self, r = requests.put( f'{self.state_api_url}/instances/{instance_id}/operations/{operation_id}', - json=json.dumps(operation.__dict__, default=str), - headers=headers + json=operation.model_dump(exclude_unset=True), + headers=headers, + verify=False ) if r.status_code == 404: return None if r.status_code != 200: - raise Exception(f'An error occurred while retrieving the result of operation {operation_id}: ({r.status_code}) {r.text}') + raise Exception(f'An error occurred while updating the status of operation {operation_id}: ({r.status_code}) {r.text}') - return operation + return r.json() except Exception as e: raise e - async def get_operation_status( + async def get_operation( self, operation_id: str, instance_id: str) -> LongRunningOperation: @@ -148,20 +147,21 @@ async def get_operation_status( "Content-Type":"application/json" } + print(f'status endpoint: {self.state_api_url}/instances/{instance_id}/operations/{operation_id}') + r = requests.get( f'{self.state_api_url}/instances/{instance_id}/operations/{operation_id}', - headers=headers + headers=headers, + verify=False ) if r.status_code == 404: return None if r.status_code != 200: - raise Exception(f'An error occurred while retrieving the result of operation {operation_id}: ({r.status_code}) {r.text}') + raise Exception(f'An error occurred while retrieving the status of the operation {operation_id}: ({r.status_code}) {r.text}') - operation_json = json.loads(r.json()) - operation = LongRunningOperation(**operation_json) - return operation + return r.json() except Exception as e: raise e @@ -194,15 +194,16 @@ async def set_operation_result( r = requests.post( f'{self.state_api_url}/instances/{instance_id}/operations/{operation_id}/result', - json=json.dumps(completion_response.__dict__, default=str), - headers=headers + json=completion_response.model_dump(), + headers=headers, + verify=False ) if r.status_code == 404: return None if r.status_code != 200: - raise Exception(f'An error occurred while retrieving the result of operation {operation_id}: ({r.status_code}) {r.text}') + raise Exception(f'An error occurred while submitting the result of operation {operation_id}: ({r.status_code}) {r.text}') except Exception as e: raise e @@ -238,7 +239,8 @@ async def get_operation_result( r = requests.get( f'{self.state_api_url}/instances/{instance_id}/operations/{operation_id}/result', - headers=headers + headers=headers, + verify=False ) if r.status_code == 404: @@ -247,9 +249,7 @@ async def get_operation_result( if r.status_code != 200: raise Exception(f'An error occurred while retrieving the result of operation {operation_id}: ({r.status_code}) {r.text}') - completion_json = json.loads(r.json()) - completion = CompletionResponse(**completion_json) - return completion + return r.json() except Exception as e: raise e @@ -283,8 +283,9 @@ async def get_operation_log( } r = requests.get( - f'{self.state_api_url}/instances/{instance_id}/operations/{operation_id}/log', - headers=headers + f'{self.state_api_url}/instances/{instance_id}/operations/{operation_id}/logs', + headers=headers, + verify=False ) if r.status_code == 404: @@ -293,8 +294,6 @@ async def get_operation_log( if r.status_code != 200: raise Exception(f'An error occurred while retrieving the log of steps for the operation {operation_id}: ({r.status_code}) {r.text}') - log_json = json.loads(r.json()) - log = CompletionResponse(**log_json) - return log + return r.json() except Exception as e: raise e From 24744be3bf1d1bb15a096d221e03d400fbd22b50 Mon Sep 17 00:00:00 2001 From: kylebunting Date: Mon, 29 Jul 2024 14:52:15 -0600 Subject: [PATCH 10/13] Disabling SSL verification on requests when running locally (env = dev) --- .../operations/operations_manager.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/python/PythonSDK/foundationallm/operations/operations_manager.py b/src/python/PythonSDK/foundationallm/operations/operations_manager.py index 4b1d2a598c..c1d4ce47f2 100644 --- a/src/python/PythonSDK/foundationallm/operations/operations_manager.py +++ b/src/python/PythonSDK/foundationallm/operations/operations_manager.py @@ -1,4 +1,5 @@ import json +import os import requests from typing import List from foundationallm.config import Configuration @@ -18,6 +19,7 @@ def __init__(self, config: Configuration): # Retrieve the State API configuration settings. self.state_api_url = config.get_value('FoundationaLLM:APIEndpoints:StateAPI:APIUrl').rstrip('/') self.state_api_key = config.get_value('FoundationaLLM:APIEndpoints:StateAPI:APIKey') + self.env = os.environ.get('env', 'prod') async def create_operation( self, @@ -51,7 +53,7 @@ async def create_operation( r = requests.post( f'{self.state_api_url}/instances/{instance_id}/operations/{operation_id}', headers=headers, - verify=False + verify=False if self.env == 'dev' else True ) if r.status_code != 200: @@ -105,7 +107,7 @@ async def update_operation(self, f'{self.state_api_url}/instances/{instance_id}/operations/{operation_id}', json=operation.model_dump(exclude_unset=True), headers=headers, - verify=False + verify=False if self.env == 'dev' else True ) if r.status_code == 404: @@ -152,7 +154,7 @@ async def get_operation( r = requests.get( f'{self.state_api_url}/instances/{instance_id}/operations/{operation_id}', headers=headers, - verify=False + verify=False if self.env == 'dev' else True ) if r.status_code == 404: @@ -196,7 +198,7 @@ async def set_operation_result( f'{self.state_api_url}/instances/{instance_id}/operations/{operation_id}/result', json=completion_response.model_dump(), headers=headers, - verify=False + verify=False if self.env == 'dev' else True ) if r.status_code == 404: @@ -240,7 +242,7 @@ async def get_operation_result( r = requests.get( f'{self.state_api_url}/instances/{instance_id}/operations/{operation_id}/result', headers=headers, - verify=False + verify=False if self.env == 'dev' else True ) if r.status_code == 404: @@ -285,7 +287,7 @@ async def get_operation_log( r = requests.get( f'{self.state_api_url}/instances/{instance_id}/operations/{operation_id}/logs', headers=headers, - verify=False + verify=False if self.env == 'dev' else True ) if r.status_code == 404: From 8750bea3cf48cfea5a99a97c6bb2d3cd0054c066 Mon Sep 17 00:00:00 2001 From: kylebunting Date: Mon, 29 Jul 2024 14:58:55 -0600 Subject: [PATCH 11/13] Updating env variable name --- .../PythonSDK/foundationallm/operations/operations_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python/PythonSDK/foundationallm/operations/operations_manager.py b/src/python/PythonSDK/foundationallm/operations/operations_manager.py index c1d4ce47f2..e7c00166ad 100644 --- a/src/python/PythonSDK/foundationallm/operations/operations_manager.py +++ b/src/python/PythonSDK/foundationallm/operations/operations_manager.py @@ -19,7 +19,7 @@ def __init__(self, config: Configuration): # Retrieve the State API configuration settings. self.state_api_url = config.get_value('FoundationaLLM:APIEndpoints:StateAPI:APIUrl').rstrip('/') self.state_api_key = config.get_value('FoundationaLLM:APIEndpoints:StateAPI:APIKey') - self.env = os.environ.get('env', 'prod') + self.env = os.environ.get('FOUNDATIONALLM_ENV', 'prod') async def create_operation( self, From 770cb4a9cb8aa299107ee1e9574cf7585dcc4761 Mon Sep 17 00:00:00 2001 From: kylebunting Date: Mon, 29 Jul 2024 15:06:16 -0600 Subject: [PATCH 12/13] Adding FOUNDATIONALLM_ENV environment variable documentation for local dev. --- docs/development/development-local.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/development/development-local.md b/docs/development/development-local.md index 2ddc1425e6..3f47e051a3 100644 --- a/docs/development/development-local.md +++ b/docs/development/development-local.md @@ -337,11 +337,15 @@ The `CoreWorker` project is a .NET worker service that acts as the Cosmos DB cha ### Python Environment Variables -Create a local environment variable named `FOUNDATIONALLM_APP_CONFIGURATION_URI`. The value should be the URI of the Azure App Configuration service and _not_ the connection string. We use role-based access controls (RBAC) to access the Azure App Configuration service, so the connection string is not required. +Create local environment variables named: + +- `FOUNDATIONALLM_APP_CONFIGURATION_URI`. The value should be the URI of the Azure App Configuration service and _not_ the connection string. We use role-based access controls (RBAC) to access the Azure App Configuration service, so the connection string is not required. +- `FOUNDATIONALLM_ENV`: This is required by the `OperationsManager` to disable the `verify` setting on requests. By setting the value to `dev`, it allows calls to the State API from LangChain and other Python-based APIs when running locally (`FOUNDATIONALLM_ENV` = `dev`) by disabling the check for a valid SSL cert on requests. This is only necessary when running the State API locally. Otherwise, the setting should be set to `prod`. | Name | Value | Description | | ---- | ----- | ----------- | | FOUNDATIONALLM_APP_CONFIGURATION_URI | REDACTED | Azure App Configuration URI | +| FOUNDATIONALLM_ENV | `dev` or `prod` | Environment specification. Acceptable values are `dev` and `prod`. Defaults to `prod` if not set. | ## Running the solution locally From 0c76f20606990fee35bca9ded27677fcc0ccb953 Mon Sep 17 00:00:00 2001 From: kylebunting Date: Tue, 30 Jul 2024 07:48:38 -0600 Subject: [PATCH 13/13] Moving logic to single location --- .../operations/operations_manager.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/python/PythonSDK/foundationallm/operations/operations_manager.py b/src/python/PythonSDK/foundationallm/operations/operations_manager.py index e7c00166ad..31ee3f1c61 100644 --- a/src/python/PythonSDK/foundationallm/operations/operations_manager.py +++ b/src/python/PythonSDK/foundationallm/operations/operations_manager.py @@ -19,7 +19,8 @@ def __init__(self, config: Configuration): # Retrieve the State API configuration settings. self.state_api_url = config.get_value('FoundationaLLM:APIEndpoints:StateAPI:APIUrl').rstrip('/') self.state_api_key = config.get_value('FoundationaLLM:APIEndpoints:StateAPI:APIKey') - self.env = os.environ.get('FOUNDATIONALLM_ENV', 'prod') + env = os.environ.get('FOUNDATIONALLM_ENV', 'prod') + self.verify_certs = False if env == 'dev' else True async def create_operation( self, @@ -53,7 +54,7 @@ async def create_operation( r = requests.post( f'{self.state_api_url}/instances/{instance_id}/operations/{operation_id}', headers=headers, - verify=False if self.env == 'dev' else True + verify=self.verify_certs ) if r.status_code != 200: @@ -107,7 +108,7 @@ async def update_operation(self, f'{self.state_api_url}/instances/{instance_id}/operations/{operation_id}', json=operation.model_dump(exclude_unset=True), headers=headers, - verify=False if self.env == 'dev' else True + verify=self.verify_certs ) if r.status_code == 404: @@ -154,7 +155,7 @@ async def get_operation( r = requests.get( f'{self.state_api_url}/instances/{instance_id}/operations/{operation_id}', headers=headers, - verify=False if self.env == 'dev' else True + verify=self.verify_certs ) if r.status_code == 404: @@ -198,7 +199,7 @@ async def set_operation_result( f'{self.state_api_url}/instances/{instance_id}/operations/{operation_id}/result', json=completion_response.model_dump(), headers=headers, - verify=False if self.env == 'dev' else True + verify=self.verify_certs ) if r.status_code == 404: @@ -242,7 +243,7 @@ async def get_operation_result( r = requests.get( f'{self.state_api_url}/instances/{instance_id}/operations/{operation_id}/result', headers=headers, - verify=False if self.env == 'dev' else True + verify=self.verify_certs ) if r.status_code == 404: @@ -287,7 +288,7 @@ async def get_operation_log( r = requests.get( f'{self.state_api_url}/instances/{instance_id}/operations/{operation_id}/logs', headers=headers, - verify=False if self.env == 'dev' else True + verify=self.verify_certs ) if r.status_code == 404: