Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
0a1b5ee
add crude keyvault base impl
nadoylemsft Sep 30, 2025
be8d0d7
upd actions for MAG
nadoylemsft Sep 30, 2025
6bb5fd0
add settings to fix
nadoylemsft Sep 30, 2025
ab28c4f
upd secret naming convention
nadoylemsft Sep 30, 2025
46945e7
upd auth types to include conn string/basic(un/pw)
nadoylemsft Oct 1, 2025
0662053
fix method name
nadoylemsft Oct 1, 2025
09f7433
add get agent helper
nadoylemsft Oct 2, 2025
e24da04
add ui trigger word and get agent helper
nadoylemsft Oct 2, 2025
e46afa2
upd function imports
nadoylemsft Oct 2, 2025
bf5e85a
upd agents call
nadoylemsft Oct 2, 2025
8aeea1a
add desc of plugins
nadoylemsft Oct 2, 2025
a4054ab
fix for admin modal loading
nadoylemsft Oct 3, 2025
5cce7bf
upd default agent handling
nadoylemsft Oct 6, 2025
5a56bdc
rmv unneeded file
nadoylemsft Oct 6, 2025
db6372e
rmv extra imp statements
nadoylemsft Oct 6, 2025
d0aff17
add new cosmos container script
nadoylemsft Oct 6, 2025
427f01d
upd instructions for consistency of code
nadoylemsft Oct 9, 2025
33ba357
adds safe calls for akv functions
nadoylemsft Oct 9, 2025
07e31c7
adds akv to personal agents
nadoylemsft Oct 9, 2025
91f3a86
fix for user agents boot issue
nadoylemsft Oct 10, 2025
2a23b84
fix global set
nadoylemsft Oct 10, 2025
570ec56
upd azure function plugin to super init
nadoylemsft Oct 10, 2025
def744b
upd to clean imports
nadoylemsft Oct 10, 2025
3b0b743
add keyvault to global actions loading
nadoylemsft Oct 10, 2025
a037caa
add plugin loading docs
nadoylemsft Oct 10, 2025
74c200c
rmv secret leak via logging
nadoylemsft Oct 10, 2025
509bc63
rmv displaying of token in logs
nadoylemsft Oct 10, 2025
0bb4a6b
fix not loading global actions for personal agents
nadoylemsft Oct 10, 2025
186a6e1
rmv unsupported characters from logging
nadoylemsft Oct 10, 2025
f11381c
fix chat links in dark mode
nadoylemsft Oct 10, 2025
b2d6613
chg order of css for links in dark mode
nadoylemsft Oct 10, 2025
967f9cb
fix chat color
nadoylemsft Oct 10, 2025
bf201f1
add default plugin print logging
nadoylemsft Oct 10, 2025
39d944e
rmv default check for nonsql plugins
nadoylemsft Oct 14, 2025
7114bac
upd requirements
nadoylemsft Oct 14, 2025
3d64e25
add keyvault and dynamic addsetting ui
nadoylemsft Oct 14, 2025
eeef42b
fix for agents/plugins with invalid akv chars
nadoylemsft Oct 15, 2025
7a0976f
add imp to appins logging
nadoylemsft Oct 15, 2025
f64702e
add security tab UI + key vault UI
nadoylemsft Oct 15, 2025
298b342
add keyvault settings
nadoylemsft Oct 15, 2025
048decf
fix for copilot findings.
nadoylemsft Oct 15, 2025
3a369b2
fix for resaving plugin without changing secret
nadoylemsft Oct 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
---
applyTo: '**'
---

# REPO SPECIFIC INSTRUCTIONS

---
Expand Down
13 changes: 13 additions & 0 deletions .github/instructions/python-lang.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
applyTo: '**'
---

# Python Language Guide

- Files should start with a comment of the file name. Ex: `# functions_personal_agents.py`

- Imports should be grouped at the top of the document after the module docstring, unless otherwise indicated by the user or for performance reasons in which case the import should be as close as possible to the usage with a documented note as to why the import is not at the top of the file.

- Use 4 spaces per indentation level. No tabs.

- Code and definitions should occur after the imports block.
15 changes: 8 additions & 7 deletions .github/workflows/docker_image_publish_nadoyle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
push:
branches:
- nadoyle
- keyvaultForSecrets

workflow_dispatch:

Expand All @@ -19,11 +20,11 @@ jobs:
uses: Azure/docker-login@v2
with:
# Container registry username
username: ${{ secrets.ACR_USERNAME }}
username: ${{ secrets.ACR_USERNAME_NADOYLE }}
# Container registry password
password: ${{ secrets.ACR_PASSWORD }}
password: ${{ secrets.ACR_PASSWORD_NADOYLE }}
# Container registry server url
login-server: ${{ secrets.ACR_LOGIN_SERVER }}
login-server: ${{ secrets.ACR_LOGIN_SERVER_NADOYLE }}

- uses: actions/checkout@v3
- name: Set up Node.js
Expand All @@ -36,7 +37,7 @@ jobs:
run: node scripts/generate-validators.mjs
- name: Build the Docker image
run:
docker build . --file application/single_app/Dockerfile --tag ${{ secrets.ACR_LOGIN_SERVER }}/simple-chat-dev:$(date +'%Y-%m-%d')_$GITHUB_RUN_NUMBER;
docker tag ${{ secrets.ACR_LOGIN_SERVER }}/simple-chat-dev:$(date +'%Y-%m-%d')_$GITHUB_RUN_NUMBER ${{ secrets.ACR_LOGIN_SERVER }}/simple-chat-dev:latest;
docker push ${{ secrets.ACR_LOGIN_SERVER }}/simple-chat-dev:$(date +'%Y-%m-%d')_$GITHUB_RUN_NUMBER;
docker push ${{ secrets.ACR_LOGIN_SERVER }}/simple-chat-dev:latest;
docker build . --file application/single_app/Dockerfile --tag ${{ secrets.ACR_LOGIN_SERVER_NADOYLE }}/simple-chat-dev:$(date +'%Y-%m-%d')_$GITHUB_RUN_NUMBER;
docker tag ${{ secrets.ACR_LOGIN_SERVER_NADOYLE }}/simple-chat-dev:$(date +'%Y-%m-%d')_$GITHUB_RUN_NUMBER ${{ secrets.ACR_LOGIN_SERVER_NADOYLE }}/simple-chat-dev:latest;
docker push ${{ secrets.ACR_LOGIN_SERVER_NADOYLE }}/simple-chat-dev:$(date +'%Y-%m-%d')_$GITHUB_RUN_NUMBER;
docker push ${{ secrets.ACR_LOGIN_SERVER_NADOYLE }}/simple-chat-dev:latest;
37 changes: 17 additions & 20 deletions application/single_app/agent_logging_chat_completion.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,13 +144,6 @@ async def invoke(self, *args, **kwargs):
}
)

log_event("[Logging Agent Request] Agent invoke started",
extra={
"agent": self.name,
"prompt_preview": [m.content[:30] for m in args[0]] if args else None
},
level=logging.DEBUG)

# Store user question context for better tool detection
if args and args[0] and hasattr(args[0][-1], 'content'):
self._user_question = args[0][-1].content
Expand All @@ -163,12 +156,14 @@ async def invoke(self, *args, **kwargs):
initial_message_count = len(args[0]) if args and args[0] else 0
result = super().invoke(*args, **kwargs)

log_event("[Logging Agent Request] Result received",
extra={
"agent": self.name,
"result_type": type(result).__name__
},
level=logging.DEBUG)
log_event(
"[Logging Agent Request] Result received",
extra={
"agent": self.name,
"result_type": type(result).__name__
},
level=logging.DEBUG
)

if hasattr(result, "__aiter__"):
# Streaming/async generator response
Expand All @@ -180,13 +175,15 @@ async def invoke(self, *args, **kwargs):
# Regular coroutine response
response = await result

log_event("[Logging Agent Request] Response received",
extra={
"agent": self.name,
"response_type": type(response).__name__,
"response_preview": str(response)[:100] if response else None
},
level=logging.DEBUG)
log_event(
"[Logging Agent Request] Response received",
extra={
"agent": self.name,
"response_type": type(response).__name__,
"response_preview": str(response)[:100] if response else None
},
level=logging.DEBUG
)

# Store the response for analysis
self._last_response = response
Expand Down
1 change: 1 addition & 0 deletions application/single_app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@

from route_external_health import *

#TODO: Remove this after speaking with Paul
configure_azure_monitor()

# =================== Session Configuration ===================
Expand Down
7 changes: 4 additions & 3 deletions application/single_app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,6 @@
else:
AUTHORITY = f"https://login.microsoftonline.com/{TENANT_ID}"

# Commercial Azure Video Indexer Endpoint
video_indexer_endpoint = "https://api.videoindexer.ai"

WORD_CHUNK_SIZE = 400

if AZURE_ENVIRONMENT == "usgovernment":
Expand All @@ -171,20 +168,23 @@
cognitive_services_scope = "https://cognitiveservices.azure.us/.default"
video_indexer_endpoint = "https://api.videoindexer.ai.azure.us"
search_resource_manager = "https://search.azure.us"
KEY_VAULT_DOMAIN = ".vault.usgovcloudapi.net"

elif AZURE_ENVIRONMENT == "custom":
resource_manager = CUSTOM_RESOURCE_MANAGER_URL_VALUE
authority = CUSTOM_IDENTITY_URL_VALUE
credential_scopes=[resource_manager + "/.default"]
cognitive_services_scope = CUSTOM_COGNITIVE_SERVICES_URL_VALUE
search_resource_manager = CUSTOM_SEARCH_RESOURCE_MANAGER_URL_VALUE
KEY_VAULT_DOMAIN = os.getenv("KEY_VAULT_DOMAIN", ".vault.azure.net")
else:
OIDC_METADATA_URL = f"https://login.microsoftonline.com/{TENANT_ID}/v2.0/.well-known/openid-configuration"
resource_manager = "https://management.azure.com"
authority = AzureAuthorityHosts.AZURE_PUBLIC_CLOUD
credential_scopes=[resource_manager + "/.default"]
cognitive_services_scope = "https://cognitiveservices.azure.com/.default"
video_indexer_endpoint = "https://api.videoindexer.ai"
KEY_VAULT_DOMAIN = ".vault.azure.net"

def get_redis_cache_infrastructure_endpoint(redis_hostname: str) -> str:
"""
Expand All @@ -205,6 +205,7 @@ def get_redis_cache_infrastructure_endpoint(redis_hostname: str) -> str:
else:
# Default to Azure Public Cloud
return f"https://{redis_hostname}.cacheinfra.windows.net:10225/appid"


storage_account_user_documents_container_name = "user-documents"
storage_account_group_documents_container_name = "group-documents"
Expand Down
23 changes: 15 additions & 8 deletions application/single_app/functions_appinsights.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,29 +44,36 @@ def log_event(
exceptionTraceback (Any, optional): If set to True, includes exception traceback.
"""
try:
# Limit message to 32767 characters
if message and isinstance(message, str) and len(message) > 32767:
message = message[:32767]

# Get logger - use Azure Monitor logger if configured, otherwise standard logger
logger = get_appinsights_logger()
if not logger:
print(f"[Log] {message} -- {extra}")
logger = logging.getLogger('standard')
if not logger.handlers:
logger.addHandler(logging.StreamHandler())
logger.setLevel(logging.INFO)

# Enhanced exception handling for Application Insights
# When exceptionTraceback=True, ensure we capture full exception context
exc_info_to_use = exceptionTraceback

# For ERROR level logs with exceptionTraceback=True, always log as exception
if level >= logging.ERROR and exceptionTraceback:
if logger and hasattr(logger, 'exception'):
# Use logger.exception() for better exception capture in Application Insights
logger.exception(message, extra=extra, stacklevel=stacklevel)
logger.exception(message, extra=extra, stacklevel=stacklevel, stack_info=includeStack, exc_info=True)
return
else:
# Fallback to standard logging with exc_info
exc_info_to_use = True

# Format message with extra properties for structured logging

print(f"[Log] {message} -- {extra}") # Debug print to console
if extra:
# For modern Azure Monitor, extra properties are automatically captured
logger.log(
Expand All @@ -85,24 +92,24 @@ def log_event(
stack_info=includeStack,
exc_info=exc_info_to_use
)

# For Azure Monitor, ensure exception-level logs are properly categorized
if level >= logging.ERROR and _azure_monitor_configured:
# Add a debug print to verify exception logging is working
print(f"[Azure Monitor] Exception logged: {message[:100]}...")

except Exception as e:
# Fallback to basic logging if anything fails
try:
fallback_logger = logging.getLogger('fallback')
if not fallback_logger.handlers:
fallback_logger.addHandler(logging.StreamHandler())
fallback_logger.setLevel(logging.INFO)

fallback_message = f"{message} | Original error: {str(e)}"
if extra:
fallback_message += f" | Extra: {extra}"

fallback_logger.log(level, fallback_message)
except:
# If even basic logging fails, print to console
Expand Down
30 changes: 13 additions & 17 deletions application/single_app/functions_global_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
import json
import traceback
from datetime import datetime

from config import cosmos_global_actions_container
from functions_keyvault import keyvault_plugin_save_helper, keyvault_plugin_get_helper, keyvault_plugin_delete_helper, SecretReturnType

def get_global_actions():
def get_global_actions(return_type=SecretReturnType.TRIGGER):
"""
Get all global actions.

Expand All @@ -25,7 +25,8 @@ def get_global_actions():
query="SELECT * FROM c",
enable_cross_partition_query=True
))

# Resolve Key Vault references for each action
actions = [keyvault_plugin_get_helper(a, scope_value=a.get('id'), scope="global", return_type=return_type) for a in actions]
return actions

except Exception as e:
Expand All @@ -34,7 +35,7 @@ def get_global_actions():
return []


def get_global_action(action_id):
def get_global_action(action_id, return_type=SecretReturnType.TRIGGER):
"""
Get a specific global action by ID.

Expand All @@ -45,13 +46,12 @@ def get_global_action(action_id):
dict: Action data or None if not found
"""
try:
from config import cosmos_global_actions_container

action = cosmos_global_actions_container.read_item(
item=action_id,
partition_key=action_id
)

# Resolve Key Vault references
action = keyvault_plugin_get_helper(action, scope_value=action_id, scope="global", return_type=return_type)
print(f"✅ Found global action: {action_id}")
return action

Expand All @@ -71,21 +71,17 @@ def save_global_action(action_data):
dict: Saved action data or None if failed
"""
try:
from config import cosmos_global_actions_container

# Ensure required fields
if 'id' not in action_data:
action_data['id'] = str(uuid.uuid4())

# Add metadata
action_data['is_global'] = True
action_data['created_at'] = datetime.utcnow().isoformat()
action_data['updated_at'] = datetime.utcnow().isoformat()

print(f"💾 Saving global action: {action_data.get('name', 'Unknown')}")

# Store secrets in Key Vault before upsert
action_data = keyvault_plugin_save_helper(action_data, scope_value=action_data.get('id'), scope="global")
result = cosmos_global_actions_container.upsert_item(body=action_data)

print(f"✅ Global action saved successfully: {result['id']}")
return result

Expand All @@ -106,15 +102,15 @@ def delete_global_action(action_id):
bool: True if successful, False otherwise
"""
try:
from config import cosmos_global_actions_container

print(f"🗑️ Deleting global action: {action_id}")

# Delete secrets from Key Vault before deleting the action
action = get_global_action(action_id)
if action:
keyvault_plugin_delete_helper(action, scope_value=action_id, scope="global")
cosmos_global_actions_container.delete_item(
item=action_id,
partition_key=action_id
)

print(f"✅ Global action deleted successfully: {action_id}")
return True

Expand Down
Loading