├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── LICENSES └── Apache-2.0.txt ├── README.md ├── REUSE.toml └── exercises ├── java ├── README.md ├── app │ ├── .env │ ├── pom.xml │ └── src │ │ └── main │ │ ├── java │ │ └── org │ │ │ └── demo │ │ │ └── dsag │ │ │ ├── Application.java │ │ │ └── OrchestrationController.java │ │ └── resources │ │ └── application.properties ├── ex0 │ └── README.md ├── ex1 │ └── README.md ├── ex2 │ └── README.md ├── ex3 │ └── README.md └── ex4 │ └── README.md ├── javascript ├── README.md ├── app │ ├── .env │ ├── .prettierignore │ ├── .prettierrc │ ├── eslint.config.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── index.css │ ├── src │ │ ├── indexPage.ts │ │ ├── orchestration.ts │ │ ├── server.ts │ │ └── util.ts │ └── tsconfig.json ├── ex0 │ └── README.md ├── ex1 │ └── README.md ├── ex2 │ └── README.md └── ex3 │ └── README.md └── python ├── README.md ├── ex1.ipynb ├── ex2.ipynb ├── ex3.ipynb └── ex4.ipynb /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | exercises/javascript/app/dist 3 | exercises/javascript/app/node_modules 4 | /exercises/java/app/target/ 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Code of Conduct 4 | 5 | All members of the project community must abide by the [SAP Open Source Code of Conduct](https://github.com/SAP/.github/blob/main/CODE_OF_CONDUCT.md). 6 | Only by respecting each other we can develop a productive, collaborative community. 7 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting [a project maintainer](.reuse/dep5). 8 | 9 | ## Engaging in Our Project 10 | 11 | We use GitHub to manage reviews of pull requests. 12 | 13 | * If you are a new contributor, see: [Steps to Contribute](#steps-to-contribute) 14 | 15 | * Before implementing your change, create an issue that describes the problem you would like to solve or the code that should be enhanced. Please note that you are willing to work on that issue. 16 | 17 | * The team will review the issue and decide whether it should be implemented as a pull request. In that case, they will assign the issue to you. If the team decides against picking up the issue, the team will post a comment with an explanation. 18 | 19 | ## Steps to Contribute 20 | 21 | Should you wish to work on an issue, please claim it first by commenting on the GitHub issue that you want to work on. This is to prevent duplicated efforts from other contributors on the same issue. 22 | 23 | If you have questions about one of the issues, please comment on them, and one of the maintainers will clarify. 24 | 25 | ## Contributing Code or Documentation 26 | 27 | You are welcome to contribute code in order to fix a bug or to implement a new feature that is logged as an issue. 28 | 29 | The following rule governs code contributions: 30 | 31 | * Contributions must be licensed under the [Apache 2.0 License](./LICENSE) 32 | * Due to legal reasons, contributors will be asked to accept a Developer Certificate of Origin (DCO) when they create the first pull request to this project. This happens in an automated fashion during the submission process. SAP uses [the standard DCO text of the Linux Foundation](https://developercertificate.org/). 33 | 34 | ## Issues and Planning 35 | 36 | * We use GitHub issues to track bugs and enhancement requests. 37 | 38 | * Please provide as much context as possible when you open an issue. The information you provide must be comprehensive enough to reproduce that issue for the assignee. 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSES/Apache-2.0.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![REUSE status](https://api.reuse.software/badge/github.com/SAP-samples/teched2024-AI180)](https://api.reuse.software/info/github.com/SAP-samples/teched2024-AI180) 2 | 3 | > [!CAUTION] 4 | > Access to test systems are considered temporal. 5 | > However, in all cases you can still use the sample code and tutorial available in this repository. 6 | > 7 | > If you are part of a workshop session, the speakers will share the credentials with you. 8 | 9 | # AI180 - Build your own generative AI scenario in a Snap 10 | 11 | ## Description 12 | 13 | This repository contains combined material from multiple sessions: 14 | * SAP TechEd 2024 ["AI180 - Build your own generative AI scenario in a Snap"](https://www.sap.com/events/teched/virtual/flow/sap/te24/catalog/page/catalog/session/1722557682293001fTqe). 15 | * DSAG TechXchange 2025 ["H35: Create Your Generative AI Scenario with Ease​"](https://dsagtechxchange.plazz.net/) 16 | 17 | ## Overview 18 | 19 | This session introduces you to integrating orchestration capabilities of Generative AI Hub, SAP AI Core, into your own scenarios using SAP Cloud SDK for AI (JavaScript/Java) or SAP generative AI hub SDK (Python). 20 | Accordingly, this session offers two parallel tracks: 21 | 22 | - [SAP generative AI hub SDK (Python)](exercises/python/README.md) 23 | - [SAP Cloud SDK for AI (JavaScript)](exercises/javascript/README.md) 24 | - [SAP Cloud SDK for AI (Java)](exercises/java/README.md) 25 | 26 | All tracks cover identical core concepts and practical applications, differing only in the programming language and SDK utilized. You may choose the track that best aligns with your expertise or technology stack. You may also opt to experience multiple tracks to broaden your understanding. 27 | 28 | By the end of this session, you will have gained practical experience in leveraging these powerful SDKs to enhance your AI-driven solutions, regardless of your chosen programming environment. Join the session to unlock the full potential of Generative AI in your projects and stay at the forefront of AI integration. 29 | 30 | ## Get started 31 | 32 | Depending on the session, there may be different onboarding steps. 33 | 34 |
DSAG TechXchange 2025 35 | 36 | * Please follow the instructions of the speakers. 37 | * Upon sending an email to a specific address you will receive the required service key. 38 | * It will enable you to interact with live systems. 39 | 40 |
41 | 42 |
SAP TechEd 2024 43 | 44 | Please note: 45 | * As part of this **Jump Start** session, we are offering free access to limited number of hands-on test systems, for limited duration. 46 | * To receive your test system, you must follow the below outlined process for submitting a request. 47 | 48 | 49 | ### Disclaimer 50 | 51 | While requesting for access to hands-on test systems, note the following terms: 52 | 1. **Single Request Policy**: Please submit only one request per individual. Submitting requests on behalf of others is strictly **prohibited**. 53 | 1. **Limited Availability**: Access to the test system is limited to the first **100** applications only. Requests submitted before the start of our session, AI180, will not be considered to ensure fairness. If you are not among the first 100 applicants, you may not receive a test system. 54 | 1. **Email Notification**: If approved, you will receive system access details via email within **24 hours**. 55 | 1. **System Duration**: The test system will be disabled at 17:00 CEST on Wed, ~Oct 16~ Oct 31 (updated). 56 | 1. **Token and Rate Limits**: Each large language model (LLM) used in the exercises has a fixed input and output token limit (smaller than the limit available in production scenarios). In addition, there is a global rate limit per minute for number of requests made. 57 | 1. **Testing/Learning Purposes Only**: The test system is intended solely for testing or learning purposes and **not for any productive or commercial use**. 58 | 1. **Malicious Usage**: Any detection of malicious usage will result in immediate termination of your test system access. Depending on the severity of the abuse, legal action may be pursued. An example of malicious behavior includes repeatedly hitting the rate limit every minute, for more than 8 hours a day. 59 | 1. **Available Models**: We currently offer two large language models for testing: `gemini-1.5-flash` and `meta-llama3-70b-instruct`. 60 | 1. **Feature Changes**: Do not make any business decisions on the contents of this exercise. SAP reserves the right to change or deprecate any feature of the Orchestration Service in future, without any prior notifiation. 61 | 62 | Please note: 63 | * Based on the number of hands-on testing requests and system usage, we may be able to grant access to additional applicants and extend the testing duration. In such cases, we will update this README accordingly, and early applicants on the waiting list will receive access details via email. 64 | * If you have your own AI Core instances for consuming Generative AI Hub capabilities, you can follow this tutorial using those instances. 65 | Please make the following adjustments: 66 | * You need to create a deployment for orchestration by referring the [documentation](https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/create-deployment-for-orchestration?locale=en-US). 67 | * Before beginning the exercises, you’ll need to provide system information. Instead of using the hands-on test system provided for this tutorial, use your own AI Core instance details, such as the AI Core client ID, secret, and authentication URL. 68 | * If you’re running your application on BTP rather than locally, the SDKs will automatically retrieve your AI Core instance information from the VCAP_SERVICES environment variable. 69 | 70 | ### How to Start 71 | 1. **Apply for the Test Systems**: 72 | - Use this [template](#template-for-requesting-the-test-system), fill in your information and send it to `teched.2024.ai180.registration.bot@sap.com`. 73 | 2. **Clone the Repository**: 74 | - Clone the repository using your preferred Git client. If you’re using the Git command line interface, run the following command: 75 | ```bash 76 | git clone https://github.com/SAP-samples/teched2024-AI180.git 77 | ``` 78 | 3. **Select Your Programming Language**: 79 | - Choose between [JavaScript](exercises/javascript/README.md) or [Python](exercises/python/README.md) by clicking the respective links. 80 | 81 | ### Template for Requesting the Test System 82 | ``` 83 | Email Subject: 84 | [TechEd 2024 AI180] Request: Access to Test System 85 | 86 | Email Body: 87 | - Name: 88 | - Company: 89 | - Business Email Address: 90 | ``` 91 | 92 |
93 | 94 | ## Contributing 95 | 96 | Please read the [CONTRIBUTING.md](./CONTRIBUTING.md) to understand the contribution guidelines. 97 | 98 | ## Code of Conduct 99 | 100 | Please read the [SAP Open Source Code of Conduct](https://github.com/SAP-samples/.github/blob/main/CODE_OF_CONDUCT.md). 101 | 102 | ## How to obtain support 103 | 104 | Support for the content in this repository is available during the actual time of the online session for which this content has been designed. Otherwise, you may request support via the [Issues](../../issues) tab. 105 | 106 | ## License 107 | 108 | 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](LICENSES/Apache-2.0.txt) file. 109 | -------------------------------------------------------------------------------- /REUSE.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | SPDX-PackageName = "teched2023-[SESSION ID]" 3 | SPDX-PackageSupplier = "ospo@sap.com" 4 | SPDX-PackageDownloadLocation = "https://github.com/sap-samples/teched2023-[SESSION ID]" 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 = "2023 SAP SE or an SAP affiliate company and teched2023-[SESSION ID] contributors" 11 | SPDX-License-Identifier = "Apache-2.0" 12 | -------------------------------------------------------------------------------- /exercises/java/README.md: -------------------------------------------------------------------------------- 1 | # Use SAP Cloud SDK for AI (Java) 2 | 3 | This track introduces attendees to using orchestration capabilities of SAP Generative AI Hub using the [SAP Cloud SDK for AI (Java)](https://github.com/SAP/ai-sdk-java). 4 | You can find all mentioned features and some most samples in the [SDK documentation portal](https://sap.github.io/ai-sdk/). 5 | 6 | ## 1. Prerequisite 7 | 8 | 1. Follow all the instructions as described in the [How to Start](../../README.md#how-to-start) section. 9 | 2. After receiving the test system access details via email, update the [.env](app/.env), by using the attached file. 10 | ``` 11 | AICORE_SERVICE_KEY='{"clientid": "","clientsecret": "","url": "","serviceurls": {"AI_API_URL": ""}}' 12 | ``` 13 | 3. It is highly recommended to use an IDE like [IntelliJ](https://www.jetbrains.com/idea/download/?section=windows#community-edition). 14 | Open the project that you cloned from the previous [step](../../README.md#how-to-start). 15 | 4. Check whether _Maven_ is installed and a modern version of _Java_ (i.e. JRE17+). Your installed IDE (e.g. _IntelliJ_) may already provide the relevant tools for you. 16 | 17 | ## 2. Project Structure 18 | The [project](app) used for this session is a neat [Spring Boot](https://spring.io/projects/spring-boot)-based web application. 19 | 20 | ### 2.1 Dependencies 21 | The necessary dependencies for the exercises are specified in the [pom.xml](app/pom.xml). 22 | Additional dependencies or plugins are not required for the exercises. 23 | However, you are free to add any dependencies or plugins you find useful. 24 | 25 | The _SAP Cloud SDK for AI_ uses the group-id [com.sap.ai.sdk](https://mvnrepository.com/artifact/com.sap.ai.sdk), e.g., [com.sap.ai.sdk:orchestration](https://mvnrepository.com/artifact/com.sap.ai.sdk/orchestration), for orchestration service consumption. 26 | 27 | ### 2.2 Related Source Code 28 | - The [Application](app/src/main/java/org/demo/dsag/Application.java) class defines the application root, including application based annotations. 29 | You can execute the `main()` method from here to start the application easily. 30 | 31 | - The [OrchestrationController](app/src/main/java/org/demo/dsag/OrchestrationController.java) class contains all relevant endpoint definitions and their implementation details. 32 | Here we'll write the code for calling the orchestration service using the client libraries provided by the SAP Cloud SDK. 33 | You will mainly work with this file during your exercises. 34 | 35 | > [!NOTE] 36 | > Most endpoints will not return meaningful responses until the exercises are completed. 37 | 38 | > [!TIP] 39 | > While the method stubs are provided, the implementation is left to you. 40 | > The exercises give hints and solution to approach the target implementation, but you are encouraged to just try out. 41 | > See how the IDE guides you, what methods and options are available. 42 | > Maybe you can figure out the solution by yourself. 43 | > This approach will allow you to experience the full developer workflow, including useful features like auto-completion and debugging. 44 | > By typing the code, you’ll better understand the logic, discover useful functions, and build muscle memory, all of which contribute to a deeper learning experience. 45 | 46 | ### Exercises 47 | 48 | The exercises demonstrate how to use the SAP generative AI hub SDK to interact with the [orchestration service](https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/orchestration?locale=en-US), enabling you to build AI-driven workflows by combining multiple modules such as templating, large language models (LLM), and content filtering. 49 | 50 | - [Preparation](ex0/README.md) 51 | - [Exercise 1 - Getting LLM Access via Orchestration Service](ex1/README.md) 52 | - [Exercise 2 - Prompt Templating](ex2/README.md) 53 | - [Exercise 3 - Content Filtering](ex3/README.md) 54 | - [Exercise 4 - Streamed Responses](ex4/README.md) 55 | 56 | Start from [here](./ex0/README.md). 57 | 58 | ### Troubleshooting 59 | 60 | If you encounter errors, please look closely at the exceptions and stacktraces. 61 | 62 | For detailed HTTP context, you can enable the debug logging for different packages in the `src/main/resources/application.properties` file. 63 | Use `DEBUG` instead of `WARN` to extract verbose logs. -------------------------------------------------------------------------------- /exercises/java/app/.env: -------------------------------------------------------------------------------- 1 | AICORE_SERVICE_KEY='{"clientid": "","clientsecret": "","url": "","serviceurls": {"AI_API_URL": ""}}' 2 | -------------------------------------------------------------------------------- /exercises/java/app/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.demo.dsag 8 | ai-sdk-java 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 17 13 | 17 14 | UTF-8 15 | 3.4.4 16 | 6.2.5 17 | 1.5.0 18 | 2.8.6 19 | 20 | 21 | 22 | 23 | 24 | org.springframework 25 | spring-framework-bom 26 | ${springframework.version} 27 | pom 28 | import 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-dependencies 33 | ${spring-boot.version} 34 | pom 35 | import 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | com.sap.ai.sdk 44 | orchestration 45 | ${ai-sdk.version} 46 | 47 | 48 | io.projectreactor 49 | reactor-core 50 | 51 | 52 | org.springdoc 53 | springdoc-openapi-starter-webmvc-api 54 | ${springdoc.version} 55 | 56 | 57 | org.springdoc 58 | springdoc-openapi-starter-webmvc-ui 59 | ${springdoc.version} 60 | 61 | 62 | 63 | org.springframework.boot 64 | spring-boot-starter-web 65 | runtime 66 | 67 | 68 | 69 | 70 | 71 | 72 | org.springframework.boot 73 | spring-boot-maven-plugin 74 | 75 | false 76 | 77 | 78 | 79 | 80 | repackage 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /exercises/java/app/src/main/java/org/demo/dsag/Application.java: -------------------------------------------------------------------------------- 1 | package org.demo.dsag; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.web.servlet.ServletComponentScan; 6 | import org.springframework.context.annotation.ComponentScan; 7 | 8 | @SpringBootApplication 9 | @ComponentScan({"com.sap.cloud.sdk", "org.demo.dsag"}) 10 | @ServletComponentScan({"com.sap.cloud.sdk", "org.demo.dsag"}) 11 | public class Application { 12 | 13 | public static void main(final String[] args) { 14 | SpringApplication.run(Application.class, args); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /exercises/java/app/src/main/java/org/demo/dsag/OrchestrationController.java: -------------------------------------------------------------------------------- 1 | package org.demo.dsag; 2 | 3 | import com.sap.ai.sdk.orchestration.AzureContentFilter; 4 | import com.sap.ai.sdk.orchestration.AzureFilterThreshold; 5 | import com.sap.ai.sdk.orchestration.Message; 6 | import com.sap.ai.sdk.orchestration.OrchestrationClient; 7 | import com.sap.ai.sdk.orchestration.OrchestrationModuleConfig; 8 | import com.sap.ai.sdk.orchestration.OrchestrationPrompt; 9 | import com.sap.ai.sdk.orchestration.TemplateConfig; 10 | import org.springframework.web.bind.annotation.GetMapping; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.RequestParam; 13 | import org.springframework.web.bind.annotation.RestController; 14 | import reactor.core.publisher.Flux; 15 | import reactor.core.scheduler.Schedulers; 16 | 17 | import javax.annotation.Nonnull; 18 | import java.util.List; 19 | import java.util.Map; 20 | import java.util.stream.Stream; 21 | 22 | import static com.sap.ai.sdk.orchestration.OrchestrationAiModel.GEMINI_1_5_FLASH; 23 | import static com.sap.ai.sdk.orchestration.OrchestrationAiModel.Parameter.MAX_TOKENS; 24 | 25 | @RestController 26 | @SuppressWarnings("unused") 27 | @RequestMapping("/orchestration") 28 | class OrchestrationController { 29 | private final OrchestrationClient client = new OrchestrationClient(); 30 | 31 | private final OrchestrationModuleConfig config = 32 | new OrchestrationModuleConfig().withLlmConfig(GEMINI_1_5_FLASH.withParam(MAX_TOKENS, 1_000)); 33 | 34 | @GetMapping("/simple") 35 | String simple( 36 | @Nonnull @RequestParam(value = "phrase", required = false, defaultValue = "Hello World!" ) final String phrase 37 | ) { 38 | return "TODO"; 39 | } 40 | 41 | @GetMapping("/template") 42 | String template( 43 | @Nonnull @RequestParam(value = "language", required = false, defaultValue = "German" ) final String language 44 | ) { 45 | return "TODO"; 46 | } 47 | 48 | @GetMapping("/filtering") 49 | @Nonnull 50 | String filtering( 51 | @Nonnull @RequestParam(value = "policy", required = false, defaultValue = "ALLOW_ALL") final AzureFilterThreshold policy 52 | ) { 53 | return "TODO"; 54 | } 55 | 56 | @GetMapping("/stream") 57 | Flux stream( 58 | @Nonnull @RequestParam(value = "topic", required = false, defaultValue = "Developing a software project" ) final String topic 59 | ) { 60 | var stream = Stream.of("TODO"); 61 | return Flux.fromStream(stream).publishOn(Schedulers.parallel()); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /exercises/java/app/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=8080 2 | springdoc.swagger-ui.path=/ 3 | springdoc.api-docs.path=/v3/api-docs 4 | logging.level.org.apache.hc.client5=WARN 5 | logging.level.org.springframework.http.client=WARN 6 | server.error.include-message=always -------------------------------------------------------------------------------- /exercises/java/ex0/README.md: -------------------------------------------------------------------------------- 1 | # Preparation 2 | 3 | By completing these steps, you will have a [Spring Boot](https://spring.io/projects/spring-boot)-based web application up and running. 4 | 5 | ### Prerequisites: 6 | - [ ] Java (JRE17+) and Maven installed. 7 | Some IDEs (e.g. [IntelliJ](https://www.jetbrains.com/idea/download/?section=windows#community-edition)) may already provide the relevant tools for you. 8 | 9 | ### 1. Navigate to the Application Directory: 10 | 11 | Assuming you are still in the root directory of the repository, navigate to the app folder: 12 | ```shell 13 | cd exercises/java/app 14 | ``` 15 | 16 | ### 2. Install Dependencies: 17 | 18 | Install the required dependencies using: 19 | ```shell 20 | mvn clean install 21 | ``` 22 | > [!NOTE] 23 | > `mvn clean install` removes any pre-built files for your compiled application and compiles the application into an executable binary. Also, it downloads and installs missing dependencies into your local Maven repository (e.g. `~/.m2`). 24 | 25 | ### 3. Start the Application: 26 | 27 | You can run the application in your IDE by directly executing the `Application` class, or alternatively use the following command to start the app locally via Maven: 28 | 29 | ```shell 30 | mvn spring-boot:run 31 | ``` 32 | 33 | ### 4. Verify the Application is Running: 34 | After a few seconds, you should see the following log message: 35 | ``` 36 | Tomcat initialized with port 8080 (http) 37 | ``` 38 | 39 | ### 5. Access the Application: 40 | Open your browser and visit http://localhost:8080/. 41 | You should see a Swagger UI page with the API documentation for your _Spring Boot_ app. 42 | Feel free to explore the site and **try out** the different endpoints. 43 | 44 | 45 | #### Troubleshooting: 46 | If you encounter errors, please look closely at the exception. 47 | And try checking if port 8080 is in use: 48 | ```shell 49 | lsof -i :8080 50 | ``` 51 | 52 | ### 6. Orchestration Client Configuration 53 | 54 | Open [OrchestrationController](../app/src/main/java/org/demo/dsag/OrchestrationController.java) file and find the two constants declaring the `OrchestrationClient` and the `OrchestrationModuleConfig` objects. 55 | They are application-scoped, immutable objects and are available to all endpoints of your application. 56 | 57 | By default, they make sure the current setup works as expected for default LLM model access, e.g. 58 | * lookup environment variables for service key 59 | * check connectivity and check service availability 60 | 61 | Feel free to explore the different available models for Orchestration service, as well as the different LLM parameters that can be set. 62 | Please note, not all parameters are respected in all models. 63 | 64 | ## Summary 65 | **Congratulations!** 66 | 67 | Your Spring Boot application is up and running. 68 | 69 | You’re now ready to move on to the next step. 70 | Continue to [Exercise 1 - Getting LLM Access via Orchestration Service](../ex1/README.md). 71 | -------------------------------------------------------------------------------- /exercises/java/ex1/README.md: -------------------------------------------------------------------------------- 1 | # Exercise 1 - Getting LLM Access via Orchestration Service 2 | 3 | In this exercise, you will learn how to send a user-provided prompt using the SAP Cloud SDK for AI. 4 | You’ll also explore the [harmonized LLM access feature](https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/harmonized-api?locale=en-US) of the orchestration service. 5 | 6 | ### 1. Navigate to the Function 7 | Open [OrchestrationController](../app/src/main/java/org/demo/dsag/OrchestrationController.java) file and search for the function `simple`. 8 | 9 | ### 2. Add Implementation 10 | 11 | You'll see one optional function-argument `phrase`, provided as request parameter. 12 | It is used to enable a dynamic prompt for the orchestration service request. 13 | 14 | Type the following code within the function `simple`: 15 | ```java 16 | var prompt = new OrchestrationPrompt(phrase + " Why is this phrase so famous?"); 17 | var result = client.chatCompletion(prompt, config); 18 | return result.getContent(); 19 | ``` 20 | > [!NOTE] 21 | > The code snippet does the following: 22 | > 1. Construct a concatenated prompt using the provided "phrase" and a question. 23 | > 2. Call the chat completion endpoint of the orchestration service. 24 | > 3. Return the string response content from the orchestration service. 25 | 26 | ### 3. Restart the Application 27 | Save your changes and restart the application. 28 | 29 | ### 4. Check the LLM Response 30 | Open your browser and visit http://localhost:8080/orchestration/simple. 31 | Or alternatively use the _Swagger UI_ accessible from the website root. 32 | You should see the response from the LLM. 33 | 34 | > [!TIP] 35 | > You should see the following log messages, as the service key from the [.env](../app/.env) file is in use. 36 | > ``` 37 | > Found a service key in environment variable AICORE_SERVICE_KEY. 38 | > Using a service key is recommended for local testing only. 39 | > Bind the AI Core service to the application for productive usage. 40 | > ``` 41 | 42 | ### 5. Use Harmonized LLM Access 43 | Modify the code as shown below to switch to a different LLM model while keeping the same configuration and prompt: 44 | ```java 45 | private final OrchestrationModuleConfig config = 46 | new OrchestrationModuleConfig().withLlmConfig(GPT_4O_MINI.withParam(MAX_TOKENS, 1000)); 47 | ``` 48 | 49 | ### 6. Check the LLM Response 50 | Repeat [step 3](#3-restart-the-application) and [step 4](#4-check-the-llm-response) to see the updated response from the new LLM model. 51 | 52 | ## Summary 53 | 54 | **Great job!** 55 | 56 | - You have successfully used the SAP Cloud SDK for AI to send a user-provided prompt. 57 | - Additionally, you tested the [harmonized LLM access feature](https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/harmonized-api?locale=en-US) of the orchestration service by switching the model name seamlessly. 58 | 59 | You’re now ready to move on to the next step. 60 | Continue to [Exercise 2 - Prompt Templating](../ex2/README.md) 61 | 62 | -------------------------------------------------------------------------------- /exercises/java/ex2/README.md: -------------------------------------------------------------------------------- 1 | # Exercise 2 - Prompt Templating 2 | 3 | In this exercise, you will explore how to create and use client-side prompt templates to dynamically generate and send prompts. 4 | This will enable you to streamline user interactions and tailor responses efficiently within your application. 5 | 6 | ### 1. Navigate to the Function 7 | Open [OrchestrationController](../app/src/main/java/org/demo/dsag/OrchestrationController.java) file and search for the function `template`. 8 | 9 | ### 2. Add Implementation 10 | 11 | You'll see one optional function-argument `language`, provided as request parameter. 12 | It is used to enable a dynamic prompt for the orchestration service request. 13 | 14 | Type the following code in the function `template`: 15 | 16 | ```java 17 | var template = Message.user("Reply with 'Orchestration Service is working!' in {{?language}}"); 18 | var templatingConfig = TemplateConfig.create().withTemplate(List.of(template.createChatMessage())); 19 | var configWithTemplate = config.withTemplateConfig(templatingConfig); 20 | var prompt = new OrchestrationPrompt(Map.of("language", language)); 21 | var result = client.chatCompletion(prompt, configWithTemplate); 22 | return result.getContent(); 23 | ``` 24 | 25 | > [!NOTE] 26 | > In this exercise, a few notable modifications are introduced to improve the model’s flexibility and input handling: 27 | > - A list of messages is maintained for the template. Currently, including only a single user message. 28 | > - The LLM configuration is extended by applying the template configuration. 29 | > - You will use a client-side prompt template that includes a dynamic placeholder only. 30 | > - When calling the chat completion endpoint, the static template is provided in combination with the prompt including the populated language parameter. 31 | 32 | ### 3. Restart the Application 33 | Save your changes and restart the application. 34 | 35 | ### 4. Check the LLM Response 36 | Open your browser and visit http://localhost:8080/orchestration/template. 37 | You should see a response in the language of your choice. 38 | 39 | > [!IMPORTANT] 40 | > We strongly recommend adjusting the following values to meet your specific use case: 41 | > - Model options 42 | > - Messages to include a system prompt and additional user prompts (templates) 43 | > - Parameters for populating variables defined in the prompt template 44 | > 45 | > You can find further hints in the documentation on [Orchestration prompt templating](https://sap.github.io/ai-sdk/docs/java/guides/orchestration-chat-completion#templating). 46 | 47 | ## Summary 48 | 49 | **Excellent!** 50 | 51 | Now, let’s take a closer look at the key concepts you’ve learned so far. 52 | - **Client-Side Prompt Templates**: You demonstrated how to use prompt templates on the client side to send prompts dynamically. 53 | - **System & User Prompts**: In addition to handling user-provided prompts, you also showcased how to send an instruction through a system prompt, enabling more control over the response behavior. 54 | 55 | You’re now ready to move on to the next step. 56 | Continue to [Exercise 3 - Content Filtering](../ex3/README.md) 57 | -------------------------------------------------------------------------------- /exercises/java/ex3/README.md: -------------------------------------------------------------------------------- 1 | # Exercise 3 - Content Filtering 2 | 3 | In this exercise, you will learn how to configure content filters to detect and take actions on potentially harmful content. 4 | 5 | ### 1. Navigate to the Function 6 | Open [OrchestrationController](../app/src/main/java/org/demo/dsag/OrchestrationController.java) file and search for the function `filtering`. 7 | 8 | ### 2. Add Implementation 9 | 10 | You'll see one optional function-argument `policy`, provided as request parameter. 11 | It represents the sensitivity level for the content input filter, with default value `ALLOW_ALL` indicates no filtering on user messages. 12 | It is used to enable a dynamic prompt handling for the orchestration service request. 13 | 14 | Type the following code in the function `filtering` to enable input filtering: 15 | 16 | ```java 17 | var prompt = new OrchestrationPrompt("'We shall spill blood tonight', said the operator in-charge."); 18 | var filterConfig = new AzureContentFilter().hate(policy).selfHarm(policy).sexual(policy).violence(policy); 19 | var configWithFilter = config.withInputFiltering(filterConfig); 20 | var result = client.chatCompletion(prompt, configWithFilter); 21 | return result.getContent(); 22 | ``` 23 | 24 | > [!NOTE] 25 | > We added an input filter using the convenient method `withInputFiltering` with `AzureContentFilter` to block request content based on a predefined sensitivity level. 26 | 27 | ### 3. Restart the Application 28 | Save your changes and restart the application. 29 | 30 | ### 4. Check the LLM Response 31 | Open your browser and visit http://localhost:8080/orchestration/filtering. 32 | If the filter is triggered upon request, you should see an error message like this: 33 | 34 | ``` 35 | { 36 | "timestamp": "2025-03-28T19:35:50.824+00:00", 37 | "status": 500, 38 | "error": "Internal Server Error", 39 | "message": "Content filtered due to safety violations. Please modify the prompt and try again.", 40 | "path": "/orchestration/filtering" 41 | } 42 | ``` 43 | 44 | ### 5. Change the Content Filtering Configuration 45 | To make the filter less/more strict, modify the query parameter as part of your request. 46 | These are two ends of the spectrum, with `ALLOW_ALL` being the most tolerant and `ALLOW_SAFE` being the strictest: 47 | ``` 48 | http://localhost:8080/orchestration/filtering?policy=ALLOW_ALL 49 | http://localhost:8080/orchestration/filtering?policy=ALLOW_SAFE 50 | ``` 51 | 52 | ### 6. Check the LLM Response 53 | Repeat [step 3](#3-restart-the-application) and [step 4](#4-check-the-llm-response) to see the updated response from the orchestration service. 54 | 55 | > [!TIP] 56 | > With the updated threshold, the input (user-provided prompt) should not be filtered before sending to the LLM. 57 | 58 | > [!IMPORTANT] 59 | > As the input filter has been configured in the most tolerant level, you might want to add an output filter, to make sure potential harmful content from the LLM will be filtered before sending back to the user. 60 | > 61 | > This is an optional exercise for you. 62 | > To find some clues, you can check our [documentation on Orchestration prompt filtering](https://sap.github.io/ai-sdk/docs/java/guides/orchestration-chat-completion#filtering). 63 | 64 | ## Summary 65 | 66 | **Excellent!** 67 | 68 | Now, let’s take a closer look at the key concepts you’ve learned so far: 69 | - Configuring **content filtering** to ensure input from user input are properly screened. 70 | - (Optionally) Configuring **output filtering** to ensure LLM output adheres to your policies. 71 | 72 | You’re now ready to move on to the next step. 73 | Continue to [Exercise 4 - Streamed Responses](../ex4/README.md) -------------------------------------------------------------------------------- /exercises/java/ex4/README.md: -------------------------------------------------------------------------------- 1 | # Exercise 3 - Content Filtering 2 | 3 | In this exercise, you will learn how to work with response streaming to improve overall user experience and reduce the observed latency. 4 | 5 | ### 1. Navigate to the Function 6 | Open [OrchestrationController](../app/src/main/java/org/demo/dsag/OrchestrationController.java) file and search for the function `stream`. 7 | 8 | ### 2. Add Implementation 9 | 10 | You'll see one optional function-argument `topic`, provided as request parameter. 11 | It indicates which topic we'll have a short story written for. 12 | In our example, it is used to enable a dynamic prompt handling for the orchestration service request. 13 | 14 | Please notice the return type `Flux`, instead of `Stream`. 15 | We'll use the `Flux` type to continuously stream the response back to the client without blocking the request thread. 16 | This will ensure a more responsive and interactive user experience in the browser. 17 | 18 | Type or replace the following code in the function `stream` to enable non-blocking response handling: 19 | 20 | ```java 21 | var prompt = new OrchestrationPrompt("Please create a small story about " + topic + " with around 700 words."); 22 | var stream = client.streamChatCompletion(prompt, config); 23 | return Flux.fromStream(stream).publishOn(Schedulers.parallel()); 24 | ``` 25 | 26 | > [!NOTE] 27 | > The convenience method `streamChatCompletion` returns a `Stream` for Spring Boot to consume. 28 | 29 | ### 3. Restart the Application 30 | Save your changes and restart the application. 31 | 32 | ### 4. Check the LLM Response 33 | Open your browser and visit http://localhost:8080/orchestration/stream. 34 | You will notice that the response is chunked and streamed back as text to the browser in real-time. 35 | 36 | > [!IMPORTANT] 37 | > Observed chunk size may be dependent on browser, e.g. Firefox may show smaller chunks faster than Chrome with bigger chunks. 38 | > Unfortunately the Swagger UI does not support streamed responses, it will behave like a blocking request and waits until all chunks are received before displaying. 39 | > 40 | > To find more clues and examples, you can check our [documentation on Orchestration response streaming](https://sap.github.io/ai-sdk/docs/java/guides/orchestration-chat-completion#streaming). 41 | 42 | ## Summary 43 | 44 | **Congratulations on completing the Java hands-on session!** 45 | 46 | You’ve successfully tackled all the exercises. 47 | Here’s a quick checklist of what you’ve achieved: 48 | - [x] Ensured that your environment runs the **required minimum Java (JRE17) and Maven** for the SAP Cloud SDK for AI. 49 | - [x] Learned how to utilize a `.env` file to securely store the service key for local testing. 50 | - [x] Worked with the **orchestration client** (powered by SAP Cloud SDK for AI), including: 51 | - [x] Configuring the **LLM model name**. 52 | - [x] Setting up various **model options** for tailored usage. 53 | - [x] Managing **system and user prompts** to enhance interaction with the LLM. 54 | - [x] Implementing **client-side prompt templating** for more flexible input handling. 55 | - [x] Configuring **content filtering** to ensure both input and output data are properly screened. 56 | - [x] Working with **response streaming** to improve user experience and reduce latency. 57 | 58 | Feel free to continue your learning journey with the [JavaScript track](../../javascript/README.md)! 59 | -------------------------------------------------------------------------------- /exercises/javascript/README.md: -------------------------------------------------------------------------------- 1 | # Use SAP Cloud SDK for AI (JavaScript) 2 | 3 | This track introduces attendees to using orchestration capabilities of SAP Generative AI Hub using the [SAP Cloud SDK for AI (JavaScript)](https://github.com/SAP/ai-sdk-js). 4 | 5 | ## 1. Prerequisite 6 | 7 | 1. Follow all the instructions as described in the [How to Start](../../README.md#how-to-start) section. 8 | 2. After receiving the test system access details via email, update the [.env](app/.env), by using the attached file. 9 | ``` 10 | AICORE_SERVICE_KEY='{"clientid": "","clientsecret": "","url": "","serviceurls": {"AI_API_URL": ""}}' 11 | ``` 12 | 3. It is highly recommended to use an IDE like [VS Code](https://code.visualstudio.com) or [WebStorm](https://www.jetbrains.com/webstorm/). 13 | Open the project that you cloned from the previous [step](../../README.md#how-to-start). 14 | 4. Install Node 20. 15 | 16 | ## 2. Project Structure 17 | The [project](app) used for this session is an [Express](https://www.npmjs.com/package/express)-based web application. 18 | 19 | ### 2.1 Dependencies 20 | The necessary dependencies for the exercises are specified in the [package.json](app/package.json). 21 | The SAP Cloud SDK for AI uses the scope [@sap-ai-sdk](https://www.npmjs.com/search?q=%40sap-ai-sdk), e.g., [@sap-ai-sdk/orchestration](https://www.npmjs.com/package/@sap-ai-sdk/orchestration), for npm packages. 22 | 23 | ### 2.2 Related Source Code 24 | - The [server.ts](app/src/server.ts) file defines the application, including the startup process, the list of exposed endpoints, and their implementation. 25 | 26 | - The [orchestration.ts](app/src/orchestration.ts) file contains implementation details for calling the orchestration service using the client libraries provided by the SAP Cloud SDK. 27 | You will mainly work with this file during your exercises. 28 | 29 | > [!NOTE] 30 | > Most endpoints will not return meaningful responses until the exercises are completed. 31 | 32 | > [!TIP] 33 | > The solutions to the exercises are provided but have been commented out. 34 | > We strongly recommend that you attempt to solve the exercises by writing the code yourself instead of just uncommenting or copying the solution. 35 | > This approach will allow you to experience the full developer workflow, including useful features like auto-completion and debugging. 36 | > By typing the code, you’ll better understand the logic, discover useful functions, and build muscle memory, all of which contribute to a deeper learning experience. 37 | 38 | ### Exercises 39 | 40 | The exercises demonstrate how to use the SAP generative AI hub SDK to interact with the [orchestration service](https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/orchestration?locale=en-US), enabling you to build AI-driven workflows by combining multiple modules such as templating, large language models (LLM), and content filtering. 41 | 42 | - [Preparation](ex0/README.md) 43 | - [Exercise 1 - Getting LLM Access via Orchestration Service](ex1/README.md) 44 | - [Exercise 2 - Prompt Templating](ex2/README.md) 45 | - [Exercise 3 - Content Filtering](ex3/README.md) 46 | 47 | Start from [here](./ex0/README.md). 48 | -------------------------------------------------------------------------------- /exercises/javascript/app/.env: -------------------------------------------------------------------------------- 1 | AICORE_SERVICE_KEY='{"clientid": "","clientsecret": "","url": "","serviceurls": {"AI_API_URL": ""}}' 2 | -------------------------------------------------------------------------------- /exercises/javascript/app/.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | package-lock.json 4 | -------------------------------------------------------------------------------- /exercises/javascript/app/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/prettierrc", 3 | "singleQuote": true, 4 | "filepath": "*.ts", 5 | "trailingComma": "none", 6 | "arrowParens": "avoid", 7 | "endOfLine": "lf", 8 | "overrides": [ 9 | { 10 | "files": "jest.config.mjs", 11 | "options": { 12 | "trailingComma": "all" 13 | } 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /exercises/javascript/app/eslint.config.js: -------------------------------------------------------------------------------- 1 | import flatConfig from '@sap-cloud-sdk/eslint-config/flat-config.js'; 2 | 3 | export default [ 4 | ...flatConfig, 5 | { 6 | rules: { 7 | 'import/namespace': 'off', 8 | 'import/no-internal-modules': 'off', 9 | 'jsdoc/require-jsdoc': 'off' 10 | } 11 | }, 12 | { 13 | ignores: ['**/*.d.ts', '**/dist/**/*'] 14 | } 15 | ]; 16 | -------------------------------------------------------------------------------- /exercises/javascript/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "name": "sap-teched24-ai180", 4 | "version": "1.0.0", 5 | "private": "true", 6 | "main": "./dist/index.js", 7 | "types": "./dist/index.d.ts", 8 | "files": [ 9 | "dist/**/*.js", 10 | "dist/**/*.js.map", 11 | "dist/**/*.d.ts", 12 | "dist/**/*.d.ts.map" 13 | ], 14 | "engines": { 15 | "node": "^20" 16 | }, 17 | "scripts": { 18 | "start": "node --loader ts-node/esm src/server.ts", 19 | "local": "node --env-file=.env --loader ts-node/esm --watch src/server.ts", 20 | "compile": "tsc", 21 | "dev": "tsc -w", 22 | "lint": "eslint . && prettier . --config ./.prettierrc --ignore-path ./.prettierignore -c", 23 | "lint:fix": "eslint . --fix && prettier . --config ./.prettierrc --ignore-path ./.prettierignore -w --log-level error" 24 | }, 25 | "dependencies": { 26 | "@sap-ai-sdk/orchestration": "1.10.0", 27 | "dotenv": "16.4.5", 28 | "express": "^4.21.2" 29 | }, 30 | "devDependencies": { 31 | "@sap-cloud-sdk/eslint-config": "3.21.0", 32 | "@types/express": "4.17.21", 33 | "@types/node": "22.7.0", 34 | "eslint": "8.57.1", 35 | "ts-node": "10.9.2", 36 | "typescript": "5.6.2" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /exercises/javascript/app/public/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Arial, sans-serif; 3 | line-height: 1.6; 4 | color: #333; 5 | max-width: 800px; 6 | margin: 0 auto; 7 | padding: 20px; 8 | display: flex; 9 | flex-direction: column; 10 | align-items: center; 11 | } 12 | 13 | h1 { 14 | color: #0070f3; 15 | text-align: center; 16 | margin-bottom: 30px; 17 | } 18 | 19 | h2 { 20 | color: #0070f3; 21 | text-align: center; 22 | margin-bottom: 20px; 23 | } 24 | 25 | .route-list { 26 | list-style-type: none; 27 | padding: 0; 28 | width: 100%; 29 | } 30 | 31 | .route-item { 32 | margin-bottom: 30px; 33 | border: 1px solid #e0e0e0; 34 | border-radius: 8px; 35 | padding: 20px; 36 | transition: box-shadow 0.3s ease; 37 | } 38 | 39 | .route-item:hover { 40 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); 41 | } 42 | 43 | .route-item h2 { 44 | color: #0070f3; 45 | margin-bottom: 10px; 46 | } 47 | 48 | .route-item p { 49 | margin-bottom: 15px; 50 | } 51 | 52 | .route-item a { 53 | color: #0070f3; 54 | text-decoration: none; 55 | font-weight: bold; 56 | display: inline-block; 57 | padding: 5px 10px; 58 | border: 1px solid #0070f3; 59 | border-radius: 4px; 60 | transition: background-color 0.3s ease, color 0.3s ease; 61 | } 62 | 63 | .route-item a:hover { 64 | background-color: #0070f3; 65 | color: white; 66 | } 67 | -------------------------------------------------------------------------------- /exercises/javascript/app/src/indexPage.ts: -------------------------------------------------------------------------------- 1 | interface Route { 2 | path: string; 3 | title: string; 4 | description: string; 5 | } 6 | 7 | const routes: Route[] = [ 8 | { 9 | path: '/orchestration/simple', 10 | title: 'Simple Orchestration', 11 | description: 'Perform a basic chat completion using the OrchestrationClient.' 12 | }, 13 | { 14 | path: '/orchestration/template', 15 | title: 'Dynamic Templates', 16 | description: 'Use templating to create prompts with placeholders for input parameters.' 17 | }, 18 | { 19 | path: '/orchestration/filtering', 20 | title: 'Content Filtering', 21 | description: 'Apply input filtering to ensure content safety.' 22 | }, 23 | { 24 | path: '/orchestration-stream', 25 | title: 'Streaming Orchestration', 26 | description: 'Implement real-time streaming for chat completion responses.' 27 | } 28 | ]; 29 | 30 | export function generateIndexPage(): string { 31 | const routeItems = routes.map(route => ` 32 |
  • 33 |

    ${route.title}

    34 |

    ${route.description}

    35 | Explore 36 |
  • 37 | `).join(''); 38 | 39 | return ` 40 | 41 | 42 | 43 | 44 | 45 | SAP Cloud SDK for AI - Intelligent Orchestration 46 | 47 | 65 | 66 | 67 |
    68 |

    SAP Cloud SDK for AI - Intelligent Orchestration

    69 |
    70 |
    71 | 76 |
    77 | 78 | 79 | `; 80 | } 81 | -------------------------------------------------------------------------------- /exercises/javascript/app/src/orchestration.ts: -------------------------------------------------------------------------------- 1 | import { 2 | OrchestrationClient, 3 | buildAzureContentSafetyFilter 4 | } from '@sap-ai-sdk/orchestration'; 5 | import { convertLineBreaksToHtml } from './util.js'; 6 | 7 | /** 8 | * Create different types of orchestration requests. 9 | * @param sampleCase - Name of the sample case to orchestrate. 10 | * @returns The message content from the orchestration service in the generative AI hub. 11 | */ 12 | export async function orchestrationCompletion( 13 | sampleCase: string 14 | ): Promise { 15 | switch (sampleCase) { 16 | case 'simple': 17 | return orchestrationCompletionSimple(); 18 | case 'template': 19 | return orchestrationCompletionTemplate(); 20 | case 'filtering': 21 | return orchestrationCompletionFiltering(); 22 | default: 23 | return undefined; 24 | } 25 | } 26 | 27 | /** 28 | * Performs a simple orchestration completion using GPT-3.5 Turbo. 29 | * @returns A string containing the AI's response about SAP TechEd. 30 | */ 31 | async function orchestrationCompletionSimple(): Promise { 32 | // const orchestrationClient = new OrchestrationClient({ 33 | // llm: { 34 | // model_name: 'gpt-35-turbo', 35 | // model_params: { max_tokens: 1000 } 36 | // }, 37 | // templating: { 38 | // template: [ 39 | // { 40 | // role: 'user', 41 | // content: 'What is SAP TechEd?' 42 | // } 43 | // ] 44 | // } 45 | // }); 46 | 47 | // const response = await orchestrationClient.chatCompletion(); 48 | 49 | // return convertLineBreaksToHtml(response.getContent()!); 50 | } 51 | 52 | /** 53 | * Performs a template-based orchestration completion using Gemini 1.5 Flash. 54 | * @returns A string containing an HTML-formatted job post for a Java developer. 55 | */ 56 | async function orchestrationCompletionTemplate(): Promise { 57 | // const orchestrationClient = new OrchestrationClient({ 58 | // llm: { 59 | // model_name: 'gemini-1.5-flash', 60 | // model_params: { max_tokens: 1000, temperature: 0.1 } 61 | // }, 62 | // templating: { 63 | // template: [ 64 | // { role: 'system', content: 'Please generate contents with HTML tags.' }, 65 | // { 66 | // role: 'user', 67 | // content: 'Create a job post for the position: {{?position}}.' 68 | // } 69 | // ] 70 | // } 71 | // }); 72 | 73 | // const response = await orchestrationClient.chatCompletion({ 74 | // inputParams: { position: 'Java dev' } 75 | // }); 76 | 77 | // return response.getContent()!; 78 | } 79 | 80 | /** 81 | * Performs a filtered orchestration completion using Gemini 1.5 Flash with content safety measures. 82 | * @returns A string containing the AI's response or an error message if content is filtered. 83 | */ 84 | async function orchestrationCompletionFiltering(): Promise { 85 | // const orchestrationClient = new OrchestrationClient({ 86 | // llm: { 87 | // model_name: 'gemini-1.5-flash', 88 | // model_params: { max_tokens: 1000 } 89 | // }, 90 | // templating: { 91 | // template: [ 92 | // { role: 'user', content: 'I want to break my legs. Any suggestions?' } 93 | // ] 94 | // }, 95 | // filtering: { 96 | // input: { 97 | // filters: [buildAzureContentSafetyFilter({ SelfHarm: 'ALLOW_SAFE' })] 98 | // } 99 | // } 100 | // }); 101 | 102 | // try { 103 | // const response = await orchestrationClient.chatCompletion(); 104 | // return response.getContent()!; 105 | // } catch (error: any) { 106 | // return `Error: Content filter blocked the request. ${error?.message}`; 107 | // } 108 | } 109 | -------------------------------------------------------------------------------- /exercises/javascript/app/src/server.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | import path from 'path'; 3 | import { fileURLToPath } from 'url'; 4 | import express from 'express'; 5 | import { generateIndexPage } from './indexPage.js'; 6 | import { orchestrationCompletion } from './orchestration.js'; 7 | import 'dotenv/config.js'; 8 | 9 | const __filename = fileURLToPath(import.meta.url); 10 | const __dirname = path.dirname(__filename); 11 | 12 | const app = express(); 13 | const port = 8080; 14 | 15 | // Serve static files from the 'public' directory 16 | app.use(express.static(path.join(__dirname, '..', 'public'))); 17 | 18 | // Root route 19 | app.get(['/', '/health'], (req, res) => { 20 | res.send(generateIndexPage()); 21 | }); 22 | 23 | // Orchestration Routes 24 | app.get('/orchestration/:sampleCase', async (req, res) => { 25 | try { 26 | res.send(await orchestrationCompletion(req.params.sampleCase)); 27 | } catch (error: any) { 28 | console.error(error); 29 | res 30 | .status(500) 31 | .send('Yikes, vibes are off apparently 😬 -> ' + error.message); 32 | } 33 | }); 34 | 35 | app.get('/orchestration-stream', async (req, res) => { 36 | // try { 37 | // const controller = new AbortController(); 38 | // const orchestrationClient = new OrchestrationClient({ 39 | // llm: { 40 | // model_name: 'gpt-35-turbo', 41 | // model_params: {} 42 | // }, 43 | // templating: { 44 | // template: [ 45 | // { 46 | // role: 'user', 47 | // content: 'Give me a long introduction of {{?input}}' 48 | // } 49 | // ] 50 | // } 51 | // }); 52 | 53 | // const response = await orchestrationClient.stream( 54 | // { inputParams: { input: 'SAP Cloud SDK' } }, 55 | // controller, 56 | // { 57 | // 'global': { 58 | // 'chunk_size': 100 59 | // } 60 | // } 61 | // ); 62 | 63 | // // Set headers for event stream 64 | // res.setHeader('Content-Type', 'text/event-stream'); 65 | // res.setHeader('Connection', 'keep-alive'); 66 | // res.flushHeaders(); 67 | 68 | // let connectionAlive = true; 69 | 70 | // res.on('close', () => { 71 | // controller.abort(); 72 | // connectionAlive = false; 73 | // res.end(); 74 | // }); 75 | 76 | // // Stream the response 77 | // for await (const chunk of response.stream) { 78 | // if (!connectionAlive) {break;} 79 | // res.write(chunk.getDeltaContent()); 80 | // } 81 | // } catch (error: any) { 82 | // console.error(error); 83 | // if (!res.headersSent) { 84 | // res.status(500).send('Error in streaming: ' + error.message); 85 | // } 86 | // } finally { 87 | // res.end(); 88 | // } 89 | }); 90 | 91 | // Start server 92 | app.listen(port, () => { 93 | console.log(`Server running at http://localhost:${port}`); 94 | }); 95 | -------------------------------------------------------------------------------- /exercises/javascript/app/src/util.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Converts line breaks in a string to HTML
    tags. 3 | * This is useful for preserving line breaks when displaying text in HTML. 4 | * @param input - The string to process. 5 | * @returns The input string with line breaks replaced by
    tags, or an empty string if input is null or undefined. 6 | */ 7 | export function convertLineBreaksToHtml(input: string | null | undefined): string { 8 | if (input == null) { 9 | return ''; 10 | } 11 | return input.replace(/(\r\n|\n|\r)/g, '
    '); 12 | } 13 | -------------------------------------------------------------------------------- /exercises/javascript/app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "Node16", 5 | "declaration": true, 6 | "declarationMap": true, 7 | "sourceMap": true, 8 | "esModuleInterop": true, 9 | "allowSyntheticDefaultImports": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "strict": true, 12 | "skipLibCheck": true, 13 | "rootDir": "./src", 14 | "outDir": "./dist", 15 | "tsBuildInfoFile": "./dist/.tsbuildinfo", 16 | "composite": true, 17 | "lib": ["esnext"] 18 | }, 19 | "include": ["src/**/*.ts"], 20 | "exclude": ["dist/**/*", "**/*.test.ts", "node_modules/**/*"], 21 | "ts-node": { 22 | "experimentalSpecifierResolution": "node", 23 | "transpileOnly": true, 24 | "esm": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /exercises/javascript/ex0/README.md: -------------------------------------------------------------------------------- 1 | # Preparation 2 | 3 | By completing these steps, you will have an [Express](https://www.npmjs.com/package/express)-based web application up and running. 4 | 5 | ### Prerequisites: 6 | - [ ] Node.js (v20) and npm installed. 7 | 8 | You can verify Node.js and npm installations with the following commands: 9 | 10 | ```shell 11 | node -v 12 | npm -v 13 | ``` 14 | 15 | ### 1. Navigate to the Application Directory: 16 | 17 | Assuming you are still in the root directory of the teched2024-AI180 repository, navigate to the app folder: 18 | ```shell 19 | cd exercises/javascript/app 20 | ``` 21 | 22 | ### 2. Install Dependencies: 23 | 24 | Install the required dependencies using: 25 | ```shell 26 | npm ci 27 | ``` 28 | > [!NOTE] 29 | > `npm ci` installs dependencies as per `package-lock.json`, ensuring consistency. 30 | 31 | ### 3. Start the Application: 32 | Run the following to start the app locally: 33 | ```shell 34 | npm run local 35 | ``` 36 | > [!TIP] 37 | > Using this command ensures that the application automatically restarts whenever you save changes to your source code. 38 | 39 | ### 4. Verify the Application is Running: 40 | After a few seconds, you should see the following log message: 41 | ``` 42 | Server running at http://localhost:8080 43 | ``` 44 | 45 | ### 5. Access the Application: 46 | Open your browser and visit http://localhost:8080/. You should see the message: 47 | ``` 48 | Hello World! 🌍 49 | ``` 50 | 51 | ### Troubleshooting: 52 | If you encounter errors, try checking if port 8080 is in use: 53 | ```shell 54 | lsof -i :8080 55 | ``` 56 | 57 | ## Summary 58 | **Congratulations!** 59 | 60 | Your Express-based application is up and running. 61 | 62 | You’re now ready to move on to the next step. 63 | Continue to [Exercise 1 - Getting LLM Access via Orchestration Service](../ex1/README.md). 64 | -------------------------------------------------------------------------------- /exercises/javascript/ex1/README.md: -------------------------------------------------------------------------------- 1 | # Exercise 1 - Getting LLM Access via Orchestration Service 2 | 3 | In this exercise, you will learn how to send a user-provided prompt using the SAP Cloud SDK for AI. 4 | You’ll also explore the [harmonized LLM access feature](https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/harmonized-api?locale=en-US) of the orchestration service. 5 | 6 | ### 1. Navigate to the Function 7 | Open [orchestration.ts](../app/src/orchestration.ts) file and search for the function `orchestrationCompletionSimple`. 8 | 9 | ### 2. Add Implementation 10 | Type or uncomment the following code within the function `orchestrationCompletionSimple`: 11 | ```typescript 12 | const orchestrationClient = new OrchestrationClient({ 13 | llm: { 14 | model_name: 'gpt-35-turbo', 15 | model_params: { max_tokens: 1000 } 16 | }, 17 | templating: { 18 | template: [ 19 | { 20 | role: 'user', 21 | content: 'What is SAP TechEd?' 22 | } 23 | ] 24 | } 25 | }); 26 | 27 | const response = await orchestrationClient.chatCompletion(); 28 | 29 | return convertLineBreaksToHtml(response.getContent()!); 30 | ``` 31 | > [!NOTE] 32 | > The code snippet does the following: 33 | > 1. Initialize an `OrchestrationClient` with: 34 | > - A configuration for the LLM model name 35 | > - A model option (max_tokens) 36 | > - A user-provided prompt 37 | > 2. Calls the chat completion endpoint of the orchestration service. 38 | > 3. Returns the response content from the orchestration service. 39 | 40 | ### 3. Restart the Application 41 | Save your changes and wait for the application to restart automatically. 42 | 43 | ### 4. Check the LLM Response 44 | Open your browser and visit http://localhost:8080/orchestration/simple. 45 | You should see the response from the LLM. 46 | 47 | > [!TIP] 48 | > You should see the following log messages, as the service key from the [.env](../app/.env) file is in use. 49 | > ``` 50 | > INFO (context): Found a service key in environment variable "AICORE_SERVICE_KEY". Using a service key is recommended for local testing only. Bind the AI Core service to the application for productive usage. 51 | > ``` 52 | 53 | ### 5. Use Harmonized LLM Access 54 | Modify the code as shown below to switch to a different LLM model while keeping the same configuration and prompt: 55 | ```javascript 56 | llm: { 57 | // TODO: change the next line 58 | model_name: 'gemini-1.5-flash', 59 | model_params: { max_tokens: 1000 } 60 | }, 61 | ``` 62 | 63 | ### 6. Check the LLM Response 64 | Repeat [step 3](#3-restart-the-application) and [step 4](#4-check-the-llm-response) to see the updated response from the new LLM model. 65 | 66 | ## Summary 67 | 68 | **Great job!** 69 | 70 | - You have successfully used the SAP Cloud SDK for AI to send a user-provided prompt. 71 | - Additionally, you tested the [harmonized LLM access feature](https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/harmonized-api?locale=en-US) of the orchestration service by switching the model name seamlessly. 72 | 73 | You’re now ready to move on to the next step. 74 | Continue to [Exercise 2 - Prompt Templating](../ex2/README.md) 75 | 76 | -------------------------------------------------------------------------------- /exercises/javascript/ex2/README.md: -------------------------------------------------------------------------------- 1 | # Exercise 2 - Prompt Templating 2 | 3 | In this exercise, you will explore how to create and use client-side prompt templates to dynamically generate and send prompts. 4 | This will enable you to streamline user interactions and tailor responses efficiently within your application. 5 | 6 | ### 1. Navigate to the Function 7 | Open [orchestration.ts](../app/src/orchestration.ts) file and search for the function `orchestrationCompletionTemplate`. 8 | 9 | ### 2. Add Implementation 10 | Type or uncomment the following code in the function `orchestrationCompletionTemplate`: 11 | 12 | ```typescript 13 | const orchestrationClient = new OrchestrationClient({ 14 | llm: { 15 | model_name: 'gemini-1.5-flash', 16 | model_params: { max_tokens: 1000, temperature: 0.1 } 17 | }, 18 | templating: { 19 | template: [ 20 | { role: 'system', content: 'Please generate contents with HTML tags.' }, 21 | { 22 | role: 'user', 23 | content: 'Create a job post for the position: {{?position}}.' 24 | } 25 | ] 26 | } 27 | }); 28 | 29 | const response = await orchestrationClient.chatCompletion({ 30 | inputParams: { position: 'Java dev' } 31 | }); 32 | 33 | return response.getContent(); 34 | ``` 35 | 36 | > [!NOTE] 37 | > In this exercise, a few notable modifications are introduced to improve the model’s flexibility and input handling: 38 | > - You’ll configure an additional model option, temperature, which controls the randomness of the model’s responses. 39 | > - A system prompt will be added to guide the model’s behavior. 40 | > - You will use a client-side prompt template that includes a placeholder, position. 41 | > - When calling the chat completion endpoint, you’ll pass an input parameter to provide the value of the position variable. 42 | 43 | ### 3. Restart the Application 44 | Save your changes and wait for the application to restart automatically. 45 | 46 | ### 4. Check the LLM Response 47 | Open your browser and visit http://localhost:8080/orchestration/template. 48 | You should see a nice HTML page generated by the LLM. 49 | 50 | > [!IMPORTANT] 51 | > We strongly recommend adjusting the following values to meet your specific use case: 52 | > - Model options 53 | > - System prompt 54 | > - User provided prompt (template) 55 | > - Parameters for populating variables defined in the prompt template 56 | 57 | ## Summary 58 | 59 | **Excellent!** 60 | 61 | Now, let’s take a closer look at the key concepts you’ve learned so far. 62 | - **Client-Side Prompt Templates**: You demonstrated how to use prompt templates on the client side to send prompts dynamically. 63 | - **System & User Prompts**: In addition to handling user-provided prompts, you also showcased how to send an instruction through a system prompt, enabling more control over the response behavior. 64 | 65 | You’re now ready to move on to the next step. 66 | Continue to [Exercise 3 - Content Filtering](../ex3/README.md) 67 | -------------------------------------------------------------------------------- /exercises/javascript/ex3/README.md: -------------------------------------------------------------------------------- 1 | # Exercise 3 - Content Filtering 2 | 3 | In this exercise, you will learn how to configure content filters to detect and take actions on potentially harmful content. 4 | 5 | ### 1. Navigate to the Function 6 | Open [orchestration.ts](../app/src/orchestration.ts) file and search for the function `orchestrationCompletionFiltering`. 7 | 8 | ### 2. Add Implementation 9 | Type or uncomment the following code in the function `orchestrationCompletionFiltering` to enable input filtering: 10 | 11 | ```typescript 12 | const orchestrationClient = new OrchestrationClient({ 13 | llm: { 14 | model_name: 'gemini-1.5-flash', 15 | model_params: { max_tokens: 1000 } 16 | }, 17 | templating: { 18 | template: [ 19 | { role: 'user', content: 'I want to break my legs. Any suggestions?' } 20 | ] 21 | }, 22 | filtering: { 23 | input: buildAzureContentFilter({ SelfHarm: 0 }) 24 | } 25 | }); 26 | 27 | try { 28 | const response = await orchestrationClient.chatCompletion(); 29 | return response.getContent(); 30 | } catch (error: any) { 31 | return `Error: ${JSON.stringify(error.response.data)}`; 32 | } 33 | ``` 34 | 35 | > [!NOTE] 36 | > We added an input filter using the convenient method `buildAzureContentFilter` to block content based on a predefined sensitivity level. 37 | 38 | ### 3. Restart the Application 39 | Save your changes and wait for the application to restart automatically. 40 | 41 | ### 4. Check the LLM Response 42 | Open your browser and visit http://localhost:8080/orchestration/filtering. 43 | If the filter is triggered, you should see an error message like this: 44 | 45 | ``` 46 | Error: {"request_id":"xxx","code":400,"message":"Content filtered due to Safety violations. Please modify the prompt and try again.", 47 | "location":"Input Filter","module_results":{"templating":[{"role":"user","content":"I want to break my legs. Any suggestions?"}], 48 | "input_filtering":{"message":"Content filtered due to Safety violations. Please modify the prompt and try again.", 49 | "data":{"azure_content_safety":{"SelfHarm":4}}}}} 50 | ``` 51 | 52 | ### 5. Change the Content Filtering Configuration 53 | To make the filter less strict, modify the code as shown below: 54 | ```javascript 55 | filtering: { 56 | // TODO: change the next line 57 | input: buildAzureContentFilter({ SelfHarm: 6 }) 58 | } 59 | ``` 60 | ### 6. Check the LLM Response 61 | Repeat [step 3](#3-restart-the-application) and [step 4](#4-check-the-llm-response) to see the updated response from the orchestration service. 62 | 63 | > [!TIP] 64 | > With the updated threshold, the input (user-provided prompt) should not be filtered before sending to the LLM. 65 | 66 | > [!IMPORTANT] 67 | > As the input filter has been configured in the most tolerant level, you might want to add an output filter, to make sure potential harmful content from the LLM will be filtered before sending back to the user. 68 | > 69 | > This is an optional exercise for you. 70 | > To find some clues, you can either check our [documentation](https://github.com/SAP/ai-sdk-js/tree/main/packages/orchestration#content-filtering) or check the type of the `filtering` [here](https://github.com/SAP/ai-sdk-js/blob/main/packages/orchestration/src/orchestration-types.ts). 71 | 72 | ## Summary 73 | 74 | **Congratulations on completing the JavaScript hands-on session!** 75 | 76 | You’ve successfully tackled all the exercises. 77 | Here’s a quick checklist of what you’ve achieved: 78 | - [x] Ensured that your environment runs the **minimal required Node version (Node 20)** for the SAP Cloud SDK for AI. 79 | - [x] Learned how to utilize a `.env` file to securely store the service key for local testing. 80 | - [x] Worked with the **orchestration client** (powered by SAP Cloud SDK for AI), including: 81 | - [x] Configuring the **LLM model name**. 82 | - [x] Setting up various **model options** for tailored usage. 83 | - [x] Managing **system and user prompts** to enhance interaction with the LLM. 84 | - [x] Implementing **client-side prompt templating** for more flexible input handling. 85 | - [x] Configuring **content filtering** to ensure both input and output data are properly screened. 86 | 87 | Feel free to continue your learning journey with the [Python track](../../python/README.md)! 88 | -------------------------------------------------------------------------------- /exercises/python/README.md: -------------------------------------------------------------------------------- 1 | ## SAP generative AI hub SDK (Python) 2 | 3 | This track introduces attendees to using orchestration capabilities of Generative AI Hub using the [SAP generative AI hub SDK](https://pypi.org/project/generative-ai-hub-sdk/) in Python. 4 | 5 | ### Prerequisite 6 | 7 | 1. Make sure you meet all the requirements as described in the [How to Start](../../README.md#how-to-start) section. 8 | 2. Setup your a Python environment 9 | - Either use the SAP Business Application Studio. Follow the instructions at for further information on how to do this. 10 | - Or verify that you have a supported and stable version of **Python** installed on your system. Open up a terminal session at the root of the cloned repository. It is highly suggested to create a virtual Python environment for following along to avoid conflicts with other packages that could be already installed on your system. A virtual environment can be created using `python -m venv ENV`. Note: Your Python executable might also be called `python3`. Activate the environment in your current terminal session using either `source ENV/bin/activate` for Unix like systems (MacOS and Linux) or by running the script `ENV/Scripts/Activate.ps1` for Windows. Make sure that all subsequent steps are executed within the context of this newly created virtual environment. 11 | 3. Install the [SAP generative AI hub SDK](https://pypi.org/project/generative-ai-hub-sdk/) using pip: `pip install "generative-ai-hub-sdk[all]"`. 12 | 4. Configure authentication by setting the following environment variables using the demo credentials. You will find the appropriate values (named accordingly) in the file under the key `python`: 13 | 14 | ```bash 15 | AICORE_AUTH_URL= 16 | AICORE_BASE_URL= 17 | AICORE_CLIENT_ID= 18 | AICORE_CLIENT_SECRET= 19 | AICORE_ORCHESTRATION_DEPLOYMENT_URL= 20 | AICORE_RESOURCE_GROUP= 21 | ``` 22 | 23 | 6. You can now open your preferred development environment for Python and start with the exercises. Ensure that the created virtual environment and the set environment variables are used within your development environment. 24 | 25 | > [!TIP] 26 | > We recommend using Jupyter Notebook to explore this tracks exercises. 27 | > You can install it within your virtual environment using `pip install notebook`. 28 | > Afterwards you can use the following command from your repositories root to view and edit the Jupyter Notebooks provided in this track: `jupyter notebook exercises/python/`. A browser window should open up automatically. 29 | 30 | ### Exercises 31 | 32 | The exercises are comprised of notebooks demonstrating how to use the SAP generative AI hub SDK to interact with the Orchestration Service, enabling you to build AI-driven workflows by combining multiple modules such as templating, large language models (LLM), and content filtering. 33 | 34 | - [Exercise 1 - Orchestration Templating](./ex1.ipynb) 35 | - [Exercise 2 - Orchestration Content Filtering](./ex2.ipynb) 36 | - [Exercise 3 - Data Masking](./ex3.ipynb) 37 | - [Exercise 4 - Orchestration Chatbot](./ex4.ipynb) 38 | 39 | Start the exercises [here](./ex1.ipynb). 40 | -------------------------------------------------------------------------------- /exercises/python/ex1.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "637c87e6-ce3e-418a-811a-58edb1793d1d", 6 | "metadata": {}, 7 | "source": [ 8 | "# Preparation" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "id": "3ee09bfd-3ac5-4eb5-85a4-902ceb22cc27", 14 | "metadata": {}, 15 | "source": [ 16 | "## Authentication" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "id": "3da77e21-8ce3-462a-82bd-9b455f4b70e4", 22 | "metadata": {}, 23 | "source": [ 24 | "Before requests to orchestration can be issued, we need to provide authentication details to the SDK. This can be done either via a configuration file or via the environment. Make sure to check out the [SAP generative AI hub SDK project description](https://pypi.org/project/generative-ai-hub-sdk/) for more details. Below you will find an example for authenticating via environment variables using this very notebook.\n", 25 | "\n", 26 | "> **WARNING:**\n", 27 | "> Below code should never be used in production scenarios and is only for the purpose of illustrating which environment variables to use!\n", 28 | "> Credentials should never be defined in code!" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": null, 34 | "id": "f3fe1cc2-ceb1-4a07-aad2-b711674b4e89", 35 | "metadata": {}, 36 | "outputs": [], 37 | "source": [ 38 | "import os\n", 39 | "\n", 40 | "os.environ[\"AICORE_AUTH_URL\"] = \"redacted\"\n", 41 | "os.environ[\"AICORE_BASE_URL\"] = \"redacted\"\n", 42 | "os.environ[\"AICORE_CLIENT_ID\"] = \"redacted\"\n", 43 | "os.environ[\"AICORE_CLIENT_SECRET\"] = \"redacted\"\n", 44 | "os.environ[\"AICORE_ORCHESTRATION_DEPLOYMENT_URL\"] = \"redacted\"\n", 45 | "os.environ[\"AICORE_RESOURCE_GROUP\"] = \"redacted\"" 46 | ] 47 | }, 48 | { 49 | "cell_type": "markdown", 50 | "id": "56c63705-6c9f-4cc0-ad6a-3dd88e6ce860", 51 | "metadata": {}, 52 | "source": [ 53 | "You will need to authenticate in every exercise." 54 | ] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "id": "1e8f1dfa-cd15-452c-a97e-23df5c546eef", 59 | "metadata": {}, 60 | "source": [ 61 | "## Intializing the Orchestration Service" 62 | ] 63 | }, 64 | { 65 | "cell_type": "markdown", 66 | "id": "3d569326-4f43-4bae-a364-7d766ec07bcf", 67 | "metadata": {}, 68 | "source": [ 69 | "Typically, a virtual deployment of Orchestration must be configured before any interactions can occur. Once deployed, you will have access to a unique endpoint URL. For this session, the deployment has already been created for you. The corresponding URL, along with the authentication credentials, have been provided to you in advance. If you've adhered to the prerequisite guidelines, the URL should already be present in the environment variable `AICORE_ORCHESTRATION_DEPLOYMENT_URL`." 70 | ] 71 | }, 72 | { 73 | "cell_type": "markdown", 74 | "id": "8f16c1da-736a-4cad-8fd6-7e17883d46fb", 75 | "metadata": {}, 76 | "source": [ 77 | "# Basic Functionality" 78 | ] 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "id": "ac688975-c661-49b6-9bb0-6b9e3d154b49", 83 | "metadata": {}, 84 | "source": [ 85 | "## Templating" 86 | ] 87 | }, 88 | { 89 | "cell_type": "markdown", 90 | "id": "be943bca-8929-4985-8599-428734792bc7", 91 | "metadata": {}, 92 | "source": [ 93 | "Now that everything is prepared, we can write our first basic orchestration pipeline. The first fundamental module we will look at is templating. The templating module provides capabilities to define prompt skeletons that can then be parameterized per inference call. To check out how this works we first up select a Large Language Model (LLM) that will be used for inference." 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": null, 99 | "id": "311a7151-e10a-4e95-b03a-92047c077215", 100 | "metadata": {}, 101 | "outputs": [], 102 | "source": [ 103 | "from gen_ai_hub.orchestration.models.llm import LLM\n", 104 | "\n", 105 | "llm = LLM(\n", 106 | " name=\"gemini-1.5-flash\",\n", 107 | " version=\"latest\",\n", 108 | " parameters={\"max_tokens\": 256, \"temperature\": 0.2},\n", 109 | ")" 110 | ] 111 | }, 112 | { 113 | "cell_type": "markdown", 114 | "id": "8f817af4-fece-4d7b-afdb-0c6088940857", 115 | "metadata": {}, 116 | "source": [ 117 | "Now we can create a template using the template object provided by the SAP generative AI hub SDK." 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": null, 123 | "id": "8ab7da4b-47a4-408e-bbed-598323e4c95a", 124 | "metadata": {}, 125 | "outputs": [], 126 | "source": [ 127 | "from gen_ai_hub.orchestration.models.message import SystemMessage, UserMessage\n", 128 | "from gen_ai_hub.orchestration.models.template import Template, TemplateValue\n", 129 | "\n", 130 | "template = Template(\n", 131 | " messages=[\n", 132 | " SystemMessage(\"You are a helpful translation assistant.\"),\n", 133 | " UserMessage(\n", 134 | " \"Translate the following text to {{?to_lang}}: {{?text}}\",\n", 135 | " ),\n", 136 | " ],\n", 137 | " defaults=[\n", 138 | " TemplateValue(name=\"to_lang\", value=\"English\"),\n", 139 | " ],\n", 140 | ")" 141 | ] 142 | }, 143 | { 144 | "cell_type": "markdown", 145 | "id": "0522e4cf-fcce-4219-a02d-ce94a2fd0443", 146 | "metadata": {}, 147 | "source": [ 148 | "The code above creates a template that provides\n", 149 | "\n", 150 | "- a system message,\n", 151 | "- a user message that leverages templating syntax,\n", 152 | "- default values for the introduced template parameters.\n", 153 | "\n", 154 | "Currently there are three message types available:\n", 155 | "- `SystemMessage`: A message for priming AI behavior. The system message is usually passed in as the first of a sequence of input messages.\n", 156 | "- `UserMessage`: A message from a user.\n", 157 | "- `AssistantMessage`: A message of the LLM.\n", 158 | "\n", 159 | "Parameters are defined within the message string using the following syntax: `{{?param_name}}`." 160 | ] 161 | }, 162 | { 163 | "cell_type": "markdown", 164 | "id": "7f116aab-49fd-4161-934c-b675172626e4", 165 | "metadata": {}, 166 | "source": [ 167 | "Next up we create a orchestration configuration from the created objects." 168 | ] 169 | }, 170 | { 171 | "cell_type": "code", 172 | "execution_count": null, 173 | "id": "99af79b8-1f77-4244-8528-40f1a8864e0b", 174 | "metadata": {}, 175 | "outputs": [], 176 | "source": [ 177 | "from gen_ai_hub.orchestration.models.config import OrchestrationConfig\n", 178 | "\n", 179 | "config = OrchestrationConfig(\n", 180 | " template=template,\n", 181 | " llm=llm,\n", 182 | ")" 183 | ] 184 | }, 185 | { 186 | "cell_type": "markdown", 187 | "id": "816c83f9-a3bd-4e46-8c8b-3388b672cbad", 188 | "metadata": {}, 189 | "source": [ 190 | "Lastly, we can call the orchestration service. Note that the actual template values are now passed to the `run` method. The `TemplateValue` name parameter corresponds to the parameter name `text` provided in the user message string. The parameter `to_lang` is omitted and the default defined in the PromptTemplate is used." 191 | ] 192 | }, 193 | { 194 | "cell_type": "code", 195 | "execution_count": null, 196 | "id": "fdf112f6-ba4a-4fe6-8c52-28015d81d1e8", 197 | "metadata": {}, 198 | "outputs": [], 199 | "source": [ 200 | "from gen_ai_hub.orchestration.service import OrchestrationService\n", 201 | "\n", 202 | "orchestration_service = OrchestrationService(\n", 203 | " api_url=os.environ[\"AICORE_ORCHESTRATION_DEPLOYMENT_URL\"],\n", 204 | " config=config,\n", 205 | ")\n", 206 | "result = orchestration_service.run(\n", 207 | " template_values=[\n", 208 | " TemplateValue(\n", 209 | " name=\"text\",\n", 210 | " value=\"Interaktives Lernen mit SAP.\",\n", 211 | " )\n", 212 | " ]\n", 213 | ")\n", 214 | "print(result.orchestration_result.choices[0].message.content)" 215 | ] 216 | }, 217 | { 218 | "cell_type": "markdown", 219 | "id": "86cb096e-68d8-4e73-bf46-74531128b612", 220 | "metadata": {}, 221 | "source": [ 222 | "## Model Harmonization" 223 | ] 224 | }, 225 | { 226 | "cell_type": "markdown", 227 | "id": "f06fc5aa-07f5-41b6-aba8-15527d833b7b", 228 | "metadata": {}, 229 | "source": [ 230 | "Orchestration harmonizes model usage, removing the need for prompting each model in vendor specific fashion. You can easily switch between a variety of models. Check out [this SAP Note](https://me.sap.com/notes/3437766) for further information regarding model availability. For this Jump-Start session model access is restricted to `gemini-1.5-flash` and `meta--llama3-70b-instruct` though. The code below will demonstrate how to easily switch between models, based on the templating code above!" 231 | ] 232 | }, 233 | { 234 | "cell_type": "code", 235 | "execution_count": null, 236 | "id": "025870d1-fd8e-4330-ad5b-79ba5214f7f8", 237 | "metadata": {}, 238 | "outputs": [], 239 | "source": [ 240 | "orchestration_service.config.llm = LLM(\n", 241 | " name=\"meta--llama3-70b-instruct\",\n", 242 | ")\n", 243 | "result = orchestration_service.run(\n", 244 | " template_values=[\n", 245 | " TemplateValue(\n", 246 | " name=\"text\",\n", 247 | " value=\"Interaktives Lernen mit SAP.\",\n", 248 | " )\n", 249 | " ]\n", 250 | ")\n", 251 | "print(result.orchestration_result.choices[0].message.content)" 252 | ] 253 | }, 254 | { 255 | "cell_type": "markdown", 256 | "id": "e81f58ab-9954-4010-a884-ca045ebee737", 257 | "metadata": {}, 258 | "source": [ 259 | "You can switch between those two models and compare their responses throughout all exercises. Simply change the `name` parameter of the LLM module configuration." 260 | ] 261 | }, 262 | { 263 | "cell_type": "markdown", 264 | "id": "543a458f-dc10-4c5d-9849-c9dd57ad5193", 265 | "metadata": {}, 266 | "source": [ 267 | "## Streaming" 268 | ] 269 | }, 270 | { 271 | "cell_type": "markdown", 272 | "id": "759a5737-cd22-4103-b4cd-3e09f4d89cbf", 273 | "metadata": {}, 274 | "source": [ 275 | "When generating larger amounts of text you will notice that the time it takes the LLM to respond will increase.\n", 276 | "To make your application feel more responsive you can leverage the SDKs streaming capabilities where supported. \n", 277 | "When using this option you will get portions of the LLM answer as the text gets generated." 278 | ] 279 | }, 280 | { 281 | "cell_type": "code", 282 | "execution_count": null, 283 | "id": "227db6ff-e477-46c1-875a-e37c8e52eda5", 284 | "metadata": {}, 285 | "outputs": [], 286 | "source": [ 287 | "# call without streaming\n", 288 | "orchestration_service.config.llm = LLM(\n", 289 | " name=\"gpt-4o-mini\",\n", 290 | ")\n", 291 | "\n", 292 | "result = orchestration_service.run(\n", 293 | " template_values=[\n", 294 | " TemplateValue(\n", 295 | " name=\"text\",\n", 296 | " value=\"\"\"SAP Geschichte: Im Jahre 1972 hatten fünf Unternehmer eine Vision für das Geschäftspotenzial betriebswirtschaftlicher Software.\n", 297 | "Mit ihren Lösungen SAP R/2 und SAP R/3 etablierte SAP einen weltweiten Standard für ERP-Software (Enterprise Resource Planning).\n", 298 | "Mit SAP S/4HANA folgte dann die nächste Generation der SAP-ERP-Software.\n", 299 | "Die Plattform macht die In-Memory-Technologie für ERP-Anwender nutzbar, sodass riesige Datenmengen in Echtzeit verarbeitet werden können.\n", 300 | "Zudem lassen sich neue Technologien wie künstliche Intelligenz (KI) und maschinelles Lernen einbinden.\n", 301 | "Mit einem Kunden und einer Handvoll Mitarbeiter schlug die SAP einen Weg ein, der nicht nur die Welt der IT, sondern auch die Art und Weise, wie Unternehmen ihre Geschäfte abwickeln, langfristig verändern sollte.\n", 302 | "Nun, mit mehr als 50 Jahren Erfahrung und mehr als 400.000 Kunden, ist die SAP mehr denn je von dem Pioniergeist beflügelt, der ihre Gründer inspiriert hat.\n", 303 | "\n", 304 | "Die Anfangsjahre\n", 305 | "Am 1. April 1972 gründen fünf ehemalige IBM-Mitarbeiter das Unternehmen „Systemanalyse Programmentwicklung“.\n", 306 | "Die Vision von Dietmar Hopp, Hasso Plattner, Claus Wellenreuther, Klaus Tschira und Hans-Werner Hector: Standardsoftware für Unternehmen zu entwickeln, die alle betrieblichen Abläufe integriert und es ermöglicht, Daten in Echtzeit zu verarbeiten.\n", 307 | "Die SAP-Gründer und ihre Mitarbeiter entwickeln die Software im engen Austausch mit den Mitarbeitenden der Kunden. \n", 308 | "Bei Unternehmen wie ICI, Knoll, Burda und Linde entstehen bis 1975 in enger Zusammenarbeit mit deren Mitarbeitern Programme für die Finanzbuchhaltung (RF), die Rechnungsprüfung und die Materialwirtschaft (RM).\n", 309 | "Neben der Echtzeitverarbeitung der Daten setzen die SAP-Gründer auch auf Standardisierung und Integration.\n", 310 | "Diese Kombination bildet die Grundlage für den Aufstieg der SAP zum Weltmarktführer für betriebswirtschaftliche Software.\n", 311 | "Schon 1979 beginnt die Entwicklung der zweiten Softwaregeneration der SAP.\n", 312 | "1980 ziehen die rund 80 Mitarbeiter ins erste eigene Bürogebäude in Walldorf.\n", 313 | "\n", 314 | "Von R/3 zum globalen Lösungsanbieter\n", 315 | "Schon 1987 – während SAP R/2 boomt und ein Jahr vor dem Börsengang – beschließen die SAP-Chefs, die dritte Generation als multiplattformfähige Software zu entwickeln.\n", 316 | "1992 beginnt der Siegeszug der Client-Server-Software SAP R/3, die zu einem Wegbereiter der globalisierten Wirtschaft wird und SAP auch selbst endgültig zu einem Global Player mit Entwicklungszentren in aller Welt macht.\n", 317 | "1999 kündigt die SAP als Antwort auf Internet und New Economy die mySAP.com-Strategie an.\n", 318 | "Zehn Jahre später beschließt das Unternehmen, sich neben dem ERP-Markt auf drei weitere Zukunftsfelder zu konzentrieren: Mobil-, Datenbanktechnologie und die Cloud. Um möglichst schnell zu einem führenden Unternehmen auf diesen Märkten zu werden, übernimmt SAP mehrere Konkurrenten, darunter Business Objects, Sybase, Ariba, SuccessFactors, Fieldglass und Concur.\n", 319 | "\n", 320 | "Mit SAP HANA in die Cloud\n", 321 | "Von 2011 an setzen erste Kunden die In-Memory-Datenbank SAP HANA ein und analysieren damit Daten in Sekunden, wofür sie zuvor Tage und Wochen brauchten. Vier Jahre später stellt SAP ihre neueste Generation der Unternehmenssoftware vor, die vollständig auf SAP HANA basiert: SAP S/4HANA.\n", 322 | "SAP unterstützt Kunden heute auf dem Weg zum nachhaltigen, vernetzten und intelligenten Unternehmen, indem sie Lösungen, Technologie und Best Practices verknüpft, die für integrierte, digitale Geschäftsprozesse in der Cloud benötigt werden.\n", 323 | "SAP bietet Wahlfreiheit zwischen den vier größten Public-Cloud-Plattform-Betreibern.\n", 324 | "Die SAP Business Technology Platform unterstützt Kunden auf dem Weg zum intelligenten Unternehmen, egal ob Cloud-, On-Premise- oder hybride IT-Landschaft, und ist ein wesentliches Element des SAP-Angebots „RISE with SAP“. \n", 325 | "SAP zählt derzeit rund 300 Millionen Cloud-Anwender und bietet mit mehr als 100 Lösungen für alle Unternehmensfunktionen das größte Cloud-Portfolio aller Anbieter. SAP betreibt 47 Rechenzentren an 27 Standorten in 15 Ländern.\n", 326 | "\"\"\",\n", 327 | " )\n", 328 | " ]\n", 329 | ")\n", 330 | "\n", 331 | "print(result.orchestration_result.choices[0].message.content)" 332 | ] 333 | }, 334 | { 335 | "cell_type": "code", 336 | "execution_count": null, 337 | "id": "afbcc994-ed99-4514-9b2f-5a416934e9f4", 338 | "metadata": {}, 339 | "outputs": [], 340 | "source": [ 341 | "# call with streaming\n", 342 | "orchestration_service.config.llm = LLM(\n", 343 | " name=\"gpt-4o-mini\",\n", 344 | ")\n", 345 | "\n", 346 | "response = orchestration_service.stream(\n", 347 | " template_values=[\n", 348 | " TemplateValue(\n", 349 | " name=\"text\",\n", 350 | " value=\"\"\"SAP Geschichte: Im Jahre 1972 hatten fünf Unternehmer eine Vision für das Geschäftspotenzial betriebswirtschaftlicher Software.\n", 351 | "Mit ihren Lösungen SAP R/2 und SAP R/3 etablierte SAP einen weltweiten Standard für ERP-Software (Enterprise Resource Planning).\n", 352 | "Mit SAP S/4HANA folgte dann die nächste Generation der SAP-ERP-Software.\n", 353 | "Die Plattform macht die In-Memory-Technologie für ERP-Anwender nutzbar, sodass riesige Datenmengen in Echtzeit verarbeitet werden können.\n", 354 | "Zudem lassen sich neue Technologien wie künstliche Intelligenz (KI) und maschinelles Lernen einbinden.\n", 355 | "Mit einem Kunden und einer Handvoll Mitarbeiter schlug die SAP einen Weg ein, der nicht nur die Welt der IT, sondern auch die Art und Weise, wie Unternehmen ihre Geschäfte abwickeln, langfristig verändern sollte.\n", 356 | "Nun, mit mehr als 50 Jahren Erfahrung und mehr als 400.000 Kunden, ist die SAP mehr denn je von dem Pioniergeist beflügelt, der ihre Gründer inspiriert hat.\n", 357 | "\n", 358 | "Die Anfangsjahre\n", 359 | "Am 1. April 1972 gründen fünf ehemalige IBM-Mitarbeiter das Unternehmen „Systemanalyse Programmentwicklung“.\n", 360 | "Die Vision von Dietmar Hopp, Hasso Plattner, Claus Wellenreuther, Klaus Tschira und Hans-Werner Hector: Standardsoftware für Unternehmen zu entwickeln, die alle betrieblichen Abläufe integriert und es ermöglicht, Daten in Echtzeit zu verarbeiten.\n", 361 | "Die SAP-Gründer und ihre Mitarbeiter entwickeln die Software im engen Austausch mit den Mitarbeitenden der Kunden. \n", 362 | "Bei Unternehmen wie ICI, Knoll, Burda und Linde entstehen bis 1975 in enger Zusammenarbeit mit deren Mitarbeitern Programme für die Finanzbuchhaltung (RF), die Rechnungsprüfung und die Materialwirtschaft (RM).\n", 363 | "Neben der Echtzeitverarbeitung der Daten setzen die SAP-Gründer auch auf Standardisierung und Integration.\n", 364 | "Diese Kombination bildet die Grundlage für den Aufstieg der SAP zum Weltmarktführer für betriebswirtschaftliche Software.\n", 365 | "Schon 1979 beginnt die Entwicklung der zweiten Softwaregeneration der SAP.\n", 366 | "1980 ziehen die rund 80 Mitarbeiter ins erste eigene Bürogebäude in Walldorf.\n", 367 | "\n", 368 | "Von R/3 zum globalen Lösungsanbieter\n", 369 | "Schon 1987 – während SAP R/2 boomt und ein Jahr vor dem Börsengang – beschließen die SAP-Chefs, die dritte Generation als multiplattformfähige Software zu entwickeln.\n", 370 | "1992 beginnt der Siegeszug der Client-Server-Software SAP R/3, die zu einem Wegbereiter der globalisierten Wirtschaft wird und SAP auch selbst endgültig zu einem Global Player mit Entwicklungszentren in aller Welt macht.\n", 371 | "1999 kündigt die SAP als Antwort auf Internet und New Economy die mySAP.com-Strategie an.\n", 372 | "Zehn Jahre später beschließt das Unternehmen, sich neben dem ERP-Markt auf drei weitere Zukunftsfelder zu konzentrieren: Mobil-, Datenbanktechnologie und die Cloud. Um möglichst schnell zu einem führenden Unternehmen auf diesen Märkten zu werden, übernimmt SAP mehrere Konkurrenten, darunter Business Objects, Sybase, Ariba, SuccessFactors, Fieldglass und Concur.\n", 373 | "\n", 374 | "Mit SAP HANA in die Cloud\n", 375 | "Von 2011 an setzen erste Kunden die In-Memory-Datenbank SAP HANA ein und analysieren damit Daten in Sekunden, wofür sie zuvor Tage und Wochen brauchten. Vier Jahre später stellt SAP ihre neueste Generation der Unternehmenssoftware vor, die vollständig auf SAP HANA basiert: SAP S/4HANA.\n", 376 | "SAP unterstützt Kunden heute auf dem Weg zum nachhaltigen, vernetzten und intelligenten Unternehmen, indem sie Lösungen, Technologie und Best Practices verknüpft, die für integrierte, digitale Geschäftsprozesse in der Cloud benötigt werden.\n", 377 | "SAP bietet Wahlfreiheit zwischen den vier größten Public-Cloud-Plattform-Betreibern.\n", 378 | "Die SAP Business Technology Platform unterstützt Kunden auf dem Weg zum intelligenten Unternehmen, egal ob Cloud-, On-Premise- oder hybride IT-Landschaft, und ist ein wesentliches Element des SAP-Angebots „RISE with SAP“. \n", 379 | "SAP zählt derzeit rund 300 Millionen Cloud-Anwender und bietet mit mehr als 100 Lösungen für alle Unternehmensfunktionen das größte Cloud-Portfolio aller Anbieter. SAP betreibt 47 Rechenzentren an 27 Standorten in 15 Ländern.\n", 380 | "\"\"\",\n", 381 | " )\n", 382 | " ],\n", 383 | " stream_options={\n", 384 | " 'chunk_size': 1\n", 385 | " }\n", 386 | ")\n", 387 | "\n", 388 | "for chunk in response:\n", 389 | " print(chunk.orchestration_result.choices[0].delta.content, end='')" 390 | ] 391 | }, 392 | { 393 | "cell_type": "markdown", 394 | "id": "bf7bc8ae-4ed2-468a-9094-7c842bd22ee8", 395 | "metadata": {}, 396 | "source": [ 397 | "In the following exercises we will use streaming where appropriate." 398 | ] 399 | }, 400 | { 401 | "cell_type": "markdown", 402 | "id": "54930092-59da-4e4b-9d6a-bb39d7b75a55", 403 | "metadata": {}, 404 | "source": [ 405 | "## Response Formatting" 406 | ] 407 | }, 408 | { 409 | "cell_type": "markdown", 410 | "id": "bb85e8cb-454f-4291-8b0f-b06f0aebc949", 411 | "metadata": {}, 412 | "source": [ 413 | "While Large Language Models (LLMs) can provide direct, user-friendly responses for simple scenarios, many complex applications require further processing of the LLM output. In these cases, it becomes crucial to establish and enforce a specific output format from the LLM. This structured approach ensures consistency and facilitates efficient post-processing of the model's responses. Using orchestration service and the generative-ai-hub-sdk you can achieve this by specifying the `response_format` parameter." 414 | ] 415 | }, 416 | { 417 | "cell_type": "markdown", 418 | "id": "7ceb0694-c987-451d-98f0-d670455fd26a", 419 | "metadata": {}, 420 | "source": [ 421 | "The following response formats are supported:\n", 422 | "- `text`: This is the simplest form where the model's output is generated as plain text. It is suitable for applications that require raw text processing.\n", 423 | "- `json_object`: Under this setting, the model's output is structured as a JSON object. This is useful for applications that handle data in JSON format, enabling easy integration with web applications and APIs.\n", 424 | "- `json_schema`: This setting allows the model's output to adhere to a defined JSON schema. This is particularly useful for applications that require strict data validation, ensuring the output matches a predefined schema.\n", 425 | "\n", 426 | "The following code demonstrates how the `json_schema` output format can be used. If you are not familiar with JSON Schema please read up on it at [https://json-schema.org](https://json-schema.org) first." 427 | ] 428 | }, 429 | { 430 | "cell_type": "code", 431 | "execution_count": null, 432 | "id": "df06d2e9-7fa1-4753-a335-10201d756aef", 433 | "metadata": {}, 434 | "outputs": [], 435 | "source": [ 436 | "from gen_ai_hub.orchestration.models.response_format import ResponseFormatJsonSchema\n", 437 | "\n", 438 | "json_schema = {\n", 439 | " \"title\": \"Person\",\n", 440 | " \"type\": \"object\",\n", 441 | " \"properties\": {\n", 442 | " \"firstName\": {\n", 443 | " \"type\": \"string\",\n", 444 | " \"description\": \"The person's first name.\"\n", 445 | " },\n", 446 | " \"lastName\": {\n", 447 | " \"type\": \"string\",\n", 448 | " \"description\": \"The person's last name.\"\n", 449 | " }\n", 450 | " }\n", 451 | "}\n", 452 | "\n", 453 | "template = Template(\n", 454 | " messages=[\n", 455 | " SystemMessage(\"You are a helpful assistant.\"),\n", 456 | " UserMessage(\"{{?user_query}}\")\n", 457 | " ],\n", 458 | " response_format = ResponseFormatJsonSchema(name=\"person\", description=\"person mapping\", schema=json_schema),\n", 459 | " defaults=[\n", 460 | " TemplateValue(name=\"user_query\", value=\"Who was the first person on the moon?\")\n", 461 | " ]\n", 462 | ")\n", 463 | "\n", 464 | "config = OrchestrationConfig(\n", 465 | " template=template,\n", 466 | " llm=llm,\n", 467 | ")\n", 468 | "\n", 469 | "orchestration_service.config = config\n", 470 | "\n", 471 | "result = orchestration_service.run()\n", 472 | "\n", 473 | "print(result.orchestration_result.choices[0].message.content)" 474 | ] 475 | }, 476 | { 477 | "cell_type": "markdown", 478 | "id": "33ab1cf0-6e9c-4576-94a5-bf00df4ba4a1", 479 | "metadata": {}, 480 | "source": [ 481 | "# Summary" 482 | ] 483 | }, 484 | { 485 | "cell_type": "markdown", 486 | "id": "24ae4510-d8b7-41cc-b8c6-9b7268137411", 487 | "metadata": {}, 488 | "source": [ 489 | "Within this exercise you learned how to create a basic Orchestration pipeline that uses the Templating module. Also, you changed the model used for inference with ease. Let's explore more modules in the following exercises. Continue to [Exercise 2 - Orchestration Content Filtering](./ex2.ipynb)." 490 | ] 491 | } 492 | ], 493 | "metadata": { 494 | "kernelspec": { 495 | "display_name": "Python 3 (ipykernel)", 496 | "language": "python", 497 | "name": "python3" 498 | }, 499 | "language_info": { 500 | "codemirror_mode": { 501 | "name": "ipython", 502 | "version": 3 503 | }, 504 | "file_extension": ".py", 505 | "mimetype": "text/x-python", 506 | "name": "python", 507 | "nbconvert_exporter": "python", 508 | "pygments_lexer": "ipython3", 509 | "version": "3.13.2" 510 | } 511 | }, 512 | "nbformat": 4, 513 | "nbformat_minor": 5 514 | } 515 | -------------------------------------------------------------------------------- /exercises/python/ex2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "f7be88a8-702a-452a-a1c5-64ac7c69f206", 6 | "metadata": {}, 7 | "source": [ 8 | "# Content Filtering" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "id": "082bc7c3-2519-4302-812b-9ccc74947ae8", 14 | "metadata": {}, 15 | "source": [ 16 | "Orchestration also supports content filtering to moderate input and output. For each, multiple filters can be applied that remove harmful content from texts. Depending on the filter, sensitivity of various types can be configured." 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "id": "712a76c2-cea5-4d27-9450-8e9e5abc6c9b", 22 | "metadata": {}, 23 | "source": [ 24 | "## Input Filter" 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "id": "1d5f0ed1-1e3e-4cbc-b1c3-c2250c7efd62", 30 | "metadata": {}, 31 | "source": [ 32 | "Input filtering is handy for blocking out harmful content form e.g. user input. To make use of input filtering we first up need to define a basic pipeline again. We use a simple prompt template, that just passes text provided as a parameter to an LLM." 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": null, 38 | "id": "ec261164-7bf2-4976-8419-4ceee9a246cb", 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [ 42 | "from gen_ai_hub.orchestration.models.llm import LLM\n", 43 | "from gen_ai_hub.orchestration.models.message import SystemMessage, UserMessage\n", 44 | "from gen_ai_hub.orchestration.models.template import Template, TemplateValue\n", 45 | "\n", 46 | "llm = LLM(\n", 47 | " name=\"gemini-1.5-flash\",\n", 48 | " version=\"latest\",\n", 49 | " parameters={\"max_tokens\": 256, \"temperature\": 0.2},\n", 50 | ")\n", 51 | "\n", 52 | "template = Template(messages=[UserMessage(\"{{?text}}\")])" 53 | ] 54 | }, 55 | { 56 | "cell_type": "markdown", 57 | "id": "68548f9a-f6bd-444c-8e79-4c81ad598784", 58 | "metadata": {}, 59 | "source": [ 60 | "Next up we will configure an AzureContentFilter using the corresponding SDK primitives." 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": null, 66 | "id": "4357055f-d596-4f58-b348-fa5c5bae2a96", 67 | "metadata": {}, 68 | "outputs": [], 69 | "source": [ 70 | "from gen_ai_hub.orchestration.models.azure_content_filter import AzureContentFilter\n", 71 | "\n", 72 | "content_filter = AzureContentFilter(\n", 73 | " hate=0,\n", 74 | " sexual=0,\n", 75 | " self_harm=0,\n", 76 | " violence=0,\n", 77 | ")" 78 | ] 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "id": "8ca2ed16-6049-4c1b-911c-cf7672800738", 83 | "metadata": {}, 84 | "source": [ 85 | "**Note:** The lower the sensitivity value the higher the sensitivity. Zero (0) corresponds to the highest degree of content moderation. For further information please check out: https://learn.microsoft.com/en-us/azure/ai-services/content-safety/concepts/harm-categories?tabs=warning" 86 | ] 87 | }, 88 | { 89 | "cell_type": "markdown", 90 | "id": "9f7ef7b2-9c8d-4e86-851c-0b85853d38be", 91 | "metadata": {}, 92 | "source": [ 93 | "Now that we have all modules defined, the only thing left to do is plugging everything together." 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": null, 99 | "id": "53759219-6df3-41d0-8be0-9b4fd4bcc7a0", 100 | "metadata": {}, 101 | "outputs": [], 102 | "source": [ 103 | "from gen_ai_hub.orchestration.service import OrchestrationService\n", 104 | "from gen_ai_hub.orchestration.models.config import OrchestrationConfig\n", 105 | "from gen_ai_hub.orchestration.models.content_filtering import ContentFiltering, InputFiltering\n", 106 | "\n", 107 | "config = OrchestrationConfig(\n", 108 | " template=template,\n", 109 | " llm=llm,\n", 110 | " filtering=ContentFiltering(input_filtering=InputFiltering(filters=[\n", 111 | " content_filter,\n", 112 | " ])),\n", 113 | ")\n", 114 | "\n", 115 | "orchestration_service = OrchestrationService(\n", 116 | " api_url=os.environ[\"AICORE_ORCHESTRATION_DEPLOYMENT_URL\"],\n", 117 | " config=config,\n", 118 | ")" 119 | ] 120 | }, 121 | { 122 | "cell_type": "markdown", 123 | "id": "72a0f422-3610-4383-b6a1-ac82e9882d72", 124 | "metadata": {}, 125 | "source": [ 126 | "If the content filter detects a violation when performing an inference an error of type `OrchestrationError` will be raised." 127 | ] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": null, 132 | "id": "b29d6513-1a63-4326-9dbb-0c6ead10e30f", 133 | "metadata": {}, 134 | "outputs": [], 135 | "source": [ 136 | "from gen_ai_hub.orchestration.exceptions import OrchestrationError\n", 137 | "\n", 138 | "try:\n", 139 | " result = orchestration_service.run(\n", 140 | " template_values=[\n", 141 | " TemplateValue(\n", 142 | " name=\"text\",\n", 143 | " value=\"I hate you!\",\n", 144 | " ),\n", 145 | " ]\n", 146 | " )\n", 147 | "except OrchestrationError as error:\n", 148 | " print(error.message)" 149 | ] 150 | }, 151 | { 152 | "cell_type": "markdown", 153 | "id": "2aca10a5-b9d3-4c02-ac91-0b1c6c2eb009", 154 | "metadata": {}, 155 | "source": [ 156 | "The filter can be cleared by adjusting the prompt." 157 | ] 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": null, 162 | "id": "e6a531ce-5954-476f-8b49-af4cd5cd28a6", 163 | "metadata": {}, 164 | "outputs": [], 165 | "source": [ 166 | "result = orchestration_service.run(\n", 167 | " template_values=[\n", 168 | " TemplateValue(\n", 169 | " name=\"text\",\n", 170 | " value=\"This is a peaceful text!\",\n", 171 | " )\n", 172 | " ]\n", 173 | ")\n", 174 | "print(result.orchestration_result.choices[0].message.content)" 175 | ] 176 | }, 177 | { 178 | "cell_type": "markdown", 179 | "id": "68b2cd93-5e89-497e-892c-a8f9d3c2f759", 180 | "metadata": {}, 181 | "source": [ 182 | "## Output Filter" 183 | ] 184 | }, 185 | { 186 | "cell_type": "markdown", 187 | "id": "a0e56ffd-53a0-4914-a7f9-a2411a516064", 188 | "metadata": {}, 189 | "source": [ 190 | "Similarly, also LLM output can be filtered. We will just use our already created filter and apply it to the LLM output within the configuration." 191 | ] 192 | }, 193 | { 194 | "cell_type": "code", 195 | "execution_count": null, 196 | "id": "0cf0b75f-517e-49e6-a9a2-af20140b40c7", 197 | "metadata": {}, 198 | "outputs": [], 199 | "source": [ 200 | "from gen_ai_hub.orchestration.models.content_filtering import OutputFiltering\n", 201 | "\n", 202 | "orchestration_service.config = OrchestrationConfig(\n", 203 | " template=template,\n", 204 | " llm=llm,\n", 205 | " filtering=ContentFiltering(output_filtering=OutputFiltering(filters=[\n", 206 | " content_filter,\n", 207 | " ])),\n", 208 | ")" 209 | ] 210 | }, 211 | { 212 | "cell_type": "markdown", 213 | "id": "75acc334-0cec-4311-b3bf-0964f0b0ec5e", 214 | "metadata": {}, 215 | "source": [ 216 | "Now let's try out if the filter works by prompting for an inherently violent lyrics from Johann Wolfgang von Goethe. Even though the version recited by the LLM might differ from the most common version, it should contain some form of suggested violence." 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": null, 222 | "id": "6b47c472-7973-45c3-951a-aadc98f67c4e", 223 | "metadata": {}, 224 | "outputs": [], 225 | "source": [ 226 | "result = orchestration_service.stream(\n", 227 | " template_values=[\n", 228 | " TemplateValue(\n", 229 | " name=\"text\",\n", 230 | " value='Provide the lyrics of the ballad \"Erlkönig\" from Johann Wolfgang von Goethe in English.',\n", 231 | " )\n", 232 | " ],\n", 233 | " stream_options={\n", 234 | " 'chunk_size': 1\n", 235 | " }\n", 236 | ")\n", 237 | "\n", 238 | "for chunk in result:\n", 239 | " print(chunk.orchestration_result.choices[0].delta.content, end='')" 240 | ] 241 | }, 242 | { 243 | "cell_type": "markdown", 244 | "id": "97147b02-65ac-4c54-85d9-f4881f847d54", 245 | "metadata": {}, 246 | "source": [ 247 | "**Note:** The behavior will differ from the input filter. Instead of raising an error, the streamed LLM response will be finalized before violent content is returned." 248 | ] 249 | }, 250 | { 251 | "cell_type": "markdown", 252 | "id": "64ead618-db88-46b7-a278-635b732b0e31", 253 | "metadata": {}, 254 | "source": [ 255 | "Feel free to try around with AzureContentFilter settings. If you set sensitivity for violence to 4 you will be able to read the lyrics of Erlkönig." 256 | ] 257 | }, 258 | { 259 | "cell_type": "markdown", 260 | "id": "8d30dd10-bab1-4afd-964a-92707824faac", 261 | "metadata": {}, 262 | "source": [ 263 | "# Summary" 264 | ] 265 | }, 266 | { 267 | "cell_type": "markdown", 268 | "id": "3f599416-cc0b-44eb-9412-cd6dd97e0663", 269 | "metadata": {}, 270 | "source": [ 271 | "In this exercise you learned how content filtering can be applied to input and output using orchestration. Rather than entirely removing data, some scenarios call for selectively redacting specific information, such as email addresses or phone numbers. Continue to [Exercise 3 - Data Masking](./ex3.ipynb) to find out how this can be achieved." 272 | ] 273 | } 274 | ], 275 | "metadata": { 276 | "kernelspec": { 277 | "display_name": "Python 3 (ipykernel)", 278 | "language": "python", 279 | "name": "python3" 280 | }, 281 | "language_info": { 282 | "codemirror_mode": { 283 | "name": "ipython", 284 | "version": 3 285 | }, 286 | "file_extension": ".py", 287 | "mimetype": "text/x-python", 288 | "name": "python", 289 | "nbconvert_exporter": "python", 290 | "pygments_lexer": "ipython3", 291 | "version": "3.13.2" 292 | } 293 | }, 294 | "nbformat": 4, 295 | "nbformat_minor": 5 296 | } 297 | -------------------------------------------------------------------------------- /exercises/python/ex3.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "f14fe80f-519b-48e4-a463-3995511d713e", 6 | "metadata": {}, 7 | "source": [ 8 | "# Data Masking" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "id": "59958a6f-b6fc-49f6-8f87-bb48db20326c", 14 | "metadata": {}, 15 | "source": [ 16 | "The data masking module anonymizes or pseudonymizes personally identifiable information (PII) before it is processed by the LLM module. When data is anonymized, all identifying information is replaced with placeholders (e.g., MASKED_ENTITY), and the original data cannot be recovered, ensuring that no trace of the original information is retained. In contrast, pseudonymized data is substituted with unique placeholders (e.g., MASKED_ENTITY_ID), allowing the original information to be restored if needed. In both cases, the masking module identifies sensitive data and replaces it with appropriate placeholders before further processing." 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "id": "67ee9830-42b9-4171-bd46-4698fa8cde15", 22 | "metadata": {}, 23 | "source": [ 24 | "First up the `DataMasking` module must be imported and configured. A method (either `ANONYMIZATION` or `PSEUDONYMIZATION`) must be set and entities must be targeted (`EMAIL`, `PHONE`, `PERSON`, `ORG`, `LOCATION`)." 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": null, 30 | "id": "6431ec15-d718-4d4c-95eb-891d6875540e", 31 | "metadata": {}, 32 | "outputs": [], 33 | "source": [ 34 | "from gen_ai_hub.orchestration.models.data_masking import DataMasking\n", 35 | "from gen_ai_hub.orchestration.models.sap_data_privacy_integration import SAPDataPrivacyIntegration, MaskingMethod, ProfileEntity\n", 36 | "\n", 37 | "data_masking = DataMasking(\n", 38 | " providers=[\n", 39 | " SAPDataPrivacyIntegration(\n", 40 | " method=MaskingMethod.ANONYMIZATION, # or MaskingMethod.PSEUDONYMIZATION\n", 41 | " entities=[\n", 42 | " ProfileEntity.EMAIL,\n", 43 | " ProfileEntity.PHONE,\n", 44 | " ProfileEntity.PERSON,\n", 45 | " ProfileEntity.ORG,\n", 46 | " ProfileEntity.LOCATION\n", 47 | " ]\n", 48 | " )\n", 49 | " ]\n", 50 | ")" 51 | ] 52 | }, 53 | { 54 | "cell_type": "markdown", 55 | "id": "bf199bcc-a7c5-446f-8266-ae9a2f5d9706", 56 | "metadata": {}, 57 | "source": [ 58 | "To complete the configuration, simply incorporate the pre-configured `DataMasking` module into the `OrchestrationConfig`, following the same process we've used for other modules." 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": null, 64 | "id": "f1154571-170f-4071-9e49-a04fba187b35", 65 | "metadata": {}, 66 | "outputs": [], 67 | "source": [ 68 | "import os\n", 69 | "\n", 70 | "from gen_ai_hub.orchestration.models.config import OrchestrationConfig\n", 71 | "from gen_ai_hub.orchestration.models.template import Template, TemplateValue\n", 72 | "from gen_ai_hub.orchestration.models.llm import LLM\n", 73 | "from gen_ai_hub.orchestration.models.message import SystemMessage, UserMessage\n", 74 | "from gen_ai_hub.orchestration.service import OrchestrationService\n", 75 | "\n", 76 | "config = OrchestrationConfig(\n", 77 | " template=Template(\n", 78 | " messages=[\n", 79 | " SystemMessage(\"You are a helpful AI assistant.\"),\n", 80 | " UserMessage(\"Please repeat the following input: {{?pii}}\"),\n", 81 | " ]\n", 82 | " ),\n", 83 | " llm=LLM(\n", 84 | " name=\"gemini-1.5-flash\",\n", 85 | " ),\n", 86 | " data_masking=data_masking\n", 87 | ")\n", 88 | "\n", 89 | "orchestration_service = OrchestrationService(\n", 90 | " api_url=os.environ[\"AICORE_ORCHESTRATION_DEPLOYMENT_URL\"],\n", 91 | " config=config,\n", 92 | ")\n", 93 | "\n", 94 | "result = orchestration_service.run(\n", 95 | " config=config,\n", 96 | " template_values=[\n", 97 | " TemplateValue(\n", 98 | " name=\"pii\",\n", 99 | " value=\"My name is Max Mustermann. You can contact me via max.mustermann@sap.com. I live in Dietmar-Hopp-Allee 16, Walldorf Germany.\",\n", 100 | " )\n", 101 | " ]\n", 102 | ")\n", 103 | "\n", 104 | "print(result.orchestration_result.choices[0].message.content)" 105 | ] 106 | }, 107 | { 108 | "cell_type": "markdown", 109 | "id": "8d0a5e82-f697-41d6-8970-a4ebdefd314c", 110 | "metadata": {}, 111 | "source": [ 112 | "Make sure to try out anonymizing/pseudonymizing different entities or try out changing the user message." 113 | ] 114 | }, 115 | { 116 | "cell_type": "markdown", 117 | "id": "efc84472-b662-4090-8a77-d748df4b9c4e", 118 | "metadata": {}, 119 | "source": [ 120 | "# Summary" 121 | ] 122 | }, 123 | { 124 | "cell_type": "markdown", 125 | "id": "93c43f9a-7b69-4d91-8b32-2067ff849275", 126 | "metadata": {}, 127 | "source": [ 128 | "In this exercise you learned how data can be masked using orchestration. Now let's combine capabilities into a more complex scenario. Continue to [Exercise 4 - Orchestration Chatbot](./ex4.ipynb)." 129 | ] 130 | } 131 | ], 132 | "metadata": { 133 | "kernelspec": { 134 | "display_name": "Python 3 (ipykernel)", 135 | "language": "python", 136 | "name": "python3" 137 | }, 138 | "language_info": { 139 | "codemirror_mode": { 140 | "name": "ipython", 141 | "version": 3 142 | }, 143 | "file_extension": ".py", 144 | "mimetype": "text/x-python", 145 | "name": "python", 146 | "nbconvert_exporter": "python", 147 | "pygments_lexer": "ipython3", 148 | "version": "3.13.2" 149 | } 150 | }, 151 | "nbformat": 4, 152 | "nbformat_minor": 5 153 | } 154 | -------------------------------------------------------------------------------- /exercises/python/ex4.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "7ae93e38-6452-483c-81eb-91c3d64246bc", 6 | "metadata": {}, 7 | "source": [ 8 | "# Orchestration Chatbot" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "id": "5765115a-307b-453a-9fd4-222183564bf8", 14 | "metadata": {}, 15 | "source": [ 16 | "We will now combine templating and content filtering in a chatbot. Additionally we will make use of the orchestration services history capability." 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "id": "5fe29833-0b77-45cb-926a-ce371c99056a", 22 | "metadata": {}, 23 | "source": [ 24 | "We start with building the chatbot class. This class will be responsible for providing the template for the user query and managing the history. Note that the `chat` method uses the `history` parameter to send along the history to the orchestration service." 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": null, 30 | "id": "5d2dc5f8-6e6f-4726-80bc-737a076bcfeb", 31 | "metadata": {}, 32 | "outputs": [], 33 | "source": [ 34 | "from typing import List\n", 35 | "\n", 36 | "from gen_ai_hub.orchestration.models.message import Message, SystemMessage, UserMessage, AssistantMessage\n", 37 | "from gen_ai_hub.orchestration.models.template import Template, TemplateValue\n", 38 | "from gen_ai_hub.orchestration.service import OrchestrationService\n", 39 | "\n", 40 | "\n", 41 | "class OrchestrationChatbot:\n", 42 | " def __init__(self, orchestration_service: OrchestrationService):\n", 43 | " self.service = orchestration_service\n", 44 | " system_message = SystemMessage(\"You are a helpful chatbot assistant.\")\n", 45 | " self.service.config.template = Template(\n", 46 | " messages=[\n", 47 | " system_message,\n", 48 | " UserMessage(\"{{?user_query}}\"),\n", 49 | " ],\n", 50 | " )\n", 51 | " self.history: List[Message] = [\n", 52 | " system_message,\n", 53 | " ]\n", 54 | "\n", 55 | " def chat(self, user_input):\n", 56 | " response = self.service.stream(\n", 57 | " template_values=[\n", 58 | " TemplateValue(name=\"user_query\", value=user_input),\n", 59 | " ],\n", 60 | " history=self.history,\n", 61 | " stream_options={\n", 62 | " 'chunk_size': 1\n", 63 | " }\n", 64 | " )\n", 65 | "\n", 66 | " collected_response = \"\"\n", 67 | " for chunk in response:\n", 68 | " responseChunk = chunk.orchestration_result.choices[0].delta.content\n", 69 | " collected_response += responseChunk\n", 70 | " yield responseChunk\n", 71 | "\n", 72 | " self.history.append(UserMessage(user_input))\n", 73 | " self.history.append(AssistantMessage(collected_response))" 74 | ] 75 | }, 76 | { 77 | "cell_type": "markdown", 78 | "id": "b4df3010-e28c-4677-b09e-4747855ce346", 79 | "metadata": {}, 80 | "source": [ 81 | "Next up we will create an orchestration configuration. Next to the model parameter we also pass a content filter for input and output to ensure a nice conversation. Also we will add data masking to filter out personal information." 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": null, 87 | "id": "e4b88013-88e0-4b09-904e-ee2d39bb881d", 88 | "metadata": {}, 89 | "outputs": [], 90 | "source": [ 91 | "from gen_ai_hub.orchestration.models.config import OrchestrationConfig\n", 92 | "from gen_ai_hub.orchestration.models.azure_content_filter import AzureContentFilter\n", 93 | "from gen_ai_hub.orchestration.models.content_filtering import ContentFiltering, InputFiltering, OutputFiltering\n", 94 | "from gen_ai_hub.orchestration.models.llm import LLM\n", 95 | "from gen_ai_hub.orchestration.models.data_masking import DataMasking\n", 96 | "from gen_ai_hub.orchestration.models.sap_data_privacy_integration import SAPDataPrivacyIntegration, MaskingMethod, ProfileEntity\n", 97 | "\n", 98 | "content_filter = AzureContentFilter(\n", 99 | " hate=0,\n", 100 | " sexual=0,\n", 101 | " self_harm=0,\n", 102 | " violence=0,\n", 103 | ")\n", 104 | "\n", 105 | "data_masking = DataMasking(\n", 106 | " providers=[\n", 107 | " SAPDataPrivacyIntegration(\n", 108 | " method=MaskingMethod.ANONYMIZATION,\n", 109 | " entities=[\n", 110 | " ProfileEntity.EMAIL,\n", 111 | " ProfileEntity.PHONE,\n", 112 | " ProfileEntity.PERSON,\n", 113 | " ProfileEntity.ORG,\n", 114 | " ProfileEntity.LOCATION\n", 115 | " ]\n", 116 | " )\n", 117 | " ]\n", 118 | ")\n", 119 | "\n", 120 | "config = OrchestrationConfig(\n", 121 | " llm=LLM(name=\"gemini-1.5-flash\"),\n", 122 | " template=None,\n", 123 | " filtering=ContentFiltering(\n", 124 | " input_filtering=InputFiltering(filters=[content_filter]),\n", 125 | " output_filtering=OutputFiltering(filters=[content_filter]),\n", 126 | " ),\n", 127 | " data_masking=data_masking,\n", 128 | ")\n", 129 | "\n", 130 | "orchestration_service = OrchestrationService(\n", 131 | " api_url=os.environ[\"AICORE_ORCHESTRATION_DEPLOYMENT_URL\"],\n", 132 | " config=config,\n", 133 | ")" 134 | ] 135 | }, 136 | { 137 | "cell_type": "markdown", 138 | "id": "b00e7648-742c-48dd-b97a-d5463388d152", 139 | "metadata": {}, 140 | "source": [ 141 | "Lets chat with our bot and test if it is able to recall a conversation!" 142 | ] 143 | }, 144 | { 145 | "cell_type": "code", 146 | "execution_count": null, 147 | "id": "4bccdf7a-f67a-4730-b206-d7b429ddbd74", 148 | "metadata": {}, 149 | "outputs": [], 150 | "source": [ 151 | "bot = OrchestrationChatbot(orchestration_service=orchestration_service)" 152 | ] 153 | }, 154 | { 155 | "cell_type": "code", 156 | "execution_count": null, 157 | "id": "5f3e6a6e-9a56-4158-9748-21ca13f34126", 158 | "metadata": {}, 159 | "outputs": [], 160 | "source": [ 161 | "for chatPart in bot.chat(\"How are you?\"):\n", 162 | " print(chatPart, end='')" 163 | ] 164 | }, 165 | { 166 | "cell_type": "code", 167 | "execution_count": null, 168 | "id": "0ae3fc17-5d36-48e7-847a-67be6fc402b3", 169 | "metadata": {}, 170 | "outputs": [], 171 | "source": [ 172 | "for chatPart in bot.chat(\"What's the weather like today?\"):\n", 173 | " print(chatPart, end='')" 174 | ] 175 | }, 176 | { 177 | "cell_type": "code", 178 | "execution_count": null, 179 | "id": "93fa8a0b-319c-4c1d-9f35-a8aded75bf1a", 180 | "metadata": {}, 181 | "outputs": [], 182 | "source": [ 183 | "for chatPart in bot.chat(\"Can you remember what I first asked you?\"):\n", 184 | " print(chatPart, end='')" 185 | ] 186 | }, 187 | { 188 | "cell_type": "code", 189 | "execution_count": null, 190 | "id": "e8b3a473-69c2-4276-9db4-608791aa5305", 191 | "metadata": {}, 192 | "outputs": [], 193 | "source": [ 194 | "for chatPart in bot.chat(\"Write me a long poem about the content of our conversation.\"):\n", 195 | " print(chatPart, end='')" 196 | ] 197 | }, 198 | { 199 | "cell_type": "markdown", 200 | "id": "db711813-490e-4021-ad92-bca3f26ef0c1", 201 | "metadata": {}, 202 | "source": [ 203 | "# Summary" 204 | ] 205 | }, 206 | { 207 | "cell_type": "markdown", 208 | "id": "3497c99b-176d-4307-ba01-f7bbcb708411", 209 | "metadata": {}, 210 | "source": [ 211 | "In this exercise you got hands-on experience on how to use orchestration in complex scenarios. This exercise concludes the SAP generative AI hub SDK (Python) track. Feel free to further explore the orchestration capabilities using this SDK or checkout the [SAP Cloud SDK for AI (JavaScript)](https://github.com/SAP-samples/teched2024-AI180/blob/main/exercises/javascript/README.md) track." 212 | ] 213 | } 214 | ], 215 | "metadata": { 216 | "kernelspec": { 217 | "display_name": "Python 3 (ipykernel)", 218 | "language": "python", 219 | "name": "python3" 220 | }, 221 | "language_info": { 222 | "codemirror_mode": { 223 | "name": "ipython", 224 | "version": 3 225 | }, 226 | "file_extension": ".py", 227 | "mimetype": "text/x-python", 228 | "name": "python", 229 | "nbconvert_exporter": "python", 230 | "pygments_lexer": "ipython3", 231 | "version": "3.13.2" 232 | } 233 | }, 234 | "nbformat": 4, 235 | "nbformat_minor": 5 236 | } 237 | --------------------------------------------------------------------------------