-
Notifications
You must be signed in to change notification settings - Fork 40
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
base: main
Are you sure you want to change the base?
Conversation
aprilk-ms
commented
Apr 15, 2025
- 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
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? |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
src/api/routes.py
Outdated
def get_agent(request: Request) -> Agent: | ||
return request.app.state.agent | ||
|
||
def get_feature_manager(request: Request): |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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]]
.
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
src/api/routes.py
Outdated
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
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' |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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? |
There was a problem hiding this comment.
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'); |
There was a problem hiding this comment.
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?