Skip to content
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

Feature: Store chat history in Cosmos DB #2063

Merged
merged 15 commits into from
Nov 12, 2024

Conversation

fujita-h
Copy link
Contributor

Purpose

This PR extends the chat history feature added in #1988 to store chat history server-side (Cosmos DB) when user authentication is enabled.

Does this introduce a breaking change?

When developers merge from main and run the server, azd up, or azd deploy, will this produce an error?
If you're not sure, try it out on an old environment.

[ ] Yes
[x] No

Does this require changes to learn.microsoft.com docs?

This repository is referenced by this tutorial
which includes deployment, settings and usage instructions. If text or screenshot need to change in the tutorial,
check the box below and notify the tutorial author. A Microsoft employee can do this for you if you're an external contributor.

[ ] Yes
[x] No

Type of change

[ ] Bugfix
[x] Feature
[ ] Code style update (formatting, local variables)
[ ] Refactoring (no functional changes, no api changes)
[ ] Documentation content changes
[ ] Other... Please describe:

Code quality checklist

See CONTRIBUTING.md for more details.

  • The current tests all pass (python -m pytest).
  • I added tests that prove my fix is effective or that my feature works
  • I ran python -m pytest --cov to verify 100% coverage of added lines
  • I ran python -m mypy to check for type errors
  • I either used the pre-commit hooks or ran ruff and black manually on my code.

@pamelafox
Copy link
Collaborator

Woot! I know this is a very popular request, thanks for the PR, I'll review it this week. In the meantime, if anyone subscribed tries it out as well, please report on the PR with your feedback as well. I'll also ask Cosmos DB team to take a look.

Copy link
Collaborator

@pamelafox pamelafox left a comment

Choose a reason for hiding this comment

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

Could you also update the README (Costs estimate with optional CosmosDB bullet), the docs (deploy_features.md with new section, other_samples.md to update Chat History table)?

@@ -397,6 +407,129 @@ async def list_uploaded(auth_claims: dict[str, Any]):
return jsonify(files), 200


@bp.post("/chat_history")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you think the chat history endpoints could be in a different file/blueprint? That would make it to bring in additional providers (I've been asked about Azure SQL, for example) without bloating app.py.
I know we only have a single file/blueprint for routes now though, so it's possible that I haven't organized it in such a way that it's feasible.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Certainly, app.py is getting too large, so I think it's good to separate the blueprint. I'll try to restructure it to separate it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Configure separate blueprint in 44bfc40

id: string;
entra_id: string;
title?: string;
_ts: number;
Copy link
Collaborator

Choose a reason for hiding this comment

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

What/why is _ts? It's a funny variable name, so it could benefit from either an inline comment or a rename.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

_ts is a timestamp record that CosmosDB assigns by default.
We can define and assign a timestamp record defined in our application, but which is better?

https://learn.microsoft.com/en-us/rest/api/cosmos-db/documents

Copy link
Collaborator

Choose a reason for hiding this comment

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

Hm, maybe we should change it in the server-side when we pass back the results? I'm imagining that if we had an Azure SQL history provider, then we wouldn't want to use _ts, we'd want them both to pass back timestamp. And ideally use the same TypeScript models. Does that make sense?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated in 11c4d9f

export type HistoryListApiResponse = {
items: {
id: string;
entra_id: string;
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is oid, right? Maybe entra_oid would be slightly clearer (versus an Entra group id)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a typo. I will fix it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 0189dfc

Copy link

github-actions bot commented Nov 6, 2024

Check Broken URLs

We have automatically detected the following broken URLs in your files. Review and fix the paths to resolve this issue.

Check the file paths and associated broken URLs inside them. For more details, check our Contributing Guide.

File Full Path Issues
README.md
#LinkLine Number
1https://aka.ms/entgptsearchblog270
2https://techcommunity.microsoft.com/t5/ai-azure-ai-services-blog/access-control-in-generative-ai-applications-with-azure/ba-p/3956408274
docs/deploy_lowcost.md
#LinkLine Number
1https://techcommunity.microsoft.com/t5/ai-azure-ai-services-blog/azure-ai-search-outperforming-vector-search-with-hybrid/ba-p/392916743
docs/customization.md
#LinkLine Number
1https://techcommunity.microsoft.com/t5/ai-azure-ai-services-blog/azure-ai-search-outperforming-vector-search-with-hybrid/ba-p/3929167120
docs/productionizing.md
#LinkLine Number
1https://techcommunity.microsoft.com/t5/azure-architecture-blog/azure-openai-landing-zone-reference-architecture/ba-p/388210286
docs/deploy_features.md
#LinkLine Number
1https://techcommunity.microsoft.com/t5/ai-azure-ai-services-blog/announcing-the-public-preview-of-integrated-vectorization-in/ba-p/3960809#:~:text=Integrated%20vectorization%20is%20a%20new%20feature%20of%20Azure,pull-indexers%2C%20and%20vectorization%20of%20text%20queries%20through%20vectorizers218
docs/data_ingestion.md
#LinkLine Number
1https://techcommunity.microsoft.com/t5/ai-azure-ai-services-blog/announcing-the-public-preview-of-integrated-vectorization-in/ba-p/3960809#:~:text=Integrated%20vectorization%20is%20a%20new%20feature%20of%20Azure,pull-indexers%2C%20and%20vectorization%20of%20text%20queries%20through%20vectorizers68
samples/data-ingestion/README.md
#LinkLine Number
1https://techcommunity.microsoft.com/t5/ai-azure-ai-services-blog/announcing-the-public-preview-of-integrated-vectorization-in/ba-p/3960809#:~:text=Integrated%20vectorization%20is%20a%20new%20feature%20of%20Azure,pull-indexers%2C%20and%20vectorization%20of%20text%20queries%20through%20vectorizers88
samples/chat/README.md
#LinkLine Number
1https://aka.ms/entgptsearchblog272
2https://techcommunity.microsoft.com/t5/ai-azure-ai-services-blog/access-control-in-generative-ai-applications-with-azure/ba-p/3956408276

@pamelafox
Copy link
Collaborator

I'm reviewing now, have made a few cosmetic changes and added a test. I'll add a few more tests tomorrow and test my deploy. Have also asked a Cosmos DB colleague to review.

Copy link

github-actions bot commented Nov 7, 2024

Check Broken URLs

We have automatically detected the following broken URLs in your files. Review and fix the paths to resolve this issue.

Check the file paths and associated broken URLs inside them. For more details, check our Contributing Guide.

File Full Path Issues
README.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azure-ai-services-blog/revolutionize-your-enterprise-data-with-chatgpt-next-gen-apps-w-azure-openai-and/3762087292
2https://techcommunity.microsoft.com/blog/azure-ai-services-blog/access-control-in-generative-ai-applications-with-azure-ai-search/3956408296
docs/deploy_lowcost.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azure-ai-services-blog/azure-ai-search-outperforming-vector-search-with-hybrid-retrieval-and-ranking-ca/392916743
docs/customization.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azure-ai-services-blog/azure-ai-search-outperforming-vector-search-with-hybrid-retrieval-and-ranking-ca/3929167120
docs/productionizing.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azurearchitectureblog/azure-openai-landing-zone-reference-architecture/388210286
docs/deploy_features.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azure-ai-services-blog/announcing-the-public-preview-of-integrated-vectorization-in-azure-ai-search/3960809218
docs/data_ingestion.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azure-ai-services-blog/announcing-the-public-preview-of-integrated-vectorization-in-azure-ai-search/396080978

Copy link

github-actions bot commented Nov 8, 2024

Check Broken URLs

We have automatically detected the following broken URLs in your files. Review and fix the paths to resolve this issue.

Check the file paths and associated broken URLs inside them. For more details, check our Contributing Guide.

File Full Path Issues
README.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azure-ai-services-blog/revolutionize-your-enterprise-data-with-chatgpt-next-gen-apps-w-azure-openai-and/3762087292
2https://techcommunity.microsoft.com/blog/azure-ai-services-blog/access-control-in-generative-ai-applications-with-azure-ai-search/3956408296
docs/deploy_lowcost.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azure-ai-services-blog/azure-ai-search-outperforming-vector-search-with-hybrid-retrieval-and-ranking-ca/392916743
docs/customization.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azure-ai-services-blog/azure-ai-search-outperforming-vector-search-with-hybrid-retrieval-and-ranking-ca/3929167120
docs/productionizing.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azurearchitectureblog/azure-openai-landing-zone-reference-architecture/388210286
docs/deploy_features.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azure-ai-services-blog/announcing-the-public-preview-of-integrated-vectorization-in-azure-ai-search/3960809218
docs/data_ingestion.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azure-ai-services-blog/announcing-the-public-preview-of-integrated-vectorization-in-azure-ai-search/396080978

@pamelafox
Copy link
Collaborator

Update: I found an issue with the authentication decorator being used for the /items/id get and delete routes and have fixed it. It's working well for me locally now.
I'll add additional pytests tomorrow.


return jsonify({"items": items, "continuation_token": continuation_token}), 200

except Exception as error:

Choose a reason for hiding this comment

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

Copy link
Collaborator

Choose a reason for hiding this comment

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

Nice suggestion! I've looked into it and I think that we do want the general Exception catching here, to make sure that we always pass JSON down to the frontend if the server errors, so the user sees that there's been an error. But we might add special handling for the CosmosDB exceptions in future if it makes our logs easier to grok.


# Get the first page, and the continuation token
try:
page = await pager.__anext__()

Choose a reason for hiding this comment

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

Consider iterating over the by_page result directly and avoiding explicit calls to await pager.anext(), i.e. process each page as soon as it’s available without awaiting when there are no more pages.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think you mean to just do:
async for page in pager:
async for item in page:

As we use similar code for other Azure Python SDKs elsewhere in this repo. That wouldn't give us the continuation token, right, as that would exhaust all the pages?

Choose a reason for hiding this comment

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

Yes, but you should still be able to get the continuation token. Something like (untested):

        items = []
        async for page in container.query_items(
            query="SELECT c.id, c.entra_oid, c.title, c.timestamp FROM c WHERE c.entra_oid = @entra_oid ORDER BY c.timestamp DESC",
            parameters=[{"name": "@entra_oid", "value": entra_oid}],
            max_item_count=count,
        ).by_page(continuation_token):
            async for item in page:
                items.append(
                    {
                        "id": item.get("id"),
                        "entra_oid": item.get("entra_oid"),
                        "title": item.get("title", "untitled"),
                        "timestamp": item.get("timestamp"),
                    }
                )
            # Update continuation token after processing the page
            continuation_token = page.continuation_token if hasattr(page, "continuation_token") else None

            # Break if no continuation token (i.e., last page)
            if not continuation_token:
                break

Just a suggestion. I think its fine as it is :-)

if not current_app.config[CONFIG_CHAT_HISTORY_COSMOS_ENABLED]:
return jsonify({"error": "Chat history not enabled"}), 405

container: ContainerProxy = current_app.config[CONFIG_COSMOS_HISTORY_CONTAINER]

Choose a reason for hiding this comment

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

This is called in every function and I assume it never changes. Could this be cached or defined globally?

Copy link
Collaborator

Choose a reason for hiding this comment

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

current_app.config is basically our global dict, it's how we access objects that were setup at the beginning of the app start. I don't think there's a performance hit, since it should be O(1) retrieval.

{"id": id, "entra_oid": entra_oid, "title": title, "answers": answers, "timestamp": timestamp}
)

return jsonify({}), 201

Choose a reason for hiding this comment

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

wonder whether the status code here should be based on the response from container.upsert_item - if the item already already exists, then it will be 200 instead of 201

Copy link
Collaborator

Choose a reason for hiding this comment

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

That's a nice idea! I can't figure out from the SDK return types what in the response would indicate that however, as all the function signatures just say that they return a dict: https://learn.microsoft.com/en-us/python/api/azure-cosmos/azure.cosmos.container.containerproxy?view=azure-python#azure-cosmos-container-containerproxy-upsert-item
And this example doesn't do anything with the dict: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/cosmos/azure-cosmos/samples/document_management.py#L149-L156
So is there some listing of what key in the dict would indicate it already existing?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I've taken a look at the dicts for a first-time ID versus a second-time ID, and I can't find any key that'd indicate whether the item already existed:
https://www.diffchecker.com/Fs0ECaUa/
So I don't think we should change the status code currently, as I'm not sure what condition to use, but happy to change in the future if you have suggestions.

Copy link

github-actions bot commented Nov 8, 2024

Check Broken URLs

We have automatically detected the following broken URLs in your files. Review and fix the paths to resolve this issue.

Check the file paths and associated broken URLs inside them. For more details, check our Contributing Guide.

File Full Path Issues
README.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azure-ai-services-blog/revolutionize-your-enterprise-data-with-chatgpt-next-gen-apps-w-azure-openai-and/3762087292
2https://techcommunity.microsoft.com/blog/azure-ai-services-blog/access-control-in-generative-ai-applications-with-azure-ai-search/3956408296
docs/deploy_lowcost.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azure-ai-services-blog/azure-ai-search-outperforming-vector-search-with-hybrid-retrieval-and-ranking-ca/392916743
docs/customization.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azure-ai-services-blog/azure-ai-search-outperforming-vector-search-with-hybrid-retrieval-and-ranking-ca/3929167120
docs/productionizing.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azurearchitectureblog/azure-openai-landing-zone-reference-architecture/388210286
docs/deploy_features.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azure-ai-services-blog/announcing-the-public-preview-of-integrated-vectorization-in-azure-ai-search/3960809218
docs/data_ingestion.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azure-ai-services-blog/announcing-the-public-preview-of-integrated-vectorization-in-azure-ai-search/396080978

Copy link

github-actions bot commented Nov 8, 2024

Check Broken URLs

We have automatically detected the following broken URLs in your files. Review and fix the paths to resolve this issue.

Check the file paths and associated broken URLs inside them. For more details, check our Contributing Guide.

File Full Path Issues
README.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azure-ai-services-blog/revolutionize-your-enterprise-data-with-chatgpt-next-gen-apps-w-azure-openai-and/3762087292
2https://techcommunity.microsoft.com/blog/azure-ai-services-blog/access-control-in-generative-ai-applications-with-azure-ai-search/3956408296
docs/deploy_lowcost.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azure-ai-services-blog/azure-ai-search-outperforming-vector-search-with-hybrid-retrieval-and-ranking-ca/392916743
docs/customization.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azure-ai-services-blog/azure-ai-search-outperforming-vector-search-with-hybrid-retrieval-and-ranking-ca/3929167120
docs/productionizing.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azurearchitectureblog/azure-openai-landing-zone-reference-architecture/388210286
docs/deploy_features.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azure-ai-services-blog/announcing-the-public-preview-of-integrated-vectorization-in-azure-ai-search/3960809218
docs/data_ingestion.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azure-ai-services-blog/announcing-the-public-preview-of-integrated-vectorization-in-azure-ai-search/396080978

@pamelafox
Copy link
Collaborator

@fujita-h I added pytest for 97% coverage of CosmosDB. While adding the tests, I made a few changes:

  • 400 instead of 405 for CosmosDB feature disabled (I think 405 is meant to specifically indicate that a different HTTP method would have worked, but none would work in this case)
  • 204 instead of 200 for successful item deletion, as that seems to be the web convention
  • Removed the check in the "/item_id" paths for "if not item_id" as I don't think there's any possible way to reach that code path- if no item_id is specified, it won't match the route and will 404 instead.

Copy link

github-actions bot commented Nov 8, 2024

Check Broken URLs

We have automatically detected the following broken URLs in your files. Review and fix the paths to resolve this issue.

Check the file paths and associated broken URLs inside them. For more details, check our Contributing Guide.

File Full Path Issues
README.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azure-ai-services-blog/revolutionize-your-enterprise-data-with-chatgpt-next-gen-apps-w-azure-openai-and/3762087292
2https://techcommunity.microsoft.com/blog/azure-ai-services-blog/access-control-in-generative-ai-applications-with-azure-ai-search/3956408296
docs/deploy_lowcost.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azure-ai-services-blog/azure-ai-search-outperforming-vector-search-with-hybrid-retrieval-and-ranking-ca/392916743
docs/customization.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azure-ai-services-blog/azure-ai-search-outperforming-vector-search-with-hybrid-retrieval-and-ranking-ca/3929167120
docs/productionizing.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azurearchitectureblog/azure-openai-landing-zone-reference-architecture/388210286
docs/deploy_features.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azure-ai-services-blog/announcing-the-public-preview-of-integrated-vectorization-in-azure-ai-search/3960809218
docs/data_ingestion.md
#LinkLine Number
1https://techcommunity.microsoft.com/blog/azure-ai-services-blog/announcing-the-public-preview-of-integrated-vectorization-in-azure-ai-search/396080978

@fujita-h
Copy link
Contributor Author

@pamelafox Thank you for adding the test, I have read and understood your corrections.

Copy link
Collaborator

@pamelafox pamelafox left a comment

Choose a reason for hiding this comment

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

I am approving this PR, as I've both tested manually and with pytest unit tests, and the CosmosDB team has also reviewed it, with no blocking issues. I'll check if @mattgotteiner wants to take a look before we merge.
Thanks @fujita-h for the great work!

Copy link

Check Broken URLs

We have automatically detected the following broken URLs in your files. Review and fix the paths to resolve this issue.

Check the file paths and associated broken URLs inside them. For more details, check our Contributing Guide.

File Full Path Issues
README.md
#LinkLine Number
1https://azure.microsoft.com/pricing/details/app-service/linux/90
2https://azure.microsoft.com/pricing/details/search/93

@pamelafox
Copy link
Collaborator

I looked at the Bicep validation error but I think that is an unrelated issue, likely due to a newer version of Bicep that's stricter.

Copy link
Collaborator

@mattgotteiner mattgotteiner left a comment

Choose a reason for hiding this comment

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

Thanks for your contributions

@pamelafox pamelafox merged commit c3810e8 into Azure-Samples:main Nov 12, 2024
16 of 17 checks passed
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.

5 participants