diff --git a/.github/workflows/platform_ci_python.yml b/.github/workflows/platform_ci_python.yml index 37443d9..af31cb9 100644 --- a/.github/workflows/platform_ci_python.yml +++ b/.github/workflows/platform_ci_python.yml @@ -51,6 +51,8 @@ - name: Check Azure CLI Availability run: | + python -m pip install --upgrade pip + pip install --upgrade -r requirements.txt echo "Current PATH: $PATH" which az ls -l $(which az) || echo "az not found in the expected directory" @@ -70,6 +72,7 @@ NEWS_ANALYST_MODEL_NAME: ${{ vars.NEWS_ANALYST_MODEL_NAME }} FINANCIAL_ANALYST_MODEL_NAME: ${{ vars.FINANCIAL_ANALYST_MODEL_NAME }} REPORT_GENERATOR_MODEL_NAME: ${{ vars.REPORT_GENERATOR_MODEL_NAME }} + APPLICATIONINSIGHTS_CONNECTION_STRING: ${{secrets.APPLICATIONINSIGHTS_CONNECTION_STRING}} run: | echo "Verifying Python Path" which python diff --git a/.github/workflows/platform_pr_python.yml b/.github/workflows/platform_pr_python.yml index 5d1fe44..98c161a 100644 --- a/.github/workflows/platform_pr_python.yml +++ b/.github/workflows/platform_pr_python.yml @@ -39,11 +39,14 @@ uses: actions/checkout@v3 - name: Check Azure CLI Availability + working-directory: python run: | echo "Current PATH: $PATH" which az ls -l $(which az) || echo "az not found in the expected directory" az --version + python -m pip install --upgrade pip + pip install --upgrade -r requirements.txt - name: Azure login uses: azure/login@v2 @@ -59,9 +62,11 @@ NEWS_ANALYST_MODEL_NAME: ${{ vars.NEWS_ANALYST_MODEL_NAME }} FINANCIAL_ANALYST_MODEL_NAME: ${{ vars.FINANCIAL_ANALYST_MODEL_NAME }} REPORT_GENERATOR_MODEL_NAME: ${{ vars.REPORT_GENERATOR_MODEL_NAME }} + APPLICATIONINSIGHTS_CONNECTION_STRING: ${{secrets.APPLICATIONINSIGHTS_CONNECTION_STRING}} run: | echo "Verifying Python Path" which python echo "Installed Packages:" + echo APPLICATIONINSIGHTS_CONNECTION_STRING=${{ secrets.APPLICATIONINSIGHTS_CONNECTION_STRING}} >> $GITHUB_ENV pip list python -u -m sk_financial_analyst.executors.single_item_executor --logging_enabled diff --git a/python/.env_sample b/python/.env_sample index c1c11ad..25d3205 100644 --- a/python/.env_sample +++ b/python/.env_sample @@ -8,3 +8,15 @@ AML_RESOURCE_GROUP_NAME= AML_WORKSPACE_NAME= SEMANTICKERNEL_EXPERIMENTAL_GENAI_ENABLE_OTEL_DIAGNOSTICS_SENSITIVE=true +OTEL_SERVICE_NAME="sk-financial-analyst-local" +OTEL_LOGS_EXPORTER="otlp" +OTEL_TRACES_EXPORTER="otlp" +OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED="true" +OTEL_EXPORTER_OTLP_PROTOCOL="grpc" +OTEL_EXPORTER_OTLP_INSECURE="true" +OTEL_LOG_LEVEL="INFO" +OTEL_RESOURCE_ATTRIBUTES="service.name=sk-financial-analyst-local" +OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4317" +OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="http://localhost:4318" +# application insights connection string is added if you prefer not using collector. +APPLICATIONINSIGHTS_CONNECTION_STRING= diff --git a/python/common/configurator/config_reader.py b/python/common/configurator/config_reader.py index 3a6f650..f476873 100644 --- a/python/common/configurator/config_reader.py +++ b/python/common/configurator/config_reader.py @@ -3,12 +3,20 @@ import os import yaml +from azure.monitor.opentelemetry import configure_azure_monitor +from common.configurator.otel import config_otel from dotenv import load_dotenv def load_yaml(file_path): """Load a YAML file.""" load_dotenv() + connection_string = os.environ.get("APPLICATIONINSIGHTS_CONNECTION_STRING") + if connection_string is not None: + print(f"connection string: {connection_string}") + configure_azure_monitor(connection_string=connection_string) + else: + config_otel() with open(file_path, "r") as stream: return yaml.safe_load(os.path.expandvars(stream.read())) diff --git a/python/requirements.txt b/python/requirements.txt index 945b264..027272c 100644 --- a/python/requirements.txt +++ b/python/requirements.txt @@ -14,7 +14,7 @@ flake8==7.1.1 pep8-naming==0.14.1 pytest==8.3.3 pytest-cov==5.0.0 -opentelemetry-instrumentation==0.49b2 +## otel collector opentelemetry-instrumentation-openai==0.33.12 opentelemetry-instrumentation-fastapi==0.49b2 opentelemetry-distro==0.49b2 @@ -22,3 +22,6 @@ opentelemetry-exporter-otlp==1.28.2 fastapi==0.115.4 uvicorn==0.32.0 pydantic==2.8.2 +## set this only when you want to directly instrument using +## application insights without collector +azure-monitor-opentelemetry==1.6.4 diff --git a/python/sk_financial_analyst/deployment/durable_function/function_app.py b/python/sk_financial_analyst/deployment/durable_function/function_app.py index e2f0e86..33ae88f 100644 --- a/python/sk_financial_analyst/deployment/durable_function/function_app.py +++ b/python/sk_financial_analyst/deployment/durable_function/function_app.py @@ -7,6 +7,8 @@ import azure.functions as func from azure.identity import DefaultAzureCredential from azure.keyvault.secrets import SecretClient +from azure.monitor.opentelemetry import configure_azure_monitor +from common.configurator import otel from llm_application.financial_health_analysis import FinancialHealthAnalysis app = df.DFApp(http_auth_level=func.AuthLevel.FUNCTION) @@ -69,6 +71,11 @@ async def generate_report(stock): structured_report_generator_model = os.environ.get("REPORT_GENERATOR_MODEL_NAME") aoai_api_version = os.environ.get("AOAI_API_VERSION") + if (os.environ.get("APPLICATIONINSIGHTS_CONNECTION_STRING")) is not None: + configure_azure_monitor() + else: + otel.config_otel() + credential = DefaultAzureCredential(managed_identity_client_id=managed_identity_client_id) aoai_token = credential.get_token(auth_provider_endpoint).token diff --git a/python/sk_financial_analyst/deployment/durable_function/requirements.txt b/python/sk_financial_analyst/deployment/durable_function/requirements.txt index d2bd46c..ba73486 100644 --- a/python/sk_financial_analyst/deployment/durable_function/requirements.txt +++ b/python/sk_financial_analyst/deployment/durable_function/requirements.txt @@ -13,3 +13,11 @@ edgartools yfinance==0.2.40 semantic-kernel pydantic==2.8.2 +## otel collector +opentelemetry-instrumentation-openai==0.33.12 +opentelemetry-instrumentation-fastapi==0.49b2 +opentelemetry-distro==0.49b2 +opentelemetry-exporter-otlp==1.28.2 +## set this only when you want to directly instrument using +## application insights without collector +azure-monitor-opentelemetry==1.6.4 diff --git a/python/sk_financial_analyst/executors/aml/aml_batch_executor.py b/python/sk_financial_analyst/executors/aml/aml_batch_executor.py index f3533af..a7be5f6 100644 --- a/python/sk_financial_analyst/executors/aml/aml_batch_executor.py +++ b/python/sk_financial_analyst/executors/aml/aml_batch_executor.py @@ -16,6 +16,8 @@ from azure.ai.ml.entities import Environment from azure.ai.ml.parallel import RunFunction, parallel_run_function from azure.identity import DefaultAzureCredential +from azure.monitor.opentelemetry import configure_azure_monitor +from common.configurator import otel from dotenv import load_dotenv @@ -118,7 +120,12 @@ def main(): """ args = parse_args() load_dotenv(override=True) - + # if application connection string is available, directly use azure monitor connection + # else collector is used. + if (os.environ.get("APPLICATIONINSIGHTS_CONNECTION_STRING")) is not None: + configure_azure_monitor(connection_string=os.environ.get("APPLICATIONINSIGHTS_CONNECTION_STRING")) + else: + otel.config_otel() subscription_id = os.getenv("AZURE_SUBSCRIPTION_ID") resource_group = os.getenv("AML_RESOURCE_GROUP_NAME") workspace = os.getenv("AML_WORKSPACE_NAME") diff --git a/python/sk_financial_analyst/executors/aml/aml_env.yaml b/python/sk_financial_analyst/executors/aml/aml_env.yaml index ee01b5e..4c78d99 100644 --- a/python/sk_financial_analyst/executors/aml/aml_env.yaml +++ b/python/sk_financial_analyst/executors/aml/aml_env.yaml @@ -10,13 +10,17 @@ dependencies: - azure-identity==1.19.0 - azure-keyvault-secrets==4.9.0 - azure-ai-evaluation==1.0.0b5 - - azure-monitor-opentelemetry-exporter==1.0.0b31 - pyyaml==6.0.2 - requests==2.32.3 - edgartools==2.34.2 - python-dotenv==1.0.1 - yfinance==0.2.40 - semantic-kernel==1.11.0 - - opentelemetry-api==1.27.0 - - opentelemetry-sdk==1.27.0 - - opentelemetry-semantic-conventions==0.48b0 + ## otel collector + - opentelemetry-instrumentation-openai==0.33.12 + - opentelemetry-instrumentation-fastapi==0.49b2 + - opentelemetry-distro==0.49b2 + - opentelemetry-exporter-otlp==1.28.2 + ## set this only when you want to directly instrument using + ## application insights without collector + - azure-monitor-opentelemetry==1.6.4 diff --git a/python/sk_financial_analyst/executors/single_item_executor.py b/python/sk_financial_analyst/executors/single_item_executor.py index 9743337..439e9c8 100644 --- a/python/sk_financial_analyst/executors/single_item_executor.py +++ b/python/sk_financial_analyst/executors/single_item_executor.py @@ -9,7 +9,7 @@ from azure.identity import DefaultAzureCredential from azure.keyvault.secrets import SecretClient -from common.configurator import config_reader, otel +from common.configurator import config_reader from opentelemetry import trace from opentelemetry.trace import SpanKind from sk_financial_analyst.llm_application.financial_health_analysis import FinancialHealthAnalysis @@ -36,7 +36,7 @@ async def generate_report(config_file, stock_ticker): config_data = config_reader.load_yaml(config_file) logger.info("Otel configuration started..") - otel.config_otel() + tracer = trace.get_tracer(__name__) logger.info("Otel configuration successful..") diff --git a/python/sk_financial_analyst/routes/routes.py b/python/sk_financial_analyst/routes/routes.py index 5a6a0f1..f656e62 100644 --- a/python/sk_financial_analyst/routes/routes.py +++ b/python/sk_financial_analyst/routes/routes.py @@ -3,7 +3,7 @@ from azure.identity import DefaultAzureCredential from azure.keyvault.secrets import SecretClient -from common.configurator import config_reader, otel +from common.configurator import config_reader from fastapi import APIRouter, FastAPI from opentelemetry import trace from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor @@ -11,6 +11,8 @@ from opentelemetry.trace import SpanKind from sk_financial_analyst.llm_application.financial_health_analysis import FinancialHealthAnalysis +# Load the configuration data +config_data = config_reader.load_yaml("./sk_financial_analyst/config/config.yaml") logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # from llm_application import financial_health_analysis @@ -19,7 +21,7 @@ router = APIRouter() logger.info("Otel configuration started..") -otel.config_otel() + OpenAIInstrumentor().instrument() FastAPIInstrumentor.instrument_app(app) tracer = trace.get_tracer(__name__) @@ -31,8 +33,7 @@ async def run_financial_health_analysis(stock_ticker: str): try: with tracer.start_as_current_span("run_financial_analysis_report", kind=SpanKind.SERVER) as span: logger.info("Starting finacial report generation..") - # Load the configuration data - config_data = config_reader.load_yaml("./sk_financial_analyst/config/config.yaml") + key_vault_url = os.environ.get("KEY_VAULT_URL") # Get values from the configuration data