Skip to content

Commit 49be7aa

Browse files
committed
Orchestration Convenience RG-Scoped Prompt Templates
1 parent 35004bb commit 49be7aa

File tree

10 files changed

+448
-33
lines changed

10 files changed

+448
-33
lines changed

orchestration/src/main/java/com/sap/ai/sdk/orchestration/TemplateConfig.java

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,28 +35,64 @@ public static OrchestrationTemplate create() {
3535
}
3636

3737
/**
38-
* Build a template reference.
38+
* Build a template reference with tenant level scope.
3939
*
4040
* @return An intermediate object to build the template reference.
4141
*/
4242
@Nonnull
43-
public static ReferenceBuilder reference() {
43+
public static ReferenceBuilder referenceTenant() {
4444
final var templ = TemplateRefByScenarioNameVersion.create();
45-
return s -> n -> v -> new OrchestrationTemplateReference(templ.scenario(s).name(n).version(v));
45+
final var scope = TemplateRefByScenarioNameVersion.ScopeEnum.TENANT;
46+
47+
return s ->
48+
n ->
49+
v ->
50+
new OrchestrationTemplateReference(
51+
templ.scenario(s).name(n).version(v).scope(scope));
52+
}
53+
54+
/**
55+
* Build a template reference with resource group scope.
56+
*
57+
* @return An intermediate object to build the template reference.
58+
*/
59+
@Nonnull
60+
public static ReferenceBuilder referenceResourceGroup() {
61+
final var templ = TemplateRefByScenarioNameVersion.create();
62+
final var scope = TemplateRefByScenarioNameVersion.ScopeEnum.RESOURCE_GROUP;
63+
64+
return s ->
65+
n ->
66+
v ->
67+
new OrchestrationTemplateReference(
68+
templ.scenario(s).name(n).version(v).scope(scope));
4669
}
4770

48-
/** Intermediate object to build a template reference. */
71+
/** Intermediate object to build a template referenceTenant. */
4972
@FunctionalInterface
5073
public interface ReferenceBuilder {
5174
/**
52-
* Build a template reference with the given id.
75+
* Build a template reference with the given id for tenant scope.
76+
*
77+
* @param id The id of the template.
78+
* @return A template reference with the given id.
79+
*/
80+
@Nonnull
81+
default OrchestrationTemplateReference byIdTenant(@Nonnull final String id) {
82+
final var scope = TemplateRefByID.ScopeEnum.TENANT;
83+
return new OrchestrationTemplateReference(TemplateRefByID.create().id(id).scope(scope));
84+
}
85+
86+
/**
87+
* Build a template reference with the given id for resource group scope.
5388
*
5489
* @param id The id of the template.
5590
* @return A template reference with the given id.
5691
*/
5792
@Nonnull
58-
default OrchestrationTemplateReference byId(@Nonnull final String id) {
59-
return new OrchestrationTemplateReference(TemplateRefByID.create().id(id));
93+
default OrchestrationTemplateReference byIdResourceGroup(@Nonnull final String id) {
94+
final var scope = TemplateRefByID.ScopeEnum.RESOURCE_GROUP;
95+
return new OrchestrationTemplateReference(TemplateRefByID.create().id(id).scope(scope));
6096
}
6197

6298
/**

orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationConvenienceUnitTest.java

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,16 +158,25 @@ void testTemplateConstruction() {
158158

159159
@Test
160160
void testTemplateReferenceConstruction() {
161-
var templateReferenceId = TemplateConfig.reference().byId("id");
161+
var templateReferenceId = TemplateConfig.referenceTenant().byIdTenant("id");
162162
var expectedTemplateReferenceId =
163163
new OrchestrationTemplateReference(TemplateRefByID.create().id("id"));
164164
var templateReferenceIdLowLevel =
165165
TemplateRef.create().templateRef(TemplateRefByID.create().id("id"));
166166
assertThat(templateReferenceId).isEqualTo(expectedTemplateReferenceId);
167167
assertThat(templateReferenceId.toLowLevel()).isEqualTo(templateReferenceIdLowLevel);
168168

169+
templateReferenceId = TemplateConfig.referenceResourceGroup().byIdResourceGroup("id");
170+
var scope = TemplateRefByID.ScopeEnum.RESOURCE_GROUP;
171+
expectedTemplateReferenceId =
172+
new OrchestrationTemplateReference(TemplateRefByID.create().id("id").scope(scope));
173+
templateReferenceIdLowLevel =
174+
TemplateRef.create().templateRef(TemplateRefByID.create().id("id").scope(scope));
175+
assertThat(templateReferenceId).isEqualTo(expectedTemplateReferenceId);
176+
assertThat(templateReferenceId.toLowLevel()).isEqualTo(templateReferenceIdLowLevel);
177+
169178
var templateReferenceScenarioNameVersion =
170-
TemplateConfig.reference().byScenario("scenario").name("name").version("version");
179+
TemplateConfig.referenceTenant().byScenario("scenario").name("name").version("version");
171180
var expectedTemplateReferenceScenarioNameVersion =
172181
new OrchestrationTemplateReference(
173182
TemplateRefByScenarioNameVersion.create()
@@ -185,6 +194,32 @@ void testTemplateReferenceConstruction() {
185194
.isEqualTo(expectedTemplateReferenceScenarioNameVersion);
186195
assertThat(templateReferenceScenarioNameVersion.toLowLevel())
187196
.isEqualTo(templateReferenceScenarioNameVersionLowLevel);
197+
198+
templateReferenceScenarioNameVersion =
199+
TemplateConfig.referenceResourceGroup()
200+
.byScenario("scenario")
201+
.name("name")
202+
.version("version");
203+
var scopeScenario = TemplateRefByScenarioNameVersion.ScopeEnum.RESOURCE_GROUP;
204+
expectedTemplateReferenceScenarioNameVersion =
205+
new OrchestrationTemplateReference(
206+
TemplateRefByScenarioNameVersion.create()
207+
.scenario("scenario")
208+
.name("name")
209+
.version("version")
210+
.scope(scopeScenario));
211+
templateReferenceScenarioNameVersionLowLevel =
212+
TemplateRef.create()
213+
.templateRef(
214+
TemplateRefByScenarioNameVersion.create()
215+
.scenario("scenario")
216+
.name("name")
217+
.version("version")
218+
.scope(scopeScenario));
219+
assertThat(templateReferenceScenarioNameVersion)
220+
.isEqualTo(expectedTemplateReferenceScenarioNameVersion);
221+
assertThat(templateReferenceScenarioNameVersion.toLowLevel())
222+
.isEqualTo(templateReferenceScenarioNameVersionLowLevel);
188223
}
189224

190225
@Test

orchestration/src/test/java/com/sap/ai/sdk/orchestration/OrchestrationUnitTest.java

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,7 +1259,7 @@ void testResponseFormatText() throws IOException {
12591259
}
12601260

12611261
@Test
1262-
void testTemplateFromPromptRegistryById() throws IOException {
1262+
void testTemplateFromPromptRegistryByIdTenant() throws IOException {
12631263
{
12641264
stubFor(
12651265
post(anyUrl())
@@ -1268,7 +1268,8 @@ void testTemplateFromPromptRegistryById() throws IOException {
12681268
.withBodyFile("templateReferenceResponse.json")
12691269
.withHeader("Content-Type", "application/json")));
12701270

1271-
var template = TemplateConfig.reference().byId("21cb1358-0bf1-4f43-870b-00f14d0f9f16");
1271+
var template =
1272+
TemplateConfig.referenceTenant().byIdTenant("21cb1358-0bf1-4f43-870b-00f14d0f9f16");
12721273
var configWithTemplate = config.withTemplateConfig(template);
12731274

12741275
var inputParams = Map.of("language", "Italian", "input", "Cloud ERP systems");
@@ -1285,15 +1286,49 @@ void testTemplateFromPromptRegistryById() throws IOException {
12851286
}
12861287

12871288
@Test
1288-
void testTemplateFromPromptRegistryByScenario() throws IOException {
1289+
void testTemplateFromPromptRegistryByIdResourceGroup() throws IOException {
1290+
{
1291+
stubFor(
1292+
post(anyUrl())
1293+
.willReturn(
1294+
aResponse()
1295+
.withBodyFile("templateReferenceResourceGroupResponse.json")
1296+
.withHeader("Content-Type", "application/json")));
1297+
1298+
var template =
1299+
TemplateConfig.referenceResourceGroup()
1300+
.byIdResourceGroup("2cd4aea1-dffb-4d5d-b96d-96e29b595025");
1301+
var configWithTemplate = config.withLlmConfig(GPT_4O_MINI).withTemplateConfig(template);
1302+
1303+
var inputParams =
1304+
Map.of(
1305+
"categories",
1306+
"Finance, Tech, Sports",
1307+
"inputExample",
1308+
"What's the latest news on the stock market?");
1309+
var prompt = new OrchestrationPrompt(inputParams);
1310+
1311+
final var response = client.chatCompletion(prompt, configWithTemplate);
1312+
assertThat(response.getContent()).startsWith("Finance");
1313+
assertThat(response.getOriginalResponse().getIntermediateResults().getTemplating())
1314+
.hasSize(2);
1315+
1316+
final String request = fileLoaderStr.apply("templateReferenceResourceGroupByIdRequest.json");
1317+
verify(postRequestedFor(anyUrl()).withRequestBody(equalToJson(request)));
1318+
}
1319+
}
1320+
1321+
@Test
1322+
void testTemplateFromPromptRegistryByScenarioTenant() throws IOException {
12891323
stubFor(
12901324
post(anyUrl())
12911325
.willReturn(
12921326
aResponse()
12931327
.withBodyFile("templateReferenceResponse.json")
12941328
.withHeader("Content-Type", "application/json")));
12951329

1296-
var template = TemplateConfig.reference().byScenario("test").name("test").version("0.0.1");
1330+
var template =
1331+
TemplateConfig.referenceTenant().byScenario("test").name("test").version("0.0.1");
12971332
var configWithTemplate = config.withTemplateConfig(template);
12981333

12991334
var inputParams = Map.of("language", "Italian", "input", "Cloud ERP systems");
@@ -1307,6 +1342,39 @@ void testTemplateFromPromptRegistryByScenario() throws IOException {
13071342
verify(postRequestedFor(anyUrl()).withRequestBody(equalToJson(request)));
13081343
}
13091344

1345+
@Test
1346+
void testTemplateFromPromptRegistryByScenarioResourceGroup() throws IOException {
1347+
stubFor(
1348+
post(anyUrl())
1349+
.willReturn(
1350+
aResponse()
1351+
.withBodyFile("templateReferenceResourceGroupResponse.json")
1352+
.withHeader("Content-Type", "application/json")));
1353+
1354+
var template =
1355+
TemplateConfig.referenceResourceGroup()
1356+
.byScenario("categorization")
1357+
.name("example-prompt-template")
1358+
.version("0.0.1");
1359+
var configWithTemplate = config.withLlmConfig(GPT_4O_MINI).withTemplateConfig(template);
1360+
1361+
var inputParams =
1362+
Map.of(
1363+
"categories",
1364+
"Finance, Tech, Sports",
1365+
"inputExample",
1366+
"What's the latest news on the stock market?");
1367+
var prompt = new OrchestrationPrompt(inputParams);
1368+
1369+
final var response = client.chatCompletion(prompt, configWithTemplate);
1370+
assertThat(response.getContent()).startsWith("Finance");
1371+
assertThat(response.getOriginalResponse().getIntermediateResults().getTemplating()).hasSize(2);
1372+
1373+
final String request =
1374+
fileLoaderStr.apply("templateReferenceResourceGroupByScenarioRequest.json");
1375+
verify(postRequestedFor(anyUrl()).withRequestBody(equalToJson(request)));
1376+
}
1377+
13101378
@Test
13111379
void testTemplateFromInput() throws IOException {
13121380
stubFor(
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
{
2+
"request_id": "176cbc1e-7451-9b78-bb3e-278abd576a66",
3+
"intermediate_results": {
4+
"templating": [
5+
{
6+
"role": "system",
7+
"content": "You classify input text into the two following categories: Finance, Tech, Sports"
8+
},
9+
{
10+
"content": "What's the latest news on the stock market?",
11+
"role": "user"
12+
}
13+
],
14+
"llm": {
15+
"id": "chatcmpl-D09WL0JIzGaEF1RwbaTKitAspDKhO",
16+
"object": "chat.completion",
17+
"created": 1768928969,
18+
"model": "gpt-4o-mini-2024-07-18",
19+
"system_fingerprint": "fp_f97eff32c5",
20+
"choices": [
21+
{
22+
"index": 0,
23+
"message": {
24+
"role": "assistant",
25+
"content": "Finance"
26+
},
27+
"finish_reason": "stop"
28+
}
29+
],
30+
"usage": {
31+
"completion_tokens": 2,
32+
"prompt_tokens": 35,
33+
"total_tokens": 37,
34+
"prompt_tokens_details": {
35+
"audio_tokens": 0,
36+
"cached_tokens": 0
37+
},
38+
"completion_tokens_details": {
39+
"accepted_prediction_tokens": 0,
40+
"audio_tokens": 0,
41+
"reasoning_tokens": 0,
42+
"rejected_prediction_tokens": 0
43+
}
44+
}
45+
}
46+
},
47+
"final_result": {
48+
"id": "chatcmpl-D09WL0JIzGaEF1RwbaTKitAspDKhO",
49+
"object": "chat.completion",
50+
"created": 1768928969,
51+
"model": "gpt-4o-mini-2024-07-18",
52+
"system_fingerprint": "fp_f97eff32c5",
53+
"choices": [
54+
{
55+
"index": 0,
56+
"message": {
57+
"role": "assistant",
58+
"content": "Finance"
59+
},
60+
"finish_reason": "stop"
61+
}
62+
],
63+
"usage": {
64+
"completion_tokens": 2,
65+
"prompt_tokens": 35,
66+
"total_tokens": 37,
67+
"prompt_tokens_details": {
68+
"audio_tokens": 0,
69+
"cached_tokens": 0
70+
},
71+
"completion_tokens_details": {
72+
"accepted_prediction_tokens": 0,
73+
"audio_tokens": 0,
74+
"reasoning_tokens": 0,
75+
"rejected_prediction_tokens": 0
76+
}
77+
}
78+
}
79+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"config": {
3+
"modules": {
4+
"prompt_templating": {
5+
"prompt": {
6+
"template_ref": {
7+
"id": "2cd4aea1-dffb-4d5d-b96d-96e29b595025",
8+
"scope": "resource_group"
9+
}
10+
},
11+
"model": {
12+
"name": "gpt-4o-mini",
13+
"version": "latest",
14+
"params": {},
15+
"timeout": 600,
16+
"max_retries": 2
17+
}
18+
}
19+
}
20+
},
21+
"placeholder_values": {
22+
"categories": "Finance, Tech, Sports",
23+
"inputExample": "What's the latest news on the stock market?"
24+
},
25+
"messages_history": []
26+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"config": {
3+
"modules": {
4+
"prompt_templating": {
5+
"prompt": {
6+
"template_ref": {
7+
"scenario": "categorization",
8+
"name": "example-prompt-template",
9+
"version": "0.0.1",
10+
"scope": "resource_group"
11+
}
12+
},
13+
"model": {
14+
"name": "gpt-4o-mini",
15+
"version": "latest",
16+
"params": {},
17+
"timeout": 600,
18+
"max_retries": 2
19+
}
20+
}
21+
}
22+
},
23+
"placeholder_values": {
24+
"categories": "Finance, Tech, Sports",
25+
"inputExample": "What's the latest news on the stock market?"
26+
},
27+
"messages_history": []
28+
}

0 commit comments

Comments
 (0)