Skip to content

Add app config and related feature flag capabilities #87

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 20 commits into
base: main
Choose a base branch
from

Conversation

aprilk-ms
Copy link

  • Updated bicep to include App Config provisioning
  • Added github workflow to deploy feature flag config
  • Integrated app config initialization/refresh process in the app code
  • Fetch agent variant for the thread in chat call (fallback to default agent if app config or feature flag is not setup)
  • On the UI side,
    • added a new thread button to start a new thread (to see a new variant assigned), and
    • show the variant name in the message title bar

image

infra/main.bicep Outdated
@description('Random seed to be used during generation of new resources suffixes.')
param seed string = newGuid()
param seed string = newGuid() // why do we need this?
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we want this? Sometimes when there is an issue, I do azd up several times and it surprised me that new set of resources got created.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, this is strange. I created an issue in this repo about it

#89

def get_agent(request: Request) -> Agent:
return request.app.state.agent

def get_feature_manager(request: Request):
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not add types for feature_manager and app_config, assuming that users might not have the modules installed. Is it the right practice?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aprilk-ms, wouldn't they have them installed from requirements.txt. Also, our provider implements dict, so it's safe to have it's type be Mapping[str, Union[str, JSON]].

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agreed. I wanted to have the types but was not sure after seeing some lines in main.py catching ModuleNotFound issue. I added the types now and see what other reviewers think.

):
# Retrieve the thread ID from the cookies (if available).
thread_id = request.cookies.get('thread_id')
agent_id = request.cookies.get('agent_id')

# Attempt to get an existing thread. If not found, create a new one.
try:
if thread_id and agent_id == agent.id:
if thread_id:
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: is there a problem to use different agents in the same thread? Why is this needed?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the agentId has been changed on the server side, I would like to avoid get_thread and avoid exception. But instead start a new chat by create_thread.

azure-core-tracing-opentelemetry
azure-monitor-opentelemetry
azure-search-documents
opentelemetry-sdk

azure-appconfiguration-provider==2.1.0b1
FeatureManagement==2.0.0b3
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: update this after new release is out

@aprilk-ms aprilk-ms marked this pull request as draft April 15, 2025 20:20
async def get_agent_variant(feature_manager, ai_client: AIProjectClient, thread_id: str):
if feature_manager:
# Fetch the variant for the feature flag "my-agent" using thread_id as targeting Id
agent_variant = feature_manager.get_variant("my-agent", thread_id)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aprilk-ms I see you are doing async. You can get an async version from from featuremanagement.aio import FeatureManager and the same thing for our provider. from azure.appconfiguration.provider.aio import load. Should work the same as the rest of the azure sdk for python in async usage.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to async as suggested.

@aprilk-ms aprilk-ms marked this pull request as ready for review April 18, 2025 08:37
@aprilk-ms
Copy link
Author

TODO: still need to update readme

@@ -388,6 +415,24 @@ module backendRoleAzureAIDeveloperRG 'core/security/role.bicep' = {
}
}

module userRoleConfigStoreDataOwner 'core/security/role.bicep' = {
name: 'user-role-config-store-data-owner'
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@howieleung I also noticed all the other userRole assignment above are using app service principal and not the caller. Is it intended?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, a while ago, I also had principalId as param at the main. But when I try to deploy from Portal UI, the service principal has role issues.

credential=DefaultAzureCredential(),
feature_flag_enabled=True,
feature_flag_refresh_enabled=True,
refresh_interval=300 # no refresh within 5 mins to avoid throttling
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: update to 2 mins

@description('Random seed to be used during generation of new resources suffixes.')
param seed string = newGuid()
param seed string = '' //newGuid() // why do we need this?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you remove newGUID, it might break AI Search resource creation.

@@ -41,6 +42,16 @@ class ChatUI {
}
}

addNewThreadClickListener() {
const newThreadButton = document.getElementById('new-thread-button');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What should happen if you refresh the browser and we call api to reload chat history?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants