Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Response Format Sample Code #538

Merged
merged 24 commits into from
Feb 17, 2025
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 38 additions & 14 deletions packages/orchestration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ Optionally, define `model_version` (default: `latest`) and `model_params` for cu

Use the orchestration client with templating to pass a prompt containing placeholders that will be replaced with input parameters during a chat completion request.
This allows for variations in the prompt based on the input parameters.
Set custom request configuration when calling `chatCompletion` function.

```ts
import { OrchestrationClient } from '@sap-ai-sdk/orchestration';
Expand All @@ -131,28 +130,53 @@ const response = await orchestrationClient.chatCompletion({
});

const responseContent = response.getContent();
const finishReason = response.getFinishReason();
const tokenUsage = response.getTokenUsage();
```

`getContent()` is a convenience method that parses the response and returns the model's output as a string.
You can use the following convenience methods for handling chat completion responses:

To retrieve the `finish_reason` for stopping the chat completion request, use the convenience method `getFinishReason()`:
- `getContent()`: parses the response and returns the model's output as a string.
- `getFinishReason()`: retrieves the finish_reason, explaining why the chat completion request stopped.
- `getTokenUsage()`: provides token usage details, namely `total_tokens`, `prompt_tokens`, and `completion_tokens`.
deekshas8 marked this conversation as resolved.
Show resolved Hide resolved

```ts
const finishReason = response.getFinishReason();
```
#### Structured Outputs

Use the `getTokenUsage()` convenience method to retrieve the token usage details of the chat completion request:
The `response_format` under `templating` guarantees that the model's output aligns with the JSON schemas specified by developers.
deekshas8 marked this conversation as resolved.
Show resolved Hide resolved
It is useful when the model is not calling a tool, but rather, responding to the user in a structured way.
By setting `strict: true`, the model ensures that its outputs conform precisely to the provided schema.

```ts
const tokenUsage = response.getTokenUsage();

console.log(
`Total tokens consumed by the request: ${tokenUsage.total_tokens}\n` +
`Input prompt tokens consumed: ${tokenUsage.prompt_tokens}\n` +
`Output text completion tokens consumed: ${tokenUsage.completion_tokens}\n`
);
templating: {
template: [
{ role: 'user', content: 'What is the capital of {{?country}}?' }
],
response_format: {
type: 'json_schema',
json_schema: {
name: 'capital_response',
strict: true,
schema: {
type: 'object',
properties: {
country_name: {
type: "string",
description: "The name of the country provided by the user."
},
capital: {
type: "string",
description: "The capital city of the country."
}
},
required: ["country_name", "capital"]
}
}
}
}
```

In addition to using JSON schema objects, a Zod schema is also supported.
deekshas8 marked this conversation as resolved.
Show resolved Hide resolved

### Prompt Registry

Alternatively, prepared templates from the [Prompt Registry](https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/prompt-registry) of SAP AI Core can be used instead of passing a template in the request.
Expand Down
110 changes: 11 additions & 99 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion sample-code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
"@langchain/textsplitters": "0.1.0",
"@sap-cloud-sdk/util": "^3.26.0",
"@types/express": "^5.0.0",
"express": "^4.21.2"
"express": "^4.21.2",
"zod-to-json-schema": "^3.24.0",
"zod": "^3.24.2"
}
}
2 changes: 2 additions & 0 deletions sample-code/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export {
orchestrationGroundingVector,
orchestrationGroundingHelpSapCom,
orchestrationChatCompletionImage,
orchestrationResponseFormat,
TranslationResponseType,
chatCompletionStreamWithJsonModuleConfig,
chatCompletionStream
} from './orchestration.js';
Expand Down
59 changes: 59 additions & 0 deletions sample-code/src/orchestration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
buildAzureContentSafetyFilter
} from '@sap-ai-sdk/orchestration';
import { createLogger } from '@sap-cloud-sdk/util';
import { z } from 'zod';
import { zodToJsonSchema } from 'zod-to-json-schema';
import type {
LlmModuleConfig,
OrchestrationStreamChunkResponse,
Expand Down Expand Up @@ -482,3 +484,60 @@ export async function orchestrationChatCompletionImage(): Promise<OrchestrationR
}
});
}

/**
* Response type that is guaranteed to be respected by the orchestration LLM response.
*/
export interface TranslationResponseType {
/**
* The language of the translation, randomly chosen by the LLM.
*/
language: string;
/**
* The translation of the input sentence.
*/
translation: string;
}
/**
* Ask about the image content using a template.
deekshas8 marked this conversation as resolved.
Show resolved Hide resolved
deekshas8 marked this conversation as resolved.
Show resolved Hide resolved
* @returns The orchestration service response.
deekshas8 marked this conversation as resolved.
Show resolved Hide resolved
*/
export async function orchestrationResponseFormat(): Promise<TranslationResponseType> {
const translationSchema = z
.object({
language: z.string(),
translation: z.string()
})
.strict();
const orchestrationClient = new OrchestrationClient({
llm,
templating: {
template: [
{
role: 'system',
content:
'You are a helpful AI that translates simple sentences into different languages. The user will provide the sentence. You then choose a language at random and provide the translation.'
},
{
role: 'user',
content: '{{?input}}'
}
],
response_format: {
type: 'json_schema',
json_schema: {
name: 'translation_response',
strict: true,
schema: zodToJsonSchema(translationSchema)
}
}
}
});

const resonse = await orchestrationClient.chatCompletion({
inputParams: {
input: 'Hello World! Why is this phrase so famous?'
}
});
return JSON.parse(resonse.getContent()!) as TranslationResponseType;
deekshas8 marked this conversation as resolved.
Show resolved Hide resolved
}
8 changes: 6 additions & 2 deletions sample-code/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import {
chatCompletionStreamWithJsonModuleConfig as orchestrationChatCompletionStreamWithJsonModuleConfig,
orchestrationGroundingHelpSapCom,
orchestrationMaskGroundingInput,
orchestrationPromptRegistry
orchestrationPromptRegistry,
orchestrationResponseFormat
} from './orchestration.js';
import {
getDeployments,
Expand Down Expand Up @@ -260,7 +261,8 @@ app.get('/orchestration/:sampleCase', async (req, res) => {
outputFiltering: orchestrationOutputFiltering,
requestConfig: orchestrationRequestConfig,
fromJson: orchestrationFromJson,
image: orchestrationChatCompletionImage
image: orchestrationChatCompletionImage,
responseFormat: orchestrationResponseFormat
}[sampleCase] || orchestrationChatCompletion;

try {
Expand All @@ -271,6 +273,8 @@ app.get('/orchestration/:sampleCase', async (req, res) => {
res.send(
`Output filter applied successfully with threshold results: ${JSON.stringify(result.data.module_results.output_filtering!.data!)}`
);
} else if (sampleCase === 'responseFormat') {
res.send(`Response format applied successfully with response: ${result}`);
} else {
res.send(result.getContent());
}
Expand Down
1 change: 1 addition & 0 deletions styles/config/vocabularies/SAP/accept.txt
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ seldomly
lookups

CDS
[Zz]od

llm's
[Ll][Ll][Mm]'s
Loading
Loading