├── .devcontainer
└── devcontainer.json
├── .editorconfig
├── .github
└── workflows
│ ├── deploy.yml
│ ├── docs.yml
│ ├── packages.yml
│ └── slides.yml
├── .gitignore
├── .prettierignore
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── SECURITY.md
├── SUPPORT.md
├── azure.yaml
├── data
├── privacy-policy.pdf
├── support.pdf
└── terms-of-service.pdf
├── docker-compose.yml
├── docs
├── _workshop-java-quarkus.md
├── assets
│ ├── aca-environment.png
│ ├── architecture.excalidraw
│ ├── architecture.png
│ ├── azd-deploy-output.png
│ ├── azure-ai-search-indexes.png
│ ├── azure-ai-search-logo.png
│ ├── azure-ai-search-results.png
│ ├── azure-ai-search.png
│ ├── azure-architecture-generic-db.excalidraw
│ ├── azure-architecture-generic-db.png
│ ├── azure-architecture.excalidraw
│ ├── azure-architecture.png
│ ├── azure-compute-services.png
│ ├── azure-container-apps.png
│ ├── azure-portal-azd.png
│ ├── azure-resource-manager.png
│ ├── banner.jpg
│ ├── chat-streaming.gif
│ ├── chatbot-answer.png
│ ├── class-diagram-model.png
│ ├── class-diagram-rest.png
│ ├── create-codespaces.png
│ ├── deployed-app.png
│ ├── follow-up-questions.png
│ ├── fork-project.png
│ ├── gh-actions.png
│ ├── gh-workflow-details.png
│ ├── github-clone.png
│ ├── ingestion-cli.png
│ ├── ingestion-deployement.png
│ ├── portal-burger.png
│ ├── qdrant-dashboard-collection.png
│ ├── qdrant-dashboard.png
│ ├── qdrant-logo.png
│ ├── rag.png
│ ├── vscode-dev-container-status.png
│ └── vscode-reopen-in-container.png
├── sections
│ ├── 00-welcome.md
│ ├── 01-intro.md
│ ├── 02-preparation.md
│ ├── 02.1-additional-setup.md
│ ├── 03-overview.md
│ ├── 04-vector-db.md
│ ├── 05-ingestion.md
│ ├── 06-chat-api.md
│ ├── 08-website.md
│ ├── 09-azure.md
│ ├── 10-deployment.md
│ ├── 10.1-ci-cd.md
│ ├── 12-conclusion.md
│ └── _old
│ │ ├── 09-azure-llm.md
│ │ └── 11-improvements.md
└── slides
│ ├── README.md
│ ├── images
│ ├── agent.png
│ ├── ai.jpg
│ ├── antonio.png
│ ├── book-langchain4j.png
│ ├── chatgpt.png
│ ├── chris-dive.jpg
│ ├── chris.jpg
│ ├── embedding.png
│ ├── julien.jpg
│ ├── llm-magazine.jpg
│ ├── llm-training.png
│ ├── microsoft-azure.png
│ ├── ms-full-logo.svg
│ ├── olivier.jpg
│ ├── openai.png
│ ├── rag.png
│ ├── sandra.jpg
│ ├── tokens.png
│ ├── tokens2.png
│ └── yohan.jpg
│ ├── slides-java-quarkus-reactor.md
│ ├── slides-java-quarkus.md
│ └── template
│ ├── Archivo-Variable.ttf
│ ├── Inconsolata-Regular.ttf
│ ├── Saira-Variable.ttf
│ ├── animate.css
│ ├── code.js
│ ├── fa-brands-400.woff2
│ ├── fa-regular-400.woff2
│ ├── fa-solid-900.woff2
│ ├── fontawesome-all.css
│ ├── fonts.css
│ ├── index.html
│ ├── remark.min.js
│ ├── rough-notation.js
│ └── style.scss
├── infra
├── abbreviations.json
├── azure
│ └── deploy-azure-openai-models.sh
├── core
│ ├── ai
│ │ └── cognitiveservices.bicep
│ ├── host
│ │ ├── container-app.bicep
│ │ ├── container-apps-environment.bicep
│ │ ├── container-apps.bicep
│ │ ├── container-registry.bicep
│ │ └── staticwebapp.bicep
│ ├── monitor
│ │ ├── applicationinsights-dashboard.bicep
│ │ ├── applicationinsights.bicep
│ │ ├── loganalytics.bicep
│ │ └── monitoring.bicep
│ ├── search
│ │ └── search-services.bicep
│ └── security
│ │ ├── managed-identity.bicep
│ │ ├── registry-access.bicep
│ │ └── role.bicep
├── main.bicep
└── main.parameters.json
├── package-lock.json
├── package.json
├── pom.xml
├── scripts
├── ingest-data.ps1
├── ingest-data.sh
├── repo
│ ├── build-docs.sh
│ └── create-packages.sh
└── setup-template.sh
├── src
├── backend
│ ├── .dockerignore
│ ├── .gitignore
│ ├── .mvn
│ │ └── wrapper
│ │ │ ├── .gitignore
│ │ │ ├── MavenWrapperDownloader.java
│ │ │ └── maven-wrapper.properties
│ ├── Dockerfile
│ ├── mvnw
│ ├── mvnw.cmd
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ ├── docker
│ │ ├── Dockerfile.jvm
│ │ ├── Dockerfile.legacy-jar
│ │ ├── Dockerfile.native
│ │ └── Dockerfile.native-micro
│ │ ├── java
│ │ └── ai
│ │ │ └── azure
│ │ │ └── openai
│ │ │ └── rag
│ │ │ └── workshop
│ │ │ └── backend
│ │ │ ├── configuration
│ │ │ ├── ChatLanguageModelAzureOpenAiProducer.java
│ │ │ ├── ChatLanguageModelOllamaProducer.java
│ │ │ ├── EmbeddingModelProducer.java
│ │ │ └── EmbeddingStoreProducer.java
│ │ │ └── rest
│ │ │ ├── ChatMessage.java
│ │ │ ├── ChatRequest.java
│ │ │ ├── ChatResource.java
│ │ │ └── ChatResponse.java
│ │ ├── plantuml
│ │ ├── class-diagram-model.puml
│ │ └── class-diagram-rest.puml
│ │ ├── resources
│ │ ├── META-INF
│ │ │ └── resources
│ │ │ │ ├── assets
│ │ │ │ ├── index-4LLPZTDq.js
│ │ │ │ ├── index-4LLPZTDq.js.map
│ │ │ │ ├── vendor-9AMKf4B_.js
│ │ │ │ └── vendor-9AMKf4B_.js.map
│ │ │ │ ├── favicon.ico
│ │ │ │ └── index.html
│ │ ├── application.properties
│ │ └── default_banner.txt
│ │ └── script
│ │ └── curl-chat-endpoint.sh
├── frontend
│ ├── .lintstagedrc
│ ├── README.md
│ ├── assets
│ │ ├── lightbulb.svg
│ │ ├── new-chat.svg
│ │ ├── question.svg
│ │ └── send.svg
│ ├── index.html
│ ├── package.json
│ ├── public
│ │ └── favicon.ico
│ ├── src
│ │ ├── api.ts
│ │ ├── components
│ │ │ ├── chat.ts
│ │ │ └── debug.ts
│ │ ├── index.ts
│ │ ├── message-parser.ts
│ │ ├── models.ts
│ │ └── vite-env.d.ts
│ ├── tsconfig.json
│ └── vite.config.ts
└── ingestion
│ ├── .dockerignore
│ ├── .gitignore
│ ├── .mvn
│ └── wrapper
│ │ ├── .gitignore
│ │ ├── MavenWrapperDownloader.java
│ │ └── maven-wrapper.properties
│ ├── Dockerfile
│ ├── mvnw
│ ├── mvnw.cmd
│ ├── pom.xml
│ └── src
│ ├── main
│ ├── java
│ │ └── ai
│ │ │ └── azure
│ │ │ └── openai
│ │ │ └── rag
│ │ │ └── workshop
│ │ │ └── ingestion
│ │ │ ├── configuration
│ │ │ ├── EmbeddingModelProducer.java
│ │ │ └── EmbeddingStoreProducer.java
│ │ │ └── rest
│ │ │ └── DocumentIngestor.java
│ └── resources
│ │ ├── application.properties
│ │ ├── default_banner.txt
│ │ └── tinylog.properties
│ └── test
│ └── java
│ └── ai
│ └── azure
│ └── openai
│ └── rag
│ └── workshop
│ └── ingestion
│ └── .gitkeep
└── trainer
├── Dockerfile
├── README.md
├── azure.yaml
├── infra
├── abbreviations.json
├── core
│ ├── ai
│ │ └── cognitiveservices.bicep
│ ├── host
│ │ ├── container-app.bicep
│ │ ├── container-apps-environment.bicep
│ │ ├── container-apps.bicep
│ │ └── container-registry.bicep
│ ├── monitor
│ │ └── loganalytics.bicep
│ └── security
│ │ ├── managed-identity.bicep
│ │ ├── registry-access.bicep
│ │ └── role.bicep
├── main.bicep
└── main.parameters.json
├── package-lock.json
├── package.json
├── src
├── app.ts
├── plugins
│ ├── config.ts
│ ├── proxy.ts
│ └── sensible.ts
└── routes
│ └── root.ts
├── test.http
└── tsconfig.json
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the
2 | // README at: https://github.com/devcontainers/templates/tree/main/src/javascript-node
3 | {
4 | "name": "OpenAI Workshop",
5 |
6 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
7 | "image": "mcr.microsoft.com/devcontainers/javascript-node:20-bullseye",
8 |
9 | // Features to add to the dev container. More info: https://containers.dev/features.
10 | "features": {
11 | "ghcr.io/devcontainers/features/node:1": {
12 | "version": "20"
13 | },
14 | "ghcr.io/devcontainers/features/java:1": {
15 | "version": "21",
16 | "installMaven": "true",
17 | "installGradle": "false"
18 | },
19 | "ghcr.io/devcontainers/features/docker-in-docker:2": {
20 | "version": 20,
21 | "moby": "false",
22 | "dockerDashComposeVersion": "v2"
23 | },
24 | "ghcr.io/devcontainers/features/azure-cli:1": {
25 | "version": "latest",
26 | "installBicep": true
27 | },
28 | "ghcr.io/devcontainers/features/github-cli:1": {},
29 | // "ghcr.io/devcontainers/features/powershell:1": {},
30 | "ghcr.io/azure/azure-dev/azd:latest": {},
31 | "ghcr.io/prulloac/devcontainer-features/ollama:1": {
32 | "pull": "mistral"
33 | }
34 | },
35 |
36 | // Configure tool-specific properties.
37 | "customizations": {
38 | "vscode": {
39 | "extensions": [
40 | "ms-azuretools.azure-dev",
41 | "ms-azuretools.vscode-bicep",
42 | "ms-azuretools.vscode-docker",
43 | "vscjava.vscode-java-pack",
44 | "esbenp.prettier-vscode",
45 | "humao.rest-client",
46 | "runem.lit-plugin"
47 | ]
48 | }
49 | },
50 |
51 | // Use 'forwardPorts' to make a list of ports inside the container available locally.
52 | "forwardPorts": [3000, 3001, 8000, 6333],
53 |
54 | // Use 'postCreateCommand' to run commands after the container is created.
55 | "postCreateCommand": "npm install -g @moaw/cli fuzz-run",
56 |
57 | // Set minimal host requirements for the container.
58 | "hostRequirements": {
59 | "memory": "8gb"
60 | }
61 |
62 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
63 | // "remoteUser": "root"
64 | }
65 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | end_of_line = lf
7 | indent_style = space
8 | indent_size = 2
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 |
12 | [*.md]
13 | max_line_length = off
14 | trim_trailing_whitespace = false
15 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Deploy to Azure
2 | on:
3 | push:
4 | # Run only when commits are pushed to main branch
5 | branches:
6 | - main
7 |
8 | # Set up permissions for deploying with secretless Azure federated credentials
9 | # https://learn.microsoft.com/en-us/azure/developer/github/connect-from-azure?tabs=azure-portal%2Clinux#set-up-azure-login-with-openid-connect-authentication
10 | permissions:
11 | id-token: write
12 | contents: read
13 |
14 | jobs:
15 | deploy:
16 | runs-on: ubuntu-latest
17 | env:
18 | AZURE_CLIENT_ID: ${{ vars.AZURE_CLIENT_ID }}
19 | AZURE_TENANT_ID: ${{ vars.AZURE_TENANT_ID }}
20 | AZURE_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }}
21 | AZURE_OPENAI_URL: ${{ vars.AZURE_OPENAI_URL }}
22 | steps:
23 | - name: Checkout
24 | uses: actions/checkout@v4
25 |
26 | - name: Install azd
27 | uses: Azure/setup-azd@v1.0.0
28 |
29 | - name: Log in with Azure (Federated Credentials)
30 | if: ${{ env.AZURE_CLIENT_ID != '' }}
31 | run: |
32 | azd auth login `
33 | --client-id "$Env:AZURE_CLIENT_ID" `
34 | --federated-credential-provider "github" `
35 | --tenant-id "$Env:AZURE_TENANT_ID"
36 | shell: pwsh
37 |
38 | - name: Build and deploy application
39 | if: ${{ env.AZURE_CLIENT_ID != '' }}
40 | run: |
41 | azd up --no-prompt
42 | env:
43 | AZURE_ENV_NAME: ${{ vars.AZURE_ENV_NAME }}
44 | AZURE_LOCATION: ${{ vars.AZURE_LOCATION }}
45 | AZURE_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }}
46 |
--------------------------------------------------------------------------------
/.github/workflows/docs.yml:
--------------------------------------------------------------------------------
1 | name: Build workshop docs
2 | on:
3 | push:
4 | branches: [main]
5 |
6 | jobs:
7 | docs:
8 | name: Build docs
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Checkout
12 | uses: actions/checkout@v4
13 | - name: Setup Node.js
14 | uses: actions/setup-node@v4
15 | with:
16 | node-version: 20
17 | - name: Install tools
18 | run: npm i -g @moaw/cli
19 | - name: Build and update docs
20 | run: |
21 | git config --global user.name "sinedied"
22 | git config --global user.email "noda@free.fr"
23 | ./scripts/repo/build-docs.sh
24 | env:
25 | GH_TOKEN: ${{ secrets.GH_TOKEN }}
26 |
--------------------------------------------------------------------------------
/.github/workflows/packages.yml:
--------------------------------------------------------------------------------
1 | name: Update workshop packages
2 | on:
3 | push:
4 | branches: [main]
5 |
6 | jobs:
7 | update_packages:
8 | name: Update
9 | runs-on: ubuntu-latest
10 | permissions:
11 | contents: write
12 | steps:
13 | - name: Checkout
14 | uses: actions/checkout@v4
15 | - name: Create packages
16 | run: ./scripts/repo/create-packages.sh
17 | - name: Update release
18 | uses: ncipollo/release-action@v1.12.0
19 | with:
20 | name: Workshop packages
21 | tag: latest
22 | artifacts: 'dist/*.tar.gz'
23 | allowUpdates: true
24 | removeArtifacts: true
25 |
--------------------------------------------------------------------------------
/.github/workflows/slides.yml:
--------------------------------------------------------------------------------
1 | name: Deploy slides
2 | on:
3 | push:
4 | branches: [main]
5 | workflow_dispatch:
6 |
7 | concurrency:
8 | group: 'pages'
9 | cancel-in-progress: true
10 |
11 | jobs:
12 | deploy_slides:
13 | name: Build and Deploy
14 | permissions:
15 | pages: write
16 | id-token: write
17 | environment:
18 | name: github-pages
19 | url: ${{ steps.deployment.outputs.page_url }}
20 | runs-on: ubuntu-latest
21 | steps:
22 | - name: Checkout
23 | uses: actions/checkout@v4
24 | - name: Setup Node.js
25 | uses: actions/setup-node@v4
26 | with:
27 | node-version: 20
28 | - name: Install tools
29 | run: npm i -g backslide
30 | - name: Build slides
31 | run: |
32 | cd docs/slides
33 | bs export --web slides-java-quarkus.md --output dist/java-quarkus
34 | - name: Setup GitHub Pages
35 | uses: actions/configure-pages@v5
36 | - name: Upload artifact
37 | uses: actions/upload-pages-artifact@v3
38 | with:
39 | path: docs/slides/dist
40 | - name: Deploy to GitHub Pages
41 | id: deployment
42 | uses: actions/deploy-pages@v4
43 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled output
2 | node_modules/
3 | dist/
4 | .tmp/
5 |
6 | # Logs
7 | logs
8 | *.log
9 | npm-debug.log*
10 | pnpm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 | lerna-debug.log*
14 |
15 | # IntelliJ
16 | .idea
17 | *.iml
18 |
19 | # Deployment
20 | *.env*
21 | .azure
22 |
23 | # DB Storage
24 | .qdrant/
25 |
26 | # Misc
27 | .DS_Store
28 | .current
29 | *dummy*
30 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | docs
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Microsoft Open Source Code of Conduct
2 |
3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
4 |
5 | Resources:
6 |
7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Microsoft Corporation.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Security
4 |
5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
6 |
7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.
8 |
9 | ## Reporting Security Issues
10 |
11 | **Please do not report security vulnerabilities through public GitHub issues.**
12 |
13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).
14 |
15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).
16 |
17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).
18 |
19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
20 |
21 | - Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
22 | - Full paths of source file(s) related to the manifestation of the issue
23 | - The location of the affected source code (tag/branch/commit or direct URL)
24 | - Any special configuration required to reproduce the issue
25 | - Step-by-step instructions to reproduce the issue
26 | - Proof-of-concept or exploit code (if possible)
27 | - Impact of the issue, including how an attacker might exploit the issue
28 |
29 | This information will help us triage your report more quickly.
30 |
31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.
32 |
33 | ## Preferred Languages
34 |
35 | We prefer all communications to be in English.
36 |
37 | ## Policy
38 |
39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
40 |
41 |
42 |
--------------------------------------------------------------------------------
/SUPPORT.md:
--------------------------------------------------------------------------------
1 | # Support
2 |
3 | ## How to file issues and get help
4 |
5 | This project uses GitHub Issues to track bugs and feature requests. Please search the existing
6 | issues before filing new issues to avoid duplicates. For new issues, file your bug or
7 | feature request as a new Issue.
8 |
9 | For help and questions about using this project, please use GitHub Issues and tag them with the
10 | **question** label.
11 |
12 | ## Microsoft Support Policy
13 |
14 | Support for this **PROJECT or PRODUCT** is limited to the resources listed above.
15 |
--------------------------------------------------------------------------------
/azure.yaml:
--------------------------------------------------------------------------------
1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json
2 |
3 | name: azure-openai-rag-workshop-java
4 | metadata:
5 | template: azure-openai-rag-workshop-java@1.0.0
6 |
7 | services:
8 | frontend:
9 | project: ./src/frontend
10 | dist: dist
11 | language: ts
12 | host: staticwebapp
13 | hooks:
14 | predeploy:
15 | windows:
16 | shell: pwsh
17 | run: Export-ModuleMember -Variable BACKEND_API_URI && npm run build
18 | interactive: true
19 | continueOnError: false
20 | posix:
21 | shell: sh
22 | run: export BACKEND_API_URI && npm run build
23 | interactive: true
24 | continueOnError: false
25 |
26 | backend:
27 | project: ./src/backend
28 | language: java
29 | host: containerapp
30 |
31 | ingestion:
32 | project: ./src/ingestion
33 | language: java
34 | host: containerapp
35 |
36 | hooks:
37 | postup:
38 | windows:
39 | shell: pwsh
40 | run: ./scripts/ingest-data.ps1
41 | interactive: true
42 | continueOnError: false
43 | posix:
44 | shell: sh
45 | run: ./scripts/ingest-data.sh
46 | interactive: true
47 | continueOnError: false
48 |
--------------------------------------------------------------------------------
/data/privacy-policy.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/data/privacy-policy.pdf
--------------------------------------------------------------------------------
/data/support.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/data/support.pdf
--------------------------------------------------------------------------------
/data/terms-of-service.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/data/terms-of-service.pdf
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | services:
2 | backend:
3 | build:
4 | dockerfile: ./src/backend/Dockerfile
5 | environment:
6 | - AZURE_OPENAI_URL=${AZURE_OPENAI_URL}
7 | - QDRANT_URL=http://qdrant:6334
8 | - LOCAL=true
9 | ports:
10 | - 3000:3000
11 |
12 | ingestion:
13 | build:
14 | dockerfile: ./src/ingestion/Dockerfile
15 | environment:
16 | - AZURE_OPENAI_URL=${AZURE_OPENAI_URL}
17 | - QDRANT_URL=http://qdrant:6334
18 | ports:
19 | - 3001:3001
20 |
21 | qdrant:
22 | image: docker.io/qdrant/qdrant:v1.12.4
23 | ports:
24 | - 6333:6333
25 | - 6334:6334
26 | volumes:
27 | - .qdrant:/qdrant/storage:z
28 |
--------------------------------------------------------------------------------
/docs/_workshop-java-quarkus.md:
--------------------------------------------------------------------------------
1 | include::sections/00-welcome.md[]
2 |
3 | ---
4 |
5 | include::sections/01-intro.md[]
6 |
7 | ---
8 |
9 | include::sections/02-preparation.md[]
10 |
11 | ---
12 |
13 | include::sections/02.1-additional-setup.md[]
14 |
15 | ---
16 |
17 | include::sections/03-overview.md[]
18 |
19 | ---
20 |
21 | include::sections/04-vector-db.md[]
22 |
23 | ---
24 |
25 | include::sections/05-ingestion.md[]
26 |
27 | ---
28 |
29 | include::sections/06-chat-api.md[]
30 |
31 | ---
32 |
33 | include::sections/08-website.md[]
34 |
35 | ---
36 |
37 | include::sections/09-azure.md[]
38 |
39 | ---
40 |
41 | include::sections/10-deployment.md[]
42 |
43 | ---
44 |
45 | include::sections/12-conclusion.md[]
46 |
--------------------------------------------------------------------------------
/docs/assets/aca-environment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/aca-environment.png
--------------------------------------------------------------------------------
/docs/assets/architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/architecture.png
--------------------------------------------------------------------------------
/docs/assets/azd-deploy-output.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/azd-deploy-output.png
--------------------------------------------------------------------------------
/docs/assets/azure-ai-search-indexes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/azure-ai-search-indexes.png
--------------------------------------------------------------------------------
/docs/assets/azure-ai-search-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/azure-ai-search-logo.png
--------------------------------------------------------------------------------
/docs/assets/azure-ai-search-results.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/azure-ai-search-results.png
--------------------------------------------------------------------------------
/docs/assets/azure-ai-search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/azure-ai-search.png
--------------------------------------------------------------------------------
/docs/assets/azure-architecture-generic-db.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/azure-architecture-generic-db.png
--------------------------------------------------------------------------------
/docs/assets/azure-architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/azure-architecture.png
--------------------------------------------------------------------------------
/docs/assets/azure-compute-services.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/azure-compute-services.png
--------------------------------------------------------------------------------
/docs/assets/azure-container-apps.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/azure-container-apps.png
--------------------------------------------------------------------------------
/docs/assets/azure-portal-azd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/azure-portal-azd.png
--------------------------------------------------------------------------------
/docs/assets/azure-resource-manager.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/azure-resource-manager.png
--------------------------------------------------------------------------------
/docs/assets/banner.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/banner.jpg
--------------------------------------------------------------------------------
/docs/assets/chat-streaming.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/chat-streaming.gif
--------------------------------------------------------------------------------
/docs/assets/chatbot-answer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/chatbot-answer.png
--------------------------------------------------------------------------------
/docs/assets/class-diagram-model.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/class-diagram-model.png
--------------------------------------------------------------------------------
/docs/assets/class-diagram-rest.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/class-diagram-rest.png
--------------------------------------------------------------------------------
/docs/assets/create-codespaces.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/create-codespaces.png
--------------------------------------------------------------------------------
/docs/assets/deployed-app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/deployed-app.png
--------------------------------------------------------------------------------
/docs/assets/follow-up-questions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/follow-up-questions.png
--------------------------------------------------------------------------------
/docs/assets/fork-project.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/fork-project.png
--------------------------------------------------------------------------------
/docs/assets/gh-actions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/gh-actions.png
--------------------------------------------------------------------------------
/docs/assets/gh-workflow-details.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/gh-workflow-details.png
--------------------------------------------------------------------------------
/docs/assets/github-clone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/github-clone.png
--------------------------------------------------------------------------------
/docs/assets/ingestion-cli.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/ingestion-cli.png
--------------------------------------------------------------------------------
/docs/assets/ingestion-deployement.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/ingestion-deployement.png
--------------------------------------------------------------------------------
/docs/assets/portal-burger.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/portal-burger.png
--------------------------------------------------------------------------------
/docs/assets/qdrant-dashboard-collection.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/qdrant-dashboard-collection.png
--------------------------------------------------------------------------------
/docs/assets/qdrant-dashboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/qdrant-dashboard.png
--------------------------------------------------------------------------------
/docs/assets/qdrant-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/qdrant-logo.png
--------------------------------------------------------------------------------
/docs/assets/rag.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/rag.png
--------------------------------------------------------------------------------
/docs/assets/vscode-dev-container-status.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/vscode-dev-container-status.png
--------------------------------------------------------------------------------
/docs/assets/vscode-reopen-in-container.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/assets/vscode-reopen-in-container.png
--------------------------------------------------------------------------------
/docs/sections/00-welcome.md:
--------------------------------------------------------------------------------
1 | ---
2 | short_title: Create your own ChatGPT with RAG
3 | description: Discover how to create and populate a vector database, create a Web chat interface and an API to expose your agent to the Web interface.
4 | type: workshop
5 | authors:
6 | - Yohan Lasorsa
7 | - Julien Dubois
8 | - Christopher Maneu
9 | - Sandra Ahlgrimm
10 | - Antonio Goncalves
11 | contacts:
12 | - '@sinedied'
13 | - '@juliendubois'
14 | - '@cmaneu'
15 | - '@sKriemhild'
16 | - '@agoncal'
17 | banner_url: assets/banner.jpg
18 | duration_minutes: 120
19 | audience: students, devs
20 | level: intermediate
21 | tags: chatgpt, openai, langchain4j, retrieval-augmented-generation, azure, containers, docker, static web apps, java, quarkus, azure ai search, azure container apps, qdrant, vector database
22 | published: false
23 | wt_id: java-0000-cxa
24 | sections_title:
25 | - Welcome
26 | ---
27 |
28 | # Create your own ChatGPT with Retrieval-Augmented-Generation
29 |
30 | In this workshop, we'll explore the fundamentals of custom ChatGPT experiences based on a corpus of documents. We will create a vector database and fill-in with data from PDF documents, and then build a chat website and API to be able to ask questions about information contained in these documents.
31 |
32 | ## You'll learn how to...
33 |
34 | - Create a knowledge base using a vector database.
35 | - Ingest documents in a vector database.
36 | - Create a Web API with [Quarkus](https://quarkus.io/).
37 | - Use [Azure OpenAI](https://azure.microsoft.com/products/ai-services/openai-service) models and [LangChain4j](https://langchain4j.github.io/langchain4j/) to generate answers based on a prompt.
38 | - Query a vector database and augment a prompt to generate responses.
39 | - Connect your Web API to a ChatGPT-like website.
40 | - (optionally) Deploy your application to Azure.
41 |
42 | ## Prerequisites
43 |
44 |
45 |
46 | | | |
47 | |-------------------|----------------------------------------------------------------------|
48 | | GitHub account | [Get a free GitHub account](https://github.com/join) |
49 | | Azure account | [Get a free Azure account](https://azure.microsoft.com/free) |
50 | | Access to Azure OpenAI API | [Request access to Azure OpenAI](https://aka.ms/oaiapply) |
51 | | A Web browser | [Get Microsoft Edge](https://www.microsoft.com/edge) |
52 | | An HTTP client | [For example curl](https://curl.se/) |
53 | | Java knowledge | [Java tutorial on W3schools](https://www.w3schools.com/java/) |
54 | | Quarkus knowledge | [Quarkus Getting Started](https://quarkus.io/guides/getting-started) |
55 |
56 |
57 |
58 |
59 |
60 | | | |
61 | |-------------------|----------------------------------------------------------------------|
62 | | GitHub account | [Get a free GitHub account](https://github.com/join) |
63 | | A Web browser | [Get Microsoft Edge](https://www.microsoft.com/edge) |
64 | | An HTTP client | [For example curl](https://curl.se/) |
65 | | Java knowledge | [Java tutorial on W3schools](https://www.w3schools.com/java/) |
66 | | Quarkus knowledge | [Quarkus Getting Started](https://quarkus.io/guides/getting-started) |
67 |
68 |
69 |
70 | We'll use [GitHub Codespaces](https://github.com/features/codespaces) to have an instant dev environment already prepared for this workshop.
71 |
72 | If you prefer to work locally, we'll also provide instructions to setup a local dev environment using either VS Code with a [dev container](https://aka.ms/vscode/ext/devcontainer) or a manual install of the needed tools with your favourite IDE (Intellij IDEA, VS Code, etc.).
73 |
74 |
75 |
76 | > Your Azure account must have `Microsoft.Authorization/roleAssignments/write` permissions, such as [Role Based Access Control Administrator](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#role-based-access-control-administrator-preview), [User Access Administrator](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#user-access-administrator), or [Owner](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#owner). Your account also needs `Microsoft.Resources/deployments/write` permissions at a subscription level to allow deployment of Azure resources.
77 | >
78 | > If you have your own personal Azure subscription, you should be good to go. If you're using an Azure subscription provided by your company, you may need to contact your IT department to ensure you have the necessary permissions.
79 |
80 |
--------------------------------------------------------------------------------
/docs/sections/01-intro.md:
--------------------------------------------------------------------------------
1 | ## Introduction
2 |
3 | Organizations of all sizes have amassed a plethora of documents over time. While generative AI, such as ChatGPT, can provide answers about general knowledge and historical events with reasonable accuracy, they can also be tailored to answer questions based on a company's internal documents.
4 |
5 |
6 |
7 | > **Accuracy in Generative AI**
8 | > Large Language Models (LLMs), like the ones powering ChatGPT, aren't designed for high-precision answers. They may produce "hallucinations", offering responses that seem authoritative but are actually incorrect. It's crucial to **inform users that the responses are AI-generated**. In this workshop, we'll explore how to generate answers that link to their information sources — this is what we call *grounding* — enabling users to verify the accuracy of the AI's responses.
9 |
10 |
11 |
12 | In this workshop, we'll guide you through building a chat application that generates responses based on your documents and deploy it to Azure. We'll touch on many different topics, but we'll take it one step at a time.
13 |
14 | ### Application architecture
15 |
16 | Below is the architecture of the application we're going to build:
17 |
18 | 
19 |
20 | Our application consists of five main components:
21 |
22 | 1. **Vector Database**: The vector database stores mathematical representations of our documents, known as _embeddings_. These are used by the Chat API to find documents relevant to a user's question.
23 |
24 | 2. **Ingestion Service**: The ingestion service feeds data from your documents into this vector database.
25 |
26 | 3. **Chat API**: This API enables a client application to send chat messages and receive answers generated from the documents in the vector database.
27 |
28 | 4. **Chat Website**: This site offers a ChatGPT-like interface for users to ask questions and receive answers about the ingested documents.
29 |
30 | 5. **OpenAI Model Deployment**: We will use the `gpt-4o-mini` model, hosted on Azure, for this workshop. The code can also be adapted to work with OpenAI's APIs or Ollame with minimal changes.
31 |
32 | ### What is Retrievial-Augmented Generation?
33 |
34 | Retrieval-Augmented generation (RAG) is a powerful technique that combines the strengths of two different approaches in natural language processing: retrieval-based methods and generative models. This hybrid approach allows for the generation of responses that are both contextually relevant and rich in content. Let's break down how this works in the context of creating a custom ChatGPT-like model.
35 |
36 | At its core, RAG involves two main components:
37 |
38 | - **Retriever**: Think "_like a search engine_", finding relevant information from a database. The retriever usually searches in a vector database. It could also - for some use cases - search on application dabases, APIs and other sources of information. In this workshop, we will implement this logic in the _Chat API_.
39 |
40 | - **Generator**: Acts like a writer, taking the prompt and information retrieved to craft a response. In this workshop, OpenAI `gpt-4o-mini` will be our generator.
41 |
42 | 
43 |
44 | The RAG process involves the following steps:
45 |
46 | 1. **Embedding Computation**: Converts a user's prompt into an embedding for similarity comparisons.
47 |
48 | 2. **Document Retrieval**: Finds the most relevant documents using the prompt's embedding. This is where systems like Azure AI Search come into play, allowing for efficient vector similarity searches.
49 |
50 | 3. **Contextual Augmentation**: Enhances the user prompt with information from retrieved documents. This step is crucial as it provides additional context and information to the generator.
51 |
52 | 4. **Response Generation**: Use the model to generate a response using the augmented prompt. The model uses the additional context provided by the retrieved documents to produce a more informed and accurate output.
53 |
54 |
--------------------------------------------------------------------------------
/docs/sections/02-preparation.md:
--------------------------------------------------------------------------------
1 | ## Preparation
2 |
3 | Before diving into development, let's set up your project environment. This includes:
4 |
5 | - Creating a new project on GitHub based on a template
6 | - Using a prepared dev container environment on either [GitHub Codespaces](https://github.com/features/codespaces) or [VS Code with Dev Containers extension](https://aka.ms/vscode/ext/devcontainer) (or a manual install of the needed tools)
7 |
8 | ### Creating your project
9 |
10 | 1. Open [this GitHub repository](https://github.com/Azure-Samples/azure-openai-rag-workshop-java)
11 | 2. Click the **Fork** button and click on **Create fork** to create a copy of the project in your own GitHub account.
12 |
13 | 
14 |
15 | Once the fork is created, select the **Code** button, then the **Codespaces** tab and click on **Create Codespaces on main**.
16 |
17 | 
18 |
19 | This will initialize a development container with all necessary tools pre-installed. Once it's ready, you have everything you need to start coding. Wait a few minutes after the UI is loaded to ensure everything is ready, as some tasks will be triggered after everything is fully loaded, such as the installation of the npm packages with `npm install`.
20 |
21 |
22 |
23 | > GitHub Codespaces provides up to 60 hours of free usage monthly for all GitHub users. You can check out [GitHub's pricing details](https://github.com/features/codespaces) for more information.
24 |
25 |
26 |
27 | #### [optional] Local Development with the dev container
28 |
29 | If you prefer working on your local machine, you can also run the dev container on your machine. If you're fine with using Codespaces, you can skip directly to the next section.
30 |
31 |
32 | 1. Ensure you have [Docker](https://www.docker.com/products/docker-desktop), [VS Code](https://code.visualstudio.com/), and the [Dev Containers extension](https://aka.ms/vscode/ext/devcontainer) installed.
33 |
34 |
35 |
36 | > You can learn more about Dev Containers in [this video series](https://learn.microsoft.com/shows/beginners-series-to-dev-containers/). You can also [check the website](https://containers.dev) and [the specification](https://github.com/devcontainers/spec).
37 |
38 |
39 |
40 | 2. In GitHub website, select the **Code** button, then the **Local** tab and copy your repository url.
41 |
42 | 
43 | 3. Clone your forked repository and then open the folder in VS Code:
44 |
45 | ```bash
46 | git clone
47 | ```
48 |
49 | 3. In VS Code, use `Ctrl+Shift+P` (or `Command+Shift+P` on macOS) to open the **command palette** and type **Reopen in Container**.
50 |
51 | 
52 |
53 | *Alt text: Screenshot of VS Code showing the "Reopen in Container" command.*
54 |
55 | The first time it will take some time to download and setup the container image, meanwhile you can go ahead and read the next sections.
56 |
57 | Once the container is ready, you will see "Dev Container: OpenAI Workshop" in the bottom left corner of VSCode:
58 |
59 | 
60 |
61 |
62 | #### [optional] Working locally without the dev container
63 |
64 | If you want to work locally without using a dev container, you need to clone the project and install the following tools:
65 |
66 | | | |
67 | |---------------|--------------------------------|
68 | | Git | [Get Git](https://git-scm.com) |
69 | | Docker v20+ | [Get Docker](https://docs.docker.com/get-docker) |
70 | | Java v17+ | [Get Java](https://www.java.com/download/) |
71 | | Node.js v20+ | [Get Node.js](https://nodejs.org) |
72 | | GitHub CLI | [Get GitHub CLI](https://cli.github.com/manual/installation) |
73 | | Azure Developer CLI | [Get Azure Developer CLI](https://learn.microsoft.com/azure/developer/azure-developer-cli/install-azd) |
74 | | Bash v3+ | [Get bash](https://www.gnu.org/software/bash/) (Windows users can use **Git bash** that comes with Git) |
75 | | A code editor | [Get VS Code](https://aka.ms/get-vscode) |
76 |
77 | You can test your setup by opening a terminal and typing:
78 |
79 | ```sh
80 | git --version
81 | docker --version
82 | java --version
83 | node --version
84 | gh --version
85 | azd version
86 | bash --version
87 | ```
88 |
--------------------------------------------------------------------------------
/docs/sections/02.1-additional-setup.md:
--------------------------------------------------------------------------------
1 | ## Complete the setup
2 |
3 | To complete the template setup, please run the following command in a terminal, at the root of the project:
4 |
5 | ```bash
6 | ./scripts/setup-template.sh quarkus
7 | ```
8 |
9 | ### Preparing the environment
10 |
11 |
12 |
13 | We have deployed an Open AI proxy service for you, so you can use it to work on this workshop locally before deploying anything to Azure.
14 |
15 | Create a `.env` file at the root of the project, and add the following content:
16 |
17 | ```bash
18 | AZURE_OPENAI_URL=$$proxy$$
19 | QDRANT_URL=http://localhost:6334
20 | ```
21 |
22 |
23 |
24 |
25 |
26 | Now you either have to deploy an Azure Open AI service to use the OpenAI API, or you can use a local LLM with Ollama.
27 |
28 | #### Using Azure Open AI
29 |
30 | You first need to deploy an Azure Open AI service to use the OpenAI API.
31 |
32 | Before moving to the next section, go to the **Azure setup** section (either on the left or using the "hamburger" menu depending of your device) to deploy the necessary resources and create your `.env` file needed.
33 |
34 | After you completed the Azure setup, you can come back here to continue the workshop.
35 |
36 |
37 |
38 | #### (Optional) Using Ollama
39 |
40 | If you have a machine with enough resources, you can run this workshop entirely locally without using any cloud resources. To do that, you first have to install [Ollama](https://ollama.com) and then run the following commands to download the models on your machine:
41 |
42 | ```bash
43 | ollama pull mistral
44 | ```
45 |
46 |
47 |
48 | > The `mistral` model with download a few gigabytes of data, so it can take some time depending on your internet connection. Using Codespaces will provide you a fast connection.
49 |
50 |
51 |
52 |
53 |
54 | > Ollama won't work in GitHub Codespaces currently, so it will only work if you are working on the workshop locally.
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | Finally, you can start the Ollama server with the following command:
63 |
64 | ```bash
65 | ollama run mistral
66 | ```
67 |
--------------------------------------------------------------------------------
/docs/sections/03-overview.md:
--------------------------------------------------------------------------------
1 | ## Overview of the project
2 |
3 | The project template you've forked is a monorepo, which means it's a single repository that houses multiple projects. Here's how it's organized, focusing on the key files and directories:
4 |
5 | ```sh
6 | .devcontainer/ # Configuration for the development container
7 | data/ # Sample PDFs to serve as custom data
8 | infra/ # Templates and scripts for Docker and Azure infrastructure
9 | scripts/ # Utility scripts for document ingestion
10 | src/ # Source code for the application's services
11 | ├── backend/ # The Chat API developed with Quarkus
12 | ├── frontend/ # The Chat website
13 | ├── ingestion/ # The service for document ingestion developed with Quarkus
14 | pom.xml # Main Maven parent POM
15 | .env # File that you created for environment variables
16 | ```
17 |
18 | We're using Java and Quarkus for our APIs and Node.js for our website, and have set up a Maven parent POM to manage dependencies across all projects from a single place. Running `mvn install` at the root installs dependencies for all backend projects ( `npm install` for the frontend), simplifying monorepo management.
19 |
20 | Otherwise, you can use your regular `mvn` commands in any project folder and it will work as usual.
21 |
22 | ### About the services
23 |
24 | We generated the base code of our differents services with the respective CLI or generator of the frameworks we'll be using, and we've pre-written several service components so you can jump straight into the most interesting parts.
25 |
26 | ### The Chat API specification
27 |
28 | Creating a chat-like experience requires two main components: a user interface and a service API. The [ChatBootAI OpenAPI specification](https://editor.swagger.io/?url=https://raw.githubusercontent.com/ChatBootAI/chatbootai-openapi/main/openapi/openapi-chatbootai.yml) standardizes their interactions. This standardization allows for the development of different client applications (like mobile apps) that can interact seamlessly with chat services written in various programming languages.
29 |
30 | #### The Chat request
31 |
32 | A chat request is sent in JSON format, and must contain at least the user's message. Other optional parameters include a flag indicating if the response should be streamed, context-specific options that can tailor the chat service's behavior and a session state object that can be used to maintain state between requests.
33 |
34 | ```json
35 | {
36 | "messages": [
37 | {
38 | "content": "Can I do some Scuba diving?",
39 | "role": "user"
40 | }
41 | ],
42 | "stream": false,
43 | "context": { ... },
44 | "session_state": null
45 | }
46 | ```
47 |
48 | #### The chat response
49 |
50 | The chat service responds with a JSON object representing the generated response. The answer is located under the message's `content` property.
51 |
52 | ```json
53 | {
54 | "choices": [
55 | {
56 | "index": 0,
57 | "message": {
58 | "content": "There is no information available about Scuba diving in the provided sources.",
59 | "role": "assistant",
60 | "context": { ... }
61 | }
62 | }
63 | ],
64 | }
65 | ```
66 |
67 | You can learn more about the [ChatBootAI OpenAPI specification here](https://editor.swagger.io/?url=https://raw.githubusercontent.com/ChatBootAI/chatbootai-openapi/main/openapi/openapi-chatbootai.yml) and on [the GitHub repo](https://github.com/ChatBootAI/chatbootai-openapi).
68 |
69 |
70 |
71 | > If streaming is enabled, the response will be a stream of JSON objects, each representing a chunk of the response. This format allows for a dynamic and real-time messaging experience, as each chunk can be sent and rendered as soon as it's ready. In that case, the response format follows the [Newline Delimited JSON (NDJSON)](https://github.com/ndjson/ndjson-spec) specification, which is a convenient way of sending structured data that may be processed one record at a time.
72 |
73 |
74 |
--------------------------------------------------------------------------------
/docs/sections/04-vector-db.md:
--------------------------------------------------------------------------------
1 | ## The vector database
2 |
3 | We'll start by creating a vector database. Vectors are arrays of numbers that represent the features or characteristics of the data. For example, an image can be converted into a vector of pixels, or a word can be converted into a vector of semantic meaning. A vector database can perform fast and accurate searches based on the similarity or distance between the vectors, rather than exact matches. This enables applications such as image recognition, natural language processing, recommendation systems, and more.
4 |
5 | ### Ingestion and retrieval
6 |
7 | In our use-case, text will be extracted out of PDF files, and this text will be *tokenized*. Tokenization is the process of splitting our text into different tokens, which will be short portions of text. Those tokens will then be converted into a *vector* and added to the database. The vector database is then able to search for similar vectors based on the distance between them.
8 |
9 | That's how our system will be able to find the most relevant data, coming from the original PDF files.
10 |
11 | This will be used in the first component (the *Retriever*) of the Retrieval Augmented Generation (RAG) pattern that we will use to build our custom ChatGPT.
12 |
13 | ### About vector databases
14 |
15 | There are many available vector databases, and a good list can be found in the supported Vector stores list from the LangChain4j project: [https://github.com/langchain4j/langchain4j](https://github.com/langchain4j/langchain4j).
16 |
17 | Some of the popular ones are:
18 |
19 | - [MemoryVectorStore](https://js.langchain.com/docs/integrations/vectorstores/memory) which is an in-memory vector store, which is great for testing and development, but not for production.
20 | - [Qdrant](https://qdrant.tech/)
21 | - [pgvector](https://github.com/pgvector/pgvector)
22 | - [Redis](https://redis.io)
23 |
24 | On Azure, you can run the vector databases listed above, or use specific Azure services that also provide this functionality, such as:
25 |
26 | - [Azure AI Search](https://azure.microsoft.com/services/search/)
27 | - [Azure Cosmos DB for MongoDB vCore](https://learn.microsoft.com/azure/cosmos-db/mongodb/vcore/)
28 |
29 | ### Introducing Qdrant
30 |
31 | 
32 |
33 | [Qdrant](https://qdrant.tech/) is an open-source vector database that is easy to use and deploy. The core of Qdrant is a vector similarity search engine that provides a production-ready service with a convenient API to store, search, and manage vectors with an additional payload. You can think of the payloads as additional pieces of information that can help you hone in on your search and also receive useful information that you can give to your users.
34 |
35 | For this workshop, we'll use Qdrant as our vector database as it works well with JavaScript and can run locally in Docker. For the RAG use-case, most vector databases will work in a similar way.
36 |
37 | ### Running Qdrant locally
38 |
39 | To start Qdrant locally we have setup a Docker Compose file. You can use the following command from the root of the project:
40 |
41 | ```bash
42 | docker compose up qdrant
43 | ```
44 |
45 | This will pull the Docker image, start Qdrant on port `6333` and mount a volume to store the data in the `.qdrant` folder. You should see logs that look like:
46 |
47 | ```text
48 | qdrant-1 | INFO qdrant::actix: Qdrant HTTP listening on 6333
49 | qdrant-1 | INFO actix_server::builder: Starting 9 workers
50 | qdrant-1 | INFO qdrant::tonic: Qdrant gRPC listening on
51 | qdrant-1 | INFO actix_server::server: Actix runtime found; starting in Actix runtime
52 | ```
53 |
54 | You can test that Qdrant is running by opening the following URL in your browser: [http://localhost:6333/dashboard](http://localhost:6333/dashboard).
55 |
56 |
57 |
58 | > In Codespaces, once the servce is running, you click on the **Open in browser** button when prompted and add `/dashboard` at the end of the URL.
59 | > You can also select the **Ports** tab in the bottom panel, right click on the URL in the **Forwarded Address** column next to the `6333` port, and select **Open in browser**.
60 |
61 |
62 |
63 | Once you tested that Qdrant is running correctly, you can stop it by pressing `CTRL+C` in your terminal or executing the following command from the root directory of the project:
64 |
65 | ```bash
66 | docker compose down qdrant
67 | ```
68 |
--------------------------------------------------------------------------------
/docs/sections/08-website.md:
--------------------------------------------------------------------------------
1 | ## Chat website
2 |
3 | Now that we have our Chat API, it's time to build the website that will use.
4 | Notice that you don't have to develop the frontend part, it's already done for you. But you need to build it and, of course, if you want to understand how it works, you can follow the instructions below.
5 |
6 | ### Introducing Vite and Lit
7 |
8 | We use [Vite](https://vitejs.dev/) as a frontend build tool, and [Lit](https://lit.dev/) as a Web components library.
9 |
10 | This frontend is built as a Single Page Application (SPA), which is similar to the well-known ChatGPT website. The main difference is that it will get its data from the Chat API that we described in the previous section.
11 | To get the frontend, run this command in the terminal **at the root of the project** to get the completed code directly, so you don't have to code it yourself:
12 |
13 | ```bash
14 | curl -fsSL https://github.com/Azure-Samples/azure-openai-rag-workshop-java/releases/download/latest/frontend.tar.gz | tar -xvz
15 | ```
16 |
17 | As you can see, the project is available in the `src/frontend` folder. From the project directory, you can run this command to start the development server:
18 |
19 | ```bash
20 | cd src/frontend
21 | npm install
22 | npm run dev
23 | ```
24 |
25 | This will start the application in development mode. Open [http://localhost:8000](http://localhost:8000) to view it in the browser.
26 |
27 |
28 |
29 | > In Codespaces, once the servce is running, you can click on the **Open in browser** button when prompted.
30 | > You can also select the **Ports** tab in the bottom panel, right click on the URL in the **Forwarded Address** column next to the `8000` port, and select **Open in browser**.
31 |
32 |
33 |
34 |
35 |
36 | > In development mode, the Web page will automatically reload when you make any change to the code. We recommend you to keep this command running in the background, and then have two windows side-by-side: one with your IDE where you will edit the code, and one with your Web browser where you can see the final result.
37 |
38 |
39 |
40 | ### Testing the completed website
41 |
42 | Now that you've downloaded the code and built the frontend, let's test the entire application. For that, you need to make sure that your Qdrant database and chat backend are running, as well as the chat website:
43 |
44 | Run these commands from the project root if you need to restart the backend services:
45 |
46 | ```bash
47 | docker compose up qdrant
48 |
49 | cd src/backend
50 | ./mvnw quarkus:dev
51 |
52 | cd src/frontend
53 | npm run dev
54 | ```
55 |
56 | Now go back to your browser at http://localhost:8000, and send a question to the chatbot. You should see the answer appear in the chat window.
57 |
58 | 
59 |
--------------------------------------------------------------------------------
/docs/sections/10-deployment.md:
--------------------------------------------------------------------------------
1 | ## Deploying to Azure
2 |
3 | Our application is now ready to be deployed to Azure! But first of all, make sure you don't deploy the application with the llama3 model. For that, make sure to remove or comment the alternative from the `src/backend/src/main/resources/application.properties` file.
4 |
5 | ```properties
6 | quarkus.http.port=3000
7 | quarkus.log.level=INFO
8 | quarkus.log.category."ai.azure.openai.rag.workshop.backend".level=DEBUG
9 | #quarkus.arc.selected-alternatives=ai.azure.openai.rag.workshop.backend.configuration.ChatLanguageModelOllamaProducer
10 | ```
11 |
12 | We'll use [Azure Static Web Apps](https://learn.microsoft.com/azure/static-web-apps/overview) to deploy the frontend, and [Azure Container Apps](https://learn.microsoft.com/azure/container-apps/overview) to deploy the backend and ingestion services.
13 |
14 | Run this command from the root of the project to build and deploy the application (this command deploys all services listed in the `azure.yaml` file located in the project root):
15 |
16 | ```bash
17 | azd deploy
18 | ```
19 |
20 | Once everything is deployed, run the ingestion process against your deployed ingestion service, using `./scripts/ingest-data.sh` script on Linux or macOS, or `./scripts/ingest-data.ps1` on Windows:
21 |
22 | ```bash
23 | ./scripts/ingest-data.sh
24 | ```
25 |
26 | This process should take a few minutes. Once it's done, you should see the URL of the deployed frontend application in the output of the command.
27 |
28 | 
29 |
30 | You can now open this URL in a browser and test the deployed application.
31 |
32 | 
33 |
34 |
35 |
36 | > You can also build and deploy the services separately by running `azd deploy `. This allows you to deploy independently the backend, frontend and ingestion services if needed.
37 | >
38 | > Even better! If you're starting from scratch and have a completed code, you can use the `azd up` command. This command combines both `azd provision` and `azd deploy` to provision the Azure resources and deploy the application in one command.
39 |
40 |
2 |
3 | > This step is entirely optional, you can skip it if you want to jump directly to the next section.
4 |
5 |
6 |
7 | ## Configuring a CI/CD pipeline
8 |
9 | We now have a working deployed application, but deploying it manually every time we make a change is not very convenient. We'll automate this process by creating a CI/CD pipeline, using [GitHub Actions](https://github.com/features/actions).
10 |
11 | ### What's CI/CD?
12 |
13 | CI/CD stands for *Continuous Integration and Continuous Deployment*.
14 |
15 | Continuous Integration is a software development practice that requires developers to integrate their code into a shared repository several times a day. Each integration can then be verified by an automated build and automated tests. By doing so, you can detect errors quickly, and locate them more easily.
16 |
17 | Continuous Deployment pushes this practice further, by preparing for a release to production after each successful build. By doing so, you can get working software into the hands of users faster.
18 |
19 | ### What's GitHub Actions?
20 |
21 | GitHub Actions is a service that lets you automate your software development workflows. A workflow is a series of steps executed one after the other. You can use workflows to build, test and deploy your code, but you can also use them to automate other tasks, like sending a notification when an issue is created.
22 |
23 | It's a great way to automate your CI/CD pipelines, and it's free for public repositories.
24 |
25 | ### Adding the deployment workflow
26 |
27 | First we need to create the GitHub Actions workflow. Create the file `.github/workflows/deploy.yml` in your repository, with this content:
28 |
29 | ```bash
30 | name: Deploy to Azure
31 | on:
32 | push:
33 | # Run only when commits are pushed to main branch
34 | branches:
35 | - main
36 |
37 | # Set up permissions for deploying with secretless Azure federated credentials
38 | # https://learn.microsoft.com/en-us/azure/developer/github/connect-from-azure?tabs=azure-portal%2Clinux#set-up-azure-login-with-openid-connect-authentication
39 | permissions:
40 | id-token: write
41 | contents: read
42 |
43 | jobs:
44 | deploy:
45 | runs-on: ubuntu-latest
46 | env:
47 | AZURE_CLIENT_ID: ${{ vars.AZURE_CLIENT_ID }}
48 | AZURE_TENANT_ID: ${{ vars.AZURE_TENANT_ID }}
49 | AZURE_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }}
50 | AZURE_OPENAI_URL: ${{ vars.AZURE_OPENAI_URL }}
51 | steps:
52 | - name: Checkout
53 | uses: actions/checkout@v3
54 |
55 | - name: Install azd
56 | uses: Azure/setup-azd@v0.1.0
57 |
58 | - name: Log in with Azure (Federated Credentials)
59 | if: ${{ env.AZURE_CLIENT_ID != '' }}
60 | run: |
61 | azd auth login `
62 | --client-id "$Env:AZURE_CLIENT_ID" `
63 | --federated-credential-provider "github" `
64 | --tenant-id "$Env:AZURE_TENANT_ID"
65 | shell: pwsh
66 |
67 | - name: Build and deploy application
68 | run: |
69 | azd up --no-prompt
70 | env:
71 | AZURE_ENV_NAME: ${{ vars.AZURE_ENV_NAME }}
72 | AZURE_LOCATION: ${{ vars.AZURE_LOCATION }}
73 | AZURE_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }}
74 | ```
75 |
76 | This workflow will run when you push a commit change to the `main` branch of your repository. What it does is log in to Azure using the `azd auth login` command, and then run the `azd up` command to provision the infrastructure, build and deploy the application.
77 |
78 | Next we need to configure your GitHub repository environment variables. These will be used by the workflow to authenticate to Azure. To do so, run this command:
79 |
80 | ```bash
81 | azd pipeline config
82 | ```
83 |
84 | You'll first be asked to log in to GitHub and then it will do the setup for you. These are the steps it will perform:
85 | - Creation of an [Azure Service Principal](https://learn.microsoft.com/entra/identity-platform/app-objects-and-service-principals?tabs=browser) to authenticate to Azure.
86 | - Set up OpenID Connect authentication between GitHub and Azure using [federated credentials](https://docs.github.com/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure).
87 | - Addition of [GitHub variables](https://docs.github.com/actions/learn-github-actions/variables) to your repository to store the IDs needed to authenticate to Azure.
88 |
89 |
90 |
91 | Since you're using the provided Open AI proxy service we have deployed, there's one extra variable that you need to set.
92 |
93 | First we need to log in to GitHub using the [GitHub CLI](https://cli.github.com/):
94 |
95 | ```bash
96 | # We need more permissions than provided by default with Codespaces
97 | unset GITHUB_TOKEN
98 | gh auth login -w
99 | ```
100 |
101 | Once you're logged in, run this command to set the value of the `AZURE_OPENAI_URL` variable in your repository:
102 |
103 | ```bash
104 | gh variable set AZURE_OPENAI_URL \
105 | --body "$$proxy$$" \
106 | --repo /
107 | ```
108 |
109 |
110 |
111 | ### Testing the deployment workflow
112 |
113 | Now that we have our workflow fully configured, we can test it by pushing a change to our repository. Commit your changes and push them to GitHub:
114 |
115 | ```bash
116 | git add .
117 | git commit -m "Setup CI/CD"
118 | git push
119 | ```
120 |
121 | The workflow will run automatically, so we can look at its progress directly on GitHub. Open your repository in a browser, and select the **Actions** tab. You should see the workflow running. It will take a few minutes to complete, but you can follow the progress in the logs by clicking on the running workflow.
122 |
123 | 
124 |
125 | Then select the job named **deploy** on the left, and you should see the logs of the workflow.
126 |
127 | 
128 |
129 | When the workflow is complete, you should see a green checkmark.
130 |
--------------------------------------------------------------------------------
/docs/sections/12-conclusion.md:
--------------------------------------------------------------------------------
1 | ## Conclusion
2 |
3 | This is the end of the workshop. We hope you enjoyed it, learned something new and more importantly, that you'll be able to take this knowledge back to your projects.
4 |
5 | If you missed any of the steps or would like to check your final code, you can run this command in the terminal at the root of the project to get the completed solution (be sure to commit your code first!):
6 |
7 | ```bash
8 | curl -fsSL https://github.com/Azure-Samples/azure-openai-rag-workshop-java/releases/download/latest/solution-java-quarkus.tar.gz | tar -xvz
9 | ```
10 |
11 |
12 |
13 | > If you experienced any issues during the workshop, please let us know by [creating an issue](https://github.com/Azure-Samples/azure-openai-rag-workshop-java/issues) on the GitHub repository.
14 |
15 |
20 |
21 | > Don't forget to delete the Azure resources once you are done running the workshop, to avoid incurring unnecessary costs!
22 |
23 |
24 |
25 | To delete the Azure resources, you can run this command:
26 |
27 | ```bash
28 | azd down --purge
29 | ```
30 |
31 | ### Going further
32 |
33 | This workshop is based on the enterprise-ready sample **ChatGPT + Enterprise data with Azure OpenAI and AI Search**:
34 |
35 | - [JavaScript version](https://github.com/Azure-Samples/azure-search-openai-javascript)
36 | - [Python version](https://github.com/Azure-Samples/azure-search-openai-demo/)
37 | - [Java version](https://github.com/Azure-Samples/azure-search-openai-demo-java)
38 | - [C# version](https://github.com/Azure-Samples/azure-search-openai-demo-csharp)
39 | - [Serverless JavaScript version](https://github.com/Azure-Samples/serverless-chat-langchainjs)
40 |
41 | If you want to go further with more advanced use-cases, authentication, history and more, you should check it out!
42 |
43 | ### References
44 |
45 | - This workshop URL: [aka.ms/ws/openai-rag-quarkus](https://aka.ms/ws/openai-rag-quarkus)
46 | - The source repository for this workshop: [GitHub link](https://github.com/Azure-Samples/azure-openai-rag-workshop-java/)
47 | - If something does not work: [Report an issue](https://github.com/Azure-Samples/azure-openai-rag-workshop-java/issues)
48 | - Introduction presentation for this workshop: [Slides](https://azure-samples.github.io/azure-openai-rag-workshop-java/java-quarkus/)
49 | - Outperforming vector search performance with hybrid retrieval and semantic ranking: [Blog post](https://techcommunity.microsoft.com/t5/ai-azure-ai-services-blog/azure-ai-search-outperforming-vector-search-with-hybrid/ba-p/3929167)
50 |
--------------------------------------------------------------------------------
/docs/sections/_old/09-azure-llm.md:
--------------------------------------------------------------------------------
1 | ## Deploy an LLM in Azure OpenAI
2 |
3 | [Azure](https://azure.microsoft.com) is Microsoft's comprehensive cloud platform, offering a vast array of services to build, deploy, and manage applications across a global network of Microsoft-managed data centers. In this workshop, we'll leverage several Azure services to run our chat application.
4 |
5 | ### Getting Started with Azure
6 |
7 |
8 |
9 | To complete this workshop, you'll need an Azure account. If you don't already have one, you can sign up for a free account, which includes Azure credits, on the [Azure website](https://azure.microsoft.com/free/).
10 |
11 |
12 |
13 | > If you already have an Azure account from your company, **DO NOT** use it for this workshop as it may have restrictions that will prevent you from completing the workshop.
14 | > You'll need to create a new account to redeem the Azure Pass.
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | To complete this workshop, you'll need an Azure account. As you're attending this workshop in-person, you can create one and obtain a free Azure Pass credit by using this link: [redeem your Azure Pass](https://azcheck.in/$$azpass$$).
23 |
24 | > If you're **not** attending this workshop in-person, you can sign up for a free account, which includes Azure credits, on the [Azure website](https://azure.microsoft.com/free/).
25 |
26 |
27 |
28 | #### Log in to Azure
29 |
30 | Begin by logging into your Azure subscription with the following command:
31 |
32 | ```sh
33 | azd auth login --use-device-code
34 | ```
35 |
36 | This command will provide you a *device code* to enter in a browser window. Follow the prompts until you're notified of a successful login.
37 |
38 |
39 | #### Setting up environment variables
40 |
41 | ```shell
42 | PROJECT="rag-workshop"
43 | RESOURCE_GROUP="rg-$PROJECT"
44 | LOCATION="swedencentral"
45 | TAG="$PROJECT"
46 | AI_SERVICE="ai-$PROJECT"
47 | AI_MODEL="gpt-4o-mini"
48 | ```
49 |
50 | #### Creating the resource group
51 |
52 | ```shell
53 | az group create \
54 | --name "$RESOURCE_GROUP" \
55 | --location "$LOCATION" \
56 | --tags system="$TAG"
57 | ```
58 |
59 | #### Creating the Cognitive Service
60 |
61 | ```shell
62 | az cognitiveservices account create \
63 | --name "$AI_SERVICE" \
64 | --resource-group "$RESOURCE_GROUP" \
65 | --location "$LOCATION" \
66 | --custom-domain "$AI_SERVICE" \
67 | --tags system="$TAG" \
68 | --kind "OpenAI" \
69 | --sku "S0"
70 | ````
71 |
72 | #### Deploying a gpt-4o-mini model
73 |
74 | ```shell
75 | az cognitiveservices account deployment create \
76 | --name "$AI_SERVICE" \
77 | --resource-group "$RESOURCE_GROUP" \
78 | --deployment-name "$AI_MODEL" \
79 | --model-name "$AI_MODEL" \
80 | --model-version "1106" \
81 | --model-format "OpenAI" \
82 | --sku-capacity 120 \
83 | --sku-name "Standard"
84 | ```
85 |
86 | #### Storing the key and endpoint in environment variables..."
87 |
88 | ```shell
89 | AZURE_OPENAI_KEY=$(
90 | az cognitiveservices account keys list \
91 | --name "$AI_SERVICE" \
92 | --resource-group "$RESOURCE_GROUP" \
93 | | jq -r .key1
94 | )
95 | AZURE_OPENAI_URL=$(
96 | az cognitiveservices account show \
97 | --name "$AI_SERVICE" \
98 | --resource-group "$RESOURCE_GROUP" \
99 | | jq -r .properties.endpoint
100 | )
101 |
102 | echo "AZURE_OPENAI_KEY=$AZURE_OPENAI_KEY"
103 | echo "AZURE_OPENAI_URL=$AZURE_OPENAI_URL"
104 | echo "AZURE_OPENAI_DEPLOYMENT_NAME=$AI_MODEL"
105 | ```
106 |
--------------------------------------------------------------------------------
/docs/slides/README.md:
--------------------------------------------------------------------------------
1 | # Slides
2 |
3 | To show the slides, you need to have Node.js installed with this tool:
4 |
5 | ```bash
6 | npm i -g backslide
7 | ```
8 |
9 | Then, you can run the following command to show the slides:
10 |
11 | ```bash
12 | cd docs/slides
13 | bs serve
14 | ```
15 |
16 | The slides will be available at http://localhost:4100.
17 |
--------------------------------------------------------------------------------
/docs/slides/images/agent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/slides/images/agent.png
--------------------------------------------------------------------------------
/docs/slides/images/ai.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/slides/images/ai.jpg
--------------------------------------------------------------------------------
/docs/slides/images/antonio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/slides/images/antonio.png
--------------------------------------------------------------------------------
/docs/slides/images/book-langchain4j.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/slides/images/book-langchain4j.png
--------------------------------------------------------------------------------
/docs/slides/images/chatgpt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/slides/images/chatgpt.png
--------------------------------------------------------------------------------
/docs/slides/images/chris-dive.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/slides/images/chris-dive.jpg
--------------------------------------------------------------------------------
/docs/slides/images/chris.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/slides/images/chris.jpg
--------------------------------------------------------------------------------
/docs/slides/images/embedding.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/slides/images/embedding.png
--------------------------------------------------------------------------------
/docs/slides/images/julien.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/slides/images/julien.jpg
--------------------------------------------------------------------------------
/docs/slides/images/llm-magazine.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/slides/images/llm-magazine.jpg
--------------------------------------------------------------------------------
/docs/slides/images/llm-training.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/slides/images/llm-training.png
--------------------------------------------------------------------------------
/docs/slides/images/microsoft-azure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/slides/images/microsoft-azure.png
--------------------------------------------------------------------------------
/docs/slides/images/ms-full-logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/slides/images/olivier.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/slides/images/olivier.jpg
--------------------------------------------------------------------------------
/docs/slides/images/openai.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/slides/images/openai.png
--------------------------------------------------------------------------------
/docs/slides/images/rag.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/slides/images/rag.png
--------------------------------------------------------------------------------
/docs/slides/images/sandra.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/slides/images/sandra.jpg
--------------------------------------------------------------------------------
/docs/slides/images/tokens.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/slides/images/tokens.png
--------------------------------------------------------------------------------
/docs/slides/images/tokens2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/slides/images/tokens2.png
--------------------------------------------------------------------------------
/docs/slides/images/yohan.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/slides/images/yohan.jpg
--------------------------------------------------------------------------------
/docs/slides/template/Archivo-Variable.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/slides/template/Archivo-Variable.ttf
--------------------------------------------------------------------------------
/docs/slides/template/Inconsolata-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/slides/template/Inconsolata-Regular.ttf
--------------------------------------------------------------------------------
/docs/slides/template/Saira-Variable.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/slides/template/Saira-Variable.ttf
--------------------------------------------------------------------------------
/docs/slides/template/code.js:
--------------------------------------------------------------------------------
1 | window.setup = function() {
2 | window.annotations = [];
3 | window.oldannotations = [];
4 | window.slideshow.on('beforeShowSlide', function (slide) {
5 | window.oldannotations = window.annotations;
6 | window.annotations = [];
7 | });
8 | window.slideshow.on('afterShowSlide', function (slide) {
9 | const element = document.querySelector('.remark-visible .remark-slide-content');
10 | element.childNodes
11 | .filter(node => node.nodeName === "SCRIPT")
12 | .forEach(node => {
13 | eval(node.innerHTML);
14 | });
15 | setTimeout(function() {
16 | window.oldannotations.forEach((annotation) => {
17 | annotation.remove();
18 | });
19 | window.oldannotations = [];
20 | }, 0);
21 | });
22 | }
23 |
24 | function rough(selector, options, delay = 500) {
25 | setTimeout(function() {
26 | document
27 | .querySelectorAll(`.remark-visible ${selector}`)
28 | .forEach((e) => {
29 | const annotation = RoughNotation.annotate(e, options);
30 |
31 | // Keep track of annotation to remove it on slide change
32 | window.annotations.push(annotation);
33 |
34 | // Fix scale
35 | const scaler = document.querySelector('.remark-slide-scaler');
36 | const scale = Number(/scale\((\d+\.?\d*)\)/.exec(scaler.style.transform)[1]);
37 | annotation._svg.style.transform = `scale(${1/scale})`;
38 |
39 | annotation.show();
40 | });
41 | }, delay);
42 | }
43 |
--------------------------------------------------------------------------------
/docs/slides/template/fa-brands-400.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/slides/template/fa-brands-400.woff2
--------------------------------------------------------------------------------
/docs/slides/template/fa-regular-400.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/slides/template/fa-regular-400.woff2
--------------------------------------------------------------------------------
/docs/slides/template/fa-solid-900.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/docs/slides/template/fa-solid-900.woff2
--------------------------------------------------------------------------------
/docs/slides/template/fonts.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Saira';
3 | font-style: normal;
4 | font-weight: 500;
5 | src: url(Saira-Variable.ttf) format('truetype');
6 | }
7 | @font-face {
8 | font-family: 'Archivo';
9 | font-style: normal;
10 | font-weight: 300 500;
11 | src: url(Archivo-Variable.ttf) format('truetype');
12 | }
13 | @font-face {
14 | font-family: 'Inconsolata';
15 | font-style: normal;
16 | font-weight: 400;
17 | src: url(Inconsolata-Regular.ttf) format('truetype');
18 | }
--------------------------------------------------------------------------------
/docs/slides/template/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{title}}
6 | {{{style}}}
7 |
8 |
9 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/infra/abbreviations.json:
--------------------------------------------------------------------------------
1 | {
2 | "analysisServicesServers": "as",
3 | "apiManagementService": "apim-",
4 | "appConfigurationStores": "appcs-",
5 | "appManagedEnvironments": "cae-",
6 | "appContainerApps": "ca-",
7 | "authorizationPolicyDefinitions": "policy-",
8 | "automationAutomationAccounts": "aa-",
9 | "blueprintBlueprints": "bp-",
10 | "blueprintBlueprintsArtifacts": "bpa-",
11 | "cacheRedis": "redis-",
12 | "cdnProfiles": "cdnp-",
13 | "cdnProfilesEndpoints": "cdne-",
14 | "cognitiveServicesAccounts": "cog-",
15 | "cognitiveServicesFormRecognizer": "cog-fr-",
16 | "cognitiveServicesTextAnalytics": "cog-ta-",
17 | "cognitiveServicesSpeech": "cog-sp-",
18 | "computeAvailabilitySets": "avail-",
19 | "computeCloudServices": "cld-",
20 | "computeDiskEncryptionSets": "des",
21 | "computeDisks": "disk",
22 | "computeDisksOs": "osdisk",
23 | "computeGalleries": "gal",
24 | "computeSnapshots": "snap-",
25 | "computeVirtualMachines": "vm",
26 | "computeVirtualMachineScaleSets": "vmss-",
27 | "containerInstanceContainerGroups": "ci",
28 | "containerRegistryRegistries": "cr",
29 | "containerServiceManagedClusters": "aks-",
30 | "databricksWorkspaces": "dbw-",
31 | "dataFactoryFactories": "adf-",
32 | "dataLakeAnalyticsAccounts": "dla",
33 | "dataLakeStoreAccounts": "dls",
34 | "dataMigrationServices": "dms-",
35 | "dBforMySQLServers": "mysql-",
36 | "dBforPostgreSQLServers": "psql-",
37 | "devicesIotHubs": "iot-",
38 | "devicesProvisioningServices": "provs-",
39 | "devicesProvisioningServicesCertificates": "pcert-",
40 | "documentDBDatabaseAccounts": "cosmos-",
41 | "eventGridDomains": "evgd-",
42 | "eventGridDomainsTopics": "evgt-",
43 | "eventGridEventSubscriptions": "evgs-",
44 | "eventHubNamespaces": "evhns-",
45 | "eventHubNamespacesEventHubs": "evh-",
46 | "hdInsightClustersHadoop": "hadoop-",
47 | "hdInsightClustersHbase": "hbase-",
48 | "hdInsightClustersKafka": "kafka-",
49 | "hdInsightClustersMl": "mls-",
50 | "hdInsightClustersSpark": "spark-",
51 | "hdInsightClustersStorm": "storm-",
52 | "hybridComputeMachines": "arcs-",
53 | "insightsActionGroups": "ag-",
54 | "insightsComponents": "appi-",
55 | "keyVaultVaults": "kv-",
56 | "kubernetesConnectedClusters": "arck",
57 | "kustoClusters": "dec",
58 | "kustoClustersDatabases": "dedb",
59 | "loadTesting": "lt-",
60 | "logicIntegrationAccounts": "ia-",
61 | "logicWorkflows": "logic-",
62 | "machineLearningServicesWorkspaces": "mlw-",
63 | "managedIdentityUserAssignedIdentities": "id-",
64 | "managementManagementGroups": "mg-",
65 | "migrateAssessmentProjects": "migr-",
66 | "networkApplicationGateways": "agw-",
67 | "networkApplicationSecurityGroups": "asg-",
68 | "networkAzureFirewalls": "afw-",
69 | "networkBastionHosts": "bas-",
70 | "networkConnections": "con-",
71 | "networkDnsZones": "dnsz-",
72 | "networkExpressRouteCircuits": "erc-",
73 | "networkFirewallPolicies": "afwp-",
74 | "networkFirewallPoliciesWebApplication": "waf",
75 | "networkFirewallPoliciesRuleGroups": "wafrg",
76 | "networkFrontDoors": "fd-",
77 | "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-",
78 | "networkLoadBalancersExternal": "lbe-",
79 | "networkLoadBalancersInternal": "lbi-",
80 | "networkLoadBalancersInboundNatRules": "rule-",
81 | "networkLocalNetworkGateways": "lgw-",
82 | "networkNatGateways": "ng-",
83 | "networkNetworkInterfaces": "nic-",
84 | "networkNetworkSecurityGroups": "nsg-",
85 | "networkNetworkSecurityGroupsSecurityRules": "nsgsr-",
86 | "networkNetworkWatchers": "nw-",
87 | "networkPrivateDnsZones": "pdnsz-",
88 | "networkPrivateLinkServices": "pl-",
89 | "networkPublicIPAddresses": "pip-",
90 | "networkPublicIPPrefixes": "ippre-",
91 | "networkRouteFilters": "rf-",
92 | "networkRouteTables": "rt-",
93 | "networkRouteTablesRoutes": "udr-",
94 | "networkTrafficManagerProfiles": "traf-",
95 | "networkVirtualNetworkGateways": "vgw-",
96 | "networkVirtualNetworks": "vnet-",
97 | "networkVirtualNetworksSubnets": "snet-",
98 | "networkVirtualNetworksVirtualNetworkPeerings": "peer-",
99 | "networkVirtualWans": "vwan-",
100 | "networkVpnGateways": "vpng-",
101 | "networkVpnGatewaysVpnConnections": "vcn-",
102 | "networkVpnGatewaysVpnSites": "vst-",
103 | "notificationHubsNamespaces": "ntfns-",
104 | "notificationHubsNamespacesNotificationHubs": "ntf-",
105 | "operationalInsightsWorkspaces": "log-",
106 | "portalDashboards": "dash-",
107 | "powerBIDedicatedCapacities": "pbi-",
108 | "purviewAccounts": "pview-",
109 | "recoveryServicesVaults": "rsv-",
110 | "resourcesResourceGroups": "rg-",
111 | "searchSearchServices": "srch-",
112 | "serviceBusNamespaces": "sb-",
113 | "serviceBusNamespacesQueues": "sbq-",
114 | "serviceBusNamespacesTopics": "sbt-",
115 | "serviceEndPointPolicies": "se-",
116 | "serviceFabricClusters": "sf-",
117 | "signalRServiceSignalR": "sigr",
118 | "sqlManagedInstances": "sqlmi-",
119 | "sqlServers": "sql-",
120 | "sqlServersDataWarehouse": "sqldw-",
121 | "sqlServersDatabases": "sqldb-",
122 | "sqlServersDatabasesStretch": "sqlstrdb-",
123 | "storageStorageAccounts": "st",
124 | "storageStorageAccountsVm": "stvm",
125 | "storSimpleManagers": "ssimp",
126 | "streamAnalyticsCluster": "asa-",
127 | "synapseWorkspaces": "syn",
128 | "synapseWorkspacesAnalyticsWorkspaces": "synw",
129 | "synapseWorkspacesSqlPoolsDedicated": "syndp",
130 | "synapseWorkspacesSqlPoolsSpark": "synsp",
131 | "timeSeriesInsightsEnvironments": "tsi-",
132 | "webServerFarms": "plan-",
133 | "webSitesAppService": "app-",
134 | "webSitesAppServiceEnvironment": "ase-",
135 | "webSitesFunctions": "func-",
136 | "webStaticSites": "stapp-"
137 | }
138 |
--------------------------------------------------------------------------------
/infra/azure/deploy-azure-openai-models.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Execute this script to deploy the needed Azure OpenAI models to execute the code.
4 | # For this, you need Azure CLI installed: https://learn.microsoft.com/cli/azure/install-azure-cli
5 |
6 | echo "Setting up environment variables..."
7 | echo "----------------------------------"
8 | PROJECT="rag-workshop"
9 | RESOURCE_GROUP="rg-$PROJECT"
10 | LOCATION="swedencentral"
11 | TAG="$PROJECT"
12 | AI_SERVICE="ai-$PROJECT"
13 | AI_MODEL="gpt-4o-mini"
14 |
15 | echo "Creating the resource group..."
16 | echo "------------------------------"
17 | az group create \
18 | --name "$RESOURCE_GROUP" \
19 | --location "$LOCATION" \
20 | --tags system="$TAG"
21 |
22 | echo "Creating the Cognitive Service..."
23 | echo "---------------------------------"
24 | az cognitiveservices account create \
25 | --name "$AI_SERVICE" \
26 | --resource-group "$RESOURCE_GROUP" \
27 | --location "$LOCATION" \
28 | --custom-domain "$AI_SERVICE" \
29 | --tags system="$TAG" \
30 | --kind "OpenAI" \
31 | --sku "S0"
32 |
33 | # If you want to know the available models, run the following Azure CLI command:
34 | # az cognitiveservices account list-models --resource-group "$RESOURCE_GROUP" --name "$AI_SERVICE" -o table
35 |
36 | echo "Deploying a gpt-4o-mini model..."
37 | echo "----------------------"
38 | az cognitiveservices account deployment create \
39 | --name "$AI_SERVICE" \
40 | --resource-group "$RESOURCE_GROUP" \
41 | --deployment-name "$AI_MODEL" \
42 | --model-name "$AI_MODEL" \
43 | --model-version "1106" \
44 | --model-format "OpenAI" \
45 | --sku-capacity 120 \
46 | --sku-name "Standard"
47 |
48 | echo "Storing the key and endpoint in environment variables..."
49 | echo "--------------------------------------------------------"
50 | AZURE_OPENAI_KEY=$(
51 | az cognitiveservices account keys list \
52 | --name "$AI_SERVICE" \
53 | --resource-group "$RESOURCE_GROUP" \
54 | | jq -r .key1
55 | )
56 | AZURE_OPENAI_URL=$(
57 | az cognitiveservices account show \
58 | --name "$AI_SERVICE" \
59 | --resource-group "$RESOURCE_GROUP" \
60 | | jq -r .properties.endpoint
61 | )
62 |
63 | echo "AZURE_OPENAI_KEY=$AZURE_OPENAI_KEY"
64 | echo "AZURE_OPENAI_URL=$AZURE_OPENAI_URL"
65 | echo "AZURE_OPENAI_DEPLOYMENT_NAME=$AI_MODEL"
66 |
--------------------------------------------------------------------------------
/infra/core/ai/cognitiveservices.bicep:
--------------------------------------------------------------------------------
1 | metadata description = 'Creates an Azure Cognitive Services instance.'
2 | param name string
3 | param location string = resourceGroup().location
4 | param tags object = {}
5 | @description('The custom subdomain name used to access the API. Defaults to the value of the name parameter.')
6 | param customSubDomainName string = name
7 | param disableLocalAuth bool = false
8 | param deployments array = []
9 | param kind string = 'OpenAI'
10 |
11 | @allowed([ 'Enabled', 'Disabled' ])
12 | param publicNetworkAccess string = 'Enabled'
13 | param sku object = {
14 | name: 'S0'
15 | }
16 |
17 | param allowedIpRules array = []
18 | param networkAcls object = empty(allowedIpRules) ? {
19 | defaultAction: 'Allow'
20 | } : {
21 | ipRules: allowedIpRules
22 | defaultAction: 'Deny'
23 | }
24 |
25 | resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = {
26 | name: name
27 | location: location
28 | tags: tags
29 | kind: kind
30 | properties: {
31 | customSubDomainName: customSubDomainName
32 | publicNetworkAccess: publicNetworkAccess
33 | networkAcls: networkAcls
34 | disableLocalAuth: disableLocalAuth
35 | }
36 | sku: sku
37 | }
38 |
39 | @batchSize(1)
40 | resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = [for deployment in deployments: {
41 | parent: account
42 | name: deployment.name
43 | properties: {
44 | model: deployment.model
45 | raiPolicyName: contains(deployment, 'raiPolicyName') ? deployment.raiPolicyName : null
46 | }
47 | sku: contains(deployment, 'sku') ? deployment.sku : {
48 | name: 'Standard'
49 | capacity: 20
50 | }
51 | }]
52 |
53 | output endpoint string = account.properties.endpoint
54 | output endpoints object = account.properties.endpoints
55 | output id string = account.id
56 | output name string = account.name
57 |
--------------------------------------------------------------------------------
/infra/core/host/container-apps-environment.bicep:
--------------------------------------------------------------------------------
1 | metadata description = 'Creates an Azure Container Apps environment.'
2 | param name string
3 | param location string = resourceGroup().location
4 | param tags object = {}
5 |
6 | @description('Name of the Application Insights resource')
7 | param applicationInsightsName string = ''
8 |
9 | @description('Specifies if Dapr is enabled')
10 | param daprEnabled bool = false
11 |
12 | @description('Name of the Log Analytics workspace')
13 | param logAnalyticsWorkspaceName string
14 |
15 | resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = {
16 | name: name
17 | location: location
18 | tags: tags
19 | properties: {
20 | appLogsConfiguration: {
21 | destination: 'log-analytics'
22 | logAnalyticsConfiguration: {
23 | customerId: logAnalyticsWorkspace.properties.customerId
24 | sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey
25 | }
26 | }
27 | daprAIInstrumentationKey: daprEnabled && !empty(applicationInsightsName) ? applicationInsights.properties.InstrumentationKey : ''
28 | }
29 | }
30 |
31 | resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = {
32 | name: logAnalyticsWorkspaceName
33 | }
34 |
35 | resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (daprEnabled && !empty(applicationInsightsName)) {
36 | name: applicationInsightsName
37 | }
38 |
39 | output defaultDomain string = containerAppsEnvironment.properties.defaultDomain
40 | output id string = containerAppsEnvironment.id
41 | output name string = containerAppsEnvironment.name
42 |
--------------------------------------------------------------------------------
/infra/core/host/container-apps.bicep:
--------------------------------------------------------------------------------
1 | metadata description = 'Creates an Azure Container Registry and an Azure Container Apps environment.'
2 | param name string
3 | param location string = resourceGroup().location
4 | param tags object = {}
5 |
6 | param containerAppsEnvironmentName string
7 | param containerRegistryName string
8 | param containerRegistryResourceGroupName string = ''
9 | param containerRegistryAdminUserEnabled bool = false
10 | param logAnalyticsWorkspaceName string
11 | param applicationInsightsName string = ''
12 | param daprEnabled bool = false
13 |
14 | module containerAppsEnvironment 'container-apps-environment.bicep' = {
15 | name: '${name}-container-apps-environment'
16 | params: {
17 | name: containerAppsEnvironmentName
18 | location: location
19 | tags: tags
20 | logAnalyticsWorkspaceName: logAnalyticsWorkspaceName
21 | applicationInsightsName: applicationInsightsName
22 | daprEnabled: daprEnabled
23 | }
24 | }
25 |
26 | module containerRegistry 'container-registry.bicep' = {
27 | name: '${name}-container-registry'
28 | scope: !empty(containerRegistryResourceGroupName) ? resourceGroup(containerRegistryResourceGroupName) : resourceGroup()
29 | params: {
30 | name: containerRegistryName
31 | location: location
32 | adminUserEnabled: containerRegistryAdminUserEnabled
33 | tags: tags
34 | }
35 | }
36 |
37 | output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain
38 | output environmentName string = containerAppsEnvironment.outputs.name
39 | output environmentId string = containerAppsEnvironment.outputs.id
40 |
41 | output registryLoginServer string = containerRegistry.outputs.loginServer
42 | output registryName string = containerRegistry.outputs.name
43 |
--------------------------------------------------------------------------------
/infra/core/host/container-registry.bicep:
--------------------------------------------------------------------------------
1 | metadata description = 'Creates an Azure Container Registry.'
2 | param name string
3 | param location string = resourceGroup().location
4 | param tags object = {}
5 |
6 | @description('Indicates whether admin user is enabled')
7 | param adminUserEnabled bool = false
8 |
9 | @description('Indicates whether anonymous pull is enabled')
10 | param anonymousPullEnabled bool = false
11 |
12 | @description('Azure ad authentication as arm policy settings')
13 | param azureADAuthenticationAsArmPolicy object = {
14 | status: 'enabled'
15 | }
16 |
17 | @description('Indicates whether data endpoint is enabled')
18 | param dataEndpointEnabled bool = false
19 |
20 | @description('Encryption settings')
21 | param encryption object = {
22 | status: 'disabled'
23 | }
24 |
25 | @description('Export policy settings')
26 | param exportPolicy object = {
27 | status: 'enabled'
28 | }
29 |
30 | @description('Metadata search settings')
31 | param metadataSearch string = 'Disabled'
32 |
33 | @description('Options for bypassing network rules')
34 | param networkRuleBypassOptions string = 'AzureServices'
35 |
36 | @description('Public network access setting')
37 | param publicNetworkAccess string = 'Enabled'
38 |
39 | @description('Quarantine policy settings')
40 | param quarantinePolicy object = {
41 | status: 'disabled'
42 | }
43 |
44 | @description('Retention policy settings')
45 | param retentionPolicy object = {
46 | days: 7
47 | status: 'disabled'
48 | }
49 |
50 | @description('Scope maps setting')
51 | param scopeMaps array = []
52 |
53 | @description('SKU settings')
54 | param sku object = {
55 | name: 'Basic'
56 | }
57 |
58 | @description('Soft delete policy settings')
59 | param softDeletePolicy object = {
60 | retentionDays: 7
61 | status: 'disabled'
62 | }
63 |
64 | @description('Trust policy settings')
65 | param trustPolicy object = {
66 | type: 'Notary'
67 | status: 'disabled'
68 | }
69 |
70 | @description('Zone redundancy setting')
71 | param zoneRedundancy string = 'Disabled'
72 |
73 | @description('The log analytics workspace ID used for logging and monitoring')
74 | param workspaceId string = ''
75 |
76 | // 2023-11-01-preview needed for metadataSearch
77 | resource containerRegistry 'Microsoft.ContainerRegistry/registries@2023-11-01-preview' = {
78 | name: name
79 | location: location
80 | tags: tags
81 | sku: sku
82 | properties: {
83 | adminUserEnabled: adminUserEnabled
84 | anonymousPullEnabled: anonymousPullEnabled
85 | dataEndpointEnabled: dataEndpointEnabled
86 | encryption: encryption
87 | metadataSearch: metadataSearch
88 | networkRuleBypassOptions: networkRuleBypassOptions
89 | policies:{
90 | quarantinePolicy: quarantinePolicy
91 | trustPolicy: trustPolicy
92 | retentionPolicy: retentionPolicy
93 | exportPolicy: exportPolicy
94 | azureADAuthenticationAsArmPolicy: azureADAuthenticationAsArmPolicy
95 | softDeletePolicy: softDeletePolicy
96 | }
97 | publicNetworkAccess: publicNetworkAccess
98 | zoneRedundancy: zoneRedundancy
99 | }
100 |
101 | resource scopeMap 'scopeMaps' = [for scopeMap in scopeMaps: {
102 | name: scopeMap.name
103 | properties: scopeMap.properties
104 | }]
105 | }
106 |
107 | // TODO: Update diagnostics to be its own module
108 | // Blocking issue: https://github.com/Azure/bicep/issues/622
109 | // Unable to pass in a `resource` scope or unable to use string interpolation in resource types
110 | resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) {
111 | name: 'registry-diagnostics'
112 | scope: containerRegistry
113 | properties: {
114 | workspaceId: workspaceId
115 | logs: [
116 | {
117 | category: 'ContainerRegistryRepositoryEvents'
118 | enabled: true
119 | }
120 | {
121 | category: 'ContainerRegistryLoginEvents'
122 | enabled: true
123 | }
124 | ]
125 | metrics: [
126 | {
127 | category: 'AllMetrics'
128 | enabled: true
129 | timeGrain: 'PT1M'
130 | }
131 | ]
132 | }
133 | }
134 |
135 | output id string = containerRegistry.id
136 | output loginServer string = containerRegistry.properties.loginServer
137 | output name string = containerRegistry.name
138 |
--------------------------------------------------------------------------------
/infra/core/host/staticwebapp.bicep:
--------------------------------------------------------------------------------
1 | metadata description = 'Creates an Azure Static Web Apps instance.'
2 | param name string
3 | param location string = resourceGroup().location
4 | param tags object = {}
5 |
6 | param sku object = {
7 | name: 'Free'
8 | tier: 'Free'
9 | }
10 |
11 | resource web 'Microsoft.Web/staticSites@2022-03-01' = {
12 | name: name
13 | location: location
14 | tags: tags
15 | sku: sku
16 | properties: {
17 | provider: 'Custom'
18 | }
19 | }
20 |
21 | output name string = web.name
22 | output uri string = 'https://${web.properties.defaultHostname}'
23 |
--------------------------------------------------------------------------------
/infra/core/monitor/applicationinsights.bicep:
--------------------------------------------------------------------------------
1 | metadata description = 'Creates an Application Insights instance based on an existing Log Analytics workspace.'
2 | param name string
3 | param dashboardName string = ''
4 | param location string = resourceGroup().location
5 | param tags object = {}
6 | param logAnalyticsWorkspaceId string
7 |
8 | resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = {
9 | name: name
10 | location: location
11 | tags: tags
12 | kind: 'web'
13 | properties: {
14 | Application_Type: 'web'
15 | WorkspaceResourceId: logAnalyticsWorkspaceId
16 | }
17 | }
18 |
19 | module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = if (!empty(dashboardName)) {
20 | name: 'application-insights-dashboard'
21 | params: {
22 | name: dashboardName
23 | location: location
24 | applicationInsightsName: applicationInsights.name
25 | }
26 | }
27 |
28 | output connectionString string = applicationInsights.properties.ConnectionString
29 | output id string = applicationInsights.id
30 | output instrumentationKey string = applicationInsights.properties.InstrumentationKey
31 | output name string = applicationInsights.name
32 |
--------------------------------------------------------------------------------
/infra/core/monitor/loganalytics.bicep:
--------------------------------------------------------------------------------
1 | metadata description = 'Creates a Log Analytics workspace.'
2 | param name string
3 | param location string = resourceGroup().location
4 | param tags object = {}
5 |
6 | resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = {
7 | name: name
8 | location: location
9 | tags: tags
10 | properties: any({
11 | retentionInDays: 30
12 | features: {
13 | searchVersion: 1
14 | }
15 | sku: {
16 | name: 'PerGB2018'
17 | }
18 | })
19 | }
20 |
21 | output id string = logAnalytics.id
22 | output name string = logAnalytics.name
23 |
--------------------------------------------------------------------------------
/infra/core/monitor/monitoring.bicep:
--------------------------------------------------------------------------------
1 | metadata description = 'Creates an Application Insights instance and a Log Analytics workspace.'
2 | param logAnalyticsName string
3 | param applicationInsightsName string
4 | param applicationInsightsDashboardName string = ''
5 | param location string = resourceGroup().location
6 | param tags object = {}
7 |
8 | module logAnalytics 'loganalytics.bicep' = {
9 | name: 'loganalytics'
10 | params: {
11 | name: logAnalyticsName
12 | location: location
13 | tags: tags
14 | }
15 | }
16 |
17 | module applicationInsights 'applicationinsights.bicep' = {
18 | name: 'applicationinsights'
19 | params: {
20 | name: applicationInsightsName
21 | location: location
22 | tags: tags
23 | dashboardName: applicationInsightsDashboardName
24 | logAnalyticsWorkspaceId: logAnalytics.outputs.id
25 | }
26 | }
27 |
28 | output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString
29 | output applicationInsightsId string = applicationInsights.outputs.id
30 | output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey
31 | output applicationInsightsName string = applicationInsights.outputs.name
32 | output logAnalyticsWorkspaceId string = logAnalytics.outputs.id
33 | output logAnalyticsWorkspaceName string = logAnalytics.outputs.name
34 |
--------------------------------------------------------------------------------
/infra/core/search/search-services.bicep:
--------------------------------------------------------------------------------
1 | metadata description = 'Creates an Azure AI Search instance.'
2 | param name string
3 | param location string = resourceGroup().location
4 | param tags object = {}
5 |
6 | param sku object = {
7 | name: 'standard'
8 | }
9 |
10 | param authOptions object = {}
11 | param disableLocalAuth bool = false
12 | param disabledDataExfiltrationOptions array = []
13 | param encryptionWithCmk object = {
14 | enforcement: 'Unspecified'
15 | }
16 | @allowed([
17 | 'default'
18 | 'highDensity'
19 | ])
20 | param hostingMode string = 'default'
21 | param networkRuleSet object = {
22 | bypass: 'None'
23 | ipRules: []
24 | }
25 | param partitionCount int = 1
26 | @allowed([
27 | 'enabled'
28 | 'disabled'
29 | ])
30 | param publicNetworkAccess string = 'enabled'
31 | param replicaCount int = 1
32 | @allowed([
33 | 'disabled'
34 | 'free'
35 | 'standard'
36 | ])
37 | param semanticSearch string = 'disabled'
38 |
39 | var searchIdentityProvider = (sku.name == 'free') ? null : {
40 | type: 'SystemAssigned'
41 | }
42 |
43 | resource search 'Microsoft.Search/searchServices@2021-04-01-preview' = {
44 | name: name
45 | location: location
46 | tags: tags
47 | // The free tier does not support managed identity
48 | identity: searchIdentityProvider
49 | properties: {
50 | authOptions: disableLocalAuth ? null : authOptions
51 | disableLocalAuth: disableLocalAuth
52 | disabledDataExfiltrationOptions: disabledDataExfiltrationOptions
53 | encryptionWithCmk: encryptionWithCmk
54 | hostingMode: hostingMode
55 | networkRuleSet: networkRuleSet
56 | partitionCount: partitionCount
57 | publicNetworkAccess: publicNetworkAccess
58 | replicaCount: replicaCount
59 | semanticSearch: semanticSearch
60 | }
61 | sku: sku
62 | }
63 |
64 | output id string = search.id
65 | output endpoint string = 'https://${name}.search.windows.net/'
66 | output name string = search.name
67 | output principalId string = !empty(searchIdentityProvider) ? search.identity.principalId : ''
68 |
69 |
--------------------------------------------------------------------------------
/infra/core/security/managed-identity.bicep:
--------------------------------------------------------------------------------
1 | param name string
2 | param location string = resourceGroup().location
3 |
4 | resource apiIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
5 | name: name
6 | location: location
7 | }
8 |
9 | output tenantId string = apiIdentity.properties.tenantId
10 | output principalId string = apiIdentity.properties.principalId
11 | output clientId string = apiIdentity.properties.clientId
12 |
--------------------------------------------------------------------------------
/infra/core/security/registry-access.bicep:
--------------------------------------------------------------------------------
1 | metadata description = 'Assigns ACR Pull permissions to access an Azure Container Registry.'
2 | param containerRegistryName string
3 | param principalId string
4 |
5 | var acrPullRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')
6 |
7 | resource aksAcrPull 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
8 | scope: containerRegistry // Use when specifying a scope that is different than the deployment scope
9 | name: guid(subscription().id, resourceGroup().id, principalId, acrPullRole)
10 | properties: {
11 | roleDefinitionId: acrPullRole
12 | principalType: 'ServicePrincipal'
13 | principalId: principalId
14 | }
15 | }
16 |
17 | resource containerRegistry 'Microsoft.ContainerRegistry/registries@2023-01-01-preview' existing = {
18 | name: containerRegistryName
19 | }
20 |
--------------------------------------------------------------------------------
/infra/core/security/role.bicep:
--------------------------------------------------------------------------------
1 | metadata description = 'Creates a role assignment for a service principal.'
2 | param principalId string
3 |
4 | @allowed([
5 | 'Device'
6 | 'ForeignGroup'
7 | 'Group'
8 | 'ServicePrincipal'
9 | 'User'
10 | ])
11 | param principalType string = 'ServicePrincipal'
12 | param roleDefinitionId string
13 |
14 | resource role 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
15 | name: guid(subscription().id, resourceGroup().id, principalId, roleDefinitionId)
16 | properties: {
17 | principalId: principalId
18 | principalType: principalType
19 | roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/infra/main.parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "environmentName": {
6 | "value": "${AZURE_ENV_NAME}"
7 | },
8 | "resourceGroupName": {
9 | "value": "${AZURE_RESOURCE_GROUP}"
10 | },
11 | "location": {
12 | "value": "${AZURE_LOCATION}"
13 | },
14 | "principalId": {
15 | "value": "${AZURE_PRINCIPAL_ID}"
16 | },
17 | "openAiLocation": {
18 | "value": "${AZURE_OPENAI_LOCATION=eastus2}"
19 | },
20 | "openAiUrl": {
21 | "value": "${AZURE_OPENAI_URL}"
22 | },
23 | "searchServiceSkuName": {
24 | "value": "${AZURE_SEARCH_SERVICE_SKU=standard}"
25 | },
26 | "chatGptDeploymentName": {
27 | "value": "${AZURE_OPENAI_CHATGPT_DEPLOYMENT=gpt-4o-mini}"
28 | },
29 | "indexName": {
30 | "value": "${INDEX_NAME=kbindex}"
31 | },
32 | "useQdrant": {
33 | "value": "${USE_QDRANT=true}"
34 | },
35 | "qdrantPort": {
36 | "value": "${QDRANT_PORT=6334}"
37 | },
38 | "isContinuousDeployment": {
39 | "value": "${CI=false}"
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "azure-openai-rag-workshop-java",
3 | "version": "1.0.0",
4 | "description": "Create your own ChatGPT with Retrieval-Augmented-Generation",
5 | "private": true,
6 | "type": "module",
7 | "directories": {
8 | "doc": "docs"
9 | },
10 | "scripts": {
11 | "start": "npm run dev --workspace=frontend",
12 | "build": "npm run build -ws --if-present",
13 | "clean": "npm run clean -ws --if-present",
14 | "format": "prettier --list-different --write ."
15 | },
16 | "repository": {
17 | "type": "git",
18 | "url": "https://github.com/Azure-Samples/azure-openai-rag-workshop-java.git"
19 | },
20 | "homepage": "https://github.com/Azure-Samples/azure-openai-rag-workshop-java",
21 | "bugs": {
22 | "url": "https://github.com/Azure-Samples/azure-openai-rag-workshop-java/issues"
23 | },
24 | "keywords": [],
25 | "author": "Microsoft",
26 | "license": "MIT",
27 | "workspaces": [
28 | "src/frontend"
29 | ],
30 | "devDependencies": {
31 | "prettier": "^3.0.3",
32 | "rimraf": "^5.0.5",
33 | "typescript": "*"
34 | },
35 | "engines": {
36 | "node": ">=20",
37 | "npm": ">=9"
38 | },
39 | "prettier": {
40 | "tabWidth": 2,
41 | "semi": true,
42 | "singleQuote": true,
43 | "printWidth": 120,
44 | "bracketSpacing": true,
45 | "overrides": [
46 | {
47 | "files": [
48 | "*.json"
49 | ],
50 | "options": {
51 | "parser": "json"
52 | }
53 | }
54 | ]
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 | ai.azure.openai.rag.workshop
7 | java
8 | 1.0.0-SNAPSHOT
9 | pom
10 | Azure OpenAI RAG Workshop
11 |
12 |
13 | src/backend
14 | src/ingestion
15 |
16 |
17 |
--------------------------------------------------------------------------------
/scripts/ingest-data.ps1:
--------------------------------------------------------------------------------
1 | $scriptPath = $MyInvocation.MyCommand.Path
2 | cd $scriptPath/../..
3 |
4 | Write-Host "Loading azd .env file from current environment"
5 | $output = azd env get-values
6 |
7 | foreach ($line in $output) {
8 | if (!$line.Contains('=')) {
9 | continue
10 | }
11 |
12 | $name, $value = $line.Split("=")
13 | $value = $value -replace '^\"|\"$'
14 | [Environment]::SetEnvironmentVariable($name, $value)
15 | }
16 |
17 | if ([string]::IsNullOrEmpty($env:INGESTION_API_URI)) {
18 | [Environment]::SetEnvironmentVariable('INGESTION_API_URI', 'http://localhost:3001')
19 | }
20 |
21 | if ([string]::IsNullOrEmpty($env:INDEX_NAME)) {
22 | [Environment]::SetEnvironmentVariable('INDEX_NAME', 'kbindex')
23 | }
24 |
25 | Write-Host 'Uploading PDF files to the ingestion API'
26 | Invoke-RestMethod -Uri "$env:INGESTION_API_URI/ingest" -Method Post -InFile "./data/privacy-policy.pdf"
27 | Invoke-RestMethod -Uri "$env:INGESTION_API_URI/ingest" -Method Post -InFile "./data/support.pdf"
28 | Invoke-RestMethod -Uri "$env:INGESTION_API_URI/ingest" -Method Post -InFile "./data/terms-of-service.pdf"
29 |
--------------------------------------------------------------------------------
/scripts/ingest-data.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | cd "$(dirname "${BASH_SOURCE[0]}")/.."
3 |
4 | if azd_env=$(azd env get-values); then
5 | echo "Loading azd .env file from current environment"
6 | export $(echo "$azd_env" | xargs)
7 | fi
8 |
9 | echo 'Uploading PDF files to the ingestion API'
10 | curl -F "file=@./data/privacy-policy.pdf" \
11 | -F "file=@./data/support.pdf" \
12 | -F "file=@./data/terms-of-service.pdf" \
13 | "${INGESTION_API_URI:-http://localhost:3001}/ingest"
14 |
--------------------------------------------------------------------------------
/scripts/repo/build-docs.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | ##############################################################################
3 | # Usage: ./build-docs.sh [--local]
4 | # Build the docs and push them to the "docs" branch on GitHub.
5 | ##############################################################################
6 |
7 | set -euo pipefail
8 | cd "$(dirname "${BASH_SOURCE[0]}")/../.."
9 |
10 | DOCS_HOME=/tmp/azure-openai-rag-workshop-java-docs
11 | GH_USER=$(git config user.name)
12 | REPO=https://$GH_USER:$GH_TOKEN@github.com/Azure-Samples/azure-openai-rag-workshop-java.git
13 |
14 | echo "Preparing all workshop docs..."
15 | echo "(temp folder: $DOCS_HOME)"
16 | rm -rf "$DOCS_HOME"
17 | mkdir -p "$DOCS_HOME"
18 |
19 | cp -R docs "$DOCS_HOME"
20 | cd "$DOCS_HOME"
21 |
22 | # Build docs
23 | cd docs
24 | moaw build _workshop-java-quarkus.md -d workshop-java-quarkus.md
25 | moaw build _workshop-java-quarkus.md -d workshop.md
26 |
27 | if [[ ${1-} == "--local" ]]; then
28 | echo "Local mode: skipping GitHub push."
29 | open "$DOCS_HOME"
30 | else
31 | # Update git repo
32 | git init
33 | git checkout -b docs
34 | git remote add origin "$REPO"
35 | git add .
36 | git commit -m "docs: prepare workshop docs"
37 | git push -u origin docs --force
38 |
39 | rm -rf "$DOCS_HOME"
40 | fi
41 |
42 | echo "Successfully updated workshop docs."
43 |
--------------------------------------------------------------------------------
/scripts/repo/create-packages.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | ##############################################################################
3 | # Usage: ./create-packages.sh
4 | # Creates packages for skippable sections of the workshop
5 | ##############################################################################
6 |
7 | set -euo pipefail
8 | cd "$(dirname "${BASH_SOURCE[0]}")/../.."
9 |
10 | target_folder=dist
11 |
12 | rm -rf "$target_folder"
13 | mkdir -p "$target_folder"
14 |
15 | copyFolder() {
16 | local src="$1"
17 | local dest="$target_folder/${2:-}"
18 | find "$src" -type d -not -path '*node_modules*' -not -path '*/.git' -not -path '*.git/*' -not -path '*/dist' -not -path '*dist/*' -exec mkdir -p '{}' "$dest/{}" ';'
19 | find "$src" -type f -not -path '*node_modules*' -not -path '*.git/*' -not -path '*dist/*' -not -path '*/.DS_Store' -exec cp -r '{}' "$dest/{}" ';'
20 | }
21 |
22 | makeArchive() {
23 | local src="$1"
24 | local name="${2:-$src}"
25 | local archive="$name.tar.gz"
26 | local cwd="${3:-}"
27 | echo "Creating $archive..."
28 | if [[ -n "$cwd" ]]; then
29 | pushd "$target_folder/$cwd" >/dev/null
30 | tar -czvf "../$archive" "$src"
31 | popd
32 | rm -rf "$target_folder/${cwd:?}"
33 | else
34 | pushd "$target_folder/$cwd" >/dev/null
35 | tar -czvf "$archive" "$src"
36 | popd
37 | rm -rf "$target_folder/${src:?}"
38 | fi
39 | }
40 |
41 | ##############################################################################
42 | # Complete solution
43 | ##############################################################################
44 | echo "Creating solution package (for Java + Quarkus)..."
45 | copyFolder . solution-java-quarkus
46 | rm -rf "$target_folder/solution-java-quarkus/.azure"
47 | rm -rf "$target_folder/solution-java-quarkus/.qdrant"
48 | rm -rf "$target_folder/solution-java-quarkus/.env"
49 | rm -rf "$target_folder/solution-java-quarkus/.env*"
50 | rm -rf "$target_folder/solution-java-quarkus/docs"
51 | rm -rf "$target_folder/solution-java-quarkus/trainer"
52 | rm -rf "$target_folder/solution-java-quarkus/scripts/repo"
53 | rm -rf "$target_folder/solution-java-quarkus/.github"
54 | rm -rf "$target_folder/solution-java-quarkus/TODO"
55 | rm -rf "$target_folder/solution-java-quarkus/SUPPORT.md"
56 | rm -rf "$target_folder/solution-java-quarkus/CODE_OF_CONDUCT.md"
57 | rm -rf "$target_folder/solution-java-quarkus/SECURITY.md"
58 | rm -rf "$target_folder/solution-java-quarkus/scripts/setup-template.sh"
59 | perl -pi -e 's/stream: false/stream: true/g' "$target_folder/solution-java-quarkus/src/frontend/src/components/chat.ts"
60 | perl -pi -e 's/qdrant:6333/qdrant:6334/g' "$target_folder/solution-java-quarkus/docker-compose.yml"
61 | makeArchive . solution-java-quarkus solution-java-quarkus
62 |
63 | ##############################################################################
64 | # Frontend
65 | ##############################################################################
66 |
67 | echo "Creating frontend package..."
68 | copyFolder src/frontend
69 | makeArchive src frontend
70 |
71 | ##############################################################################
72 | # Deployment (CI/CD)
73 | ##############################################################################
74 |
75 | echo "Creating CI/CD package..."
76 | mkdir -p "$target_folder/ci-cd/.github/workflows"
77 | cp .github/workflows/deploy.yml "$target_folder/ci-cd/.github/workflows/deploy.yml"
78 | makeArchive . ci-cd ci-cd
79 |
--------------------------------------------------------------------------------
/src/backend/.dockerignore:
--------------------------------------------------------------------------------
1 | !target/*-runner
2 | !target/*-runner.jar
3 | !target/lib/*
4 | !target/quarkus-app/*
--------------------------------------------------------------------------------
/src/backend/.gitignore:
--------------------------------------------------------------------------------
1 | #Maven
2 | target/
3 | pom.xml.tag
4 | pom.xml.releaseBackup
5 | pom.xml.versionsBackup
6 | release.properties
7 | .flattened-pom.xml
8 |
9 | # Eclipse
10 | .project
11 | .classpath
12 | .settings/
13 | bin/
14 |
15 | # IntelliJ
16 | .idea
17 | *.ipr
18 | *.iml
19 | *.iws
20 |
21 | # NetBeans
22 | nb-configuration.xml
23 |
24 | # Visual Studio Code
25 | .vscode
26 | .factorypath
27 |
28 | # OSX
29 | .DS_Store
30 |
31 | # Vim
32 | *.swp
33 | *.swo
34 |
35 | # patch
36 | *.orig
37 | *.rej
38 |
39 | # Local environment
40 | .env
41 |
42 | # Plugin directory
43 | /.quarkus/cli/plugins/
44 |
45 |
46 | # Quinoa
47 | node_modules/
48 | build/
49 | dist/
50 | .quinoa/
51 | dist-ssr
52 | *.local
53 |
--------------------------------------------------------------------------------
/src/backend/.mvn/wrapper/.gitignore:
--------------------------------------------------------------------------------
1 | maven-wrapper.jar
2 |
--------------------------------------------------------------------------------
/src/backend/.mvn/wrapper/MavenWrapperDownloader.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the Apache Software Foundation (ASF) under one
3 | * or more contributor license agreements. See the NOTICE file
4 | * distributed with this work for additional information
5 | * regarding copyright ownership. The ASF licenses this file
6 | * to you under the Apache License, Version 2.0 (the
7 | * "License"); you may not use this file except in compliance
8 | * with the License. You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing,
13 | * software distributed under the License is distributed on an
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | * KIND, either express or implied. See the License for the
16 | * specific language governing permissions and limitations
17 | * under the License.
18 | */
19 |
20 | import java.io.IOException;
21 | import java.io.InputStream;
22 | import java.net.Authenticator;
23 | import java.net.PasswordAuthentication;
24 | import java.net.URL;
25 | import java.nio.file.Files;
26 | import java.nio.file.Path;
27 | import java.nio.file.Paths;
28 | import java.nio.file.StandardCopyOption;
29 |
30 | public final class MavenWrapperDownloader
31 | {
32 | private static final String WRAPPER_VERSION = "3.2.0";
33 |
34 | private static final boolean VERBOSE = Boolean.parseBoolean( System.getenv( "MVNW_VERBOSE" ) );
35 |
36 | public static void main( String[] args )
37 | {
38 | log( "Apache Maven Wrapper Downloader " + WRAPPER_VERSION );
39 |
40 | if ( args.length != 2 )
41 | {
42 | System.err.println( " - ERROR wrapperUrl or wrapperJarPath parameter missing" );
43 | System.exit( 1 );
44 | }
45 |
46 | try
47 | {
48 | log( " - Downloader started" );
49 | final URL wrapperUrl = new URL( args[0] );
50 | final String jarPath = args[1].replace( "..", "" ); // Sanitize path
51 | final Path wrapperJarPath = Paths.get( jarPath ).toAbsolutePath().normalize();
52 | downloadFileFromURL( wrapperUrl, wrapperJarPath );
53 | log( "Done" );
54 | }
55 | catch ( IOException e )
56 | {
57 | System.err.println( "- Error downloading: " + e.getMessage() );
58 | if ( VERBOSE )
59 | {
60 | e.printStackTrace();
61 | }
62 | System.exit( 1 );
63 | }
64 | }
65 |
66 | private static void downloadFileFromURL( URL wrapperUrl, Path wrapperJarPath )
67 | throws IOException
68 | {
69 | log( " - Downloading to: " + wrapperJarPath );
70 | if ( System.getenv( "MVNW_USERNAME" ) != null && System.getenv( "MVNW_PASSWORD" ) != null )
71 | {
72 | final String username = System.getenv( "MVNW_USERNAME" );
73 | final char[] password = System.getenv( "MVNW_PASSWORD" ).toCharArray();
74 | Authenticator.setDefault( new Authenticator()
75 | {
76 | @Override
77 | protected PasswordAuthentication getPasswordAuthentication()
78 | {
79 | return new PasswordAuthentication( username, password );
80 | }
81 | } );
82 | }
83 | try ( InputStream inStream = wrapperUrl.openStream() )
84 | {
85 | Files.copy( inStream, wrapperJarPath, StandardCopyOption.REPLACE_EXISTING );
86 | }
87 | log( " - Downloader complete" );
88 | }
89 |
90 | private static void log( String msg )
91 | {
92 | if ( VERBOSE )
93 | {
94 | System.out.println( msg );
95 | }
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/src/backend/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | # Licensed to the Apache Software Foundation (ASF) under one
2 | # or more contributor license agreements. See the NOTICE file
3 | # distributed with this work for additional information
4 | # regarding copyright ownership. The ASF licenses this file
5 | # to you under the Apache License, Version 2.0 (the
6 | # "License"); you may not use this file except in compliance
7 | # with the License. You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 | # KIND, either express or implied. See the License for the
15 | # specific language governing permissions and limitations
16 | # under the License.
17 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip
18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
19 |
--------------------------------------------------------------------------------
/src/backend/src/main/docker/Dockerfile.jvm:
--------------------------------------------------------------------------------
1 | ####
2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
3 | #
4 | # Before building the container image run:
5 | #
6 | # ./mvnw package
7 | #
8 | # Then, build the image with:
9 | #
10 | # docker build -f src/main/docker/Dockerfile.jvm -t quarkus/java-quarkus-jvm .
11 | #
12 | # Then run the container using:
13 | #
14 | # docker run -i --rm -p 8080:8080 quarkus/java-quarkus-jvm
15 | #
16 | # If you want to include the debug port into your docker image
17 | # you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005.
18 | # Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005
19 | # when running the container
20 | #
21 | # Then run the container using :
22 | #
23 | # docker run -i --rm -p 8080:8080 quarkus/java-quarkus-jvm
24 | #
25 | # This image uses the `run-java.sh` script to run the application.
26 | # This scripts computes the command line to execute your Java application, and
27 | # includes memory/GC tuning.
28 | # You can configure the behavior using the following environment properties:
29 | # - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class")
30 | # - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options
31 | # in JAVA_OPTS (example: "-Dsome.property=foo")
32 | # - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is
33 | # used to calculate a default maximal heap memory based on a containers restriction.
34 | # If used in a container without any memory constraints for the container then this
35 | # option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio
36 | # of the container available memory as set here. The default is `50` which means 50%
37 | # of the available memory is used as an upper boundary. You can skip this mechanism by
38 | # setting this value to `0` in which case no `-Xmx` option is added.
39 | # - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This
40 | # is used to calculate a default initial heap memory based on the maximum heap memory.
41 | # If used in a container without any memory constraints for the container then this
42 | # option has no effect. If there is a memory constraint then `-Xms` is set to a ratio
43 | # of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx`
44 | # is used as the initial heap size. You can skip this mechanism by setting this value
45 | # to `0` in which case no `-Xms` option is added (example: "25")
46 | # - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS.
47 | # This is used to calculate the maximum value of the initial heap memory. If used in
48 | # a container without any memory constraints for the container then this option has
49 | # no effect. If there is a memory constraint then `-Xms` is limited to the value set
50 | # here. The default is 4096MB which means the calculated value of `-Xms` never will
51 | # be greater than 4096MB. The value of this variable is expressed in MB (example: "4096")
52 | # - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output
53 | # when things are happening. This option, if set to true, will set
54 | # `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true").
55 | # - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example:
56 | # true").
57 | # - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787").
58 | # - CONTAINER_CORE_LIMIT: A calculated core limit as described in
59 | # https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2")
60 | # - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024").
61 | # - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion.
62 | # (example: "20")
63 | # - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking.
64 | # (example: "40")
65 | # - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection.
66 | # (example: "4")
67 | # - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus
68 | # previous GC times. (example: "90")
69 | # - GC_METASPACE_SIZE: The initial metaspace size. (example: "20")
70 | # - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100")
71 | # - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should
72 | # contain the necessary JRE command-line options to specify the required GC, which
73 | # will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC).
74 | # - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080")
75 | # - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080")
76 | # - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be
77 | # accessed directly. (example: "foo.example.com,bar.example.com")
78 | #
79 | ###
80 | FROM registry.access.redhat.com/ubi8/openjdk-17:1.18
81 |
82 | ENV LANGUAGE='en_US:en'
83 |
84 |
85 | # We make four distinct layers so if there are application changes the library layers can be re-used
86 | COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/
87 | COPY --chown=185 target/quarkus-app/*.jar /deployments/
88 | COPY --chown=185 target/quarkus-app/app/ /deployments/app/
89 | COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/
90 |
91 | EXPOSE 8080
92 | USER 185
93 | ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
94 | ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
95 |
96 | ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]
97 |
98 |
--------------------------------------------------------------------------------
/src/backend/src/main/docker/Dockerfile.legacy-jar:
--------------------------------------------------------------------------------
1 | ####
2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
3 | #
4 | # Before building the container image run:
5 | #
6 | # ./mvnw package -Dquarkus.package.type=legacy-jar
7 | #
8 | # Then, build the image with:
9 | #
10 | # docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/java-quarkus-legacy-jar .
11 | #
12 | # Then run the container using:
13 | #
14 | # docker run -i --rm -p 8080:8080 quarkus/java-quarkus-legacy-jar
15 | #
16 | # If you want to include the debug port into your docker image
17 | # you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005.
18 | # Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005
19 | # when running the container
20 | #
21 | # Then run the container using :
22 | #
23 | # docker run -i --rm -p 8080:8080 quarkus/java-quarkus-legacy-jar
24 | #
25 | # This image uses the `run-java.sh` script to run the application.
26 | # This scripts computes the command line to execute your Java application, and
27 | # includes memory/GC tuning.
28 | # You can configure the behavior using the following environment properties:
29 | # - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class")
30 | # - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options
31 | # in JAVA_OPTS (example: "-Dsome.property=foo")
32 | # - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is
33 | # used to calculate a default maximal heap memory based on a containers restriction.
34 | # If used in a container without any memory constraints for the container then this
35 | # option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio
36 | # of the container available memory as set here. The default is `50` which means 50%
37 | # of the available memory is used as an upper boundary. You can skip this mechanism by
38 | # setting this value to `0` in which case no `-Xmx` option is added.
39 | # - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This
40 | # is used to calculate a default initial heap memory based on the maximum heap memory.
41 | # If used in a container without any memory constraints for the container then this
42 | # option has no effect. If there is a memory constraint then `-Xms` is set to a ratio
43 | # of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx`
44 | # is used as the initial heap size. You can skip this mechanism by setting this value
45 | # to `0` in which case no `-Xms` option is added (example: "25")
46 | # - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS.
47 | # This is used to calculate the maximum value of the initial heap memory. If used in
48 | # a container without any memory constraints for the container then this option has
49 | # no effect. If there is a memory constraint then `-Xms` is limited to the value set
50 | # here. The default is 4096MB which means the calculated value of `-Xms` never will
51 | # be greater than 4096MB. The value of this variable is expressed in MB (example: "4096")
52 | # - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output
53 | # when things are happening. This option, if set to true, will set
54 | # `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true").
55 | # - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example:
56 | # true").
57 | # - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787").
58 | # - CONTAINER_CORE_LIMIT: A calculated core limit as described in
59 | # https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2")
60 | # - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024").
61 | # - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion.
62 | # (example: "20")
63 | # - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking.
64 | # (example: "40")
65 | # - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection.
66 | # (example: "4")
67 | # - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus
68 | # previous GC times. (example: "90")
69 | # - GC_METASPACE_SIZE: The initial metaspace size. (example: "20")
70 | # - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100")
71 | # - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should
72 | # contain the necessary JRE command-line options to specify the required GC, which
73 | # will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC).
74 | # - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080")
75 | # - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080")
76 | # - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be
77 | # accessed directly. (example: "foo.example.com,bar.example.com")
78 | #
79 | ###
80 | FROM registry.access.redhat.com/ubi8/openjdk-17:1.18
81 |
82 | ENV LANGUAGE='en_US:en'
83 |
84 |
85 | COPY target/lib/* /deployments/lib/
86 | COPY target/*-runner.jar /deployments/quarkus-run.jar
87 |
88 | EXPOSE 8080
89 | USER 185
90 | ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
91 | ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
92 |
93 | ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]
94 |
--------------------------------------------------------------------------------
/src/backend/src/main/docker/Dockerfile.native:
--------------------------------------------------------------------------------
1 | ####
2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode.
3 | #
4 | # Before building the container image run:
5 | #
6 | # ./mvnw package -Dnative
7 | #
8 | # Then, build the image with:
9 | #
10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/java-quarkus .
11 | #
12 | # Then run the container using:
13 | #
14 | # docker run -i --rm -p 8080:8080 quarkus/java-quarkus
15 | #
16 | ###
17 | FROM registry.access.redhat.com/ubi8/ubi-minimal:8.9
18 | WORKDIR /work/
19 | RUN chown 1001 /work \
20 | && chmod "g+rwX" /work \
21 | && chown 1001:root /work
22 | COPY --chown=1001:root target/*-runner /work/application
23 |
24 | EXPOSE 8080
25 | USER 1001
26 |
27 | ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"]
28 |
--------------------------------------------------------------------------------
/src/backend/src/main/docker/Dockerfile.native-micro:
--------------------------------------------------------------------------------
1 | ####
2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode.
3 | # It uses a micro base image, tuned for Quarkus native executables.
4 | # It reduces the size of the resulting container image.
5 | # Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image.
6 | #
7 | # Before building the container image run:
8 | #
9 | # ./mvnw package -Dnative
10 | #
11 | # Then, build the image with:
12 | #
13 | # docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/java-quarkus .
14 | #
15 | # Then run the container using:
16 | #
17 | # docker run -i --rm -p 8080:8080 quarkus/java-quarkus
18 | #
19 | ###
20 | FROM quay.io/quarkus/quarkus-micro-image:2.0
21 | WORKDIR /work/
22 | RUN chown 1001 /work \
23 | && chmod "g+rwX" /work \
24 | && chown 1001:root /work
25 | COPY --chown=1001:root target/*-runner /work/application
26 |
27 | EXPOSE 8080
28 | USER 1001
29 |
30 | ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"]
31 |
--------------------------------------------------------------------------------
/src/backend/src/main/java/ai/azure/openai/rag/workshop/backend/configuration/ChatLanguageModelAzureOpenAiProducer.java:
--------------------------------------------------------------------------------
1 | package ai.azure.openai.rag.workshop.backend.configuration;
2 |
3 | import dev.langchain4j.model.azure.AzureOpenAiChatModel;
4 | import dev.langchain4j.model.chat.ChatLanguageModel;
5 | import jakarta.enterprise.inject.Produces;
6 | import org.eclipse.microprofile.config.inject.ConfigProperty;
7 |
8 | import com.azure.core.credential.TokenRequestContext;
9 | import com.azure.identity.DefaultAzureCredential;
10 | import com.azure.identity.DefaultAzureCredentialBuilder;
11 |
12 | import static java.time.Duration.ofSeconds;
13 | import org.slf4j.Logger;
14 | import org.slf4j.LoggerFactory;
15 |
16 | public class ChatLanguageModelAzureOpenAiProducer {
17 |
18 | private static final Logger log = LoggerFactory.getLogger(ChatLanguageModelAzureOpenAiProducer.class);
19 |
20 | @ConfigProperty(name = "AZURE_OPENAI_URL")
21 | String azureOpenAiEndpoint;
22 |
23 | @ConfigProperty(name = "AZURE_OPENAI_DEPLOYMENT_NAME", defaultValue = "gpt-4o-mini")
24 | String azureOpenAiDeploymentName;
25 |
26 | @Produces
27 | public ChatLanguageModel chatLanguageModel() {
28 | // initialize chat model here
29 | AzureOpenAiChatModel model;
30 |
31 | try {
32 | // Use the current user identity to authenticate with Azure OpenAI.
33 | // (no secrets needed, just use `az login` or `azd auth login` locally, and managed identity when deployed on Azure).
34 | DefaultAzureCredential credentials = new DefaultAzureCredentialBuilder().build();
35 |
36 | // Try requesting a token, so we can fallback to the other builder if it doesn't work
37 | TokenRequestContext request = new TokenRequestContext();
38 | request.addScopes("https://cognitiveservices.azure.com/.default");
39 | credentials.getTokenSync(request);
40 |
41 | model = AzureOpenAiChatModel.builder()
42 | .tokenCredential(credentials)
43 | .endpoint(azureOpenAiEndpoint)
44 | .deploymentName(azureOpenAiDeploymentName)
45 | .timeout(ofSeconds(60))
46 | .logRequestsAndResponses(true)
47 | .build();
48 | } catch (Exception e) {
49 | // Default value for local execution
50 | log.info("### Using fallback configuration for OpenAI");
51 | model = AzureOpenAiChatModel.builder()
52 | .apiKey("__dummy")
53 | .endpoint(azureOpenAiEndpoint)
54 | .deploymentName(azureOpenAiDeploymentName)
55 | .timeout(ofSeconds(60))
56 | .logRequestsAndResponses(true)
57 | .build();
58 | }
59 |
60 | log.info("### Producing ChatLanguageModel with AzureOpenAiChatModel");
61 |
62 | return model;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/backend/src/main/java/ai/azure/openai/rag/workshop/backend/configuration/ChatLanguageModelOllamaProducer.java:
--------------------------------------------------------------------------------
1 | package ai.azure.openai.rag.workshop.backend.configuration;
2 |
3 | import dev.langchain4j.model.chat.ChatLanguageModel;
4 | import dev.langchain4j.model.ollama.OllamaChatModel;
5 | import jakarta.enterprise.inject.Alternative;
6 | import jakarta.enterprise.inject.Produces;
7 | import static java.time.Duration.ofSeconds;
8 | import org.eclipse.microprofile.config.inject.ConfigProperty;
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 |
12 | @Alternative
13 | public class ChatLanguageModelOllamaProducer {
14 |
15 | private static final Logger log = LoggerFactory.getLogger(ChatLanguageModelOllamaProducer.class);
16 |
17 | @ConfigProperty(name = "OLLAMA_BASE_URL", defaultValue = "http://localhost:11434")
18 | String ollamaBaseUrl;
19 |
20 | @ConfigProperty(name = "OLLAMA_MODEL_NAME", defaultValue = "mistral")
21 | String ollamaModelName;
22 |
23 | @Produces
24 | public ChatLanguageModel chatLanguageModel() {
25 |
26 | log.info("### Producing ChatLanguageModel with OllamaChatModel");
27 |
28 | return OllamaChatModel.builder()
29 | .baseUrl(ollamaBaseUrl)
30 | .modelName(ollamaModelName)
31 | .timeout(ofSeconds(60))
32 | .build();
33 | }
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/src/backend/src/main/java/ai/azure/openai/rag/workshop/backend/configuration/EmbeddingModelProducer.java:
--------------------------------------------------------------------------------
1 | package ai.azure.openai.rag.workshop.backend.configuration;
2 |
3 | import dev.langchain4j.model.embedding.onnx.allminilml6v2.AllMiniLmL6V2EmbeddingModel;
4 | import dev.langchain4j.model.embedding.EmbeddingModel;
5 | import jakarta.enterprise.inject.Produces;
6 |
7 | public class EmbeddingModelProducer {
8 |
9 | @Produces
10 | public EmbeddingModel embeddingModel() {
11 | return new AllMiniLmL6V2EmbeddingModel();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/backend/src/main/java/ai/azure/openai/rag/workshop/backend/configuration/EmbeddingStoreProducer.java:
--------------------------------------------------------------------------------
1 | package ai.azure.openai.rag.workshop.backend.configuration;
2 |
3 | import dev.langchain4j.data.segment.TextSegment;
4 | import dev.langchain4j.store.embedding.EmbeddingStore;
5 | import dev.langchain4j.store.embedding.qdrant.QdrantEmbeddingStore;
6 | import jakarta.enterprise.inject.Produces;
7 | import org.eclipse.microprofile.config.inject.ConfigProperty;
8 |
9 | import java.net.URI;
10 | import java.net.URISyntaxException;
11 |
12 | public class EmbeddingStoreProducer {
13 |
14 | @ConfigProperty(name = "AZURE_SEARCH_INDEX", defaultValue = "kbindex")
15 | String azureSearchIndexName;
16 |
17 | @ConfigProperty(name = "QDRANT_URL", defaultValue = "http://localhost:6334")
18 | String qdrantUrl;
19 |
20 | @Produces
21 | public EmbeddingStore embeddingStore() throws URISyntaxException {
22 | String qdrantHostname = new URI(qdrantUrl).getHost();
23 | int qdrantPort = new URI(qdrantUrl).getPort();
24 | return QdrantEmbeddingStore.builder()
25 | .collectionName(azureSearchIndexName)
26 | .host(qdrantHostname)
27 | .port(qdrantPort)
28 | .build();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/backend/src/main/java/ai/azure/openai/rag/workshop/backend/rest/ChatMessage.java:
--------------------------------------------------------------------------------
1 | package ai.azure.openai.rag.workshop.backend.rest;
2 |
3 | public class ChatMessage {
4 |
5 | public String content;
6 | public String role;
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/src/backend/src/main/java/ai/azure/openai/rag/workshop/backend/rest/ChatRequest.java:
--------------------------------------------------------------------------------
1 | package ai.azure.openai.rag.workshop.backend.rest;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | public class ChatRequest {
7 |
8 | public List messages = new ArrayList<>();
9 | public double temperature = 1f;
10 | public double topP = 1f;
11 | public String user;
12 | }
13 |
--------------------------------------------------------------------------------
/src/backend/src/main/java/ai/azure/openai/rag/workshop/backend/rest/ChatResource.java:
--------------------------------------------------------------------------------
1 | package ai.azure.openai.rag.workshop.backend.rest;
2 |
3 | import dev.langchain4j.data.embedding.Embedding;
4 | import dev.langchain4j.data.message.AiMessage;
5 | import dev.langchain4j.data.message.ChatMessage;
6 | import dev.langchain4j.data.message.SystemMessage;
7 | import dev.langchain4j.data.message.UserMessage;
8 | import dev.langchain4j.data.segment.TextSegment;
9 | import dev.langchain4j.model.chat.ChatLanguageModel;
10 | import dev.langchain4j.model.embedding.EmbeddingModel;
11 | import dev.langchain4j.model.output.Response;
12 | import dev.langchain4j.store.embedding.EmbeddingMatch;
13 | import dev.langchain4j.store.embedding.EmbeddingSearchRequest;
14 | import dev.langchain4j.store.embedding.EmbeddingSearchResult;
15 | import dev.langchain4j.store.embedding.EmbeddingStore;
16 | import jakarta.inject.Inject;
17 | import jakarta.ws.rs.*;
18 | import org.slf4j.Logger;
19 | import org.slf4j.LoggerFactory;
20 |
21 | import java.util.ArrayList;
22 | import java.util.List;
23 |
24 | @Path("/chat")
25 | public class ChatResource {
26 |
27 | private static final Logger log = LoggerFactory.getLogger(ChatResource.class);
28 |
29 | private static final String SYSTEM_MESSAGE_PROMPT = """
30 | Assistant helps the Consto Real Estate company customers with support questions regarding terms of service, privacy policy, and questions about support requests.
31 | Be brief in your answers.
32 | Answer ONLY with the facts listed in the list of sources below.
33 | If there isn't enough information below, say you don't know.
34 | Do not generate answers that don't use the sources below.
35 | If asking a clarifying question to the user would help, ask the question.
36 | For tabular information return it as an html table.
37 | Do not return markdown format.
38 | If the question is not in English, answer in the language used in the question.
39 | Each source has a name followed by colon and the actual information, always include the source name for each fact you use in the response.
40 | Use square brackets to reference the source, for example: [info1.txt].
41 | Don't combine sources, list each source separately, for example: [info1.txt][info2.pdf].
42 | """;
43 |
44 | @Inject
45 | EmbeddingModel embeddingModel;
46 |
47 | @Inject
48 | EmbeddingStore embeddingStore;
49 |
50 | @Inject
51 | ChatLanguageModel chatLanguageModel;
52 |
53 | @POST
54 | @Consumes({"application/json"})
55 | @Produces({"application/json"})
56 | public ChatResponse chat(ChatRequest chatRequest) {
57 |
58 | String question = chatRequest.messages.get(chatRequest.messages.size() - 1).content;
59 |
60 | // Embed the question (convert the user's question into vectors that represent the meaning)
61 | log.info("### Embed the question (convert the question into vectors that represent the meaning) using embeddedQuestion model");
62 | Embedding embeddedQuestion = embeddingModel.embed(question).content();
63 | log.debug("# Vector length: {}", embeddedQuestion.vector().length);
64 |
65 | // Find relevant embeddings from Qdrant based on the user's question
66 | log.info("### Find relevant embeddings from Qdrant based on the question");
67 | EmbeddingSearchResult relevant = embeddingStore.search(EmbeddingSearchRequest.builder()
68 | .queryEmbedding(embeddedQuestion)
69 | .build());
70 |
71 | // Builds chat history using the relevant embeddings
72 | log.info("### Builds chat history using the relevant embeddings");
73 | List chatMessages = new ArrayList<>();
74 | chatMessages.add(SystemMessage.from(SYSTEM_MESSAGE_PROMPT));
75 | String userMessage = question + "\n\nSources:\n";
76 | for (EmbeddingMatch textSegmentEmbeddingMatch : relevant.matches()) {
77 | userMessage += textSegmentEmbeddingMatch.embedded().metadata().getString("filename") + ": " + textSegmentEmbeddingMatch.embedded().text() + "\n";
78 | }
79 | chatMessages.add(UserMessage.from(userMessage));
80 |
81 | // Invoke the LLM
82 | log.info("### Invoke the LLM");
83 | Response response = chatLanguageModel.generate(chatMessages);
84 |
85 | // Return the response
86 | return ChatResponse.fromMessage(response.content().text());
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/backend/src/main/java/ai/azure/openai/rag/workshop/backend/rest/ChatResponse.java:
--------------------------------------------------------------------------------
1 | package ai.azure.openai.rag.workshop.backend.rest;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | public class ChatResponse {
7 |
8 | /**
9 | * Create a ChatResponse when there is only one message to return.
10 | */
11 | public static ChatResponse fromMessage(String message) {
12 | ChatResponse chatResponse = new ChatResponse();
13 | ChatResponse.Choice choice = new ChatResponse.Choice();
14 | choice.index = 0;
15 | choice.message = new ai.azure.openai.rag.workshop.backend.rest.ChatMessage();
16 | choice.message.content = message;
17 | choice.message.role = "assistant";
18 | chatResponse.choices.add(choice);
19 | return chatResponse;
20 | }
21 |
22 | public List choices = new ArrayList<>();
23 |
24 | public static class Choice {
25 | public int index;
26 | public ChatMessage message;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/backend/src/main/plantuml/class-diagram-model.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | skinparam dpi 600
3 |
4 | allow_mixing
5 | hide empty members
6 |
7 | 'Image
8 | class ChatMessage {
9 | +String message
10 | }
11 | class ChatRequest
12 | class ChatResponse
13 | class Choice
14 | ChatRequest --> "*" ChatMessage
15 | ChatResponse --> "*" Choice
16 | Choice --> ChatMessage
17 | @enduml
18 |
--------------------------------------------------------------------------------
/src/backend/src/main/plantuml/class-diagram-rest.puml:
--------------------------------------------------------------------------------
1 | @startuml
2 | skinparam dpi 600
3 |
4 | allow_mixing
5 | hide empty members
6 | 'left to right direction
7 |
8 | 'Image
9 | class ChatResource {
10 | +ChatResponse chat(ChatRequest)
11 | }
12 | class ChatLanguageModelProducer
13 | class EmbeddingModelProducer
14 | class EmbeddingStoreProducer
15 | ChatResource <.. ChatLanguageModelProducer: @Inject
16 | ChatResource <.. EmbeddingModelProducer: @Inject
17 | ChatResource <.. EmbeddingStoreProducer: @Inject
18 | @enduml
19 |
--------------------------------------------------------------------------------
/src/backend/src/main/resources/META-INF/resources/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/src/backend/src/main/resources/META-INF/resources/favicon.ico
--------------------------------------------------------------------------------
/src/backend/src/main/resources/META-INF/resources/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | ChatGPT with Enterprise Data
9 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/src/backend/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | quarkus.http.port=3000
2 | quarkus.log.level=INFO
3 | quarkus.log.category."ai.azure.openai.rag.workshop.backend".level=DEBUG
4 | #quarkus.arc.selected-alternatives=ai.azure.openai.rag.workshop.backend.configuration.ChatLanguageModelOllamaProducer
5 |
--------------------------------------------------------------------------------
/src/backend/src/main/resources/default_banner.txt:
--------------------------------------------------------------------------------
1 | ______ _ _
2 | | ___ \ | | | |
3 | | |_/ / __ _ ___| | _____ _ __ __| |
4 | | ___ \/ _` |/ __| |/ / _ \ '_ \ / _` |
5 | | |_/ / (_| | (__| < __/ | | | (_| |
6 | \____/ \__,_|\___|_|\_\___|_| |_|\__,_|
7 |
8 |
--------------------------------------------------------------------------------
/src/backend/src/main/script/curl-chat-endpoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | ##############################################################################
3 | # Usage: ./curl-chat-endpoint.sh
4 | # Curls the Chat Endpoint with a POST request
5 | ##############################################################################
6 |
7 | curl -X 'POST' \
8 | 'http://localhost:3000/chat' \
9 | -H 'accept: */*' \
10 | -H 'Content-Type: application/json' \
11 | -d '{
12 | "messages": [
13 | {
14 | "content": "What is the information that is collected automatically?",
15 | "role": "user"
16 | }
17 | ],
18 | "model": "gpt-4o-mini",
19 | "temperature": 0.7,
20 | "topP": 0.5,
21 | "user": "joedoe"
22 | }'
23 |
--------------------------------------------------------------------------------
/src/frontend/.lintstagedrc:
--------------------------------------------------------------------------------
1 | {
2 | "*.{js,jsx,ts,tsx}": ["eslint --fix", "lit-analyzer"],
3 | "*": ["prettier --ignore-unknown --write"]
4 | }
5 |
--------------------------------------------------------------------------------
/src/frontend/README.md:
--------------------------------------------------------------------------------
1 | # Chat frontend
2 |
3 | This project uses [Vite](https://vitejs.dev/) as a frontend build tool, and [Lit](https://lit.dev/) as a web components library.
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `npm run dev`
10 |
11 | To start the app in dev mode.\
12 | Open [http://localhost:8000](http://localhost:8000) to view it in the browser.
13 |
14 | ### `npm run build`
15 |
16 | To build the app for production to the `dist` folder.
17 |
--------------------------------------------------------------------------------
/src/frontend/assets/lightbulb.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/frontend/assets/new-chat.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/frontend/assets/question.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/frontend/assets/send.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/frontend/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | ChatGPT with Enterprise Data
9 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/src/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "version": "1.0.0",
4 | "description": "Frontend for the ChatGPT RAG workshop",
5 | "private": true,
6 | "type": "module",
7 | "scripts": {
8 | "dev": "vite --port 8000 --host",
9 | "build": "vite build",
10 | "watch": "vite build --watch --minify false",
11 | "lint": "lit-analyzer",
12 | "clean": "npx rimraf dist"
13 | },
14 | "author": "Microsoft",
15 | "license": "MIT",
16 | "dependencies": {
17 | "lit": "^3.0.0"
18 | },
19 | "devDependencies": {
20 | "lit-analyzer": "^2.0.1",
21 | "typescript": "^5.2.2",
22 | "vite": "^5.0.12"
23 | },
24 | "files": ["dist"]
25 | }
26 |
--------------------------------------------------------------------------------
/src/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/azure-openai-rag-workshop-java/eaea0c88e138fdc2abea473b19d624f47e905303/src/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/src/frontend/src/api.ts:
--------------------------------------------------------------------------------
1 | import { type ChatResponse, type ChatRequestOptions, type ChatResponseChunk } from './models.js';
2 |
3 | export const apiBaseUrl = import.meta.env.VITE_BACKEND_API_URI || '';
4 |
5 | export async function getCompletion(options: ChatRequestOptions) {
6 | const apiUrl = options.apiUrl || apiBaseUrl;
7 | const response = await fetch(`${apiUrl}/chat`, {
8 | method: 'POST',
9 | headers: { 'Content-Type': 'application/json' },
10 | body: JSON.stringify({
11 | messages: options.messages,
12 | stream: options.stream,
13 | context: {
14 | top: options.top,
15 | temperature: options.temperature,
16 | },
17 | }),
18 | });
19 |
20 | if (options.stream) {
21 | return getChunksFromResponse(response as Response, options.chunkIntervalMs);
22 | }
23 |
24 | const json: ChatResponse = await response.json();
25 | if (response.status > 299 || !response.ok) {
26 | throw new Error(json.error || 'Unknown error');
27 | }
28 |
29 | return json;
30 | }
31 |
32 | export function getCitationUrl(citation: string): string {
33 | return `${apiBaseUrl}/content/${citation}`;
34 | }
35 |
36 | export class NdJsonParserStream extends TransformStream {
37 | private buffer: string = '';
38 | constructor() {
39 | let controller: TransformStreamDefaultController;
40 | super({
41 | start: (_controller) => {
42 | controller = _controller;
43 | },
44 | transform: (chunk) => {
45 | const jsonChunks = chunk.split('\n').filter(Boolean);
46 | for (const jsonChunk of jsonChunks) {
47 | try {
48 | this.buffer += jsonChunk;
49 | controller.enqueue(JSON.parse(this.buffer));
50 | this.buffer = '';
51 | } catch {
52 | // Invalid JSON, wait for next chunk
53 | }
54 | }
55 | },
56 | });
57 | }
58 | }
59 |
60 | export async function* getChunksFromResponse(response: Response, intervalMs: number): AsyncGenerator {
61 | const reader = response.body?.pipeThrough(new TextDecoderStream()).pipeThrough(new NdJsonParserStream()).getReader();
62 | if (!reader) {
63 | throw new Error('No response body or body is not readable');
64 | }
65 |
66 | let value: JSON | undefined;
67 | let done: boolean;
68 | while ((({ value, done } = await reader.read()), !done)) {
69 | yield new Promise((resolve) => {
70 | setTimeout(() => {
71 | resolve(value as T);
72 | }, intervalMs);
73 | });
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/frontend/src/components/debug.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable unicorn/template-indent */
2 | import { LitElement, css, html } from 'lit';
3 | import { map } from 'lit/directives/map.js';
4 | import { customElement, property } from 'lit/decorators.js';
5 | import { unsafeHTML } from 'lit/directives/unsafe-html.js';
6 | import { type ChatDebugDetails } from '../models.js';
7 |
8 | export type DebugComponentOptions = {
9 | strings: {
10 | thoughtsTitle: string;
11 | supportingContentTitle: string;
12 | };
13 | };
14 |
15 | @customElement('azc-debug')
16 | export class DebugComponent extends LitElement {
17 | @property({ type: Object }) details: ChatDebugDetails = { thoughts: '', dataPoints: [] };
18 | @property({ type: Object }) options!: DebugComponentOptions;
19 | @property({ type: Boolean }) showThoughtProcess = true;
20 |
21 | protected renderThoughtProcess = (thoughtProcess: string) => {
22 | return html`${unsafeHTML(thoughtProcess)}`;
23 | };
24 |
25 | protected renderDataPoints = (dataPoints: string[]) => {
26 | const infos = dataPoints.map((dataPoint) => {
27 | const [title, ...extract] = dataPoint.split(':');
28 | return { title, extract: extract.join(':') };
29 | });
30 | return html`