16
16
SpeechSynthesizer ,
17
17
)
18
18
from azure .core .exceptions import ResourceNotFoundError
19
- from azure .cosmos .aio import ContainerProxy , CosmosClient
20
19
from azure .identity .aio import (
21
20
AzureDeveloperCliCredential ,
22
21
ManagedIdentityCredential ,
54
53
from approaches .chatreadretrievereadvision import ChatReadRetrieveReadVisionApproach
55
54
from approaches .retrievethenread import RetrieveThenReadApproach
56
55
from approaches .retrievethenreadvision import RetrieveThenReadVisionApproach
56
+ from chat_history .cosmosdb import chat_history_cosmosdb_bp
57
57
from config import (
58
58
CONFIG_ASK_APPROACH ,
59
59
CONFIG_ASK_VISION_APPROACH ,
63
63
CONFIG_CHAT_HISTORY_BROWSER_ENABLED ,
64
64
CONFIG_CHAT_HISTORY_COSMOS_ENABLED ,
65
65
CONFIG_CHAT_VISION_APPROACH ,
66
- CONFIG_COSMOS_HISTORY_CONTAINER ,
67
66
CONFIG_CREDENTIAL ,
68
67
CONFIG_GPT4V_DEPLOYED ,
69
68
CONFIG_INGESTER ,
@@ -407,129 +406,6 @@ async def list_uploaded(auth_claims: dict[str, Any]):
407
406
return jsonify (files ), 200
408
407
409
408
410
- @bp .post ("/chat_history" )
411
- @authenticated
412
- async def post_chat_history (auth_claims : Dict [str , Any ]):
413
- if not current_app .config [CONFIG_CHAT_HISTORY_COSMOS_ENABLED ]:
414
- return jsonify ({"error" : "Chat history not enabled" }), 405
415
-
416
- container = cast (ContainerProxy , current_app .config [CONFIG_COSMOS_HISTORY_CONTAINER ])
417
- if not container :
418
- return jsonify ({"error" : "Chat history not enabled" }), 405
419
-
420
- entra_oid = auth_claims .get ("oid" )
421
- if not entra_oid :
422
- return jsonify ({"error" : "User OID not found" }), 401
423
-
424
- try :
425
- request_json = await request .get_json ()
426
- id = request_json .get ("id" )
427
- answers = request_json .get ("answers" )
428
- title = answers [0 ][0 ][:50 ] + "..." if len (answers [0 ][0 ]) > 50 else answers [0 ][0 ]
429
-
430
- await container .upsert_item ({"id" : id , "entra_oid" : entra_oid , "title" : title , "answers" : answers })
431
-
432
- return jsonify ({}), 201
433
- except Exception as error :
434
- return error_response (error , "/chat_history" )
435
-
436
-
437
- @bp .post ("/chat_history/items" )
438
- @authenticated
439
- async def get_chat_history (auth_claims : Dict [str , Any ]):
440
- if not current_app .config [CONFIG_CHAT_HISTORY_COSMOS_ENABLED ]:
441
- return jsonify ({"error" : "Chat history not enabled" }), 405
442
-
443
- container = cast (ContainerProxy , current_app .config [CONFIG_COSMOS_HISTORY_CONTAINER ])
444
- if not container :
445
- return jsonify ({"error" : "Chat history not enabled" }), 405
446
-
447
- entra_oid = auth_claims .get ("oid" )
448
- if not entra_oid :
449
- return jsonify ({"error" : "User OID not found" }), 401
450
-
451
- try :
452
- request_json = await request .get_json ()
453
- count = request_json .get ("count" , 20 )
454
- continuation_token = request_json .get ("continuation_token" )
455
-
456
- res = container .query_items (
457
- query = "SELECT c.id, c.entra_oid, c.title, c._ts FROM c WHERE c.entra_oid = @entra_oid ORDER BY c._ts DESC" ,
458
- parameters = [dict (name = "@entra_oid" , value = entra_oid )],
459
- max_item_count = count ,
460
- )
461
-
462
- # set the continuation token for the next page
463
- pager = res .by_page (continuation_token )
464
-
465
- # Get the first page, and the continuation token
466
- try :
467
- page = await pager .__anext__ ()
468
- continuation_token = pager .continuation_token # type: ignore
469
-
470
- items = []
471
- async for item in page :
472
- items .append (item )
473
-
474
- # If there are no page, StopAsyncIteration is raised
475
- except StopAsyncIteration :
476
- items = []
477
- continuation_token = None
478
-
479
- return jsonify ({"items" : items , "continuation_token" : continuation_token }), 200
480
-
481
- except Exception as error :
482
- return error_response (error , "/chat_history/items" )
483
-
484
-
485
- @bp .get ("/chat_history/items/<path>" )
486
- @authenticated_path
487
- async def get_chat_history_session (path : str , auth_claims : Dict [str , Any ]):
488
- if not current_app .config [CONFIG_CHAT_HISTORY_COSMOS_ENABLED ]:
489
- return jsonify ({"error" : "Chat history not enabled" }), 405
490
-
491
- container = cast (ContainerProxy , current_app .config [CONFIG_COSMOS_HISTORY_CONTAINER ])
492
- if not container :
493
- return jsonify ({"error" : "Chat history not enabled" }), 405
494
-
495
- if not path :
496
- return jsonify ({"error" : "Invalid path" }), 400
497
-
498
- entra_oid = auth_claims .get ("oid" )
499
- if not entra_oid :
500
- return jsonify ({"error" : "User OID not found" }), 401
501
-
502
- try :
503
- res = await container .read_item (item = path , partition_key = entra_oid )
504
- return jsonify (res ), 200
505
- except Exception as error :
506
- return error_response (error , f"/chat_history/items/{ path } " )
507
-
508
-
509
- @bp .delete ("/chat_history/items/<path>" )
510
- @authenticated_path
511
- async def delete_chat_history_session (path : str , auth_claims : Dict [str , Any ]):
512
- if not current_app .config [CONFIG_CHAT_HISTORY_COSMOS_ENABLED ]:
513
- return jsonify ({"error" : "Chat history not enabled" }), 405
514
-
515
- container = cast (ContainerProxy , current_app .config [CONFIG_COSMOS_HISTORY_CONTAINER ])
516
- if not container :
517
- return jsonify ({"error" : "Chat history not enabled" }), 405
518
-
519
- if not path :
520
- return jsonify ({"error" : "Invalid path" }), 400
521
-
522
- entra_oid = auth_claims .get ("oid" )
523
- if not entra_oid :
524
- return jsonify ({"error" : "User OID not found" }), 401
525
-
526
- try :
527
- await container .delete_item (item = path , partition_key = entra_oid )
528
- return jsonify ({}), 200
529
- except Exception as error :
530
- return error_response (error , f"/chat_history/items/{ path } " )
531
-
532
-
533
409
@bp .before_app_serving
534
410
async def setup_clients ():
535
411
# Replace these with your own values, either in environment variables or directly here
@@ -585,12 +461,8 @@ async def setup_clients():
585
461
USE_SPEECH_INPUT_BROWSER = os .getenv ("USE_SPEECH_INPUT_BROWSER" , "" ).lower () == "true"
586
462
USE_SPEECH_OUTPUT_BROWSER = os .getenv ("USE_SPEECH_OUTPUT_BROWSER" , "" ).lower () == "true"
587
463
USE_SPEECH_OUTPUT_AZURE = os .getenv ("USE_SPEECH_OUTPUT_AZURE" , "" ).lower () == "true"
588
-
589
464
USE_CHAT_HISTORY_BROWSER = os .getenv ("USE_CHAT_HISTORY_BROWSER" , "" ).lower () == "true"
590
465
USE_CHAT_HISTORY_COSMOS = os .getenv ("USE_CHAT_HISTORY_COSMOS" , "" ).lower () == "true"
591
- AZURE_COSMOSDB_ACCOUNT = os .getenv ("AZURE_COSMOSDB_ACCOUNT" )
592
- AZURE_CHAT_HISTORY_DATABASE = os .getenv ("AZURE_CHAT_HISTORY_DATABASE" )
593
- AZURE_CHAT_HISTORY_CONTAINER = os .getenv ("AZURE_CHAT_HISTORY_CONTAINER" )
594
466
595
467
# WEBSITE_HOSTNAME is always set by App Service, RUNNING_IN_PRODUCTION is set in main.bicep
596
468
RUNNING_ON_AZURE = os .getenv ("WEBSITE_HOSTNAME" ) is not None or os .getenv ("RUNNING_IN_PRODUCTION" ) is not None
@@ -620,6 +492,9 @@ async def setup_clients():
620
492
current_app .logger .info ("Setting up Azure credential using AzureDeveloperCliCredential for home tenant" )
621
493
azure_credential = AzureDeveloperCliCredential (process_timeout = 60 )
622
494
495
+ # Set the Azure credential in the app config for use in other parts of the app
496
+ current_app .config [CONFIG_CREDENTIAL ] = azure_credential
497
+
623
498
# Set up clients for AI Search and Storage
624
499
search_client = SearchClient (
625
500
endpoint = f"https://{ AZURE_SEARCH_SERVICE } .search.windows.net" ,
@@ -694,21 +569,6 @@ async def setup_clients():
694
569
)
695
570
current_app .config [CONFIG_INGESTER ] = ingester
696
571
697
- if USE_CHAT_HISTORY_COSMOS :
698
- current_app .logger .info ("USE_CHAT_HISTORY_COSMOS is true, setting up CosmosDB client" )
699
- if not AZURE_COSMOSDB_ACCOUNT :
700
- raise ValueError ("AZURE_COSMOSDB_ACCOUNT must be set when USE_CHAT_HISTORY_COSMOS is true" )
701
- if not AZURE_CHAT_HISTORY_DATABASE :
702
- raise ValueError ("AZURE_CHAT_HISTORY_DATABASE must be set when USE_CHAT_HISTORY_COSMOS is true" )
703
- if not AZURE_CHAT_HISTORY_CONTAINER :
704
- raise ValueError ("AZURE_CHAT_HISTORY_CONTAINER must be set when USE_CHAT_HISTORY_COSMOS is true" )
705
- cosmos_client = CosmosClient (
706
- url = f"https://{ AZURE_COSMOSDB_ACCOUNT } .documents.azure.com:443/" , credential = azure_credential
707
- )
708
- cosmos_db = cosmos_client .get_database_client (AZURE_CHAT_HISTORY_DATABASE )
709
- cosmos_container = cosmos_db .get_container_client (AZURE_CHAT_HISTORY_CONTAINER )
710
- current_app .config [CONFIG_COSMOS_HISTORY_CONTAINER ] = cosmos_container
711
-
712
572
# Used by the OpenAI SDK
713
573
openai_client : AsyncOpenAI
714
574
@@ -723,7 +583,6 @@ async def setup_clients():
723
583
current_app .config [CONFIG_SPEECH_SERVICE_VOICE ] = AZURE_SPEECH_VOICE
724
584
# Wait until token is needed to fetch for the first time
725
585
current_app .config [CONFIG_SPEECH_SERVICE_TOKEN ] = None
726
- current_app .config [CONFIG_CREDENTIAL ] = azure_credential
727
586
728
587
if OPENAI_HOST .startswith ("azure" ):
729
588
api_version = os .getenv ("AZURE_OPENAI_API_VERSION" ) or "2024-03-01-preview"
@@ -867,6 +726,7 @@ async def close_clients():
867
726
def create_app ():
868
727
app = Quart (__name__ )
869
728
app .register_blueprint (bp )
729
+ app .register_blueprint (chat_history_cosmosdb_bp )
870
730
871
731
if os .getenv ("APPLICATIONINSIGHTS_CONNECTION_STRING" ):
872
732
app .logger .info ("APPLICATIONINSIGHTS_CONNECTION_STRING is set, enabling Azure Monitor" )
0 commit comments