Skip to content

Commit

Permalink
Merge pull request #2134 from solliancenet/kb-http-request-improvements
Browse files Browse the repository at this point in the history
Using aiohttp library to allow async HTTP requests in Python SDK
  • Loading branch information
ciprianjichici authored Jan 17, 2025
2 parents 48e8fcc + 15504a0 commit 3e48f13
Show file tree
Hide file tree
Showing 17 changed files with 289 additions and 371 deletions.
2 changes: 1 addition & 1 deletion src/python/LangChainAPI/LangChainAPI.pyproj
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@
</ItemGroup>
<ItemGroup>
<Compile Include="app\dependencies.py" />
<Compile Include="app\lifespan_manager.py" />
<Compile Include="app\main.py" />
<Compile Include="app\routers\manage.py" />
<Compile Include="app\routers\completions.py" />
<Compile Include="app\routers\status.py" />
<Compile Include="app\routers\__init__.py" />
Expand Down
50 changes: 7 additions & 43 deletions src/python/LangChainAPI/app/dependencies.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,16 @@
"""
Provides dependencies for API calls.
"""
import logging
import time
from app.lifespan_manager import get_config
from fastapi import Depends, HTTPException
from fastapi.security import APIKeyHeader
from foundationallm.config import Configuration
from foundationallm.telemetry import Telemetry

__config: Configuration = None
API_NAME = 'LangChainAPI'
# Initialize telemetry logging
logger = Telemetry.get_logger(__name__)

def get_config(action: str = None) -> Configuration:
"""
Obtains the application configuration settings.
Returns
-------
Configuration
Returns the application configuration settings.
"""
global __config

start = time.time()
if action is not None and action=='refresh':
__config = Configuration()
else:
__config = __config or Configuration()
end = time.time()
print(f'Time to load config: {end-start}')
return __config

async def validate_api_key_header(x_api_key: str = Depends(APIKeyHeader(name='X-API-Key'))) -> bool:
async def validate_api_key_header(x_api_key: str = Depends(APIKeyHeader(name='X-API-Key')), config: Configuration = Depends(get_config)) -> bool:
"""
Validates that the X-API-Key value in the request header matches the key expected for this API.
Expand All @@ -44,27 +24,11 @@ async def validate_api_key_header(x_api_key: str = Depends(APIKeyHeader(name='X-
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:APIEndpoints:{API_NAME}:Essentials:APIKey')
result = x_api_key == config.get_value(f'FoundationaLLM:APIEndpoints:LangChainAPI:Essentials:APIKey')

if not result:
logging.error('Invalid API key. You must provide a valid API key in the X-API-KEY header.')
logger.error('Invalid API key. You must provide a valid API key in the X-API-KEY header.')
raise HTTPException(
status_code = 401,
detail = 'Invalid API key. You must provide a valid API key in the X-API-KEY header.'
)

def handle_exception(exception: Exception, status_code: int = 500):
"""
Handles an exception that occurred while processing a request.
Parameters
----------
exception : Exception
The exception that occurred.
"""
logging.error(exception, stack_info=True, exc_info=True)
raise HTTPException(
status_code = status_code,
detail = str(exception)
) from exception
44 changes: 44 additions & 0 deletions src/python/LangChainAPI/app/lifespan_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from aiohttp import ClientSession
from contextlib import asynccontextmanager
from foundationallm.config import Configuration
from foundationallm.plugins import PluginManager, plugin_manager
from foundationallm.telemetry import Telemetry

config: Configuration = None
http_client_session: ClientSession = None
plugin_manager: PluginManager = None

@asynccontextmanager
async def lifespan(app):
"""Async context manager for the FastAPI application lifespan."""
global config
global http_client_session
global plugin_manager

# Create the application configuration
config = Configuration()

# Create an aiohttp client session
http_client_session = ClientSession()

# Create the plugin manager
Telemetry.configure_monitoring(config, f'FoundationaLLM:APIEndpoints:LangChainAPI:Essentials:AppInsightsConnectionString', 'LangChainAPI')
plugin_manager = PluginManager(config, Telemetry.get_logger(__name__))
plugin_manager.load_external_modules()

yield

# Perform shutdown actions here
await http_client_session.close()

async def get_config() -> Configuration:
"""Retrieves the application configuration."""
return config

async def get_http_client_session() -> ClientSession:
"""Retrieves the aiohttp client session."""
return http_client_session

async def get_plugin_manager() -> PluginManager:
"""Retrieves the plugin manager."""
return plugin_manager
35 changes: 9 additions & 26 deletions src/python/LangChainAPI/app/main.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,16 @@
"""
Main entry-point for the FoundationaLLM LangChainAPI.
Runs web server exposing the API.
"""
from fastapi import FastAPI
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from app.dependencies import API_NAME, get_config
from app.routers import (
manage,
completions,
status
)
from foundationallm.plugins import PluginManager
from foundationallm.telemetry import Telemetry

# Open a connection to the app configuration
config = get_config()
# Start collecting telemetry
Telemetry.configure_monitoring(config, f'FoundationaLLM:APIEndpoints:{API_NAME}:Essentials:AppInsightsConnectionString', API_NAME)

plugin_manager = PluginManager(config, Telemetry.get_logger(__name__))
plugin_manager.load_external_modules()
from app.lifespan_manager import lifespan
from app.routers import completions, status

app = FastAPI(
title=f'FoundationaLLM {API_NAME}',
lifespan=lifespan,
title=f'FoundationaLLM LangChainAPI',
summary='API for interacting with large language models using the LangChain orchestrator.',
description=f"""The FoundationaLLM {API_NAME} is a wrapper around LangChain functionality
description=f"""The FoundationaLLM LangChainAPI is a wrapper around LangChain functionality
contained in the foundationallm.core Python SDK.""",
version='1.0.0',
contact={
Expand All @@ -38,17 +24,14 @@
license_info={
'name': 'FoundationaLLM Software License',
'url': 'https://www.foundationallm.ai/license',
},
config=config,
plugin_manager=plugin_manager
}
)

FastAPIInstrumentor.instrument_app(app)

app.include_router(manage.router)
app.include_router(completions.router)
app.include_router(status.router)

FastAPIInstrumentor.instrument_app(app)

@app.get('/')
async def root():
"""
Expand All @@ -59,4 +42,4 @@ async def root():
str
Returns a JSON object containing a message and value.
"""
return { 'message': f'FoundationaLLM {API_NAME}' }
return { 'message': f'FoundationaLLM LangChainAPI' }
Loading

0 comments on commit 3e48f13

Please sign in to comment.