├── .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 | [](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 |
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 |
--------------------------------------------------------------------------------