├── .gitignore ├── LICENSE ├── LICENSES └── Apache-2.0.txt ├── README.md ├── REUSE.toml ├── docs ├── anonymization-usage.md ├── api-documentation.md └── images │ ├── Architecture.png │ └── roadmap.png └── samples ├── harmonized-rag-app ├── .cdsrc.json ├── .gitignore ├── README.md ├── app │ ├── chat-ui │ │ ├── README.md │ │ ├── annotations.cds │ │ ├── package.json │ │ ├── ui5-deploy.yaml │ │ ├── ui5.yaml │ │ ├── webapp │ │ │ ├── Component.js │ │ │ ├── controller │ │ │ │ ├── App.controller.js │ │ │ │ ├── InitialRightScreen.controller.js │ │ │ │ ├── LeftScreen.controller.js │ │ │ │ └── OfficalRightScreen.controller.js │ │ │ ├── css │ │ │ │ └── style.css │ │ │ ├── i18n │ │ │ │ └── i18n.properties │ │ │ ├── index.html │ │ │ ├── manifest.json │ │ │ ├── model │ │ │ │ └── models.js │ │ │ ├── resources │ │ │ │ └── images │ │ │ │ │ └── SAP_AI_Core_Service_Icon.svg │ │ │ ├── test │ │ │ │ ├── flpSandbox.html │ │ │ │ └── locate-reuse-libs.js │ │ │ └── view │ │ │ │ ├── App.view.xml │ │ │ │ ├── FileManagement.fragment.xml │ │ │ │ ├── FileUploading.fragment.xml │ │ │ │ ├── InitialRightScreen.view.xml │ │ │ │ ├── LeftScreen.view.xml │ │ │ │ └── OfficalRightScreen.view.xml │ │ └── xs-app.json │ ├── package.json │ ├── services.cds │ └── xs-app.json ├── db │ ├── last-dev │ │ └── csn.json │ ├── schema.cds │ ├── src │ │ └── .hdiconfig │ └── undeploy.json ├── docs │ └── images │ │ ├── BTP-login.png │ │ ├── generate-embeddings.png │ │ ├── hybrid-testing-ui.png │ │ ├── qna.png │ │ ├── rag-arch.png │ │ └── upload-document.png ├── mta.yaml ├── package.json ├── srv │ ├── chat-service.cds │ ├── chat-service.js │ ├── embedding-storage.cds │ ├── embedding-storage.js │ ├── memory-helper.js │ └── server.js └── xs-security.json ├── hr-approval-rag-usecase ├── .cdsrc.json ├── .eslintrc ├── .gitignore ├── README.md ├── app │ ├── hr-approval-ui │ │ ├── README.md │ │ ├── annotations.cds │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── ui5-deploy.yaml │ │ ├── ui5.yaml │ │ ├── webapp │ │ │ ├── Component.js │ │ │ ├── controller │ │ │ │ ├── App.controller.js │ │ │ │ ├── InitialRightScreen.controller.js │ │ │ │ ├── LeftScreen.controller.js │ │ │ │ └── OfficalRightScreen.controller.js │ │ │ ├── css │ │ │ │ └── style.css │ │ │ ├── i18n │ │ │ │ └── i18n.properties │ │ │ ├── index.html │ │ │ ├── manifest.json │ │ │ ├── model │ │ │ │ └── models.js │ │ │ ├── resources │ │ │ │ └── images │ │ │ │ │ └── SAP_AI_Core_Service_Icon.svg │ │ │ ├── test │ │ │ │ ├── flpSandbox.html │ │ │ │ └── locate-reuse-libs.js │ │ │ └── view │ │ │ │ ├── App.view.xml │ │ │ │ ├── FileManagement.fragment.xml │ │ │ │ ├── FileUploading.fragment.xml │ │ │ │ ├── InitialRightScreen.view.xml │ │ │ │ ├── LeftScreen.view.xml │ │ │ │ └── OfficalRightScreen.view.xml │ │ └── xs-app.json │ ├── package-lock.json │ ├── package.json │ ├── services.cds │ └── xs-app.json ├── db │ ├── schema.cds │ ├── src │ │ └── .hdiconfig │ └── undeploy.json ├── docs │ └── images │ │ └── hr-policy-chatbot.png ├── mta.yaml ├── package-lock.json ├── package.json ├── srv │ ├── chat-service.cds │ ├── chat-service.js │ ├── embedding-storage.cds │ ├── embedding-storage.js │ ├── memory-helper.js │ ├── server.js │ └── sf-connection-util.js └── xs-security.json ├── personalized-email-usecase ├── .cdsrc.json ├── .eslintrc ├── .gitignore ├── README.md ├── app │ ├── emp-gen-anz-ui │ │ ├── README.md │ │ ├── annotations.cds │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── ui5-deploy.yaml │ │ ├── ui5.yaml │ │ ├── webapp │ │ │ ├── Component.js │ │ │ ├── i18n │ │ │ │ └── i18n.properties │ │ │ ├── index.html │ │ │ ├── localService │ │ │ │ └── metadata.xml │ │ │ ├── manifest.json │ │ │ └── test │ │ │ │ ├── flpSandbox.html │ │ │ │ ├── integration │ │ │ │ ├── FirstJourney.js │ │ │ │ ├── opaTests.qunit.html │ │ │ │ ├── opaTests.qunit.js │ │ │ │ └── pages │ │ │ │ │ ├── EmployeeList.js │ │ │ │ │ └── EmployeeObjectPage.js │ │ │ │ ├── testsuite.qunit.html │ │ │ │ └── testsuite.qunit.js │ │ └── xs-app.json │ └── services.cds ├── db │ ├── data │ │ └── sap.cap-Employee.csv │ ├── schema.cds │ ├── src │ │ ├── .hdiconfig │ │ ├── .hdinamespace │ │ └── defaults │ │ │ └── default_access_role.hdbrole │ └── undeploy.json ├── images │ └── personalized-email-solution-diagram.png ├── manifest.yml ├── mta.yaml ├── package-lock.json ├── package.json ├── resources │ └── empgenanzui.zip ├── services-manifest.yml ├── srv │ ├── ai-core-service.js │ ├── employee-service.cds │ └── employee-service.js └── xs-security.json └── rag-quickstart-app ├── .cdsrc.json ├── .gitignore ├── README.md ├── app ├── hr-approval-ui │ ├── README.md │ ├── annotations.cds │ ├── package.json │ ├── ui5-deploy.yaml │ ├── ui5.yaml │ ├── webapp │ │ ├── Component.js │ │ ├── controller │ │ │ ├── App.controller.js │ │ │ ├── InitialRightScreen.controller.js │ │ │ ├── LeftScreen.controller.js │ │ │ └── OfficalRightScreen.controller.js │ │ ├── css │ │ │ └── style.css │ │ ├── i18n │ │ │ └── i18n.properties │ │ ├── index.html │ │ ├── manifest.json │ │ ├── model │ │ │ └── models.js │ │ ├── resources │ │ │ └── images │ │ │ │ └── SAP_AI_Core_Service_Icon.svg │ │ ├── test │ │ │ ├── flpSandbox.html │ │ │ └── locate-reuse-libs.js │ │ └── view │ │ │ ├── App.view.xml │ │ │ ├── FileManagement.fragment.xml │ │ │ ├── FileUploading.fragment.xml │ │ │ ├── InitialRightScreen.view.xml │ │ │ ├── LeftScreen.view.xml │ │ │ └── OfficalRightScreen.view.xml │ └── xs-app.json ├── package.json ├── services.cds └── xs-app.json ├── db ├── schema.cds ├── src │ └── .hdiconfig └── undeploy.json ├── docs └── images │ ├── BTP-login.png │ ├── generate-embeddings.png │ ├── hybrid-testing-ui.png │ ├── qna.png │ ├── rag-arch.png │ └── upload-document.png ├── mta.yaml ├── package.json ├── srv ├── chat-service.cds ├── chat-service.js ├── embedding-storage.cds ├── embedding-storage.js ├── memory-helper.js └── server.js └── xs-security.json /.gitignore: -------------------------------------------------------------------------------- 1 | **/@types 2 | edmx/ 3 | default-env.json 4 | packages/messageBox 5 | reviews/msg-box 6 | reviews/db/test.db 7 | 8 | # added by cds 9 | .cdsrc-private.json 10 | _out 11 | *.db 12 | *.sqlite 13 | connection.properties 14 | default-*.json 15 | .cdsrc-private.json 16 | gen/ 17 | node_modules/ 18 | target/ 19 | build/ 20 | 21 | # Web IDE, App Studio 22 | .che/ 23 | .gen/ 24 | package-lock.json 25 | 26 | # MTA 27 | *_mta_build_tmp 28 | *.mtar 29 | mta_archives/ 30 | dist/ 31 | *.zip 32 | *.pdf 33 | .env 34 | *.mta 35 | *.zip 36 | 37 | # Other 38 | .DS_Store 39 | *.orig 40 | *.log 41 | 42 | *.iml 43 | *.flattened-pom.xml 44 | 45 | # IDEs 46 | .vscode 47 | .eslintrc 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![REUSE status](https://api.reuse.software/badge/github.com/SAP-samples/cap-llm-plugin-samples)](https://api.reuse.software/info/github.com/SAP-samples/cap-llm-plugin-samples) 2 | 3 | 4 | # CAP LLM Plugin 5 | 6 | CAP LLM Plugin helps developers create tailored Generative AI based CAP applications: 7 | 8 | 1. Without exposing confidential data to LLM by anonymizing sensitive data leveraging SAP HANA Cloud Data Anonymization. 9 | 2. Seamlessly generate vector embeddings via SAP AI Core. 10 | 3. Easily retrieve Chat Completion response via SAP AI Core. 11 | 4. Effortlessly perform similarity search via SAP HANA Cloud Vector engine. 12 | 5. Simplified single RAG (retrieval-augmented generation) retrieval method powered by SAP AI Core and SAP HANA Cloud Vector Engine. 13 | 6. Access the harmonized chat completion API of the SAP AI Core Orchestration service. 14 | 15 | ## Architecture 16 | 17 | ![architecture](./docs/images/Architecture.png) 18 | 19 | ## ✔️ Anonymization Features 20 | 21 | 22 | | **Feature** | **Details** 23 | | :-------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------ 24 | | Seamlessly anonymize sensitive data using a variety of SAP HANA Cloud's anonymization capabilities | Effortlessly anonymize sensitive data within a CAP application by employing a single `@anonymize` annotation using a diverse range of SAP HANA Cloud's anonymization algorithms, including but not limited to:
  • [k-Anonymity](https://help.sap.com/docs/SAP_HANA_PLATFORM/f88e51df089949b2af06ac891c77abf8/205f52e73c4a422e91fb9a0fbd5f3ec6.html)
  • [l-Diversity](https://help.sap.com/docs/SAP_HANA_PLATFORM/f88e51df089949b2af06ac891c77abf8/eeb681e53a06434ca8a0fd20ab9c2b7c.html)
  • [Differential Privacy](https://help.sap.com/docs/SAP_HANA_PLATFORM/f88e51df089949b2af06ac891c77abf8/ace3f36bad754cc9bbfe2bf473fccf2f.html)
  • | 25 | | Effortlessly replace the anonymized data within the LLM response with genuine information| Given that the data provided to the LLM consists of anonymized information, the CAP LLM plugin ensures a seamless replacement of anonymized content within the LLM response with the corresponding authentic data. 26 | 27 | ## 🎯 LLM Access Layer Features 28 | 29 | | **Feature** | **Details** 30 | | :-------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------ 31 | | Embedding generation via SAP AI Core | Easily connect to embedding models via SAP AI Core and generate embeddings seamlessly | 32 | | Similarity search | Leverage the SAP HANA Cloud's Vector engine to perform similarity search via CAP LLM Plugin | 33 | | Chat LLM Access via SAP AI Core | Simple access to LLM models via SAP AI Core with simplified method for chat completion | 34 | | Streamlining RAG retrieval | Single method to streamline the entire RAG retrieval process leveraging SAP AI Core and SAP HANA Cloud Vector Engine | 35 | | :new: Orchestration Service Support | Support for SAP AI Core orchestration service's harmonized chat completion APIs 36 | 37 | 38 | ## 📚 Samples and documentation 39 | 40 | For API documentation for the CAP LLM plugin, refer the [api-documentation](./docs/api-documentation.md). 41 | 42 | For how to use Anonymization with CAP LLM plugin, refer the [anonymization-usage](./docs/anonymization-usage.md). 43 | 44 | For sample use cases leveraging CAP LLM Plugin, refer to [Samples](./samples/). 45 | 46 | 47 | ## How to obtain support 48 | [Create an issue](https://github.com/SAP-samples/cap-llm-plugin-samples/issues) in this repository if you find a bug from the plugin. 49 | 50 | For other support, [ask a question in SAP Community](https://answers.sap.com/questions/ask.html). 51 | 52 | ## License 53 | Copyright (c) 2024 SAP SE or an SAP affiliate company. All rights reserved. This project is licensed under the Apache Software License, version 2.0 except as noted otherwise in the [LICENSE](LICENSE) file. 54 | -------------------------------------------------------------------------------- /REUSE.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | SPDX-PackageName = "cap-llm-plugin-samples" 3 | SPDX-PackageSupplier = "akash.amarendra@sap.com, feng.liang@sap.com" 4 | SPDX-PackageDownloadLocation = "https://github.com/SAP-samples/cap-llm-plugin-samples" 5 | SPDX-PackageComment = "The code in this project may include calls to APIs (\"API Calls\") of\n SAP or third-party products or services developed outside of this project\n (\"External Products\").\n \"APIs\" means application programming interfaces, as well as their respective\n specifications and implementing code that allows software to communicate with\n other software.\n API Calls to External Products are not licensed under the open source license\n that governs this project. The use of such API Calls and related External\n Products are subject to applicable additional agreements with the relevant\n provider of the External Products. In no event shall the open source license\n that governs this project grant any rights in or to any External Products,or\n alter, expand or supersede any terms of the applicable additional agreements.\n If you have a valid license agreement with SAP for the use of a particular SAP\n External Product, then you may make use of any API Calls included in this\n project's code for that SAP External Product, subject to the terms of such\n license agreement. If you do not have a valid license agreement for the use of\n a particular SAP External Product, then you may only make use of any API Calls\n in this project for that SAP External Product for your internal, non-productive\n and non-commercial test and evaluation of such API Calls. Nothing herein grants\n you any rights to use or access any SAP External Product, or provide any third\n parties the right to use of access any SAP External Product, through API Calls." 6 | 7 | [[annotations]] 8 | path = "**" 9 | precedence = "aggregate" 10 | SPDX-FileCopyrightText = "2024 SAP SE or an SAP affiliate company and cap-llm-plugin-samples contributors" 11 | SPDX-License-Identifier = "Apache-2.0" 12 | -------------------------------------------------------------------------------- /docs/images/Architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-samples/cap-llm-plugin-samples/0eb4b50433fdfcfc29071203374cce4f8653f6bb/docs/images/Architecture.png -------------------------------------------------------------------------------- /docs/images/roadmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-samples/cap-llm-plugin-samples/0eb4b50433fdfcfc29071203374cce4f8653f6bb/docs/images/roadmap.png -------------------------------------------------------------------------------- /samples/harmonized-rag-app/.cdsrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "cdsc": { 3 | "beta": { 4 | "vectorType": true 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /samples/harmonized-rag-app/.gitignore: -------------------------------------------------------------------------------- 1 | _out 2 | *.db 3 | *.sqlite 4 | connection.properties 5 | default-*.json 6 | .cdsrc-private.json 7 | gen/ 8 | node_modules/ 9 | target/ 10 | build/ 11 | 12 | # Web IDE, App Studio 13 | .che/ 14 | .gen/ 15 | 16 | # MTA 17 | *_mta_build_tmp 18 | *.mtar 19 | mta_archives/ 20 | dist/ 21 | *.zip 22 | .env 23 | *.mta 24 | *.zip 25 | 26 | # Other 27 | .DS_Store 28 | *.orig 29 | *.log 30 | 31 | *.iml 32 | *.flattened-pom.xml 33 | 34 | # IDEs 35 | .vscode 36 | # .idea -------------------------------------------------------------------------------- /samples/harmonized-rag-app/app/chat-ui/README.md: -------------------------------------------------------------------------------- 1 | ## Application Details 2 | | | 3 | | ------------- | 4 | |**Generation Date and Time**
    Wed Feb 28 2024 21:51:37 GMT+0000 (Coordinated Universal Time)| 5 | |**App Generator**
    @sap/generator-fiori-freestyle| 6 | |**App Generator Version**
    1.12.4| 7 | |**Generation Platform**
    SAP Business Application Studio| 8 | |**Template Used**
    simple| 9 | |**Service Type**
    Local Cap| 10 | |**Service URL**
    http://localhost:4004/odata/v4/chat/ 11 | |**Module Name**
    chat-ui| 12 | |**Application Title**
    This is App Title| 13 | |**Namespace**
    | 14 | |**UI5 Theme**
    sap_horizon| 15 | |**UI5 Version**
    1.120.9| 16 | |**Enable Code Assist Libraries**
    False| 17 | |**Enable TypeScript**
    False| 18 | |**Add Eslint configuration**
    False| 19 | 20 | ## chat-ui 21 | 22 | An SAP Fiori application. 23 | 24 | ### Starting the generated app 25 | 26 | - This app has been generated using the SAP Fiori tools - App Generator, as part of the SAP Fiori tools suite. In order to launch the generated app, simply start your CAP project and navigate to the following location in your browser: 27 | 28 | http://localhost:4004/chat-ui/webapp/index.html 29 | 30 | #### Pre-requisites: 31 | 32 | 1. Active NodeJS LTS (Long Term Support) version and associated supported NPM version. (See https://nodejs.org) 33 | 34 | 35 | -------------------------------------------------------------------------------- /samples/harmonized-rag-app/app/chat-ui/annotations.cds: -------------------------------------------------------------------------------- 1 | using ChatService as service from '../../srv/chat-service'; -------------------------------------------------------------------------------- /samples/harmonized-rag-app/app/chat-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chat-ui", 3 | "version": "0.0.1", 4 | "description": "An SAP Fiori application.", 5 | "keywords": [ 6 | "ui5", 7 | "openui5", 8 | "sapui5" 9 | ], 10 | "main": "webapp/index.html", 11 | "scripts": { 12 | "deploy-config": "npx -p @sap/ux-ui5-tooling fiori add deploy-config cf", 13 | "build:cf": "ui5 build preload --clean-dest --config ui5-deploy.yaml --include-task=generateCachebusterInfo" 14 | }, 15 | "devDependencies": { 16 | "@sap/ui5-builder-webide-extension": "^1.1.8", 17 | "ui5-task-zipper": "^0.5.0", 18 | "mbt": "^1.2.18", 19 | "@ui5/cli": "^2.14.10" 20 | }, 21 | "ui5": { 22 | "dependencies": [ 23 | "@sap/ui5-builder-webide-extension", 24 | "ui5-task-zipper", 25 | "mbt" 26 | ] 27 | } 28 | } -------------------------------------------------------------------------------- /samples/harmonized-rag-app/app/chat-ui/ui5-deploy.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json 2 | specVersion: '2.4' 3 | metadata: 4 | name: harmonizedragappui 5 | type: application 6 | resources: 7 | configuration: 8 | propertiesFileSourceEncoding: UTF-8 9 | builder: 10 | resources: 11 | excludes: 12 | - "/test/**" 13 | - "/localService/**" 14 | customTasks: 15 | - name: webide-extension-task-updateManifestJson 16 | afterTask: replaceVersion 17 | configuration: 18 | appFolder: webapp 19 | destDir: dist 20 | - name: ui5-task-zipper 21 | afterTask: generateCachebusterInfo 22 | configuration: 23 | archiveName: harmonizedragappui 24 | additionalFiles: 25 | - xs-app.json 26 | -------------------------------------------------------------------------------- /samples/harmonized-rag-app/app/chat-ui/ui5.yaml: -------------------------------------------------------------------------------- 1 | specVersion: "2.5" 2 | metadata: 3 | name: harmonizedragappui 4 | type: application 5 | framework: 6 | name: OpenUI5 7 | version: "1.120.7" 8 | libraries: 9 | - name: sap.m 10 | - name: sap.ui.core 11 | - name: themelib_sap_horizon 12 | - name: sap.f 13 | - name: sap.ui.layout 14 | - name: sap.ui.unified 15 | -------------------------------------------------------------------------------- /samples/harmonized-rag-app/app/chat-ui/webapp/Component.js: -------------------------------------------------------------------------------- 1 | /** 2 | * eslint-disable @sap/ui5-jsdocs/no-jsdoc 3 | */ 4 | 5 | sap.ui.define([ 6 | "sap/ui/core/UIComponent", 7 | "sap/ui/Device", 8 | "harmonizedragappui/model/models" 9 | ], 10 | function (UIComponent, Device, models) { 11 | "use strict"; 12 | 13 | return UIComponent.extend("harmonizedragappui.Component", { 14 | metadata: { 15 | manifest: "json" 16 | }, 17 | 18 | /** 19 | * The component is initialized by UI5 automatically during the startup of the app and calls the init method once. 20 | * @public 21 | * @override 22 | */ 23 | init: function () { 24 | // call the base component's init function 25 | UIComponent.prototype.init.apply(this, arguments); 26 | 27 | // enable routing 28 | this.getRouter().initialize(); 29 | 30 | // set the device model 31 | this.setModel(models.createDeviceModel(), "device"); 32 | } 33 | }); 34 | } 35 | ); -------------------------------------------------------------------------------- /samples/harmonized-rag-app/app/chat-ui/webapp/controller/App.controller.js: -------------------------------------------------------------------------------- 1 | sap.ui.define( 2 | [ 3 | "sap/ui/core/mvc/Controller" 4 | ], 5 | function(BaseController) { 6 | "use strict"; 7 | 8 | return BaseController.extend("harmonizedragappui.controller.App", { 9 | onInit: function() { 10 | sessionStorage.setItem("isDeployedVersion", "false"); 11 | } 12 | }); 13 | } 14 | ); 15 | -------------------------------------------------------------------------------- /samples/harmonized-rag-app/app/chat-ui/webapp/css/style.css: -------------------------------------------------------------------------------- 1 | /* Enter your custom styles here */ 2 | .sapFAvatar.sapFAvatarInitials > .sapFAvatarInitialsHolder { 3 | font-size: large; 4 | } 5 | 6 | .sapMScrollCont { 7 | padding-bottom: 0.75rem; 8 | box-sizing: border-box; 9 | -webkit-box-flex: 1; 10 | flex: 1; 11 | position: relative; 12 | -webkit-flex: 1; 13 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 14 | } 15 | 16 | .sapUiSizeCompact .sapTntNavLI { 17 | padding: 1.5rem 0.75rem 0.75rem 0.75rem; 18 | box-sizing: border-box; 19 | position: relative; 20 | overflow: hidden; 21 | width: 100%; 22 | } 23 | 24 | .sapUiSizeCompact .sapMPageWithFooter.sapMPageWithFloatingFooter > section { 25 | bottom: 0; 26 | scroll-padding-bottom: calc(2.5rem + 0.5rem + 2px); 27 | padding-bottom: 4.5rem; 28 | padding-top: 1rem; 29 | } 30 | -------------------------------------------------------------------------------- /samples/harmonized-rag-app/app/chat-ui/webapp/i18n/i18n.properties: -------------------------------------------------------------------------------- 1 | # This is the resource bundle for harmonizedragappui 2 | 3 | #Texts for manifest.json 4 | 5 | #XTIT: Application name 6 | appTitle=Chat Demo of CAP LLM Plugin 7 | 8 | #YDES: Application description 9 | appDescription=An SAP Fiori application. 10 | #XTIT: Main view title 11 | title=Chat Demo of CAP LLM Plugin 12 | 13 | flpTitle=HR Approval Demo 14 | 15 | flpSubtitle=Powered by CAP LLM Plugin and SuccessFactor 16 | -------------------------------------------------------------------------------- /samples/harmonized-rag-app/app/chat-ui/webapp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | This is App Title 8 | 13 | 25 | 26 | 27 |
    34 | 35 | -------------------------------------------------------------------------------- /samples/harmonized-rag-app/app/chat-ui/webapp/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "_version": "1.59.0", 3 | "sap.app": { 4 | "id": "harmonizedragappui", 5 | "type": "application", 6 | "i18n": "i18n/i18n.properties", 7 | "applicationVersion": { 8 | "version": "0.0.1" 9 | }, 10 | "title": "{{appTitle}}", 11 | "description": "{{appDescription}}", 12 | "resources": "resources.json", 13 | "sourceTemplate": { 14 | "id": "@sap/generator-fiori:basic", 15 | "version": "1.12.4", 16 | "toolsId": "2f10dee8-63c3-46f0-a270-d704153ff59a" 17 | }, 18 | "dataSources": { 19 | "mainService": { 20 | "uri": "/odata/v4/chat/", 21 | "type": "OData", 22 | "settings": { 23 | "annotations": [], 24 | "odataVersion": "4.0" 25 | } 26 | }, 27 | "fileService":{ 28 | "uri": "/odata/v4/embedding-storage/", 29 | "type": "OData", 30 | "settings": { 31 | "annotations": [], 32 | "odataVersion": "4.0" 33 | } 34 | } 35 | }, 36 | "crossNavigation": { 37 | "inbounds": { 38 | "Chat-Display": { 39 | "semanticObject": "Chat", 40 | "action": "Display", 41 | "title": "{{flpTitle}}", 42 | "subTitle": "{{flpSubtitle}}", 43 | "signature": { 44 | "parameters": {}, 45 | "additionalParameters": "allowed" 46 | } 47 | } 48 | } 49 | } 50 | }, 51 | "sap.ui": { 52 | "technology": "UI5", 53 | "icons": { 54 | "icon": "", 55 | "favIcon": "", 56 | "phone": "", 57 | "phone@2": "", 58 | "tablet": "", 59 | "tablet@2": "" 60 | }, 61 | "deviceTypes": { 62 | "desktop": true, 63 | "tablet": true, 64 | "phone": true 65 | } 66 | }, 67 | "sap.ui5": { 68 | "flexEnabled": true, 69 | "dependencies": { 70 | "minUI5Version": "1.120.9", 71 | "libs": { 72 | "sap.m": {}, 73 | "sap.ui.core": {}, 74 | "sap.f": {}, 75 | "sap.suite.ui.generic.template": {}, 76 | "sap.ui.comp": {}, 77 | "sap.ui.generic.app": {}, 78 | "sap.ui.table": {}, 79 | "sap.ushell": {}, 80 | "sap.ui.unified": {} 81 | } 82 | }, 83 | "contentDensities": { 84 | "compact": true, 85 | "cozy": true 86 | }, 87 | "models": { 88 | "i18n": { 89 | "type": "sap.ui.model.resource.ResourceModel", 90 | "settings": { 91 | "bundleName": "harmonizedragappui.i18n.i18n" 92 | } 93 | }, 94 | "": { 95 | "dataSource": "mainService", 96 | "preload": true, 97 | "settings": { 98 | "synchronizationMode": "None", 99 | "operationMode": "Server", 100 | "autoExpandSelect": true, 101 | "earlyRequests": true 102 | } 103 | }, 104 | "files":{ 105 | "dataSource": "fileService", 106 | "preload": true, 107 | "settings": { 108 | "synchronizationMode": "None", 109 | "operationMode": "Server", 110 | "autoExpandSelect": true, 111 | "earlyRequests": true 112 | } 113 | } 114 | }, 115 | "resources": { 116 | "css": [ 117 | { 118 | "uri": "css/style.css" 119 | } 120 | ] 121 | }, 122 | "rootView": { 123 | "viewName": "harmonizedragappui.view.App", 124 | "type": "XML", 125 | "async": true, 126 | "id": "App" 127 | }, 128 | "routing": { 129 | "config": { 130 | "routerClass": "sap.f.routing.Router", 131 | "viewType": "XML", 132 | "async": true, 133 | "viewPath": "harmonizedragappui.view", 134 | "controlId": "flexibleColumnLayout", 135 | "transition": "slide" 136 | }, 137 | "routes": [ 138 | { 139 | "pattern": "", 140 | "name": "home", 141 | "target":[ 142 | "leftScreen", 143 | "initialRightScreen" 144 | ], 145 | "layout": "TwoColumnsMidExpanded" 146 | }, 147 | { 148 | "pattern": "conversation/{conversationID}", 149 | "name": "conversation", 150 | "target":[ 151 | "leftScreen", 152 | "officalRightScreen" 153 | ], 154 | "layout": "TwoColumnsMidExpanded" 155 | } 156 | ], 157 | "targets": { 158 | "leftScreen":{ 159 | "viewId": "lefeScreenPage", 160 | "viewName": "LeftScreen", 161 | "controlAggregation": "beginColumnPages" 162 | }, 163 | "initialRightScreen":{ 164 | "viewId": "initialRightScreen", 165 | "viewName": "InitialRightScreen", 166 | "controlAggregation": "midColumnPages" 167 | }, 168 | "officalRightScreen":{ 169 | "viewId": "officalRightScreen", 170 | "viewName": "OfficalRightScreen", 171 | "controlAggregation": "midColumnPages" 172 | } 173 | } 174 | } 175 | }, 176 | "sap.cloud": { 177 | "public": true, 178 | "service": "harmonizedragapp.app" 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /samples/harmonized-rag-app/app/chat-ui/webapp/model/models.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/model/json/JSONModel", 3 | "sap/ui/Device" 4 | ], 5 | /** 6 | * provide app-view type models (as in the first "V" in MVVC) 7 | * 8 | * @param {typeof sap.ui.model.json.JSONModel} JSONModel 9 | * @param {typeof sap.ui.Device} Device 10 | * 11 | * @returns {Function} createDeviceModel() for providing runtime info for the device the UI5 app is running on 12 | */ 13 | function (JSONModel, Device) { 14 | "use strict"; 15 | 16 | return { 17 | createDeviceModel: function () { 18 | var oModel = new JSONModel(Device); 19 | oModel.setDefaultBindingMode("OneWay"); 20 | return oModel; 21 | } 22 | }; 23 | }); -------------------------------------------------------------------------------- /samples/harmonized-rag-app/app/chat-ui/webapp/test/flpSandbox.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{appTitle}} 9 | 10 | 25 | 56 | 57 | 58 | 59 | 70 | 71 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /samples/harmonized-rag-app/app/chat-ui/webapp/view/App.view.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /samples/harmonized-rag-app/app/chat-ui/webapp/view/FileUploading.fragment.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | 31 | 32 | 41 | 46 | 51 | 56 | 57 | 58 | 59 | 60 | 61 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /samples/harmonized-rag-app/app/chat-ui/webapp/view/InitialRightScreen.view.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 16 | 17 | 18 | 25 | 33 | 34 | 35 | 36 | 37 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /samples/harmonized-rag-app/app/chat-ui/webapp/view/LeftScreen.view.xml: -------------------------------------------------------------------------------- 1 | 9 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 25 | 26 | 32 | 33 | 34 | 35 | 40 | 58 | 64 | 65 | 66 | 67 | 69 | 70 | 75 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /samples/harmonized-rag-app/app/chat-ui/webapp/view/OfficalRightScreen.view.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 16 | 17 | 19 | 31 | 41 | 42 | 43 | 44 | 45 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /samples/harmonized-rag-app/app/chat-ui/xs-app.json: -------------------------------------------------------------------------------- 1 | { 2 | "welcomeFile": "/index.html", 3 | "authenticationMethod": "route", 4 | "routes": [ 5 | { 6 | "source": "^/odata/v4/chat/(.*)$", 7 | "target": "/odata/v4/chat/$1", 8 | "destination": "harmonized-rag-app-srv", 9 | "authenticationType": "xsuaa", 10 | "csrfProtection": false, 11 | "cacheControl": "no-cache, no-store, must-revalidate" 12 | }, 13 | { 14 | "source": "^/odata/v4/embedding-storage/(.*)$", 15 | "target": "/odata/v4/embedding-storage/$1", 16 | "destination": "harmonized-rag-app-srv", 17 | "authenticationType": "xsuaa", 18 | "csrfProtection": false, 19 | "cacheControl": "no-cache, no-store, must-revalidate" 20 | }, 21 | { 22 | "source": "^/user-api/currentUser$", 23 | "target": "/currentUser", 24 | "service": "sap-approuter-userapi" 25 | }, 26 | { 27 | "source": "^/resources/(.*)$", 28 | "target": "/resources/$1", 29 | "authenticationType": "none", 30 | "destination": "ui5" 31 | }, 32 | { 33 | "source": "^/test-resources/(.*)$", 34 | "target": "/test-resources/$1", 35 | "authenticationType": "none", 36 | "destination": "ui5" 37 | }, 38 | { 39 | "source": "^(.*)$", 40 | "target": "$1", 41 | "service": "html5-apps-repo-rt", 42 | "authenticationType": "xsuaa" 43 | } 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /samples/harmonized-rag-app/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "approuter", 3 | "dependencies": { 4 | "@sap/approuter": "^14.0.0" 5 | }, 6 | "engines": { 7 | "node": "^18.0.0" 8 | }, 9 | "scripts": { 10 | "start": "node node_modules/@sap/approuter/approuter.js" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /samples/harmonized-rag-app/app/services.cds: -------------------------------------------------------------------------------- 1 | 2 | using from './chat-ui/annotations'; -------------------------------------------------------------------------------- /samples/harmonized-rag-app/app/xs-app.json: -------------------------------------------------------------------------------- 1 | { 2 | "welcomeFile": "app/index.html", 3 | "routes": [ 4 | { 5 | "source": "^/app/(.*)$", 6 | "target": "$1", 7 | "localDir": ".", 8 | "cacheControl": "no-cache, no-store, must-revalidate" 9 | }, 10 | { 11 | "source": "^/(.*)$", 12 | "target": "$1", 13 | "destination": "srv-api", 14 | "csrfProtection": true 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /samples/harmonized-rag-app/db/last-dev/csn.json: -------------------------------------------------------------------------------- 1 | { 2 | "namespace": "sap.tisce.demo", 3 | "definitions": {}, 4 | "meta": { 5 | "creator": "CDS Compiler v4.9.8", 6 | "build": "CDS Build v7.9.5" 7 | }, 8 | "$version": "2.0" 9 | } -------------------------------------------------------------------------------- /samples/harmonized-rag-app/db/schema.cds: -------------------------------------------------------------------------------- 1 | namespace sap.tisce.demo; 2 | 3 | using { 4 | cuid, 5 | managed 6 | } from '@sap/cds/common'; 7 | 8 | entity Conversation { 9 | 10 | key cID : UUID not null; 11 | userID: String; 12 | creation_time: Timestamp; 13 | last_update_time: Timestamp; 14 | title: String; 15 | to_messages: Composition of many Message on to_messages.cID = $self; 16 | } 17 | 18 | entity Message { 19 | 20 | key cID: Association to Conversation; 21 | key mID: UUID not null; 22 | role: String; 23 | content: LargeString; 24 | creation_time: Timestamp; 25 | } 26 | 27 | entity DocumentChunk: cuid, managed{ 28 | 29 | text_chunk: LargeString; 30 | metadata_column: LargeString; 31 | embedding: Vector(1536); 32 | } 33 | 34 | 35 | entity Files: cuid, managed{ 36 | @Core.MediaType: mediaType @Core.ContentDisposition.Filename: fileName 37 | content: LargeBinary; 38 | @Core.IsMediaType: true 39 | mediaType: String; 40 | fileName: String; 41 | size: String; 42 | } 43 | 44 | -------------------------------------------------------------------------------- /samples/harmonized-rag-app/db/src/.hdiconfig: -------------------------------------------------------------------------------- 1 | { 2 | "file_suffixes": { 3 | "csv": { 4 | "plugin_name": "com.sap.hana.di.tabledata.source" 5 | }, 6 | "hdbafllangprocedure": { 7 | "plugin_name": "com.sap.hana.di.afllangprocedure" 8 | }, 9 | "hdbanalyticprivilege": { 10 | "plugin_name": "com.sap.hana.di.analyticprivilege" 11 | }, 12 | "hdbcalculationview": { 13 | "plugin_name": "com.sap.hana.di.calculationview" 14 | }, 15 | "hdbcollection": { 16 | "plugin_name": "com.sap.hana.di.collection" 17 | }, 18 | "hdbconstraint": { 19 | "plugin_name": "com.sap.hana.di.constraint" 20 | }, 21 | "hdbdropcreatetable": { 22 | "plugin_name": "com.sap.hana.di.dropcreatetable" 23 | }, 24 | "hdbflowgraph": { 25 | "plugin_name": "com.sap.hana.di.flowgraph" 26 | }, 27 | "hdbfunction": { 28 | "plugin_name": "com.sap.hana.di.function" 29 | }, 30 | "hdbgraphworkspace": { 31 | "plugin_name": "com.sap.hana.di.graphworkspace" 32 | }, 33 | "hdbhadoopmrjob": { 34 | "plugin_name": "com.sap.hana.di.virtualfunctionpackage.hadoop" 35 | }, 36 | "hdbindex": { 37 | "plugin_name": "com.sap.hana.di.index" 38 | }, 39 | "hdblibrary": { 40 | "plugin_name": "com.sap.hana.di.library" 41 | }, 42 | "hdbmigrationtable": { 43 | "plugin_name": "com.sap.hana.di.table.migration" 44 | }, 45 | "hdbprocedure": { 46 | "plugin_name": "com.sap.hana.di.procedure" 47 | }, 48 | "hdbprojectionview": { 49 | "plugin_name": "com.sap.hana.di.projectionview" 50 | }, 51 | "hdbprojectionviewconfig": { 52 | "plugin_name": "com.sap.hana.di.projectionview.config" 53 | }, 54 | "hdbreptask": { 55 | "plugin_name": "com.sap.hana.di.reptask" 56 | }, 57 | "hdbresultcache": { 58 | "plugin_name": "com.sap.hana.di.resultcache" 59 | }, 60 | "hdbrole": { 61 | "plugin_name": "com.sap.hana.di.role" 62 | }, 63 | "hdbroleconfig": { 64 | "plugin_name": "com.sap.hana.di.role.config" 65 | }, 66 | "hdbsearchruleset": { 67 | "plugin_name": "com.sap.hana.di.searchruleset" 68 | }, 69 | "hdbsequence": { 70 | "plugin_name": "com.sap.hana.di.sequence" 71 | }, 72 | "hdbstatistics": { 73 | "plugin_name": "com.sap.hana.di.statistics" 74 | }, 75 | "hdbstructuredprivilege": { 76 | "plugin_name": "com.sap.hana.di.structuredprivilege" 77 | }, 78 | "hdbsynonym": { 79 | "plugin_name": "com.sap.hana.di.synonym" 80 | }, 81 | "hdbsynonymconfig": { 82 | "plugin_name": "com.sap.hana.di.synonym.config" 83 | }, 84 | "hdbsystemversioning": { 85 | "plugin_name": "com.sap.hana.di.systemversioning" 86 | }, 87 | "hdbtable": { 88 | "plugin_name": "com.sap.hana.di.table" 89 | }, 90 | "hdbtabledata": { 91 | "plugin_name": "com.sap.hana.di.tabledata" 92 | }, 93 | "hdbtabletype": { 94 | "plugin_name": "com.sap.hana.di.tabletype" 95 | }, 96 | "hdbtrigger": { 97 | "plugin_name": "com.sap.hana.di.trigger" 98 | }, 99 | "hdbview": { 100 | "plugin_name": "com.sap.hana.di.view" 101 | }, 102 | "hdbvirtualfunction": { 103 | "plugin_name": "com.sap.hana.di.virtualfunction" 104 | }, 105 | "hdbvirtualfunctionconfig": { 106 | "plugin_name": "com.sap.hana.di.virtualfunction.config" 107 | }, 108 | "hdbvirtualpackagehadoop": { 109 | "plugin_name": "com.sap.hana.di.virtualpackage.hadoop" 110 | }, 111 | "hdbvirtualpackagesparksql": { 112 | "plugin_name": "com.sap.hana.di.virtualpackage.sparksql" 113 | }, 114 | "hdbvirtualprocedure": { 115 | "plugin_name": "com.sap.hana.di.virtualprocedure" 116 | }, 117 | "hdbvirtualprocedureconfig": { 118 | "plugin_name": "com.sap.hana.di.virtualprocedure.config" 119 | }, 120 | "hdbvirtualtable": { 121 | "plugin_name": "com.sap.hana.di.virtualtable" 122 | }, 123 | "hdbvirtualtableconfig": { 124 | "plugin_name": "com.sap.hana.di.virtualtable.config" 125 | }, 126 | "properties": { 127 | "plugin_name": "com.sap.hana.di.tabledata.properties" 128 | }, 129 | "tags": { 130 | "plugin_name": "com.sap.hana.di.tabledata.properties" 131 | }, 132 | "txt": { 133 | "plugin_name": "com.sap.hana.di.copyonly" 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /samples/harmonized-rag-app/db/undeploy.json: -------------------------------------------------------------------------------- 1 | [ 2 | "src/gen/**/*.hdbview", 3 | "src/gen/**/*.hdbindex", 4 | "src/gen/**/*.hdbconstraint", 5 | "src/gen/**/*_drafts.hdbtable" 6 | ] 7 | -------------------------------------------------------------------------------- /samples/harmonized-rag-app/docs/images/BTP-login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-samples/cap-llm-plugin-samples/0eb4b50433fdfcfc29071203374cce4f8653f6bb/samples/harmonized-rag-app/docs/images/BTP-login.png -------------------------------------------------------------------------------- /samples/harmonized-rag-app/docs/images/generate-embeddings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-samples/cap-llm-plugin-samples/0eb4b50433fdfcfc29071203374cce4f8653f6bb/samples/harmonized-rag-app/docs/images/generate-embeddings.png -------------------------------------------------------------------------------- /samples/harmonized-rag-app/docs/images/hybrid-testing-ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-samples/cap-llm-plugin-samples/0eb4b50433fdfcfc29071203374cce4f8653f6bb/samples/harmonized-rag-app/docs/images/hybrid-testing-ui.png -------------------------------------------------------------------------------- /samples/harmonized-rag-app/docs/images/qna.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-samples/cap-llm-plugin-samples/0eb4b50433fdfcfc29071203374cce4f8653f6bb/samples/harmonized-rag-app/docs/images/qna.png -------------------------------------------------------------------------------- /samples/harmonized-rag-app/docs/images/rag-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-samples/cap-llm-plugin-samples/0eb4b50433fdfcfc29071203374cce4f8653f6bb/samples/harmonized-rag-app/docs/images/rag-arch.png -------------------------------------------------------------------------------- /samples/harmonized-rag-app/docs/images/upload-document.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-samples/cap-llm-plugin-samples/0eb4b50433fdfcfc29071203374cce4f8653f6bb/samples/harmonized-rag-app/docs/images/upload-document.png -------------------------------------------------------------------------------- /samples/harmonized-rag-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "harmonized-rag-app", 3 | "version": "1.0.0", 4 | "description": "A simple CAP project.", 5 | "repository": "", 6 | "license": "UNLICENSED", 7 | "private": true, 8 | "dependencies": { 9 | "@sap-ai-sdk/orchestration": "*", 10 | "@sap-cloud-sdk/http-client": "^3.12.0", 11 | "@sap/cds": "^7.6.3", 12 | "@sap/cds-compiler": "^4.6.2", 13 | "@sap/cds-hana": "^2", 14 | "@sap/xssec": "^3", 15 | "basic-auth": "^2.0.1", 16 | "cap-llm-plugin": "1.5.0", 17 | "cds-swagger-ui-express": "^0.8.0", 18 | "cors": "^2.8.5", 19 | "dotenv": "^16.4.4", 20 | "downloadjs": "^1.4.7", 21 | "express": "^4", 22 | "langchain": "^0.1.19", 23 | "passport": "^0.7.0", 24 | "pdf-lib": "^1.17.1", 25 | "pdf-parse": "^1.1.1", 26 | "pdfkit": "^0.14.0", 27 | "save": "^2.9.0", 28 | "uuid": "^9.0.1" 29 | }, 30 | "devDependencies": { 31 | "@cap-js/sqlite": "^1", 32 | "@sap/cds-dk": "^8", 33 | "@sap/ux-specification": "^1.120.4", 34 | "axios": "^1.5.1", 35 | "chai": "^4.3.10", 36 | "chai-as-promised": "^7.1.1", 37 | "chai-http": "^4.4.0", 38 | "chai-subset": "^1.6.0", 39 | "hana-cli": "^3.202312.1", 40 | "jest": "^29.7.0", 41 | "mocha": "^10.3.0", 42 | "rimraf": "^3.0.2" 43 | }, 44 | "scripts": { 45 | "start": "cds-serve", 46 | "watch-chat-ui": "cds watch --open chat-ui/webapp/index.html?sap-ui-xx-viewCache=false", 47 | "undeploy": "cf undeploy harmonized-rag-app --delete-services --delete-service-keys --delete-service-brokers", 48 | "build": "rimraf resources mta_archives && mbt build --mtar archive", 49 | "deploy": "cf deploy mta_archives/archive.mtar --retries 1" 50 | }, 51 | "cds": { 52 | "build": { 53 | "target": "gen" 54 | }, 55 | "requires": { 56 | "db": "hana", 57 | "gen-ai-hub": { 58 | "text-embedding-ada-002": { 59 | "destinationName": "GenAIHubDestination", 60 | "deploymentUrl": "/v2/inference/deployments/", 61 | "resourceGroup": "default", 62 | "apiVersion": "2024-02-15-preview", 63 | "modelName": "text-embedding-ada-002" 64 | } 65 | }, 66 | "GenAIHubDestination": { 67 | "kind": "rest", 68 | "credentials": { 69 | "destination": "aicore-destination", 70 | "requestTimeout": "300000" 71 | } 72 | }, 73 | "[hybrid]": { 74 | "db": "hana", 75 | "cap-llm-plugin": { 76 | "impl": "cap-llm-plugin/srv/cap-llm-plugin.js" 77 | }, 78 | "destinations": true, 79 | "auth": { 80 | "passport": { 81 | "strategy": "mock", 82 | "users": { 83 | "JohnDole@tester.sap.com": { 84 | "ID": "dummy.user@com", 85 | "password": "initial" 86 | } 87 | } 88 | } 89 | } 90 | }, 91 | "[production]": { 92 | "auth": "xsuaa", 93 | "db": "hana", 94 | "cap-llm-plugin": { 95 | "impl": "cap-llm-plugin/srv/cap-llm-plugin.js" 96 | }, 97 | "destinations": true 98 | } 99 | }, 100 | "sapux": [ 101 | "app/chat-ui" 102 | ] 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /samples/harmonized-rag-app/srv/chat-service.cds: -------------------------------------------------------------------------------- 1 | using {sap.tisce.demo as db} from '../db/schema'; 2 | 3 | service ChatService @(requires: 'authenticated-user') { 4 | 5 | entity Conversation @(restrict: [{ 6 | grant: ['READ','WRITE', 'DELETE'], 7 | where: 'userID = $user' 8 | }]) as projection on db.Conversation; 9 | entity Message as projection on db.Message; 10 | 11 | type RagResponse_AdditionalContents { 12 | 13 | score : String; 14 | pageContent : String; 15 | } 16 | 17 | type RagResponse { 18 | role : String; 19 | content : String; 20 | messageTime : String; 21 | additionalContents : array of RagResponse_AdditionalContents; 22 | } 23 | 24 | action getChatRagResponse(conversationId : String, messageId : String, message_time : Timestamp, user_id : String, user_query : String) returns RagResponse; 25 | function deleteChatData() returns String; 26 | } 27 | -------------------------------------------------------------------------------- /samples/harmonized-rag-app/srv/embedding-storage.cds: -------------------------------------------------------------------------------- 1 | using {sap.tisce.demo as db} from '../db/schema'; 2 | 3 | service EmbeddingStorageService @(requires: 'authenticated-user') { 4 | 5 | entity DocumentChunk as 6 | projection on db.DocumentChunk 7 | excluding { 8 | embedding 9 | }; 10 | 11 | entity Files @(restrict: [{ 12 | grant: [ 13 | 'READ', 14 | 'WRITE', 15 | 'UPDATE', 16 | 'DELETE' 17 | ], 18 | where: 'createdBy = $user' 19 | }]) as projection on db.Files; 20 | 21 | action storeEmbeddings(uuid : String) returns String; 22 | function deleteEmbeddings() returns String; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /samples/harmonized-rag-app/srv/memory-helper.js: -------------------------------------------------------------------------------- 1 | /* Optional..Good to have feature to persist conversations in HANA Cloud */ 2 | 3 | const cds = require('@sap/cds'); 4 | const { INSERT, SELECT, UPDATE } = cds.ql; 5 | const { v4: uuidv4 } = require('uuid'); 6 | 7 | // Helper method to get the current timestamp 8 | function getCurrentTimestamp() { 9 | return new Date().toISOString(); 10 | } 11 | 12 | // Helper method to insert the messages and update the latest conversation timestamp in db 13 | async function insertMessage(messageEntity, messageRecord, conversationId, conversationEntity, messageTime) { 14 | 15 | console.log(`Inserting new message for conversation id: ${conversationId}`); 16 | const messageInsertionStatus = await INSERT.into(messageEntity).entries([messageRecord]); 17 | if (!messageInsertionStatus) { 18 | throw new Error("Insertion of message into db failed!"); 19 | }; 20 | 21 | console.log(`Updating the time for conversation id: ${conversationId}`); 22 | const updateConversationStatus = await UPDATE(conversationEntity).set`last_update_time = ${messageTime}`.where`cID = ${conversationId}`; 23 | if (updateConversationStatus !== 1) { 24 | throw new Error("Updating the conversation time failed!"); 25 | } 26 | } 27 | 28 | // Helper method to handle conversation memory in HANA CLoud before RAG LLM call. 29 | async function storeRetrieveMessages(conversationId, messageId, message_time, user_id, user_query, Conversation, Message, modelName) { 30 | try { 31 | 32 | let memoryContext = []; 33 | // Check if conversation exists in the db 34 | const isConversationPresent = await SELECT.from(Conversation).where({ "cID": conversationId }); 35 | // If conversation is present, select messages from db and store it in memory context obj 36 | 37 | if (isConversationPresent.length > 0) { 38 | console.log(`Retrieving messages for conversation id: ${conversationId}`); 39 | const messageSelectStmt = await SELECT.from(Message).where({ "cID_cID": conversationId }).orderBy('creation_time'); 40 | //parse the memory context for different models 41 | 42 | if (messageSelectStmt.length > 0) { 43 | //for gpt-4 or anthropic--claude-3-sonnet models 44 | if (modelName === "gpt-4" ) { 45 | messageSelectStmt.forEach(message => { 46 | memoryContext.push({ 47 | "role": message.role, 48 | "content": message.content 49 | }); 50 | }); 51 | } 52 | //Parse the responses of other chat models supported by the CAP LLM Plugin 53 | else { 54 | throw new Error(`Model ${modelName} not supported.`); 55 | } 56 | } 57 | else { 58 | throw new Error(`Messages corresponding to conversation id: ${conversationId} not present!`) ; 59 | } 60 | } 61 | 62 | // If conversation is not present, insert the conversation into db 63 | else { 64 | //const conversationTitle = await getConversationSummarization(user_query); 65 | console.log(`Inserting new conversation for conversation id: ${conversationId}`); 66 | const currentTimestamp = getCurrentTimestamp(); 67 | const conversationEntry = { 68 | "cID": conversationId, 69 | "userID": user_id, 70 | "creation_time": currentTimestamp, 71 | "last_update_time": currentTimestamp, 72 | "title": user_query, 73 | }; 74 | const conversationInsertStatus = await INSERT.into(Conversation).entries([conversationEntry]); 75 | if (!conversationInsertStatus) { 76 | throw new Error("Insertion of conversation into db failed!"); 77 | }; 78 | } 79 | 80 | // In both cases, insert the message into db 81 | const messageRecord = { 82 | "cID_cID": conversationId, 83 | "mID": messageId, 84 | "role": "user", 85 | "content": user_query, 86 | "creation_time": message_time 87 | }; 88 | 89 | await insertMessage(Message, messageRecord, conversationId, Conversation, message_time); 90 | return memoryContext; 91 | } 92 | 93 | catch (error) { 94 | // Handle any errors that occur during the execution 95 | console.log('Error handling memory prior to RAG response:', error); 96 | throw error; 97 | } 98 | } 99 | 100 | // Helper method to handle conversation memory in HANA CLoud after RAG LLM call. 101 | async function storeModelResponse(conversationId, message_time, chatRagResponse, Message, Conversation) { 102 | try { 103 | const aiMessageRecord = { 104 | "cID_cID": conversationId, 105 | "mID": uuidv4(), 106 | "role": chatRagResponse.role, 107 | "content": chatRagResponse.content, 108 | "creation_time": message_time 109 | }; 110 | 111 | // Insert the assistant message to db 112 | await insertMessage(Message, aiMessageRecord, conversationId, Conversation, getCurrentTimestamp()); 113 | } 114 | catch (error) { 115 | // Handle any errors that occur during the execution 116 | console.log('Error handling memory post RAG response:', error); 117 | throw error; 118 | } 119 | 120 | } 121 | 122 | 123 | module.exports = { 124 | storeRetrieveMessages, 125 | storeModelResponse 126 | }; -------------------------------------------------------------------------------- /samples/harmonized-rag-app/srv/server.js: -------------------------------------------------------------------------------- 1 | const cds = require("@sap/cds"); 2 | const cors = require("cors"); 3 | 4 | cds.on("bootstrap", (app) => { 5 | app.use(cors()); 6 | }); 7 | module.exports = cds.server; 8 | 9 | const cds_swagger = require("cds-swagger-ui-express") 10 | /* 11 | * Use User Provided Variable to distinguish DEV, UAT, STAGE, PROD env deployment 12 | */ 13 | 14 | // if (process.env.dev == 'true' || process.env.NODE_ENV !== 'production') { 15 | // const cds_swagger = require('cds-swagger-ui-express'); 16 | // cds.on('bootstrap', app => app.use(cds_swagger())); 17 | // } 18 | cds.on('bootstrap', app => app.use(cds_swagger())) -------------------------------------------------------------------------------- /samples/harmonized-rag-app/xs-security.json: -------------------------------------------------------------------------------- 1 | { 2 | "xsappname": "harmonized-rag-app", 3 | "tenant-mode": "dedicated", 4 | "description": "Security profile of called application", 5 | "scopes": [], 6 | "attributes": [], 7 | "role-templates": [] 8 | } 9 | -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/.cdsrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "cdsc": { 3 | "beta": { 4 | "vectorType": true 5 | } 6 | }, 7 | "requires":{ 8 | "GENERATIVE_AI_HUB": { 9 | "CHAT_MODEL_DESTINATION_NAME": "AICoreAzureOpenAIDestination", 10 | "CHAT_MODEL_DEPLOYMENT_URL": " . For example: /v2/inference/deployments/", 11 | "CHAT_MODEL_RESOURCE_GROUP": "", 12 | "CHAT_MODEL_API_VERSION": "", 13 | "EMBEDDING_MODEL_DESTINATION_NAME": "AICoreAzureOpenAIDestination", 14 | "EMBEDDING_MODEL_DEPLOYMENT_URL": ". For example: /v2/inference/deployments/", 15 | "EMBEDDING_MODEL_RESOURCE_GROUP": "default", 16 | "EMBEDDING_MODEL_API_VERSION": "" 17 | }, 18 | "AICoreAzureOpenAIDestination": { 19 | "kind": "rest", 20 | "credentials": { 21 | "destination": "openai-aicore-api", 22 | "requestTimeout": "300000" 23 | } 24 | }, 25 | "SUCCESS_FACTORS_CREDENTIALS": { 26 | "AUTHORIZATION_HEADER": "", 27 | "USER_ID": "" 28 | } 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint:recommended", 3 | "env": { 4 | "es2022": true, 5 | "node": true, 6 | "jest": true, 7 | "mocha": true 8 | }, 9 | "globals": { 10 | "SELECT": true, 11 | "INSERT": true, 12 | "UPSERT": true, 13 | "UPDATE": true, 14 | "DELETE": true, 15 | "CREATE": true, 16 | "DROP": true, 17 | "CDL": true, 18 | "CQL": true, 19 | "CXL": true, 20 | "cds": true 21 | }, 22 | "rules": { 23 | "no-console": "off", 24 | "require-atomic-updates": "off" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/.gitignore: -------------------------------------------------------------------------------- 1 | # CAP hr-approval-rag-usecase 2 | _out 3 | *.db 4 | *.sqlite 5 | connection.properties 6 | default-*.json 7 | .cdsrc-private.json 8 | gen/ 9 | node_modules/ 10 | target/ 11 | 12 | # Web IDE, App Studio 13 | .che/ 14 | .gen/ 15 | 16 | # MTA 17 | *_mta_build_tmp 18 | *.mtar 19 | mta_archives/ 20 | 21 | # Other 22 | .DS_Store 23 | *.orig 24 | *.log 25 | 26 | *.iml 27 | *.flattened-pom.xml 28 | 29 | # IDEs 30 | # .vscode 31 | # .idea 32 | 33 | # @cap-js/cds-typer 34 | @cds-models 35 | -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/README.md: -------------------------------------------------------------------------------- 1 | # HR Policy Chat Assistant 2 | 3 | This sample CAP application uses the CAP LLM Plugin to simplify the process of accessing HANA Vector/Embedding features, connectivity to AI Core and automate the entire RAG retrieval flow. It demonstrates a RAG scenario where users can ask any questions regarding leave requests, other HR policy and the application leverages RAG architecture to combine HR Policy documents with the leave data residing in SAP SuccessFactors systems to provide appropriate response. 4 | 5 | ![Solution Diagram](./docs/images/hr-policy-chatbot.png) 6 | 7 | ### Pre-requisites: 8 | 9 | 1. [Create an instance of SAP AI Core](https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/create-service-instance) and make sure to choose the service plan extended to activate Generative AI Hub and continue creating a Service Key. 10 | 11 | 2. [Create deployments](https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/create-deployment-for-generative-ai-model-in-sap-ai-core) for a model support ChatCompletion (e.g, gpt-35-turbo or gpt-4) and an embedding model (text-embedding-ada-002) and note down the Deployment IDs for each. All available models are listed here. 12 | 13 | 3. [Create a Destination](https://help.sap.com/docs/btp/sap-business-technology-platform/create-destination) for Generative AI Hub in the SAP BTP Cockpit of your subaccount based on the Service Key of SAP AI Core you created in the previous step: 14 | 15 | ``` 16 | Name: GENERATIVE_AI_HUB 17 | Description: SAP AI Core deployed service (generative AI hub) 18 | URL: /v2 # make sure to add /v2! 19 | Type: HTTP 20 | ProxyType: Internet 21 | Authentication: OAuth2ClientCredentials 22 | tokenServiceURL: /oauth/token 23 | clientId: 24 | clientSecret: 25 | # Additional Properties: 26 | URL.headers.AI-Resource-Group: default # adjust if necessary 27 | URL.headers.Content-Type: application/json 28 | HTML5.DynamicDestination: true 29 | ``` 30 | 31 | 4. [Create SAP HANA Cloud](https://help.sap.com/docs/HANA_CLOUD_ALIBABA_CLOUD/683a53aec4fc408783bbb2dd8e47afeb/7d4071a49c204dfc9e542c5e47b53156.html) with Vector Engine (QRC 1/2024 or later). 32 | 33 | 34 | 5. Configure the Generative AI Hub and SuccessFactors connection details. 35 | 36 | Refer [SuccessFactors authorization header details](https://help.sap.com/docs/SAP_SUCCESSFACTORS_PLATFORM/d599f15995d348a1b45ba5603e2aba9b/5c8bca0af1654b05a83193b2922dcee2.html). 37 | 38 | For example, in `.cdsrc.json` file. Refer the [documentation](https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/create-deployment-for-generative-ai-model-in-sap-ai-core) for more details. 39 | 40 | ``` 41 | { 42 | "cdsc": { 43 | "beta": { 44 | "vectorType": true 45 | } 46 | }, 47 | "requires":{ 48 | "GENERATIVE_AI_HUB": { 49 | "CHAT_MODEL_DESTINATION_NAME": "AICoreAzureOpenAIDestination", 50 | "CHAT_MODEL_DEPLOYMENT_URL": " . For example: /v2/inference/deployments/", 51 | "CHAT_MODEL_RESOURCE_GROUP": "", 52 | "CHAT_MODEL_API_VERSION": "", 53 | "EMBEDDING_MODEL_DESTINATION_NAME": "AICoreAzureOpenAIDestination", 54 | "EMBEDDING_MODEL_DEPLOYMENT_URL": ". For example: /v2/inference/deployments/", 55 | "EMBEDDING_MODEL_RESOURCE_GROUP": "default", 56 | "EMBEDDING_MODEL_API_VERSION": "" 57 | }, 58 | "AICoreAzureOpenAIDestination": { 59 | "kind": "rest", 60 | "credentials": { 61 | "destination": "", 62 | "requestTimeout": "300000" 63 | } 64 | }, 65 | "SUCCESS_FACTORS_CREDENTIALS": { 66 | "AUTHORIZATION_HEADER": "", 67 | "USER_ID": "" 68 | } 69 | } 70 | 71 | } 72 | 73 | ``` 74 | 75 | ## Getting started 76 | 77 | - Clone this repo. 78 | - Connect to subaccount with HANA Cloud instance and authenticate to cf: 79 | ` cf api ` 80 | ` cf login` 81 | 82 | - Install node modules using `npm i` 83 | 84 | ## Hybrid testing 85 | 86 | - Bind the following services to application: 87 | - hana cloud 88 | - destination service 89 | 90 | - Build artifacts and deploy to HANA: 91 | 92 | `cds build --production` 93 | `cds deploy --to hana:` 94 | 95 | - Build server and run application: 96 | 97 | `cds build` 98 | `cds watch --profile dev` 99 | 100 | ## Deploy on SAP BTP: 101 | 102 | - Run the following command to deploy server: 103 | 104 | `cds build --production` 105 | 106 | - Build and deploy mtar 107 | 108 | ``` 109 | mbt build 110 | cf deploy mta_archives/ 111 | ``` 112 | 113 | ## How to use the application: 114 | 115 | - Upload policy document and generate embeddings from UI. 116 | - Use the chat based UI for retrieving answers to the question. -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/app/hr-approval-ui/README.md: -------------------------------------------------------------------------------- 1 | ## Application Details 2 | | | 3 | | ------------- | 4 | |**Generation Date and Time**
    Wed Feb 28 2024 21:51:37 GMT+0000 (Coordinated Universal Time)| 5 | |**App Generator**
    @sap/generator-fiori-freestyle| 6 | |**App Generator Version**
    1.12.4| 7 | |**Generation Platform**
    SAP Business Application Studio| 8 | |**Template Used**
    simple| 9 | |**Service Type**
    Local Cap| 10 | |**Service URL**
    http://localhost:4004/odata/v4/chat/ 11 | |**Module Name**
    hr-approval-ui| 12 | |**Application Title**
    This is App Title| 13 | |**Namespace**
    | 14 | |**UI5 Theme**
    sap_horizon| 15 | |**UI5 Version**
    1.120.9| 16 | |**Enable Code Assist Libraries**
    False| 17 | |**Enable TypeScript**
    False| 18 | |**Add Eslint configuration**
    False| 19 | 20 | ## hr-approval-ui 21 | 22 | An SAP Fiori application. 23 | 24 | ### Starting the generated app 25 | 26 | - This app has been generated using the SAP Fiori tools - App Generator, as part of the SAP Fiori tools suite. In order to launch the generated app, simply start your CAP project and navigate to the following location in your browser: 27 | 28 | http://localhost:4004/hr-approval-ui/webapp/index.html 29 | 30 | #### Pre-requisites: 31 | 32 | 1. Active NodeJS LTS (Long Term Support) version and associated supported NPM version. (See https://nodejs.org) 33 | 34 | 35 | -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/app/hr-approval-ui/annotations.cds: -------------------------------------------------------------------------------- 1 | using ChatService as service from '../../srv/chat-service'; -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/app/hr-approval-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hr-approval-ui", 3 | "version": "0.0.1", 4 | "description": "An SAP Fiori application.", 5 | "keywords": [ 6 | "ui5", 7 | "openui5", 8 | "sapui5" 9 | ], 10 | "main": "webapp/index.html", 11 | "scripts": { 12 | "deploy-config": "npx -p @sap/ux-ui5-tooling fiori add deploy-config cf", 13 | "build:cf": "ui5 build preload --clean-dest --config ui5-deploy.yaml --include-task=generateCachebusterInfo" 14 | }, 15 | "devDependencies": { 16 | "@sap/ui5-builder-webide-extension": "^1.1.8", 17 | "ui5-task-zipper": "^0.5.0", 18 | "mbt": "^1.2.18", 19 | "@ui5/cli": "^2.14.10" 20 | }, 21 | "ui5": { 22 | "dependencies": [ 23 | "@sap/ui5-builder-webide-extension", 24 | "ui5-task-zipper", 25 | "mbt" 26 | ] 27 | } 28 | } -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/app/hr-approval-ui/ui5-deploy.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json 2 | specVersion: '2.4' 3 | metadata: 4 | name: hrapprovalui 5 | type: application 6 | resources: 7 | configuration: 8 | propertiesFileSourceEncoding: UTF-8 9 | builder: 10 | resources: 11 | excludes: 12 | - "/test/**" 13 | - "/localService/**" 14 | customTasks: 15 | - name: webide-extension-task-updateManifestJson 16 | afterTask: replaceVersion 17 | configuration: 18 | appFolder: webapp 19 | destDir: dist 20 | - name: ui5-task-zipper 21 | afterTask: generateCachebusterInfo 22 | configuration: 23 | archiveName: hrapprovalui 24 | additionalFiles: 25 | - xs-app.json 26 | -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/app/hr-approval-ui/ui5.yaml: -------------------------------------------------------------------------------- 1 | specVersion: "2.5" 2 | metadata: 3 | name: hrapprovalui 4 | type: application 5 | framework: 6 | name: OpenUI5 7 | version: "1.120.7" 8 | libraries: 9 | - name: sap.m 10 | - name: sap.ui.core 11 | - name: themelib_sap_horizon 12 | - name: sap.f 13 | - name: sap.ui.layout 14 | - name: sap.ui.unified 15 | -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/app/hr-approval-ui/webapp/Component.js: -------------------------------------------------------------------------------- 1 | /** 2 | * eslint-disable @sap/ui5-jsdocs/no-jsdoc 3 | */ 4 | 5 | sap.ui.define([ 6 | "sap/ui/core/UIComponent", 7 | "sap/ui/Device", 8 | "hrapprovalui/model/models" 9 | ], 10 | function (UIComponent, Device, models) { 11 | "use strict"; 12 | 13 | return UIComponent.extend("hrapprovalui.Component", { 14 | metadata: { 15 | manifest: "json" 16 | }, 17 | 18 | /** 19 | * The component is initialized by UI5 automatically during the startup of the app and calls the init method once. 20 | * @public 21 | * @override 22 | */ 23 | init: function () { 24 | // call the base component's init function 25 | UIComponent.prototype.init.apply(this, arguments); 26 | 27 | // enable routing 28 | this.getRouter().initialize(); 29 | 30 | // set the device model 31 | this.setModel(models.createDeviceModel(), "device"); 32 | } 33 | }); 34 | } 35 | ); -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/app/hr-approval-ui/webapp/controller/App.controller.js: -------------------------------------------------------------------------------- 1 | sap.ui.define( 2 | [ 3 | "sap/ui/core/mvc/Controller" 4 | ], 5 | function(BaseController) { 6 | "use strict"; 7 | 8 | return BaseController.extend("hrapprovalui.controller.App", { 9 | onInit: function() { 10 | } 11 | }); 12 | } 13 | ); 14 | -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/app/hr-approval-ui/webapp/css/style.css: -------------------------------------------------------------------------------- 1 | /* Enter your custom styles here */ 2 | .sapFAvatar.sapFAvatarInitials > .sapFAvatarInitialsHolder { 3 | font-size: large; 4 | } 5 | 6 | .sapMScrollCont { 7 | padding-bottom: 0.75rem; 8 | box-sizing: border-box; 9 | -webkit-box-flex: 1; 10 | flex: 1; 11 | position: relative; 12 | -webkit-flex: 1; 13 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 14 | } 15 | 16 | .sapUiSizeCompact .sapTntNavLI { 17 | padding: 1.5rem 0.75rem 0.75rem 0.75rem; 18 | box-sizing: border-box; 19 | position: relative; 20 | overflow: hidden; 21 | width: 100%; 22 | } 23 | 24 | .sapUiSizeCompact .sapMPageWithFooter.sapMPageWithFloatingFooter > section { 25 | bottom: 0; 26 | scroll-padding-bottom: calc(2.5rem + 0.5rem + 2px); 27 | padding-bottom: 4.5rem; 28 | padding-top: 1rem; 29 | } 30 | -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/app/hr-approval-ui/webapp/i18n/i18n.properties: -------------------------------------------------------------------------------- 1 | # This is the resource bundle for hrapprovalui 2 | 3 | #Texts for manifest.json 4 | 5 | #XTIT: Application name 6 | appTitle=Chat Demo of CAP LLM Plugin 7 | 8 | #YDES: Application description 9 | appDescription=An SAP Fiori application. 10 | #XTIT: Main view title 11 | title=Chat Demo of CAP LLM Plugin 12 | 13 | flpTitle=HR Approval Demo 14 | 15 | flpSubtitle=Powered by CAP LLM Plugin and SuccessFactor 16 | -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/app/hr-approval-ui/webapp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | This is App Title 8 | 13 | 25 | 26 | 27 |
    34 | 35 | -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/app/hr-approval-ui/webapp/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "_version": "1.59.0", 3 | "sap.app": { 4 | "id": "hrapprovalui", 5 | "type": "application", 6 | "i18n": "i18n/i18n.properties", 7 | "applicationVersion": { 8 | "version": "0.0.1" 9 | }, 10 | "title": "{{appTitle}}", 11 | "description": "{{appDescription}}", 12 | "resources": "resources.json", 13 | "sourceTemplate": { 14 | "id": "@sap/generator-fiori:basic", 15 | "version": "1.12.4", 16 | "toolsId": "2f10dee8-63c3-46f0-a270-d704153ff59a" 17 | }, 18 | "dataSources": { 19 | "mainService": { 20 | "uri": "/odata/v4/chat/", 21 | "type": "OData", 22 | "settings": { 23 | "annotations": [], 24 | "odataVersion": "4.0" 25 | } 26 | }, 27 | "fileService":{ 28 | "uri": "/odata/v4/embedding-storage/", 29 | "type": "OData", 30 | "settings": { 31 | "annotations": [], 32 | "odataVersion": "4.0" 33 | } 34 | } 35 | }, 36 | "crossNavigation": { 37 | "inbounds": { 38 | "Chat-Display": { 39 | "semanticObject": "Chat", 40 | "action": "Display", 41 | "title": "{{flpTitle}}", 42 | "subTitle": "{{flpSubtitle}}", 43 | "signature": { 44 | "parameters": {}, 45 | "additionalParameters": "allowed" 46 | } 47 | } 48 | } 49 | } 50 | }, 51 | "sap.ui": { 52 | "technology": "UI5", 53 | "icons": { 54 | "icon": "", 55 | "favIcon": "", 56 | "phone": "", 57 | "phone@2": "", 58 | "tablet": "", 59 | "tablet@2": "" 60 | }, 61 | "deviceTypes": { 62 | "desktop": true, 63 | "tablet": true, 64 | "phone": true 65 | } 66 | }, 67 | "sap.ui5": { 68 | "flexEnabled": true, 69 | "dependencies": { 70 | "minUI5Version": "1.120.9", 71 | "libs": { 72 | "sap.m": {}, 73 | "sap.ui.core": {}, 74 | "sap.f": {}, 75 | "sap.suite.ui.generic.template": {}, 76 | "sap.ui.comp": {}, 77 | "sap.ui.generic.app": {}, 78 | "sap.ui.table": {}, 79 | "sap.ushell": {}, 80 | "sap.ui.unified": {} 81 | } 82 | }, 83 | "contentDensities": { 84 | "compact": true, 85 | "cozy": true 86 | }, 87 | "models": { 88 | "i18n": { 89 | "type": "sap.ui.model.resource.ResourceModel", 90 | "settings": { 91 | "bundleName": "hrapprovalui.i18n.i18n" 92 | } 93 | }, 94 | "": { 95 | "dataSource": "mainService", 96 | "preload": true, 97 | "settings": { 98 | "synchronizationMode": "None", 99 | "operationMode": "Server", 100 | "autoExpandSelect": true, 101 | "earlyRequests": true 102 | } 103 | }, 104 | "files":{ 105 | "dataSource": "fileService", 106 | "preload": true, 107 | "settings": { 108 | "synchronizationMode": "None", 109 | "operationMode": "Server", 110 | "autoExpandSelect": true, 111 | "earlyRequests": true 112 | } 113 | } 114 | }, 115 | "resources": { 116 | "css": [ 117 | { 118 | "uri": "css/style.css" 119 | } 120 | ] 121 | }, 122 | "rootView": { 123 | "viewName": "hrapprovalui.view.App", 124 | "type": "XML", 125 | "async": true, 126 | "id": "App" 127 | }, 128 | "routing": { 129 | "config": { 130 | "routerClass": "sap.f.routing.Router", 131 | "viewType": "XML", 132 | "async": true, 133 | "viewPath": "hrapprovalui.view", 134 | "controlId": "flexibleColumnLayout", 135 | "transition": "slide" 136 | }, 137 | "routes": [ 138 | { 139 | "pattern": "", 140 | "name": "home", 141 | "target":[ 142 | "leftScreen", 143 | "initialRightScreen" 144 | ], 145 | "layout": "TwoColumnsMidExpanded" 146 | }, 147 | { 148 | "pattern": "conversation/{conversationID}", 149 | "name": "conversation", 150 | "target":[ 151 | "leftScreen", 152 | "officalRightScreen" 153 | ], 154 | "layout": "TwoColumnsMidExpanded" 155 | } 156 | ], 157 | "targets": { 158 | "leftScreen":{ 159 | "viewId": "lefeScreenPage", 160 | "viewName": "LeftScreen", 161 | "controlAggregation": "beginColumnPages" 162 | }, 163 | "initialRightScreen":{ 164 | "viewId": "initialRightScreen", 165 | "viewName": "InitialRightScreen", 166 | "controlAggregation": "midColumnPages" 167 | }, 168 | "officalRightScreen":{ 169 | "viewId": "officalRightScreen", 170 | "viewName": "OfficalRightScreen", 171 | "controlAggregation": "midColumnPages" 172 | } 173 | } 174 | } 175 | }, 176 | "sap.cloud": { 177 | "public": true, 178 | "service": "hr.app" 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/app/hr-approval-ui/webapp/model/models.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/model/json/JSONModel", 3 | "sap/ui/Device" 4 | ], 5 | /** 6 | * provide app-view type models (as in the first "V" in MVVC) 7 | * 8 | * @param {typeof sap.ui.model.json.JSONModel} JSONModel 9 | * @param {typeof sap.ui.Device} Device 10 | * 11 | * @returns {Function} createDeviceModel() for providing runtime info for the device the UI5 app is running on 12 | */ 13 | function (JSONModel, Device) { 14 | "use strict"; 15 | 16 | return { 17 | createDeviceModel: function () { 18 | var oModel = new JSONModel(Device); 19 | oModel.setDefaultBindingMode("OneWay"); 20 | return oModel; 21 | } 22 | }; 23 | }); -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/app/hr-approval-ui/webapp/test/flpSandbox.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{appTitle}} 9 | 10 | 25 | 56 | 57 | 58 | 59 | 70 | 71 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/app/hr-approval-ui/webapp/view/App.view.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/app/hr-approval-ui/webapp/view/FileUploading.fragment.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | 31 | 32 | 41 | 46 | 51 | 56 | 57 | 58 | 59 | 60 | 61 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/app/hr-approval-ui/webapp/view/InitialRightScreen.view.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 16 | 17 | 18 | 25 | 33 | 34 | 35 | 36 | 37 |
    38 | 43 | 51 | 52 | 53 |
    54 | 55 |
    56 | 57 |
    -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/app/hr-approval-ui/webapp/view/LeftScreen.view.xml: -------------------------------------------------------------------------------- 1 | 9 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 25 | 26 | 32 | 33 | 34 | 35 | 40 | 58 | 64 | 65 | 66 | 67 | 69 | 70 | 75 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/app/hr-approval-ui/webapp/view/OfficalRightScreen.view.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 16 | 17 | 19 | 31 | 41 | 42 | 43 | 44 | 45 |
    46 | 51 | 59 | 60 | 61 |
    62 | 63 |
    64 | 65 |
    -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/app/hr-approval-ui/xs-app.json: -------------------------------------------------------------------------------- 1 | { 2 | "welcomeFile": "/index.html", 3 | "authenticationMethod": "route", 4 | "routes": [ 5 | { 6 | "source": "^/odata/v4/chat/(.*)$", 7 | "target": "/odata/v4/chat/$1", 8 | "destination": "hr-approval-rag-usecase-srv", 9 | "authenticationType": "xsuaa", 10 | "csrfProtection": false, 11 | "cacheControl": "no-cache, no-store, must-revalidate" 12 | }, 13 | { 14 | "source": "^/odata/v4/embedding-storage/(.*)$", 15 | "target": "/odata/v4/embedding-storage/$1", 16 | "destination": "hr-approval-rag-usecase-srv", 17 | "authenticationType": "xsuaa", 18 | "csrfProtection": false, 19 | "cacheControl": "no-cache, no-store, must-revalidate" 20 | }, 21 | { 22 | "source": "^/user-api/currentUser$", 23 | "target": "/currentUser", 24 | "service": "sap-approuter-userapi" 25 | }, 26 | { 27 | "source": "^/resources/(.*)$", 28 | "target": "/resources/$1", 29 | "authenticationType": "none", 30 | "destination": "ui5" 31 | }, 32 | { 33 | "source": "^/test-resources/(.*)$", 34 | "target": "/test-resources/$1", 35 | "authenticationType": "none", 36 | "destination": "ui5" 37 | }, 38 | { 39 | "source": "^(.*)$", 40 | "target": "$1", 41 | "service": "html5-apps-repo-rt", 42 | "authenticationType": "xsuaa" 43 | } 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "approuter", 3 | "dependencies": { 4 | "@sap/approuter": "^14.0.0" 5 | }, 6 | "engines": { 7 | "node": "^18.0.0" 8 | }, 9 | "scripts": { 10 | "start": "node node_modules/@sap/approuter/approuter.js" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/app/services.cds: -------------------------------------------------------------------------------- 1 | 2 | using from './hr-approval-ui/annotations'; -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/app/xs-app.json: -------------------------------------------------------------------------------- 1 | { 2 | "welcomeFile": "app/index.html", 3 | "routes": [ 4 | { 5 | "source": "^/app/(.*)$", 6 | "target": "$1", 7 | "localDir": ".", 8 | "cacheControl": "no-cache, no-store, must-revalidate" 9 | }, 10 | { 11 | "source": "^/(.*)$", 12 | "target": "$1", 13 | "destination": "srv-api", 14 | "csrfProtection": true 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/db/schema.cds: -------------------------------------------------------------------------------- 1 | namespace sap.tisce.demo; 2 | 3 | using { 4 | cuid, 5 | managed 6 | } from '@sap/cds/common'; 7 | 8 | entity Conversation { 9 | 10 | key cID : UUID not null; 11 | userID: String; 12 | creation_time: Timestamp; 13 | last_update_time: Timestamp; 14 | title: String; 15 | to_messages: Composition of many Message on to_messages.cID = $self; 16 | } 17 | 18 | entity Message { 19 | 20 | key cID: Association to Conversation; 21 | key mID: UUID not null; 22 | role: String; 23 | content: LargeString; 24 | creation_time: Timestamp; 25 | } 26 | 27 | entity DocumentChunk 28 | { 29 | text_chunk: LargeString; 30 | metadata_column: LargeString; 31 | embedding: Vector(1536); 32 | } 33 | 34 | 35 | entity Files: cuid, managed{ 36 | @Core.MediaType: mediaType @Core.ContentDisposition.Filename: fileName 37 | content: LargeBinary; 38 | @Core.IsMediaType: true 39 | mediaType: String; 40 | fileName: String; 41 | size: String; 42 | } 43 | 44 | -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/db/src/.hdiconfig: -------------------------------------------------------------------------------- 1 | { 2 | "file_suffixes": { 3 | "csv": { 4 | "plugin_name": "com.sap.hana.di.tabledata.source" 5 | }, 6 | "hdbafllangprocedure": { 7 | "plugin_name": "com.sap.hana.di.afllangprocedure" 8 | }, 9 | "hdbanalyticprivilege": { 10 | "plugin_name": "com.sap.hana.di.analyticprivilege" 11 | }, 12 | "hdbcalculationview": { 13 | "plugin_name": "com.sap.hana.di.calculationview" 14 | }, 15 | "hdbcollection": { 16 | "plugin_name": "com.sap.hana.di.collection" 17 | }, 18 | "hdbconstraint": { 19 | "plugin_name": "com.sap.hana.di.constraint" 20 | }, 21 | "hdbdropcreatetable": { 22 | "plugin_name": "com.sap.hana.di.dropcreatetable" 23 | }, 24 | "hdbflowgraph": { 25 | "plugin_name": "com.sap.hana.di.flowgraph" 26 | }, 27 | "hdbfunction": { 28 | "plugin_name": "com.sap.hana.di.function" 29 | }, 30 | "hdbgraphworkspace": { 31 | "plugin_name": "com.sap.hana.di.graphworkspace" 32 | }, 33 | "hdbhadoopmrjob": { 34 | "plugin_name": "com.sap.hana.di.virtualfunctionpackage.hadoop" 35 | }, 36 | "hdbindex": { 37 | "plugin_name": "com.sap.hana.di.index" 38 | }, 39 | "hdblibrary": { 40 | "plugin_name": "com.sap.hana.di.library" 41 | }, 42 | "hdbmigrationtable": { 43 | "plugin_name": "com.sap.hana.di.table.migration" 44 | }, 45 | "hdbprocedure": { 46 | "plugin_name": "com.sap.hana.di.procedure" 47 | }, 48 | "hdbprojectionview": { 49 | "plugin_name": "com.sap.hana.di.projectionview" 50 | }, 51 | "hdbprojectionviewconfig": { 52 | "plugin_name": "com.sap.hana.di.projectionview.config" 53 | }, 54 | "hdbreptask": { 55 | "plugin_name": "com.sap.hana.di.reptask" 56 | }, 57 | "hdbresultcache": { 58 | "plugin_name": "com.sap.hana.di.resultcache" 59 | }, 60 | "hdbrole": { 61 | "plugin_name": "com.sap.hana.di.role" 62 | }, 63 | "hdbroleconfig": { 64 | "plugin_name": "com.sap.hana.di.role.config" 65 | }, 66 | "hdbsearchruleset": { 67 | "plugin_name": "com.sap.hana.di.searchruleset" 68 | }, 69 | "hdbsequence": { 70 | "plugin_name": "com.sap.hana.di.sequence" 71 | }, 72 | "hdbstatistics": { 73 | "plugin_name": "com.sap.hana.di.statistics" 74 | }, 75 | "hdbstructuredprivilege": { 76 | "plugin_name": "com.sap.hana.di.structuredprivilege" 77 | }, 78 | "hdbsynonym": { 79 | "plugin_name": "com.sap.hana.di.synonym" 80 | }, 81 | "hdbsynonymconfig": { 82 | "plugin_name": "com.sap.hana.di.synonym.config" 83 | }, 84 | "hdbsystemversioning": { 85 | "plugin_name": "com.sap.hana.di.systemversioning" 86 | }, 87 | "hdbtable": { 88 | "plugin_name": "com.sap.hana.di.table" 89 | }, 90 | "hdbtabledata": { 91 | "plugin_name": "com.sap.hana.di.tabledata" 92 | }, 93 | "hdbtabletype": { 94 | "plugin_name": "com.sap.hana.di.tabletype" 95 | }, 96 | "hdbtrigger": { 97 | "plugin_name": "com.sap.hana.di.trigger" 98 | }, 99 | "hdbview": { 100 | "plugin_name": "com.sap.hana.di.view" 101 | }, 102 | "hdbvirtualfunction": { 103 | "plugin_name": "com.sap.hana.di.virtualfunction" 104 | }, 105 | "hdbvirtualfunctionconfig": { 106 | "plugin_name": "com.sap.hana.di.virtualfunction.config" 107 | }, 108 | "hdbvirtualpackagehadoop": { 109 | "plugin_name": "com.sap.hana.di.virtualpackage.hadoop" 110 | }, 111 | "hdbvirtualpackagesparksql": { 112 | "plugin_name": "com.sap.hana.di.virtualpackage.sparksql" 113 | }, 114 | "hdbvirtualprocedure": { 115 | "plugin_name": "com.sap.hana.di.virtualprocedure" 116 | }, 117 | "hdbvirtualprocedureconfig": { 118 | "plugin_name": "com.sap.hana.di.virtualprocedure.config" 119 | }, 120 | "hdbvirtualtable": { 121 | "plugin_name": "com.sap.hana.di.virtualtable" 122 | }, 123 | "hdbvirtualtableconfig": { 124 | "plugin_name": "com.sap.hana.di.virtualtable.config" 125 | }, 126 | "properties": { 127 | "plugin_name": "com.sap.hana.di.tabledata.properties" 128 | }, 129 | "tags": { 130 | "plugin_name": "com.sap.hana.di.tabledata.properties" 131 | }, 132 | "txt": { 133 | "plugin_name": "com.sap.hana.di.copyonly" 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/db/undeploy.json: -------------------------------------------------------------------------------- 1 | [ 2 | "src/gen/**/*.hdbview", 3 | "src/gen/**/*.hdbindex", 4 | "src/gen/**/*.hdbconstraint", 5 | "src/gen/**/*_drafts.hdbtable" 6 | ] 7 | -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/docs/images/hr-policy-chatbot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-samples/cap-llm-plugin-samples/0eb4b50433fdfcfc29071203374cce4f8653f6bb/samples/hr-approval-rag-usecase/docs/images/hr-policy-chatbot.png -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hr-approval-rag-usecase", 3 | "version": "1.0.0", 4 | "description": "A simple CAP project.", 5 | "repository": "", 6 | "license": "UNLICENSED", 7 | "private": true, 8 | "dependencies": { 9 | "@sap-cloud-sdk/http-client": "^3.12.0", 10 | "@sap/cds": "^7.6.3", 11 | "@sap/cds-compiler": "^4.6.2", 12 | "@sap/cds-hana": "^2", 13 | "@sap/xssec": "^3", 14 | "basic-auth": "^2.0.1", 15 | "cap-llm-plugin": "^1.3.0", 16 | "cds-swagger-ui-express": "^0.8.0", 17 | "cors": "^2.8.5", 18 | "dotenv": "^16.4.4", 19 | "downloadjs": "^1.4.7", 20 | "express": "^4", 21 | "langchain": "^0.1.19", 22 | "passport": "^0.7.0", 23 | "pdf-lib": "^1.17.1", 24 | "pdf-parse": "^1.1.1", 25 | "pdfkit": "^0.14.0", 26 | "uuid": "^9.0.1" 27 | }, 28 | "devDependencies": { 29 | "@cap-js/sqlite": "^1", 30 | "@sap/cds-dk": "^7", 31 | "@sap/ux-specification": "^1.120.4", 32 | "rimraf": "^3.0.2" 33 | }, 34 | "scripts": { 35 | "start": "cds-serve", 36 | "watch-hr-approval-ui": "cds watch --open hr-approval-ui/webapp/index.html?sap-ui-xx-viewCache=false", 37 | "undeploy": "cf undeploy hr-approval-rag-usecase --delete-services --delete-service-keys --delete-service-brokers", 38 | "build": "rimraf resources mta_archives && mbt build --mtar archive", 39 | "deploy": "cf deploy mta_archives/archive.mtar --retries 1" 40 | }, 41 | "cds": { 42 | "requires": { 43 | "[dev]": { 44 | "db": "hana", 45 | "cap-llm-plugin": { 46 | "impl": "cap-llm-plugin/srv/cap-llm-plugin.js" 47 | }, 48 | "destinations": true, 49 | "jsonplaceholder": { 50 | "kind": "rest", 51 | "credentials": { 52 | "url": "https://jsonplaceholder.typicode.com", 53 | "requestTimeout": 30000 54 | } 55 | }, 56 | "destination_sf": { 57 | "kind": "rest", 58 | "credentials": { 59 | "url": "", 60 | "requestTimeout": 30000 61 | } 62 | }, 63 | "auth": { 64 | "passport": { 65 | "strategy": "mock", 66 | "users": { 67 | "JohnDole@tester.sap.com": { 68 | "ID": "JohnDole@tester.sap.com", 69 | "password": "initial" 70 | } 71 | } 72 | } 73 | } 74 | }, 75 | "[production]": { 76 | "auth": "xsuaa", 77 | "db": "hana", 78 | "cap-llm-plugin": { 79 | "impl": "cap-llm-plugin/srv/cap-llm-plugin.js" 80 | }, 81 | "destinations": true, 82 | "jsonplaceholder": { 83 | "kind": "rest", 84 | "credentials": { 85 | "url": "https://jsonplaceholder.typicode.com", 86 | "requestTimeout": 30000 87 | } 88 | }, 89 | "destination_sf": { 90 | "kind": "rest", 91 | "credentials": { 92 | "url": "", 93 | "requestTimeout": 30000 94 | } 95 | } 96 | } 97 | }, 98 | "sapux": [ 99 | "app/hr-approval-ui" 100 | ] 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/srv/chat-service.cds: -------------------------------------------------------------------------------- 1 | using {sap.tisce.demo as db} from '../db/schema'; 2 | 3 | service ChatService @(requires: 'authenticated-user') { 4 | 5 | entity Conversation @(restrict: [{ 6 | grant: ['READ','WRITE', 'DELETE'], 7 | where: 'userID = $user' 8 | }]) as projection on db.Conversation; 9 | entity Message as projection on db.Message; 10 | 11 | type RagResponse_AdditionalContents { 12 | 13 | score : String; 14 | pageContent : String; 15 | } 16 | 17 | type RagResponse { 18 | role : String; 19 | content : String; 20 | messageTime : String; 21 | additionalContents : array of RagResponse_AdditionalContents; 22 | } 23 | 24 | action getChatRagResponse(conversationId : String, messageId : String, message_time : Timestamp, user_id : String, user_query : String) returns RagResponse; 25 | function deleteChatData() returns String; 26 | } 27 | -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/srv/embedding-storage.cds: -------------------------------------------------------------------------------- 1 | using {sap.tisce.demo as db} from '../db/schema'; 2 | 3 | service EmbeddingStorageService @(requires: 'authenticated-user') { 4 | 5 | entity DocumentChunk as 6 | projection on db.DocumentChunk 7 | excluding { 8 | embedding 9 | }; 10 | 11 | entity Files @(restrict: [{ 12 | grant: [ 13 | 'READ', 14 | 'WRITE', 15 | 'UPDATE', 16 | 'DELETE' 17 | ], 18 | where: 'createdBy = $user' 19 | }]) as projection on db.Files; 20 | 21 | action storeEmbeddings(uuid : String) returns String; 22 | function deleteEmbeddings() returns String; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/srv/server.js: -------------------------------------------------------------------------------- 1 | const cds = require("@sap/cds"); 2 | const cors = require("cors"); 3 | 4 | cds.on("bootstrap", (app) => { 5 | app.use(cors()); 6 | }); 7 | module.exports = cds.server; 8 | 9 | const cds_swagger = require("cds-swagger-ui-express") 10 | /* 11 | * Use User Provided Variable to distinguish DEV, UAT, STAGE, PROD env deployment 12 | */ 13 | 14 | // if (process.env.dev == 'true' || process.env.NODE_ENV !== 'production') { 15 | // const cds_swagger = require('cds-swagger-ui-express'); 16 | // cds.on('bootstrap', app => app.use(cds_swagger())); 17 | // } 18 | cds.on('bootstrap', app => app.use(cds_swagger())) -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/srv/sf-connection-util.js: -------------------------------------------------------------------------------- 1 | const cds = require("@sap/cds") 2 | 3 | AUTHORIZATION_HEADER = cds.env.requires["SUCCESS_FACTORS_CREDENTIALS"]["AUTHORIZATION_HEADER"] 4 | 5 | // Returns the user object with same structure as we get from SF API 6 | async function getUserInfoById(userId){ 7 | const destination_sf = await cds.connect.to('destination_sf') 8 | let result = await destination_sf.send({ 9 | query: `GET /odata/v2/User('${userId}')?$format=json`, 10 | headers: { Authorization: AUTHORIZATION_HEADER } 11 | }) 12 | if(result){ 13 | return result.d 14 | } 15 | else return null 16 | } 17 | 18 | async function getUserManagerId(userId){ 19 | const destination_sf = await cds.connect.to('destination_sf') 20 | let result = await destination_sf.send({ 21 | query: `GET /odata/v2/User('${userId}')/manager?$format=json`, 22 | headers: { Authorization: AUTHORIZATION_HEADER } 23 | }) 24 | if(result?.d?.userId){ 25 | return result.d?.userId 26 | } 27 | else{ 28 | return null 29 | } 30 | } 31 | 32 | async function getDirectReportsById(userId){ 33 | const destination_sf = await cds.connect.to('destination_sf') 34 | let result = await destination_sf.send({ 35 | query: `GET /odata/v2/User?$filter=manager/userId eq '${userId}'&$format=JSON`, 36 | headers: { Authorization: AUTHORIZATION_HEADER } 37 | }) 38 | 39 | if(result?.d?.results){ 40 | let resArr = [] 41 | for (i of result.d.results) { 42 | resArr.push({ 43 | userId : i.userId, 44 | displayName : i.displayName 45 | }) 46 | } 47 | return resArr 48 | } 49 | else return [] 50 | } 51 | 52 | // Just for datetime value from SF API 53 | 54 | async function getEmployeeTime( 55 | userId, 56 | displayName, 57 | startDate, //2024-03-10T00:00:00 58 | endDate, //2024-03-10T00:00:00 59 | approvalStatus = 'CANCELLED', 60 | approvalStatusOperator = 'ne', 61 | timeType = 'TT_VAC_REC' 62 | ){ 63 | const destination_sf = await cds.connect.to('destination_sf') 64 | let result = await destination_sf.send({ 65 | query: `GET /odata/v2/EmployeeTime?&$format=json&$filter=userId eq '${userId}' and approvalStatus ${approvalStatusOperator} '${approvalStatus}' and startDate gt datetime'${startDate}' and endDate lt datetime'${endDate}' and timeType eq '${timeType}'`, 66 | headers: { Authorization: AUTHORIZATION_HEADER } 67 | }) 68 | if(result?.d?.results){ 69 | 70 | let resObj = { 71 | userId: userId, 72 | displayName: displayName, 73 | vacations: [] 74 | } 75 | console.log(displayName) 76 | for(i of result.d.results){ 77 | if(i.absenceDurationCategory == 'MULTI_DAY'){ 78 | console.log(i.startDate) 79 | console.log(i.endDate) 80 | resObj.vacations.push({ 81 | startDate: i.startDate.substr(6, i.startDate.length - 8), 82 | endDate: i.endDate.substr(6, i.endDate.length - 8) 83 | }) 84 | 85 | } 86 | else{ 87 | //same code just in case we have diff logic after 88 | resObj.vacations.push({ 89 | startDate: i.startDate.substr(6, i.startDate.length - 8), 90 | endDate: i.endDate.substr(6, i.endDate.length - 8) 91 | }) 92 | } 93 | } 94 | return resObj 95 | } 96 | else return null 97 | } 98 | 99 | async function getPeersVacationTimeByUserId( 100 | userId, 101 | startDate, //2024-03-10T00:00:00 102 | endDate, //2024-03-10T00:00:00 103 | noOfDatesToExtend, //before startDate and after endDate 104 | approvalStatus = 'CANCELLED', 105 | approvalStatusOperator = 'ne', 106 | timeType = 'TT_VAC_REC' 107 | ){ 108 | let managerId = await getUserManagerId(userId) 109 | let peers = await getDirectReportsById(managerId) 110 | 111 | let dt_startDate = new Date(Date.parse(startDate)) 112 | let changed_dt_startDate = dt_startDate.setDate(dt_startDate.getDate() - noOfDatesToExtend) 113 | let dt_endDate = new Date(Date.parse(endDate)) 114 | let changed_dt_endDate = dt_endDate.setDate(dt_endDate.getDate() + noOfDatesToExtend) 115 | 116 | let startDate_lc = timestampToString(changed_dt_startDate) 117 | let endDate_lc = timestampToString(changed_dt_endDate) 118 | 119 | if(peers){ 120 | 121 | let resArr = [] 122 | 123 | for(i of peers){ 124 | let timeobj = await getEmployeeTime( 125 | i.userId, 126 | i.displayName, 127 | startDate_lc, 128 | endDate_lc, 129 | approvalStatus, 130 | approvalStatusOperator, 131 | timeType 132 | ) 133 | resArr.push(timeobj) 134 | } 135 | 136 | return resArr 137 | } 138 | else return [] 139 | 140 | } 141 | 142 | function timestampToString(timestamp) { 143 | // Create a new Date object with the timestamp (in milliseconds) 144 | const date = new Date(timestamp); 145 | // Get the year, month, day, hours, minutes, and seconds 146 | const year = date.getFullYear(); 147 | const month = String(date.getMonth() + 1).padStart(2, '0'); // Month is zero-indexed 148 | const day = String(date.getDate()).padStart(2, '0'); 149 | const hours = String(date.getHours()).padStart(2, '0'); 150 | const minutes = String(date.getMinutes()).padStart(2, '0'); 151 | const seconds = String(date.getSeconds()).padStart(2, '0'); 152 | 153 | // Create the formatted string 154 | const formattedString = `${year}-${month}-${day}T${hours}:${minutes}:${seconds}`; 155 | 156 | return formattedString; 157 | } 158 | 159 | module.exports = { getUserInfoById, getUserManagerId, getDirectReportsById, getEmployeeTime, getPeersVacationTimeByUserId }; 160 | -------------------------------------------------------------------------------- /samples/hr-approval-rag-usecase/xs-security.json: -------------------------------------------------------------------------------- 1 | { 2 | "xsappname": "hr-approval-rag-usecase", 3 | "tenant-mode": "dedicated", 4 | "description": "Security profile of called application", 5 | "scopes": [], 6 | "attributes": [], 7 | "role-templates": [] 8 | } 9 | -------------------------------------------------------------------------------- /samples/personalized-email-usecase/.cdsrc.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /samples/personalized-email-usecase/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint:recommended", 3 | "env": { 4 | "es2022": true, 5 | "node": true, 6 | "jest": true, 7 | "mocha": true 8 | }, 9 | "globals": { 10 | "SELECT": true, 11 | "INSERT": true, 12 | "UPSERT": true, 13 | "UPDATE": true, 14 | "DELETE": true, 15 | "CREATE": true, 16 | "DROP": true, 17 | "CDL": true, 18 | "CQL": true, 19 | "CXL": true, 20 | "cds": true 21 | }, 22 | "rules": { 23 | "no-console": "off", 24 | "require-atomic-updates": "off" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /samples/personalized-email-usecase/.gitignore: -------------------------------------------------------------------------------- 1 | # CAP emp-gen-anz-project 2 | _out 3 | *.db 4 | *.sqlite 5 | connection.properties 6 | default-*.json 7 | .cdsrc-private.json 8 | gen/ 9 | node_modules/ 10 | target/ 11 | 12 | # Web IDE, App Studio 13 | .che/ 14 | .gen/ 15 | 16 | # MTA 17 | *_mta_build_tmp 18 | *.mtar 19 | mta_archives/ 20 | 21 | # Other 22 | .DS_Store 23 | *.orig 24 | *.log 25 | 26 | *.iml 27 | *.flattened-pom.xml 28 | 29 | # IDEs 30 | # .vscode 31 | # .idea 32 | -------------------------------------------------------------------------------- /samples/personalized-email-usecase/README.md: -------------------------------------------------------------------------------- 1 | # Employee Personalized Email Use Case using CAP LLM Plugin 2 | 3 | Consider a CAP application where personalized emails need to be generated to congratulate employees on their contributions to the company. This process involves feeding sensitive employee information to the LLM to generate personalized employee emails. However, we want to ensure that the confidentiality of this sensitive information is preserved while retaining sufficient business context. In this scenario, the CAP LLM Plugin can be used to seamlessly anonymize the sensitive employee data by applying SAP HANA Cloud data anonymization techniques. 4 | 5 | ![Solution Diagram](./images/personalized-email-solution-diagram.png) 6 | 7 | ## Pre-requisites 8 | 9 | - Ensure that cloud foundry is enabled in the subaccount [guide](https://help.sap.com/docs/DIGITALPAYMENTS/a5c364402f8d4c0b99f6a4c7de385a56/223db7192df44425b66bba122110779b.html). 10 | - Ensure that HANA service instance has been created [guide](https://help.sap.com/docs/HANA_SERVICE_CF/cc53ad464a57404b8d453bbadbc81ceb/21418824b23a401aa116d9ad42dd5ba6.html) and the entitlements for 'SAP HANA Schemas and HDI Containers' have been added in the subaccount [guide](https://help.sap.com/docs/btp/sap-business-technology-platform/configure-entitlements-and-quotas-for-subaccounts). 11 | - Ensure you have an instance of SAP Generative AI Hub using this [guide](https://github.com/SAP-samples/btp-cap-genai-rag/blob/main/docs/tutorial/2-setup/4-generativeAIhub.md#setup-the-generative-ai-hub-in-sap-ai-core). 12 | 13 | 14 | ## Folder structure 15 | 16 | It contains these folders and files, following our recommended project layout: 17 | 18 | File or Folder | Purpose 19 | ---------|---------- 20 | `app/` | content for UI frontends goes here 21 | `db/` | your domain models and data go here 22 | `srv/` | your service models and code go here 23 | `package.json` | project metadata and configuration 24 | `readme.md` | this getting started guide 25 | 26 | 27 | ## Getting started 28 | 29 | - Clone this repo. 30 | - Connect to subaccount with HANA Cloud instance and authenticate to cf: 31 | ` cf api ` 32 | ` cf login` 33 | - Create a destination service and add the destination to the destination service: 34 | `cf create-service destination lite employee-anyz-usecase-destination-service` 35 | 36 | Add the destination created in the pre-requisite [step](https://github.com/SAP-samples/btp-cap-genai-rag/blob/main/docs/tutorial/2-setup/4-generativeAIhub.md#create-destination) to the destination service in the BTP Cockpit space by referring to the [guide](https://help.sap.com/docs/connectivity/sap-btp-connectivity-cf/destinations-pointing-to-service-instances). 37 | 38 | - Replace the placeholders in the `srv/ai-core-service.js` with appropriate values. Ensure that the "AI_CORE_DESTINATION_NAME" configured in package.json file matches the one configured in `srv/ai-core-service.js` file. 39 | 40 | For running it locally perform the following steps: 41 | 42 | - Create HDI container (HANA service instance) and bind it to the CAP pplication as follows: 43 | `cf create-service hana hdi-shared emp-anz-proj-db` 44 | `cf create-service-key emp-anz-proj-db SharedDevKey` 45 | `cds bind -2 emp-anz-proj-db:SharedDevKey` 46 | 47 | - Bind the previously created desination service to the CAP application as follows: 48 | `cf create-service-key employee-anyz-usecase-destination-service SharedDevKey` 49 | `cds bind -2 employee-anyz-usecase-destination-service:SharedDevKey` 50 | 51 | - cds build 52 | `cds build` 53 | 54 | - Deploy the db artifacts 55 | `cds deploy --to hana:emp-anz-proj-db` 56 | 57 | - Start the app locally 58 | `cds watch --profile hybrid` 59 | 60 | Deploy the application to BTP as follows: 61 | 62 | - Build the mtar file using the following command: 63 | `mbt build -t ./` 64 | 65 | - Deploy the mtar to BTP using the following command: 66 | `cf deploy ` 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /samples/personalized-email-usecase/app/emp-gen-anz-ui/README.md: -------------------------------------------------------------------------------- 1 | ## Application Details 2 | | | 3 | | ------------- | 4 | |**Generation Date and Time**
    Wed Dec 06 2023 00:02:22 GMT+0000 (Coordinated Universal Time)| 5 | |**App Generator**
    @sap/generator-fiori-elements| 6 | |**App Generator Version**
    1.11.5| 7 | |**Generation Platform**
    SAP Business Application Studio| 8 | |**Template Used**
    List Report Page V4| 9 | |**Service Type**
    Local Cap| 10 | |**Service URL**
    http://localhost:4004/odata/v4/employee/ 11 | |**Module Name**
    emp-gen-anz-ui| 12 | |**Application Title**
    emp-gen-anz-ui| 13 | |**Namespace**
    | 14 | |**UI5 Theme**
    sap_horizon| 15 | |**UI5 Version**
    1.120.1| 16 | |**Enable Code Assist Libraries**
    False| 17 | |**Enable TypeScript**
    False| 18 | |**Add Eslint configuration**
    False| 19 | |**Main Entity**
    Employee| 20 | 21 | ## emp-gen-anz-ui 22 | 23 | A Fiori application. 24 | 25 | ### Starting the generated app 26 | 27 | - This app has been generated using the SAP Fiori tools - App Generator, as part of the SAP Fiori tools suite. In order to launch the generated app, simply start your CAP project and navigate to the following location in your browser: 28 | 29 | http://localhost:4004/emp-gen-anz-ui/webapp/index.html 30 | 31 | #### Pre-requisites: 32 | 33 | 1. Active NodeJS LTS (Long Term Support) version and associated supported NPM version. (See https://nodejs.org) 34 | 35 | 36 | -------------------------------------------------------------------------------- /samples/personalized-email-usecase/app/emp-gen-anz-ui/annotations.cds: -------------------------------------------------------------------------------- 1 | using EmployeeService as service from '../../srv/employee-service'; 2 | 3 | annotate service.Employee with @( 4 | UI.LineItem : [ 5 | { 6 | $Type : 'UI.DataField', 7 | Label : 'id', 8 | Value : id, 9 | }, 10 | { 11 | $Type : 'UI.DataField', 12 | Label : 'name', 13 | Value : name, 14 | }, 15 | { 16 | $Type : 'UI.DataField', 17 | Label : 'region', 18 | Value : region, 19 | }, 20 | { 21 | $Type : 'UI.DataField', 22 | Label : 'tlevel', 23 | Value : tlevel, 24 | }, 25 | { 26 | $Type : 'UI.DataField', 27 | Label : 'gender', 28 | Value : gender, 29 | }, 30 | { 31 | $Type : 'UI.DataField', 32 | Label : 'age', 33 | Value : age, 34 | }, 35 | { 36 | $Type : 'UI.DataField', 37 | Label : 'ssn', 38 | Value : ssn, 39 | }, 40 | ] 41 | ); 42 | annotate service.Employee with @( 43 | UI.FieldGroup #GeneratedGroup1 : { 44 | $Type : 'UI.FieldGroupType', 45 | Data : [ 46 | { 47 | $Type : 'UI.DataField', 48 | Label : 'id', 49 | Value : id, 50 | }, 51 | { 52 | $Type : 'UI.DataField', 53 | Label : 'name', 54 | Value : name, 55 | }, 56 | { 57 | $Type : 'UI.DataField', 58 | Label : 'region', 59 | Value : region, 60 | }, 61 | { 62 | $Type : 'UI.DataField', 63 | Label : 'tlevel', 64 | Value : tlevel, 65 | }, 66 | { 67 | $Type : 'UI.DataField', 68 | Label : 'gender', 69 | Value : gender, 70 | }, 71 | { 72 | $Type : 'UI.DataField', 73 | Label : 'age', 74 | Value : age, 75 | }, 76 | { 77 | $Type : 'UI.DataField', 78 | Label : 'ssn', 79 | Value : ssn, 80 | }, 81 | { 82 | $Type : 'UI.DataField', 83 | Label : 'personalizedEmail', 84 | Value : personalizedEmail, 85 | }, 86 | ], 87 | }, 88 | UI.Facets : [ 89 | { 90 | $Type : 'UI.ReferenceFacet', 91 | ID : 'GeneratedFacet1', 92 | Label : 'General Information', 93 | Target : '@UI.FieldGroup#GeneratedGroup1', 94 | }, 95 | ] 96 | ); 97 | -------------------------------------------------------------------------------- /samples/personalized-email-usecase/app/emp-gen-anz-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "emp-gen-anz-ui", 3 | "version": "0.0.1", 4 | "description": "A Fiori application.", 5 | "keywords": [ 6 | "ui5", 7 | "openui5", 8 | "sapui5" 9 | ], 10 | "main": "webapp/index.html", 11 | "scripts": { 12 | "deploy-config": "npx -p @sap/ux-ui5-tooling fiori add deploy-config cf", 13 | "build:cf": "ui5 build preload --clean-dest --config ui5-deploy.yaml --include-task=generateCachebusterInfo" 14 | }, 15 | "devDependencies": { 16 | "@sap/ui5-builder-webide-extension": "^1.1.8", 17 | "ui5-task-zipper": "^0.5.0", 18 | "mbt": "^1.2.18", 19 | "@ui5/cli": "^2.14.10" 20 | }, 21 | "ui5": { 22 | "dependencies": [ 23 | "@sap/ui5-builder-webide-extension", 24 | "ui5-task-zipper", 25 | "mbt" 26 | ] 27 | } 28 | } -------------------------------------------------------------------------------- /samples/personalized-email-usecase/app/emp-gen-anz-ui/ui5-deploy.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json 2 | specVersion: '2.4' 3 | metadata: 4 | name: empgenanzui 5 | type: application 6 | resources: 7 | configuration: 8 | propertiesFileSourceEncoding: UTF-8 9 | builder: 10 | resources: 11 | excludes: 12 | - "/test/**" 13 | - "/localService/**" 14 | customTasks: 15 | - name: webide-extension-task-updateManifestJson 16 | afterTask: replaceVersion 17 | configuration: 18 | appFolder: webapp 19 | destDir: dist 20 | - name: ui5-task-zipper 21 | afterTask: generateCachebusterInfo 22 | configuration: 23 | archiveName: empgenanzui 24 | additionalFiles: 25 | - xs-app.json 26 | -------------------------------------------------------------------------------- /samples/personalized-email-usecase/app/emp-gen-anz-ui/ui5.yaml: -------------------------------------------------------------------------------- 1 | specVersion: "2.5" 2 | metadata: 3 | name: empgenanzui 4 | type: application 5 | -------------------------------------------------------------------------------- /samples/personalized-email-usecase/app/emp-gen-anz-ui/webapp/Component.js: -------------------------------------------------------------------------------- 1 | sap.ui.define( 2 | ["sap/fe/core/AppComponent"], 3 | function (Component) { 4 | "use strict"; 5 | 6 | return Component.extend("empgenanzui.Component", { 7 | metadata: { 8 | manifest: "json" 9 | } 10 | }); 11 | } 12 | ); -------------------------------------------------------------------------------- /samples/personalized-email-usecase/app/emp-gen-anz-ui/webapp/i18n/i18n.properties: -------------------------------------------------------------------------------- 1 | # This is the resource bundle for empgenanzui 2 | 3 | #Texts for manifest.json 4 | 5 | #XTIT: Application name 6 | appTitle=emp-gen-anz-ui 7 | 8 | #YDES: Application description 9 | appDescription=A Fiori application. 10 | 11 | flpTitle=emp-obj-page 12 | -------------------------------------------------------------------------------- /samples/personalized-email-usecase/app/emp-gen-anz-ui/webapp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | emp-gen-anz-ui 8 | 13 | 25 | 26 | 27 |
    34 | 35 | -------------------------------------------------------------------------------- /samples/personalized-email-usecase/app/emp-gen-anz-ui/webapp/localService/metadata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /samples/personalized-email-usecase/app/emp-gen-anz-ui/webapp/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "_version": "1.59.0", 3 | "sap.app": { 4 | "id": "empgenanzui", 5 | "type": "application", 6 | "i18n": "i18n/i18n.properties", 7 | "applicationVersion": { 8 | "version": "0.0.1" 9 | }, 10 | "title": "{{appTitle}}", 11 | "description": "{{appDescription}}", 12 | "resources": "resources.json", 13 | "sourceTemplate": { 14 | "id": "@sap/generator-fiori:lrop", 15 | "version": "1.11.5", 16 | "toolsId": "29ca6cff-4169-49a9-a5da-052692d73fb3" 17 | }, 18 | "dataSources": { 19 | "mainService": { 20 | "uri": "/odata/v4/employee/", 21 | "type": "OData", 22 | "settings": { 23 | "annotations": [], 24 | "localUri": "localService/metadata.xml", 25 | "odataVersion": "4.0" 26 | } 27 | } 28 | }, 29 | "crossNavigation": { 30 | "inbounds": { 31 | "emp-obj-emp-action": { 32 | "semanticObject": "emp-obj", 33 | "action": "emp-action", 34 | "title": "{{flpTitle}}", 35 | "signature": { 36 | "parameters": {}, 37 | "additionalParameters": "allowed" 38 | } 39 | } 40 | } 41 | } 42 | }, 43 | "sap.ui": { 44 | "technology": "UI5", 45 | "icons": { 46 | "icon": "", 47 | "favIcon": "", 48 | "phone": "", 49 | "phone@2": "", 50 | "tablet": "", 51 | "tablet@2": "" 52 | }, 53 | "deviceTypes": { 54 | "desktop": true, 55 | "tablet": true, 56 | "phone": true 57 | } 58 | }, 59 | "sap.ui5": { 60 | "flexEnabled": true, 61 | "dependencies": { 62 | "minUI5Version": "1.120.1", 63 | "libs": { 64 | "sap.m": {}, 65 | "sap.ui.core": {}, 66 | "sap.ushell": {}, 67 | "sap.fe.templates": {} 68 | } 69 | }, 70 | "contentDensities": { 71 | "compact": true, 72 | "cozy": true 73 | }, 74 | "models": { 75 | "i18n": { 76 | "type": "sap.ui.model.resource.ResourceModel", 77 | "settings": { 78 | "bundleName": "empgenanzui.i18n.i18n" 79 | } 80 | }, 81 | "": { 82 | "dataSource": "mainService", 83 | "preload": true, 84 | "settings": { 85 | "synchronizationMode": "None", 86 | "operationMode": "Server", 87 | "autoExpandSelect": true, 88 | "earlyRequests": true 89 | } 90 | }, 91 | "@i18n": { 92 | "type": "sap.ui.model.resource.ResourceModel", 93 | "uri": "i18n/i18n.properties" 94 | } 95 | }, 96 | "resources": { 97 | "css": [] 98 | }, 99 | "routing": { 100 | "config": {}, 101 | "routes": [ 102 | { 103 | "pattern": ":?query:", 104 | "name": "EmployeeList", 105 | "target": "EmployeeList" 106 | }, 107 | { 108 | "pattern": "Employee({key}):?query:", 109 | "name": "EmployeeObjectPage", 110 | "target": "EmployeeObjectPage" 111 | } 112 | ], 113 | "targets": { 114 | "EmployeeList": { 115 | "type": "Component", 116 | "id": "EmployeeList", 117 | "name": "sap.fe.templates.ListReport", 118 | "options": { 119 | "settings": { 120 | "contextPath": "/Employee", 121 | "variantManagement": "Page", 122 | "navigation": { 123 | "Employee": { 124 | "detail": { 125 | "route": "EmployeeObjectPage" 126 | } 127 | } 128 | } 129 | } 130 | } 131 | }, 132 | "EmployeeObjectPage": { 133 | "type": "Component", 134 | "id": "EmployeeObjectPage", 135 | "name": "sap.fe.templates.ObjectPage", 136 | "options": { 137 | "settings": { 138 | "editableHeaderContent": false, 139 | "contextPath": "/Employee" 140 | } 141 | } 142 | } 143 | } 144 | } 145 | }, 146 | "sap.fiori": { 147 | "registrationIds": [], 148 | "archeType": "transactional" 149 | }, 150 | "sap.cloud": { 151 | "public": true, 152 | "service": "emp-gen-anz-project-ui" 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /samples/personalized-email-usecase/app/emp-gen-anz-ui/webapp/test/flpSandbox.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{appTitle}} 9 | 10 | 25 | 56 | 57 | 58 | 59 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /samples/personalized-email-usecase/app/emp-gen-anz-ui/webapp/test/integration/FirstJourney.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/test/opaQunit" 3 | ], function (opaTest) { 4 | "use strict"; 5 | 6 | var Journey = { 7 | run: function() { 8 | QUnit.module("First journey"); 9 | 10 | opaTest("Start application", function (Given, When, Then) { 11 | Given.iStartMyApp(); 12 | 13 | Then.onTheEmployeeList.iSeeThisPage(); 14 | 15 | }); 16 | 17 | 18 | opaTest("Navigate to ObjectPage", function (Given, When, Then) { 19 | // Note: this test will fail if the ListReport page doesn't show any data 20 | 21 | When.onTheEmployeeList.onFilterBar().iExecuteSearch(); 22 | 23 | Then.onTheEmployeeList.onTable().iCheckRows(); 24 | 25 | When.onTheEmployeeList.onTable().iPressRow(0); 26 | Then.onTheEmployeeObjectPage.iSeeThisPage(); 27 | 28 | }); 29 | 30 | opaTest("Teardown", function (Given, When, Then) { 31 | // Cleanup 32 | Given.iTearDownMyApp(); 33 | }); 34 | } 35 | } 36 | 37 | return Journey; 38 | }); -------------------------------------------------------------------------------- /samples/personalized-email-usecase/app/emp-gen-anz-ui/webapp/test/integration/opaTests.qunit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Integration tests 5 | 6 | 7 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
    28 |
    29 | 30 | -------------------------------------------------------------------------------- /samples/personalized-email-usecase/app/emp-gen-anz-ui/webapp/test/integration/opaTests.qunit.js: -------------------------------------------------------------------------------- 1 | sap.ui.require( 2 | [ 3 | 'sap/fe/test/JourneyRunner', 4 | 'empgenanzui/test/integration/FirstJourney', 5 | 'empgenanzui/test/integration/pages/EmployeeList', 6 | 'empgenanzui/test/integration/pages/EmployeeObjectPage' 7 | ], 8 | function(JourneyRunner, opaJourney, EmployeeList, EmployeeObjectPage) { 9 | 'use strict'; 10 | var JourneyRunner = new JourneyRunner({ 11 | // start index.html in web folder 12 | launchUrl: sap.ui.require.toUrl('empgenanzui') + '/index.html' 13 | }); 14 | 15 | 16 | JourneyRunner.run( 17 | { 18 | pages: { 19 | onTheEmployeeList: EmployeeList, 20 | onTheEmployeeObjectPage: EmployeeObjectPage 21 | } 22 | }, 23 | opaJourney.run 24 | ); 25 | } 26 | ); -------------------------------------------------------------------------------- /samples/personalized-email-usecase/app/emp-gen-anz-ui/webapp/test/integration/pages/EmployeeList.js: -------------------------------------------------------------------------------- 1 | sap.ui.define(['sap/fe/test/ListReport'], function(ListReport) { 2 | 'use strict'; 3 | 4 | var CustomPageDefinitions = { 5 | actions: {}, 6 | assertions: {} 7 | }; 8 | 9 | return new ListReport( 10 | { 11 | appId: 'empgenanzui', 12 | componentId: 'EmployeeList', 13 | contextPath: '/Employee' 14 | }, 15 | CustomPageDefinitions 16 | ); 17 | }); -------------------------------------------------------------------------------- /samples/personalized-email-usecase/app/emp-gen-anz-ui/webapp/test/integration/pages/EmployeeObjectPage.js: -------------------------------------------------------------------------------- 1 | sap.ui.define(['sap/fe/test/ObjectPage'], function(ObjectPage) { 2 | 'use strict'; 3 | 4 | var CustomPageDefinitions = { 5 | actions: {}, 6 | assertions: {} 7 | }; 8 | 9 | return new ObjectPage( 10 | { 11 | appId: 'empgenanzui', 12 | componentId: 'EmployeeObjectPage', 13 | contextPath: '/Employee' 14 | }, 15 | CustomPageDefinitions 16 | ); 17 | }); -------------------------------------------------------------------------------- /samples/personalized-email-usecase/app/emp-gen-anz-ui/webapp/test/testsuite.qunit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | QUnit test suite 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /samples/personalized-email-usecase/app/emp-gen-anz-ui/webapp/test/testsuite.qunit.js: -------------------------------------------------------------------------------- 1 | window.suite = function() { 2 | 'use strict'; 3 | 4 | // eslint-disable-next-line 5 | var oSuite = new parent.jsUnitTestSuite(), 6 | 7 | sContextPath = location.pathname.substring(0, location.pathname.lastIndexOf('/') + 1); 8 | oSuite.addTestPage(sContextPath + 'integration/opaTests.qunit.html'); 9 | 10 | return oSuite; 11 | }; -------------------------------------------------------------------------------- /samples/personalized-email-usecase/app/emp-gen-anz-ui/xs-app.json: -------------------------------------------------------------------------------- 1 | { 2 | "welcomeFile": "/index.html", 3 | "authenticationMethod": "route", 4 | "routes": [ 5 | { 6 | "source": "^/odata/(.*)$", 7 | "target": "/odata/$1", 8 | "destination": "emp-gen-anz-project-srv-api", 9 | "authenticationType": "xsuaa", 10 | "csrfProtection": false 11 | }, 12 | { 13 | "source": "^/resources/(.*)$", 14 | "target": "/resources/$1", 15 | "authenticationType": "none", 16 | "destination": "ui5" 17 | }, 18 | { 19 | "source": "^/test-resources/(.*)$", 20 | "target": "/test-resources/$1", 21 | "authenticationType": "none", 22 | "destination": "ui5" 23 | }, 24 | { 25 | "source": "^(.*)$", 26 | "target": "$1", 27 | "service": "html5-apps-repo-rt", 28 | "authenticationType": "xsuaa" 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /samples/personalized-email-usecase/app/services.cds: -------------------------------------------------------------------------------- 1 | 2 | using from './emp-gen-anz-ui/annotations'; -------------------------------------------------------------------------------- /samples/personalized-email-usecase/db/data/sap.cap-Employee.csv: -------------------------------------------------------------------------------- 1 | ID;NAME;REGION;TLEVEL;GENDER;AGE;SSN;PERSONALIZEDEMAIL 2 | 1002;James;NA;T1;Male;27;63434869; 3 | 1005;Sam;EMEA;T1;Male;42;63495958; 4 | 1003;Keally;EMEA;T2;Female;50;63486746; 5 | 1006;Alex;APJ;T2;Male;12;63477635; -------------------------------------------------------------------------------- /samples/personalized-email-usecase/db/schema.cds: -------------------------------------------------------------------------------- 1 | using { Currency, managed, sap } from '@sap/cds/common'; 2 | namespace sap.cap; 3 | 4 | 5 | @anonymize : `ALGORITHM 'K-ANONYMITY' PARAMETERS '{"k" : 2, "recoding":"multi_dimensional_relaxed", "data_change_strategy": "static"}'` 6 | entity Employee { 7 | key id : Integer @anonymize : `{"is_sequence": true}`; 8 | name : String ; 9 | region : String @anonymize: `{"is_quasi_identifier" : true, "hierarchy": {"embedded" : [["APJ"],["EMEA"],["NA"]]}}`; 10 | tlevel : String @anonymize :`{"is_quasi_identifier" : true, "hierarchy": {"embedded" : [["T1","T1-T2"],["T2","T1-T2"],["T3","T2-T3"]]}}`; 11 | gender : String @anonymize : `{"is_quasi_identifier" : true, "hierarchy": {"embedded" : [["Female"],["Male"]]}}`; 12 | age: String @anonymize : `{"is_quasi_identifier" : true, "hierarchy": {"embedded" : [["27", "15"], ["42", "45"], ["50", "45"], ["12", "15"]]}}`; 13 | ssn : String ; 14 | personalizedEmail : String; 15 | } 16 | -------------------------------------------------------------------------------- /samples/personalized-email-usecase/db/src/.hdiconfig: -------------------------------------------------------------------------------- 1 | { 2 | "file_suffixes": { 3 | "csv": { 4 | "plugin_name": "com.sap.hana.di.tabledata.source" 5 | }, 6 | "hdbafllangprocedure": { 7 | "plugin_name": "com.sap.hana.di.afllangprocedure" 8 | }, 9 | "hdbanalyticprivilege": { 10 | "plugin_name": "com.sap.hana.di.analyticprivilege" 11 | }, 12 | "hdbcalculationview": { 13 | "plugin_name": "com.sap.hana.di.calculationview" 14 | }, 15 | "hdbcollection": { 16 | "plugin_name": "com.sap.hana.di.collection" 17 | }, 18 | "hdbconstraint": { 19 | "plugin_name": "com.sap.hana.di.constraint" 20 | }, 21 | "hdbdropcreatetable": { 22 | "plugin_name": "com.sap.hana.di.dropcreatetable" 23 | }, 24 | "hdbflowgraph": { 25 | "plugin_name": "com.sap.hana.di.flowgraph" 26 | }, 27 | "hdbfunction": { 28 | "plugin_name": "com.sap.hana.di.function" 29 | }, 30 | "hdbgraphworkspace": { 31 | "plugin_name": "com.sap.hana.di.graphworkspace" 32 | }, 33 | "hdbhadoopmrjob": { 34 | "plugin_name": "com.sap.hana.di.virtualfunctionpackage.hadoop" 35 | }, 36 | "hdbindex": { 37 | "plugin_name": "com.sap.hana.di.index" 38 | }, 39 | "hdblibrary": { 40 | "plugin_name": "com.sap.hana.di.library" 41 | }, 42 | "hdbmigrationtable": { 43 | "plugin_name": "com.sap.hana.di.table.migration" 44 | }, 45 | "hdbprocedure": { 46 | "plugin_name": "com.sap.hana.di.procedure" 47 | }, 48 | "hdbprojectionview": { 49 | "plugin_name": "com.sap.hana.di.projectionview" 50 | }, 51 | "hdbprojectionviewconfig": { 52 | "plugin_name": "com.sap.hana.di.projectionview.config" 53 | }, 54 | "hdbreptask": { 55 | "plugin_name": "com.sap.hana.di.reptask" 56 | }, 57 | "hdbresultcache": { 58 | "plugin_name": "com.sap.hana.di.resultcache" 59 | }, 60 | "hdbrole": { 61 | "plugin_name": "com.sap.hana.di.role" 62 | }, 63 | "hdbroleconfig": { 64 | "plugin_name": "com.sap.hana.di.role.config" 65 | }, 66 | "hdbsearchruleset": { 67 | "plugin_name": "com.sap.hana.di.searchruleset" 68 | }, 69 | "hdbsequence": { 70 | "plugin_name": "com.sap.hana.di.sequence" 71 | }, 72 | "hdbstatistics": { 73 | "plugin_name": "com.sap.hana.di.statistics" 74 | }, 75 | "hdbstructuredprivilege": { 76 | "plugin_name": "com.sap.hana.di.structuredprivilege" 77 | }, 78 | "hdbsynonym": { 79 | "plugin_name": "com.sap.hana.di.synonym" 80 | }, 81 | "hdbsynonymconfig": { 82 | "plugin_name": "com.sap.hana.di.synonym.config" 83 | }, 84 | "hdbsystemversioning": { 85 | "plugin_name": "com.sap.hana.di.systemversioning" 86 | }, 87 | "hdbtable": { 88 | "plugin_name": "com.sap.hana.di.table" 89 | }, 90 | "hdbtabledata": { 91 | "plugin_name": "com.sap.hana.di.tabledata" 92 | }, 93 | "hdbtabletype": { 94 | "plugin_name": "com.sap.hana.di.tabletype" 95 | }, 96 | "hdbtrigger": { 97 | "plugin_name": "com.sap.hana.di.trigger" 98 | }, 99 | "hdbview": { 100 | "plugin_name": "com.sap.hana.di.view" 101 | }, 102 | "hdbvirtualfunction": { 103 | "plugin_name": "com.sap.hana.di.virtualfunction" 104 | }, 105 | "hdbvirtualfunctionconfig": { 106 | "plugin_name": "com.sap.hana.di.virtualfunction.config" 107 | }, 108 | "hdbvirtualpackagehadoop": { 109 | "plugin_name": "com.sap.hana.di.virtualpackage.hadoop" 110 | }, 111 | "hdbvirtualpackagesparksql": { 112 | "plugin_name": "com.sap.hana.di.virtualpackage.sparksql" 113 | }, 114 | "hdbvirtualprocedure": { 115 | "plugin_name": "com.sap.hana.di.virtualprocedure" 116 | }, 117 | "hdbvirtualprocedureconfig": { 118 | "plugin_name": "com.sap.hana.di.virtualprocedure.config" 119 | }, 120 | "hdbvirtualtable": { 121 | "plugin_name": "com.sap.hana.di.virtualtable" 122 | }, 123 | "hdbvirtualtableconfig": { 124 | "plugin_name": "com.sap.hana.di.virtualtable.config" 125 | }, 126 | "properties": { 127 | "plugin_name": "com.sap.hana.di.tabledata.properties" 128 | }, 129 | "tags": { 130 | "plugin_name": "com.sap.hana.di.tabledata.properties" 131 | }, 132 | "txt": { 133 | "plugin_name": "com.sap.hana.di.copyonly" 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /samples/personalized-email-usecase/db/src/.hdinamespace: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "subfolder": "ignore" 4 | } -------------------------------------------------------------------------------- /samples/personalized-email-usecase/db/src/defaults/default_access_role.hdbrole: -------------------------------------------------------------------------------- 1 | { 2 | "role": { 3 | "name": "default_access_role", 4 | "schema_privileges": [ 5 | { 6 | "privileges": [ 7 | "SELECT", 8 | "INSERT", 9 | "UPDATE", 10 | "DELETE", 11 | "EXECUTE", 12 | "CREATE TEMPORARY TABLE", 13 | "SELECT CDS METADATA", 14 | "ALTER", 15 | "CREATE ANY" 16 | ] 17 | } 18 | ] 19 | } 20 | } -------------------------------------------------------------------------------- /samples/personalized-email-usecase/db/undeploy.json: -------------------------------------------------------------------------------- 1 | [ 2 | "src/gen/**/*.hdbview", 3 | "src/gen/**/*.hdbindex", 4 | "src/gen/**/*.hdbconstraint" 5 | ] 6 | -------------------------------------------------------------------------------- /samples/personalized-email-usecase/images/personalized-email-solution-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-samples/cap-llm-plugin-samples/0eb4b50433fdfcfc29071203374cce4f8653f6bb/samples/personalized-email-usecase/images/personalized-email-solution-diagram.png -------------------------------------------------------------------------------- /samples/personalized-email-usecase/manifest.yml: -------------------------------------------------------------------------------- 1 | # Generated manifest.yml based on template version 0.1.0 2 | # appName = emp-gen-anz-project 3 | # language=nodejs 4 | # multitenancy=false 5 | --- 6 | applications: 7 | # ----------------------------------------------------------------------------------- 8 | # Backend Service 9 | # ----------------------------------------------------------------------------------- 10 | - name: emp-gen-anz-project-srv 11 | random-route: true # for development only 12 | path: gen/srv 13 | memory: 256M 14 | buildpack: nodejs_buildpack 15 | 16 | -------------------------------------------------------------------------------- /samples/personalized-email-usecase/mta.yaml: -------------------------------------------------------------------------------- 1 | _schema-version: "3.1" 2 | ID: emp-gen-anz-project 3 | description: A simple CAP project. 4 | version: 1.0.0 5 | modules: 6 | - name: emp-gen-anz-project-srv 7 | type: nodejs 8 | path: gen/srv 9 | requires: 10 | - name: emp-gen-anz-project-db 11 | - name: uaa_emp-gen-anz-project 12 | - name: emp-gen-anz-project-destination-service 13 | provides: 14 | - name: srv-api 15 | properties: 16 | srv-url: ${default-url} 17 | parameters: 18 | buildpack: nodejs_buildpack 19 | disk-quota: 1024M 20 | memory: 256M 21 | build-parameters: 22 | builder: npm 23 | ignore: 24 | - .env 25 | - ./node_modules 26 | - name: emp-gen-anz-project-db-deployer 27 | type: hdb 28 | path: gen/db 29 | requires: 30 | - name: emp-gen-anz-project-db 31 | parameters: 32 | buildpack: nodejs_buildpack 33 | disk-quota: 1024M 34 | ignore: 35 | - .env 36 | - ./node_modules 37 | memory: 256M 38 | - name: emp-gen-anz-project-destination-content 39 | type: com.sap.application.content 40 | requires: 41 | - name: emp-gen-anz-project-destination-service 42 | parameters: 43 | content-target: true 44 | - name: emp-gen-anz-project_html_repo_host 45 | parameters: 46 | service-key: 47 | name: emp-gen-anz-project_html_repo_host-key 48 | - name: uaa_emp-gen-anz-project 49 | parameters: 50 | service-key: 51 | name: uaa_emp-gen-anz-project-key 52 | parameters: 53 | content: 54 | instance: 55 | destinations: 56 | - Name: emp_gen_anz_project_ui_emp_gen_anz_project_html_repo_host 57 | ServiceInstanceName: emp-gen-anz-project-html5-app-host-service 58 | ServiceKeyName: emp-gen-anz-project_html_repo_host-key 59 | sap.cloud.service: emp-gen-anz-project-ui 60 | - Authentication: OAuth2UserTokenExchange 61 | Name: emp_gen_anz_project_ui_uaa_emp_gen_anz_project 62 | ServiceInstanceName: emp-gen-anz-project-xsuaa-service 63 | ServiceKeyName: uaa_emp-gen-anz-project-key 64 | sap.cloud.service: emp-gen-anz-project-ui 65 | existing_destinations_policy: ignore 66 | build-parameters: 67 | no-source: true 68 | - name: emp-gen-anz-project-app-content 69 | type: com.sap.application.content 70 | path: . 71 | requires: 72 | - name: emp-gen-anz-project_html_repo_host 73 | parameters: 74 | content-target: true 75 | build-parameters: 76 | build-result: resources 77 | requires: 78 | - artifacts: 79 | - empgenanzui.zip 80 | name: empgenanzui 81 | target-path: resources/ 82 | - name: empgenanzui 83 | type: html5 84 | path: app/emp-gen-anz-ui 85 | build-parameters: 86 | build-result: dist 87 | builder: custom 88 | commands: 89 | - npm install 90 | - npm run build:cf 91 | supported-platforms: [] 92 | resources: 93 | - name: emp-gen-anz-project-db 94 | type: com.sap.xs.hdi-container 95 | parameters: 96 | service: hana 97 | service-plan: hdi-shared 98 | - name: emp-gen-anz-project-destination-service 99 | type: org.cloudfoundry.managed-service 100 | parameters: 101 | config: 102 | HTML5Runtime_enabled: true 103 | init_data: 104 | instance: 105 | destinations: 106 | - Authentication: NoAuthentication 107 | Name: ui5 108 | ProxyType: Internet 109 | Type: HTTP 110 | URL: https://ui5.sap.com 111 | - Authentication: NoAuthentication 112 | HTML5.DynamicDestination: true 113 | HTML5.ForwardAuthToken: true 114 | HTML5.Timeout: 30000 115 | Name: emp-gen-anz-project-srv-api 116 | ProxyType: Internet 117 | Type: HTTP 118 | URL: ~{srv-api/srv-url} 119 | existing_destinations_policy: update 120 | version: 1.0.0 121 | service: destination 122 | service-name: emp-gen-anz-project-destination-service 123 | service-plan: lite 124 | requires: 125 | - name: srv-api 126 | - name: emp-gen-anz-project_html_repo_host 127 | type: org.cloudfoundry.managed-service 128 | parameters: 129 | service: html5-apps-repo 130 | service-name: emp-gen-anz-project-html5-app-host-service 131 | service-plan: app-host 132 | - name: uaa_emp-gen-anz-project 133 | type: org.cloudfoundry.managed-service 134 | parameters: 135 | path: ./xs-security.json 136 | service: xsuaa 137 | service-name: emp-gen-anz-project-xsuaa-service 138 | service-plan: application 139 | parameters: 140 | deploy_mode: html5-repo 141 | enable-parallel-deployments: true 142 | build-parameters: 143 | before-all: 144 | - builder: custom 145 | commands: 146 | - npx cds build --production 147 | -------------------------------------------------------------------------------- /samples/personalized-email-usecase/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "emp-gen-anz-project", 3 | "version": "1.0.0", 4 | "description": "A simple CAP project.", 5 | "repository": "", 6 | "license": "UNLICENSED", 7 | "private": true, 8 | "dependencies": { 9 | "@sap-cloud-sdk/http-client": "*", 10 | "@sap-cloud-sdk/resilience": "*", 11 | "@sap/cds": "^7", 12 | "@sap/cds-hana": "^2", 13 | "@sap/hana-client": "^2.18.27", 14 | "@sap/xssec": "^3", 15 | "cap-llm-plugin" : "*", 16 | "express": "^4", 17 | "passport": "^0" 18 | }, 19 | "devDependencies": { 20 | "@cap-js/sqlite": "^1", 21 | "@sap/cds-dk": "^7", 22 | "@sap/ux-specification": "^1.120.0", 23 | "rimraf": "^3.0.2" 24 | }, 25 | "scripts": { 26 | "start": "cds-serve", 27 | "watch-emp-gen-anz-module": "cds watch --open emp-gen-anz-module/webapp/index.html?sap-ui-xx-viewCache=false", 28 | "undeploy": "cf undeploy emp-gen-anz-project --delete-services --delete-service-keys --delete-service-brokers", 29 | "build": "rimraf resources mta_archives && mbt build --mtar archive", 30 | "deploy": "cf deploy mta_archives/archive.mtar --retries 1", 31 | "watch-emp-proj-test": "cds watch --open emp-proj-test/webapp/index.html?sap-ui-xx-viewCache=false", 32 | "watch-emp-gen-anz-ui": "cds watch --open emp-gen-anz-ui/webapp/index.html?sap-ui-xx-viewCache=false" 33 | }, 34 | "cds": { 35 | "requires": { 36 | "db": "hana", 37 | "destinations": true, 38 | "cap-llm-plugin": true, 39 | "": { 40 | "kind": "rest", 41 | "credentials": { 42 | "destination": "", 43 | "requestTimeout": "300000" 44 | } 45 | } 46 | }, 47 | "sapux": [ 48 | "app/emp-gen-anz-module", 49 | "app/emp-gen-anz-module" 50 | ] 51 | }, 52 | "sapux": [ 53 | "app/emp-proj-test", 54 | "app/emp-gen-anz-ui" 55 | ] 56 | } -------------------------------------------------------------------------------- /samples/personalized-email-usecase/resources/empgenanzui.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-samples/cap-llm-plugin-samples/0eb4b50433fdfcfc29071203374cce4f8653f6bb/samples/personalized-email-usecase/resources/empgenanzui.zip -------------------------------------------------------------------------------- /samples/personalized-email-usecase/services-manifest.yml: -------------------------------------------------------------------------------- 1 | # Generated services-manifest.yml based on template version 0.1.0 2 | # appName = emp-gen-anz-project 3 | --- 4 | create-services: 5 | -------------------------------------------------------------------------------- /samples/personalized-email-usecase/srv/ai-core-service.js: -------------------------------------------------------------------------------- 1 | const resourceGroupId = ``; 2 | const deploymentId = ""; 3 | const AI_CORE_DESTINATION = ""; 4 | const API_VERSION = ""; 5 | 6 | async function getPersonalizedEmail(data) 7 | { 8 | const template = ` 9 | You are Christian, CEO of Azira. 10 | 11 | Generate a personalized message to the employee congratulating them on their contribution to the team. 12 | 13 | The employee information is formatted as a dictionary. The key in the dictionary is "Employee Attribute" and value is "Employee value". 14 | 15 | Here is the employee information is formatted as a dictionary 16 | ${data} 17 | 18 | Include atleast two paragraphs. The email should be written with these values in mind. 19 | 20 | Important: Instead of using employee value in the email use the corresponding employee attribute with double quotes. This is very important as the next steps depend on it. 21 | 22 | Do not include any additional information apart from the email body. Address the employee using "NAME" enclosed in double quotes. 23 | 24 | Finally, double-check if the generated email contains any employee value, if so replace it with corresponding employee attribute with double quotes. 25 | ` 26 | if (deploymentId) { 27 | const aiCoreService = await cds.connect.to(AI_CORE_DESTINATION); 28 | const payload = { 29 | messages: [{ role: "user", content: template }], 30 | max_tokens: 16000, 31 | temperature: 0.0, 32 | }; 33 | const headers = { 34 | "Content-Type": "application/json", 35 | "AI-Resource-Group": resourceGroupId, 36 | }; 37 | const response = await aiCoreService.send({ 38 | // @ts-ignore 39 | query: `POST /inference/deployments/${deploymentId}/chat/completions?api-version=${API_VERSION}`, 40 | data: payload, 41 | headers: headers, 42 | }); 43 | console.log(JSON.stringify(response)); 44 | return response["choices"][0]?.message?.content; 45 | } else { 46 | return `No deployment found for this tenant`; 47 | } 48 | } 49 | 50 | module.exports=getPersonalizedEmail; -------------------------------------------------------------------------------- /samples/personalized-email-usecase/srv/employee-service.cds: -------------------------------------------------------------------------------- 1 | using { sap.cap as cap } from '../db/schema'; 2 | service EmployeeService { 3 | entity Employee as projection on cap.Employee; 4 | } -------------------------------------------------------------------------------- /samples/personalized-email-usecase/srv/employee-service.js: -------------------------------------------------------------------------------- 1 | const cds = require('@sap/cds') 2 | const getPersonalizedEmail = require('./ai-core-service') 3 | 4 | module.exports = cds.service.impl(function () { 5 | this.after('READ', 'Employee', async function (data) { 6 | const { 7 | Employee 8 | } = cds.entities; 9 | 10 | if (Object.keys(data).length === 1) { 11 | const actualData = data[0]; 12 | 13 | //Retrieve the anonymized data from HANA Cloud 14 | const anonymizer = await cds.connect.to("cap-llm-plugin"); 15 | let anonymizedEmployees = await anonymizer.getAnonymizedData("EmployeeService.Employee", [actualData.id]); 16 | 17 | //For each of the requested employees, generate the persoanlized email by passing the anonymized data to LLM. Then, replace the anonymized data with actaul employee data. 18 | const stringData = JSON.stringify(anonymizedEmployees); 19 | let personalizedLlmResponse = await getPersonalizedEmail(stringData); 20 | 21 | for (const [key, value] of Object.entries(actualData)) { personalizedLlmResponse = personalizedLlmResponse.replaceAll(`"` + key.toUpperCase() + `"`, value) } 22 | 23 | const n = await UPDATE(Employee).set({ 24 | ID: actualData.id, 25 | REGION: actualData.region, 26 | TLEVEL: actualData.tlevel, 27 | GENDER: actualData.gender, 28 | AGE: actualData.age, 29 | SSN: actualData.ssn, 30 | PERSONALIZEDEMAIL: personalizedLlmResponse 31 | }).where({ 32 | ID: actualData.id 33 | }); 34 | n > 0 || req.error(404); 35 | } 36 | }) 37 | 38 | }) 39 | -------------------------------------------------------------------------------- /samples/personalized-email-usecase/xs-security.json: -------------------------------------------------------------------------------- 1 | { 2 | "xsappname": "emp-gen-anz-project", 3 | "tenant-mode": "dedicated", 4 | "description": "Security profile of called application", 5 | "scopes": [ 6 | { 7 | "name": "uaa.user", 8 | "description": "UAA" 9 | } 10 | ], 11 | "role-templates": [ 12 | { 13 | "name": "Token_Exchange", 14 | "description": "UAA", 15 | "scope-references": [ 16 | "uaa.user" 17 | ] 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /samples/rag-quickstart-app/.cdsrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "cdsc": { 3 | "beta": { 4 | "vectorType": true 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /samples/rag-quickstart-app/.gitignore: -------------------------------------------------------------------------------- 1 | _out 2 | *.db 3 | *.sqlite 4 | connection.properties 5 | default-*.json 6 | .cdsrc-private.json 7 | gen/ 8 | node_modules/ 9 | target/ 10 | build/ 11 | 12 | # Web IDE, App Studio 13 | .che/ 14 | .gen/ 15 | 16 | # MTA 17 | *_mta_build_tmp 18 | *.mtar 19 | mta_archives/ 20 | dist/ 21 | *.zip 22 | *.pdf 23 | .env 24 | *.mta 25 | *.zip 26 | 27 | # Other 28 | .DS_Store 29 | *.orig 30 | *.log 31 | 32 | *.iml 33 | *.flattened-pom.xml 34 | 35 | # IDEs 36 | .vscode 37 | # .idea -------------------------------------------------------------------------------- /samples/rag-quickstart-app/app/hr-approval-ui/README.md: -------------------------------------------------------------------------------- 1 | ## Application Details 2 | | | 3 | | ------------- | 4 | |**Generation Date and Time**
    Wed Feb 28 2024 21:51:37 GMT+0000 (Coordinated Universal Time)| 5 | |**App Generator**
    @sap/generator-fiori-freestyle| 6 | |**App Generator Version**
    1.12.4| 7 | |**Generation Platform**
    SAP Business Application Studio| 8 | |**Template Used**
    simple| 9 | |**Service Type**
    Local Cap| 10 | |**Service URL**
    http://localhost:4004/odata/v4/chat/ 11 | |**Module Name**
    hr-approval-ui| 12 | |**Application Title**
    This is App Title| 13 | |**Namespace**
    | 14 | |**UI5 Theme**
    sap_horizon| 15 | |**UI5 Version**
    1.120.9| 16 | |**Enable Code Assist Libraries**
    False| 17 | |**Enable TypeScript**
    False| 18 | |**Add Eslint configuration**
    False| 19 | 20 | ## hr-approval-ui 21 | 22 | An SAP Fiori application. 23 | 24 | ### Starting the generated app 25 | 26 | - This app has been generated using the SAP Fiori tools - App Generator, as part of the SAP Fiori tools suite. In order to launch the generated app, simply start your CAP project and navigate to the following location in your browser: 27 | 28 | http://localhost:4004/hr-approval-ui/webapp/index.html 29 | 30 | #### Pre-requisites: 31 | 32 | 1. Active NodeJS LTS (Long Term Support) version and associated supported NPM version. (See https://nodejs.org) 33 | 34 | 35 | -------------------------------------------------------------------------------- /samples/rag-quickstart-app/app/hr-approval-ui/annotations.cds: -------------------------------------------------------------------------------- 1 | using ChatService as service from '../../srv/chat-service'; -------------------------------------------------------------------------------- /samples/rag-quickstart-app/app/hr-approval-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hr-approval-ui", 3 | "version": "0.0.1", 4 | "description": "An SAP Fiori application.", 5 | "keywords": [ 6 | "ui5", 7 | "openui5", 8 | "sapui5" 9 | ], 10 | "main": "webapp/index.html", 11 | "scripts": { 12 | "deploy-config": "npx -p @sap/ux-ui5-tooling fiori add deploy-config cf", 13 | "build:cf": "ui5 build preload --clean-dest --config ui5-deploy.yaml --include-task=generateCachebusterInfo" 14 | }, 15 | "devDependencies": { 16 | "@sap/ui5-builder-webide-extension": "^1.1.8", 17 | "ui5-task-zipper": "^0.5.0", 18 | "mbt": "^1.2.18", 19 | "@ui5/cli": "^2.14.10" 20 | }, 21 | "ui5": { 22 | "dependencies": [ 23 | "@sap/ui5-builder-webide-extension", 24 | "ui5-task-zipper", 25 | "mbt" 26 | ] 27 | } 28 | } -------------------------------------------------------------------------------- /samples/rag-quickstart-app/app/hr-approval-ui/ui5-deploy.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json 2 | specVersion: '2.4' 3 | metadata: 4 | name: hrapprovalui 5 | type: application 6 | resources: 7 | configuration: 8 | propertiesFileSourceEncoding: UTF-8 9 | builder: 10 | resources: 11 | excludes: 12 | - "/test/**" 13 | - "/localService/**" 14 | customTasks: 15 | - name: webide-extension-task-updateManifestJson 16 | afterTask: replaceVersion 17 | configuration: 18 | appFolder: webapp 19 | destDir: dist 20 | - name: ui5-task-zipper 21 | afterTask: generateCachebusterInfo 22 | configuration: 23 | archiveName: hrapprovalui 24 | additionalFiles: 25 | - xs-app.json 26 | -------------------------------------------------------------------------------- /samples/rag-quickstart-app/app/hr-approval-ui/ui5.yaml: -------------------------------------------------------------------------------- 1 | specVersion: "2.5" 2 | metadata: 3 | name: hrapprovalui 4 | type: application 5 | framework: 6 | name: OpenUI5 7 | version: "1.120.7" 8 | libraries: 9 | - name: sap.m 10 | - name: sap.ui.core 11 | - name: themelib_sap_horizon 12 | - name: sap.f 13 | - name: sap.ui.layout 14 | - name: sap.ui.unified 15 | -------------------------------------------------------------------------------- /samples/rag-quickstart-app/app/hr-approval-ui/webapp/Component.js: -------------------------------------------------------------------------------- 1 | /** 2 | * eslint-disable @sap/ui5-jsdocs/no-jsdoc 3 | */ 4 | 5 | sap.ui.define([ 6 | "sap/ui/core/UIComponent", 7 | "sap/ui/Device", 8 | "hrapprovalui/model/models" 9 | ], 10 | function (UIComponent, Device, models) { 11 | "use strict"; 12 | 13 | return UIComponent.extend("hrapprovalui.Component", { 14 | metadata: { 15 | manifest: "json" 16 | }, 17 | 18 | /** 19 | * The component is initialized by UI5 automatically during the startup of the app and calls the init method once. 20 | * @public 21 | * @override 22 | */ 23 | init: function () { 24 | // call the base component's init function 25 | UIComponent.prototype.init.apply(this, arguments); 26 | 27 | // enable routing 28 | this.getRouter().initialize(); 29 | 30 | // set the device model 31 | this.setModel(models.createDeviceModel(), "device"); 32 | } 33 | }); 34 | } 35 | ); -------------------------------------------------------------------------------- /samples/rag-quickstart-app/app/hr-approval-ui/webapp/controller/App.controller.js: -------------------------------------------------------------------------------- 1 | sap.ui.define( 2 | [ 3 | "sap/ui/core/mvc/Controller" 4 | ], 5 | function(BaseController) { 6 | "use strict"; 7 | 8 | return BaseController.extend("hrapprovalui.controller.App", { 9 | onInit: function() { 10 | sessionStorage.setItem("isDeployedVersion", "true"); 11 | } 12 | }); 13 | } 14 | ); 15 | -------------------------------------------------------------------------------- /samples/rag-quickstart-app/app/hr-approval-ui/webapp/css/style.css: -------------------------------------------------------------------------------- 1 | /* Enter your custom styles here */ 2 | .sapFAvatar.sapFAvatarInitials > .sapFAvatarInitialsHolder { 3 | font-size: large; 4 | } 5 | 6 | .sapMScrollCont { 7 | padding-bottom: 0.75rem; 8 | box-sizing: border-box; 9 | -webkit-box-flex: 1; 10 | flex: 1; 11 | position: relative; 12 | -webkit-flex: 1; 13 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 14 | } 15 | 16 | .sapUiSizeCompact .sapTntNavLI { 17 | padding: 1.5rem 0.75rem 0.75rem 0.75rem; 18 | box-sizing: border-box; 19 | position: relative; 20 | overflow: hidden; 21 | width: 100%; 22 | } 23 | 24 | .sapUiSizeCompact .sapMPageWithFooter.sapMPageWithFloatingFooter > section { 25 | bottom: 0; 26 | scroll-padding-bottom: calc(2.5rem + 0.5rem + 2px); 27 | padding-bottom: 4.5rem; 28 | padding-top: 1rem; 29 | } 30 | -------------------------------------------------------------------------------- /samples/rag-quickstart-app/app/hr-approval-ui/webapp/i18n/i18n.properties: -------------------------------------------------------------------------------- 1 | # This is the resource bundle for hrapprovalui 2 | 3 | #Texts for manifest.json 4 | 5 | #XTIT: Application name 6 | appTitle=Chat Demo of CAP LLM Plugin 7 | 8 | #YDES: Application description 9 | appDescription=An SAP Fiori application. 10 | #XTIT: Main view title 11 | title=Chat Demo of CAP LLM Plugin 12 | 13 | flpTitle=HR Approval Demo 14 | 15 | flpSubtitle=Powered by CAP LLM Plugin and SuccessFactor 16 | -------------------------------------------------------------------------------- /samples/rag-quickstart-app/app/hr-approval-ui/webapp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | This is App Title 8 | 13 | 25 | 26 | 27 |
    34 | 35 | -------------------------------------------------------------------------------- /samples/rag-quickstart-app/app/hr-approval-ui/webapp/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "_version": "1.59.0", 3 | "sap.app": { 4 | "id": "hrapprovalui", 5 | "type": "application", 6 | "i18n": "i18n/i18n.properties", 7 | "applicationVersion": { 8 | "version": "0.0.1" 9 | }, 10 | "title": "{{appTitle}}", 11 | "description": "{{appDescription}}", 12 | "resources": "resources.json", 13 | "sourceTemplate": { 14 | "id": "@sap/generator-fiori:basic", 15 | "version": "1.12.4", 16 | "toolsId": "2f10dee8-63c3-46f0-a270-d704153ff59a" 17 | }, 18 | "dataSources": { 19 | "mainService": { 20 | "uri": "/odata/v4/chat/", 21 | "type": "OData", 22 | "settings": { 23 | "annotations": [], 24 | "odataVersion": "4.0" 25 | } 26 | }, 27 | "fileService":{ 28 | "uri": "/odata/v4/embedding-storage/", 29 | "type": "OData", 30 | "settings": { 31 | "annotations": [], 32 | "odataVersion": "4.0" 33 | } 34 | } 35 | }, 36 | "crossNavigation": { 37 | "inbounds": { 38 | "Chat-Display": { 39 | "semanticObject": "Chat", 40 | "action": "Display", 41 | "title": "{{flpTitle}}", 42 | "subTitle": "{{flpSubtitle}}", 43 | "signature": { 44 | "parameters": {}, 45 | "additionalParameters": "allowed" 46 | } 47 | } 48 | } 49 | } 50 | }, 51 | "sap.ui": { 52 | "technology": "UI5", 53 | "icons": { 54 | "icon": "", 55 | "favIcon": "", 56 | "phone": "", 57 | "phone@2": "", 58 | "tablet": "", 59 | "tablet@2": "" 60 | }, 61 | "deviceTypes": { 62 | "desktop": true, 63 | "tablet": true, 64 | "phone": true 65 | } 66 | }, 67 | "sap.ui5": { 68 | "flexEnabled": true, 69 | "dependencies": { 70 | "minUI5Version": "1.120.9", 71 | "libs": { 72 | "sap.m": {}, 73 | "sap.ui.core": {}, 74 | "sap.f": {}, 75 | "sap.suite.ui.generic.template": {}, 76 | "sap.ui.comp": {}, 77 | "sap.ui.generic.app": {}, 78 | "sap.ui.table": {}, 79 | "sap.ushell": {}, 80 | "sap.ui.unified": {} 81 | } 82 | }, 83 | "contentDensities": { 84 | "compact": true, 85 | "cozy": true 86 | }, 87 | "models": { 88 | "i18n": { 89 | "type": "sap.ui.model.resource.ResourceModel", 90 | "settings": { 91 | "bundleName": "hrapprovalui.i18n.i18n" 92 | } 93 | }, 94 | "": { 95 | "dataSource": "mainService", 96 | "preload": true, 97 | "settings": { 98 | "synchronizationMode": "None", 99 | "operationMode": "Server", 100 | "autoExpandSelect": true, 101 | "earlyRequests": true 102 | } 103 | }, 104 | "files":{ 105 | "dataSource": "fileService", 106 | "preload": true, 107 | "settings": { 108 | "synchronizationMode": "None", 109 | "operationMode": "Server", 110 | "autoExpandSelect": true, 111 | "earlyRequests": true 112 | } 113 | } 114 | }, 115 | "resources": { 116 | "css": [ 117 | { 118 | "uri": "css/style.css" 119 | } 120 | ] 121 | }, 122 | "rootView": { 123 | "viewName": "hrapprovalui.view.App", 124 | "type": "XML", 125 | "async": true, 126 | "id": "App" 127 | }, 128 | "routing": { 129 | "config": { 130 | "routerClass": "sap.f.routing.Router", 131 | "viewType": "XML", 132 | "async": true, 133 | "viewPath": "hrapprovalui.view", 134 | "controlId": "flexibleColumnLayout", 135 | "transition": "slide" 136 | }, 137 | "routes": [ 138 | { 139 | "pattern": "", 140 | "name": "home", 141 | "target":[ 142 | "leftScreen", 143 | "initialRightScreen" 144 | ], 145 | "layout": "TwoColumnsMidExpanded" 146 | }, 147 | { 148 | "pattern": "conversation/{conversationID}", 149 | "name": "conversation", 150 | "target":[ 151 | "leftScreen", 152 | "officalRightScreen" 153 | ], 154 | "layout": "TwoColumnsMidExpanded" 155 | } 156 | ], 157 | "targets": { 158 | "leftScreen":{ 159 | "viewId": "lefeScreenPage", 160 | "viewName": "LeftScreen", 161 | "controlAggregation": "beginColumnPages" 162 | }, 163 | "initialRightScreen":{ 164 | "viewId": "initialRightScreen", 165 | "viewName": "InitialRightScreen", 166 | "controlAggregation": "midColumnPages" 167 | }, 168 | "officalRightScreen":{ 169 | "viewId": "officalRightScreen", 170 | "viewName": "OfficalRightScreen", 171 | "controlAggregation": "midColumnPages" 172 | } 173 | } 174 | } 175 | }, 176 | "sap.cloud": { 177 | "public": true, 178 | "service": "ragquickstart.app" 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /samples/rag-quickstart-app/app/hr-approval-ui/webapp/model/models.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/model/json/JSONModel", 3 | "sap/ui/Device" 4 | ], 5 | /** 6 | * provide app-view type models (as in the first "V" in MVVC) 7 | * 8 | * @param {typeof sap.ui.model.json.JSONModel} JSONModel 9 | * @param {typeof sap.ui.Device} Device 10 | * 11 | * @returns {Function} createDeviceModel() for providing runtime info for the device the UI5 app is running on 12 | */ 13 | function (JSONModel, Device) { 14 | "use strict"; 15 | 16 | return { 17 | createDeviceModel: function () { 18 | var oModel = new JSONModel(Device); 19 | oModel.setDefaultBindingMode("OneWay"); 20 | return oModel; 21 | } 22 | }; 23 | }); -------------------------------------------------------------------------------- /samples/rag-quickstart-app/app/hr-approval-ui/webapp/test/flpSandbox.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{appTitle}} 9 | 10 | 25 | 56 | 57 | 58 | 59 | 70 | 71 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /samples/rag-quickstart-app/app/hr-approval-ui/webapp/view/App.view.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /samples/rag-quickstart-app/app/hr-approval-ui/webapp/view/FileUploading.fragment.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | 31 | 32 | 41 | 46 | 51 | 56 | 57 | 58 | 59 | 60 | 61 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /samples/rag-quickstart-app/app/hr-approval-ui/webapp/view/InitialRightScreen.view.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 16 | 17 | 18 | 25 | 33 | 34 | 35 | 36 | 37 |
    38 | 43 | 51 | 52 | 53 |
    54 | 55 |
    56 | 57 |
    -------------------------------------------------------------------------------- /samples/rag-quickstart-app/app/hr-approval-ui/webapp/view/LeftScreen.view.xml: -------------------------------------------------------------------------------- 1 | 9 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 25 | 26 | 32 | 33 | 34 | 35 | 40 | 58 | 64 | 65 | 66 | 67 | 69 | 70 | 75 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /samples/rag-quickstart-app/app/hr-approval-ui/webapp/view/OfficalRightScreen.view.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 16 | 17 | 19 | 31 | 41 | 42 | 43 | 44 | 45 |
    46 | 51 | 59 | 60 | 61 |
    62 | 63 |
    64 | 65 |
    -------------------------------------------------------------------------------- /samples/rag-quickstart-app/app/hr-approval-ui/xs-app.json: -------------------------------------------------------------------------------- 1 | { 2 | "welcomeFile": "/index.html", 3 | "authenticationMethod": "route", 4 | "routes": [ 5 | { 6 | "source": "^/odata/v4/chat/(.*)$", 7 | "target": "/odata/v4/chat/$1", 8 | "destination": "rag-quick-start-app-srv", 9 | "authenticationType": "xsuaa", 10 | "csrfProtection": false, 11 | "cacheControl": "no-cache, no-store, must-revalidate" 12 | }, 13 | { 14 | "source": "^/odata/v4/embedding-storage/(.*)$", 15 | "target": "/odata/v4/embedding-storage/$1", 16 | "destination": "rag-quick-start-app-srv", 17 | "authenticationType": "xsuaa", 18 | "csrfProtection": false, 19 | "cacheControl": "no-cache, no-store, must-revalidate" 20 | }, 21 | { 22 | "source": "^/user-api/currentUser$", 23 | "target": "/currentUser", 24 | "service": "sap-approuter-userapi" 25 | }, 26 | { 27 | "source": "^/resources/(.*)$", 28 | "target": "/resources/$1", 29 | "authenticationType": "none", 30 | "destination": "ui5" 31 | }, 32 | { 33 | "source": "^/test-resources/(.*)$", 34 | "target": "/test-resources/$1", 35 | "authenticationType": "none", 36 | "destination": "ui5" 37 | }, 38 | { 39 | "source": "^(.*)$", 40 | "target": "$1", 41 | "service": "html5-apps-repo-rt", 42 | "authenticationType": "xsuaa" 43 | } 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /samples/rag-quickstart-app/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "approuter", 3 | "dependencies": { 4 | "@sap/approuter": "^14.0.0" 5 | }, 6 | "engines": { 7 | "node": "^18.0.0" 8 | }, 9 | "scripts": { 10 | "start": "node node_modules/@sap/approuter/approuter.js" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /samples/rag-quickstart-app/app/services.cds: -------------------------------------------------------------------------------- 1 | 2 | using from './hr-approval-ui/annotations'; -------------------------------------------------------------------------------- /samples/rag-quickstart-app/app/xs-app.json: -------------------------------------------------------------------------------- 1 | { 2 | "welcomeFile": "app/index.html", 3 | "routes": [ 4 | { 5 | "source": "^/app/(.*)$", 6 | "target": "$1", 7 | "localDir": ".", 8 | "cacheControl": "no-cache, no-store, must-revalidate" 9 | }, 10 | { 11 | "source": "^/(.*)$", 12 | "target": "$1", 13 | "destination": "srv-api", 14 | "csrfProtection": true 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /samples/rag-quickstart-app/db/schema.cds: -------------------------------------------------------------------------------- 1 | namespace sap.tisce.demo; 2 | 3 | using { 4 | cuid, 5 | managed 6 | } from '@sap/cds/common'; 7 | 8 | entity Conversation { 9 | 10 | key cID : UUID not null; 11 | userID: String; 12 | creation_time: Timestamp; 13 | last_update_time: Timestamp; 14 | title: String; 15 | to_messages: Composition of many Message on to_messages.cID = $self; 16 | } 17 | 18 | entity Message { 19 | 20 | key cID: Association to Conversation; 21 | key mID: UUID not null; 22 | role: String; 23 | content: LargeString; 24 | creation_time: Timestamp; 25 | } 26 | 27 | entity DocumentChunk 28 | { 29 | text_chunk: LargeString; 30 | metadata_column: LargeString; 31 | embedding: Vector(1536); 32 | } 33 | 34 | 35 | entity Files: cuid, managed{ 36 | @Core.MediaType: mediaType @Core.ContentDisposition.Filename: fileName 37 | content: LargeBinary; 38 | @Core.IsMediaType: true 39 | mediaType: String; 40 | fileName: String; 41 | size: String; 42 | } 43 | 44 | -------------------------------------------------------------------------------- /samples/rag-quickstart-app/db/src/.hdiconfig: -------------------------------------------------------------------------------- 1 | { 2 | "file_suffixes": { 3 | "csv": { 4 | "plugin_name": "com.sap.hana.di.tabledata.source" 5 | }, 6 | "hdbafllangprocedure": { 7 | "plugin_name": "com.sap.hana.di.afllangprocedure" 8 | }, 9 | "hdbanalyticprivilege": { 10 | "plugin_name": "com.sap.hana.di.analyticprivilege" 11 | }, 12 | "hdbcalculationview": { 13 | "plugin_name": "com.sap.hana.di.calculationview" 14 | }, 15 | "hdbcollection": { 16 | "plugin_name": "com.sap.hana.di.collection" 17 | }, 18 | "hdbconstraint": { 19 | "plugin_name": "com.sap.hana.di.constraint" 20 | }, 21 | "hdbdropcreatetable": { 22 | "plugin_name": "com.sap.hana.di.dropcreatetable" 23 | }, 24 | "hdbflowgraph": { 25 | "plugin_name": "com.sap.hana.di.flowgraph" 26 | }, 27 | "hdbfunction": { 28 | "plugin_name": "com.sap.hana.di.function" 29 | }, 30 | "hdbgraphworkspace": { 31 | "plugin_name": "com.sap.hana.di.graphworkspace" 32 | }, 33 | "hdbhadoopmrjob": { 34 | "plugin_name": "com.sap.hana.di.virtualfunctionpackage.hadoop" 35 | }, 36 | "hdbindex": { 37 | "plugin_name": "com.sap.hana.di.index" 38 | }, 39 | "hdblibrary": { 40 | "plugin_name": "com.sap.hana.di.library" 41 | }, 42 | "hdbmigrationtable": { 43 | "plugin_name": "com.sap.hana.di.table.migration" 44 | }, 45 | "hdbprocedure": { 46 | "plugin_name": "com.sap.hana.di.procedure" 47 | }, 48 | "hdbprojectionview": { 49 | "plugin_name": "com.sap.hana.di.projectionview" 50 | }, 51 | "hdbprojectionviewconfig": { 52 | "plugin_name": "com.sap.hana.di.projectionview.config" 53 | }, 54 | "hdbreptask": { 55 | "plugin_name": "com.sap.hana.di.reptask" 56 | }, 57 | "hdbresultcache": { 58 | "plugin_name": "com.sap.hana.di.resultcache" 59 | }, 60 | "hdbrole": { 61 | "plugin_name": "com.sap.hana.di.role" 62 | }, 63 | "hdbroleconfig": { 64 | "plugin_name": "com.sap.hana.di.role.config" 65 | }, 66 | "hdbsearchruleset": { 67 | "plugin_name": "com.sap.hana.di.searchruleset" 68 | }, 69 | "hdbsequence": { 70 | "plugin_name": "com.sap.hana.di.sequence" 71 | }, 72 | "hdbstatistics": { 73 | "plugin_name": "com.sap.hana.di.statistics" 74 | }, 75 | "hdbstructuredprivilege": { 76 | "plugin_name": "com.sap.hana.di.structuredprivilege" 77 | }, 78 | "hdbsynonym": { 79 | "plugin_name": "com.sap.hana.di.synonym" 80 | }, 81 | "hdbsynonymconfig": { 82 | "plugin_name": "com.sap.hana.di.synonym.config" 83 | }, 84 | "hdbsystemversioning": { 85 | "plugin_name": "com.sap.hana.di.systemversioning" 86 | }, 87 | "hdbtable": { 88 | "plugin_name": "com.sap.hana.di.table" 89 | }, 90 | "hdbtabledata": { 91 | "plugin_name": "com.sap.hana.di.tabledata" 92 | }, 93 | "hdbtabletype": { 94 | "plugin_name": "com.sap.hana.di.tabletype" 95 | }, 96 | "hdbtrigger": { 97 | "plugin_name": "com.sap.hana.di.trigger" 98 | }, 99 | "hdbview": { 100 | "plugin_name": "com.sap.hana.di.view" 101 | }, 102 | "hdbvirtualfunction": { 103 | "plugin_name": "com.sap.hana.di.virtualfunction" 104 | }, 105 | "hdbvirtualfunctionconfig": { 106 | "plugin_name": "com.sap.hana.di.virtualfunction.config" 107 | }, 108 | "hdbvirtualpackagehadoop": { 109 | "plugin_name": "com.sap.hana.di.virtualpackage.hadoop" 110 | }, 111 | "hdbvirtualpackagesparksql": { 112 | "plugin_name": "com.sap.hana.di.virtualpackage.sparksql" 113 | }, 114 | "hdbvirtualprocedure": { 115 | "plugin_name": "com.sap.hana.di.virtualprocedure" 116 | }, 117 | "hdbvirtualprocedureconfig": { 118 | "plugin_name": "com.sap.hana.di.virtualprocedure.config" 119 | }, 120 | "hdbvirtualtable": { 121 | "plugin_name": "com.sap.hana.di.virtualtable" 122 | }, 123 | "hdbvirtualtableconfig": { 124 | "plugin_name": "com.sap.hana.di.virtualtable.config" 125 | }, 126 | "properties": { 127 | "plugin_name": "com.sap.hana.di.tabledata.properties" 128 | }, 129 | "tags": { 130 | "plugin_name": "com.sap.hana.di.tabledata.properties" 131 | }, 132 | "txt": { 133 | "plugin_name": "com.sap.hana.di.copyonly" 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /samples/rag-quickstart-app/db/undeploy.json: -------------------------------------------------------------------------------- 1 | [ 2 | "src/gen/**/*.hdbview", 3 | "src/gen/**/*.hdbindex", 4 | "src/gen/**/*.hdbconstraint", 5 | "src/gen/**/*_drafts.hdbtable" 6 | ] 7 | -------------------------------------------------------------------------------- /samples/rag-quickstart-app/docs/images/BTP-login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-samples/cap-llm-plugin-samples/0eb4b50433fdfcfc29071203374cce4f8653f6bb/samples/rag-quickstart-app/docs/images/BTP-login.png -------------------------------------------------------------------------------- /samples/rag-quickstart-app/docs/images/generate-embeddings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-samples/cap-llm-plugin-samples/0eb4b50433fdfcfc29071203374cce4f8653f6bb/samples/rag-quickstart-app/docs/images/generate-embeddings.png -------------------------------------------------------------------------------- /samples/rag-quickstart-app/docs/images/hybrid-testing-ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-samples/cap-llm-plugin-samples/0eb4b50433fdfcfc29071203374cce4f8653f6bb/samples/rag-quickstart-app/docs/images/hybrid-testing-ui.png -------------------------------------------------------------------------------- /samples/rag-quickstart-app/docs/images/qna.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-samples/cap-llm-plugin-samples/0eb4b50433fdfcfc29071203374cce4f8653f6bb/samples/rag-quickstart-app/docs/images/qna.png -------------------------------------------------------------------------------- /samples/rag-quickstart-app/docs/images/rag-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-samples/cap-llm-plugin-samples/0eb4b50433fdfcfc29071203374cce4f8653f6bb/samples/rag-quickstart-app/docs/images/rag-arch.png -------------------------------------------------------------------------------- /samples/rag-quickstart-app/docs/images/upload-document.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-samples/cap-llm-plugin-samples/0eb4b50433fdfcfc29071203374cce4f8653f6bb/samples/rag-quickstart-app/docs/images/upload-document.png -------------------------------------------------------------------------------- /samples/rag-quickstart-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rag-quick-start-app", 3 | "version": "1.0.0", 4 | "description": "A simple CAP project.", 5 | "repository": "", 6 | "license": "UNLICENSED", 7 | "private": true, 8 | "dependencies": { 9 | "@sap-cloud-sdk/http-client": "^3.12.0", 10 | "@sap/cds": "^7.6.3", 11 | "@sap/cds-compiler": "^4.6.2", 12 | "@sap/cds-hana": "^2", 13 | "@sap/xssec": "^3", 14 | "basic-auth": "^2.0.1", 15 | "cap-llm-plugin": "^1.4.4", 16 | "cds-swagger-ui-express": "^0.8.0", 17 | "cors": "^2.8.5", 18 | "dotenv": "^16.4.4", 19 | "downloadjs": "^1.4.7", 20 | "express": "^4", 21 | "langchain": "^0.1.19", 22 | "passport": "^0.7.0", 23 | "pdf-lib": "^1.17.1", 24 | "pdf-parse": "^1.1.1", 25 | "pdfkit": "^0.14.0", 26 | "uuid": "^9.0.1" 27 | }, 28 | "devDependencies": { 29 | "@cap-js/sqlite": "^1", 30 | "@sap/cds-dk": "^8", 31 | "@sap/ux-specification": "^1.120.4", 32 | "rimraf": "^3.0.2", 33 | "axios": "^1.5.1", 34 | "chai": "^4.3.10", 35 | "chai-as-promised": "^7.1.1", 36 | "chai-http": "^4.4.0", 37 | "chai-subset": "^1.6.0", 38 | "hana-cli": "^3.202312.1", 39 | "jest": "^29.7.0", 40 | "mocha": "^10.3.0" 41 | }, 42 | "scripts": { 43 | "start": "cds-serve", 44 | "watch-hr-approval-ui": "cds watch --open hr-approval-ui/webapp/index.html?sap-ui-xx-viewCache=false", 45 | "undeploy": "cf undeploy rag-quick-start-app --delete-services --delete-service-keys --delete-service-brokers", 46 | "build": "rimraf resources mta_archives && mbt build --mtar archive", 47 | "deploy": "cf deploy mta_archives/archive.mtar --retries 1" 48 | }, 49 | "cds": { 50 | "requires": { 51 | "db": "hana", 52 | "gen-ai-hub": { 53 | "gpt-4": { 54 | "destinationName": "GenAIHubDestination", 55 | "deploymentUrl": "/v2/inference/deployments/{deploymentID like d15b199f47cf6e11}", 56 | "resourceGroup": "default", 57 | "apiVersion": "2024-02-15-preview", 58 | "modelName": "gpt-4" 59 | }, 60 | "text-embedding-ada-002": { 61 | "destinationName": "GenAIHubDestination", 62 | "deploymentUrl": "/v2/inference/deployments/{deploymentID like d15b199f47cf6e11}", 63 | "resourceGroup": "default", 64 | "apiVersion": "2024-02-15-preview", 65 | "modelName": "text-embedding-ada-002" 66 | } 67 | }, 68 | "GenAIHubDestination": { 69 | "kind": "rest", 70 | "credentials": { 71 | "destination": "aicore-destination", 72 | "requestTimeout": "300000" 73 | } 74 | }, 75 | "[hybrid]": { 76 | "db": "hana", 77 | "cap-llm-plugin": { 78 | "impl": "cap-llm-plugin/srv/cap-llm-plugin.js" 79 | }, 80 | "destinations": true, 81 | "auth": { 82 | "passport": { 83 | "strategy": "mock", 84 | "users": { 85 | "JohnDole@tester.sap.com": { 86 | "ID": "dummy.user@com", 87 | "password": "initial" 88 | } 89 | } 90 | } 91 | } 92 | }, 93 | "[production]": { 94 | "auth": "xsuaa", 95 | "db": "hana", 96 | "cap-llm-plugin": { 97 | "impl": "cap-llm-plugin/srv/cap-llm-plugin.js" 98 | }, 99 | "destinations": true 100 | } 101 | }, 102 | "sapux": [ 103 | "app/hr-approval-ui" 104 | ] 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /samples/rag-quickstart-app/srv/chat-service.cds: -------------------------------------------------------------------------------- 1 | using {sap.tisce.demo as db} from '../db/schema'; 2 | 3 | service ChatService @(requires: 'authenticated-user') { 4 | 5 | entity Conversation @(restrict: [{ 6 | grant: ['READ','WRITE', 'DELETE'], 7 | where: 'userID = $user' 8 | }]) as projection on db.Conversation; 9 | entity Message as projection on db.Message; 10 | 11 | type RagResponse_AdditionalContents { 12 | 13 | score : String; 14 | pageContent : String; 15 | } 16 | 17 | type RagResponse { 18 | role : String; 19 | content : String; 20 | messageTime : String; 21 | additionalContents : array of RagResponse_AdditionalContents; 22 | } 23 | 24 | action getChatRagResponse(conversationId : String, messageId : String, message_time : Timestamp, user_id : String, user_query : String) returns RagResponse; 25 | function deleteChatData() returns String; 26 | } 27 | -------------------------------------------------------------------------------- /samples/rag-quickstart-app/srv/embedding-storage.cds: -------------------------------------------------------------------------------- 1 | using {sap.tisce.demo as db} from '../db/schema'; 2 | 3 | service EmbeddingStorageService @(requires: 'authenticated-user') { 4 | 5 | entity DocumentChunk as 6 | projection on db.DocumentChunk 7 | excluding { 8 | embedding 9 | }; 10 | 11 | entity Files @(restrict: [{ 12 | grant: [ 13 | 'READ', 14 | 'WRITE', 15 | 'UPDATE', 16 | 'DELETE' 17 | ], 18 | where: 'createdBy = $user' 19 | }]) as projection on db.Files; 20 | 21 | action storeEmbeddings(uuid : String) returns String; 22 | function deleteEmbeddings() returns String; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /samples/rag-quickstart-app/srv/memory-helper.js: -------------------------------------------------------------------------------- 1 | /* Optional..Good to have feature to persist conversations in HANA Cloud */ 2 | 3 | const cds = require('@sap/cds'); 4 | const { INSERT, SELECT, UPDATE } = cds.ql; 5 | const { v4: uuidv4 } = require('uuid'); 6 | 7 | // Helper method to get the current timestamp 8 | function getCurrentTimestamp() { 9 | return new Date().toISOString(); 10 | } 11 | 12 | // Helper method to insert the messages and update the latest conversation timestamp in db 13 | async function insertMessage(messageEntity, messageRecord, conversationId, conversationEntity, messageTime) { 14 | 15 | console.log(`Inserting new message for conversation id: ${conversationId}`); 16 | const messageInsertionStatus = await INSERT.into(messageEntity).entries([messageRecord]); 17 | if (!messageInsertionStatus) { 18 | throw new Error("Insertion of message into db failed!"); 19 | }; 20 | 21 | console.log(`Updating the time for conversation id: ${conversationId}`); 22 | const updateConversationStatus = await UPDATE(conversationEntity).set`last_update_time = ${messageTime}`.where`cID = ${conversationId}`; 23 | if (updateConversationStatus !== 1) { 24 | throw new Error("Updating the conversation time failed!"); 25 | } 26 | } 27 | 28 | // Helper method to handle conversation memory in HANA CLoud before RAG LLM call. 29 | async function storeRetrieveMessages(conversationId, messageId, message_time, user_id, user_query, Conversation, Message, modelName) { 30 | try { 31 | 32 | let memoryContext = []; 33 | // Check if conversation exists in the db 34 | const isConversationPresent = await SELECT.from(Conversation).where({ "cID": conversationId }); 35 | // If conversation is present, select messages from db and store it in memory context obj 36 | 37 | if (isConversationPresent.length > 0) { 38 | console.log(`Retrieving messages for conversation id: ${conversationId}`); 39 | const messageSelectStmt = await SELECT.from(Message).where({ "cID_cID": conversationId }).orderBy('creation_time'); 40 | //parse the memory context for different models 41 | 42 | if (messageSelectStmt.length > 0) { 43 | //for gpt-4 or anthropic--claude-3-sonnet models 44 | if (modelName === "gpt-4" ) { 45 | messageSelectStmt.forEach(message => { 46 | memoryContext.push({ 47 | "role": message.role, 48 | "content": message.content 49 | }); 50 | }); 51 | } 52 | //Parse the responses of other chat models supported by the CAP LLM Plugin 53 | else { 54 | throw new Error(`Model ${modelName} not supported.`); 55 | } 56 | } 57 | else { 58 | throw new Error(`Messages corresponding to conversation id: ${conversationId} not present!`) ; 59 | } 60 | } 61 | 62 | // If conversation is not present, insert the conversation into db 63 | else { 64 | //const conversationTitle = await getConversationSummarization(user_query); 65 | console.log(`Inserting new conversation for conversation id: ${conversationId}`); 66 | const currentTimestamp = getCurrentTimestamp(); 67 | const conversationEntry = { 68 | "cID": conversationId, 69 | "userID": user_id, 70 | "creation_time": currentTimestamp, 71 | "last_update_time": currentTimestamp, 72 | "title": user_query, 73 | }; 74 | const conversationInsertStatus = await INSERT.into(Conversation).entries([conversationEntry]); 75 | if (!conversationInsertStatus) { 76 | throw new Error("Insertion of conversation into db failed!"); 77 | }; 78 | } 79 | 80 | // In both cases, insert the message into db 81 | const messageRecord = { 82 | "cID_cID": conversationId, 83 | "mID": messageId, 84 | "role": "user", 85 | "content": user_query, 86 | "creation_time": message_time 87 | }; 88 | 89 | await insertMessage(Message, messageRecord, conversationId, Conversation, message_time); 90 | return memoryContext; 91 | } 92 | 93 | catch (error) { 94 | // Handle any errors that occur during the execution 95 | console.log('Error handling memory prior to RAG response:', error); 96 | throw error; 97 | } 98 | } 99 | 100 | // Helper method to handle conversation memory in HANA CLoud after RAG LLM call. 101 | async function storeModelResponse(conversationId, message_time, chatRagResponse, Message, Conversation) { 102 | try { 103 | const aiMessageRecord = { 104 | "cID_cID": conversationId, 105 | "mID": uuidv4(), 106 | "role": chatRagResponse.role, 107 | "content": chatRagResponse.content, 108 | "creation_time": message_time 109 | }; 110 | 111 | // Insert the assistant message to db 112 | await insertMessage(Message, aiMessageRecord, conversationId, Conversation, getCurrentTimestamp()); 113 | } 114 | catch (error) { 115 | // Handle any errors that occur during the execution 116 | console.log('Error handling memory post RAG response:', error); 117 | throw error; 118 | } 119 | 120 | } 121 | 122 | 123 | module.exports = { 124 | storeRetrieveMessages, 125 | storeModelResponse 126 | }; -------------------------------------------------------------------------------- /samples/rag-quickstart-app/srv/server.js: -------------------------------------------------------------------------------- 1 | const cds = require("@sap/cds"); 2 | const cors = require("cors"); 3 | 4 | cds.on("bootstrap", (app) => { 5 | app.use(cors()); 6 | }); 7 | module.exports = cds.server; 8 | 9 | const cds_swagger = require("cds-swagger-ui-express") 10 | /* 11 | * Use User Provided Variable to distinguish DEV, UAT, STAGE, PROD env deployment 12 | */ 13 | 14 | // if (process.env.dev == 'true' || process.env.NODE_ENV !== 'production') { 15 | // const cds_swagger = require('cds-swagger-ui-express'); 16 | // cds.on('bootstrap', app => app.use(cds_swagger())); 17 | // } 18 | cds.on('bootstrap', app => app.use(cds_swagger())) -------------------------------------------------------------------------------- /samples/rag-quickstart-app/xs-security.json: -------------------------------------------------------------------------------- 1 | { 2 | "xsappname": "rag-quick-start-app", 3 | "tenant-mode": "dedicated", 4 | "description": "Security profile of called application", 5 | "scopes": [], 6 | "attributes": [], 7 | "role-templates": [] 8 | } 9 | --------------------------------------------------------------------------------