@sap-ai-sdk/orchestration
Version:
SAP Cloud SDK for AI is the official Software Development Kit (SDK) for **SAP AI Core**, **SAP Generative AI Hub**, and **Orchestration Service**.
152 lines • 6.23 kB
JavaScript
import { executeRequest } from '@sap-ai-sdk/core';
import { createLogger } from '@sap-cloud-sdk/util';
import yaml from 'yaml';
import { registryControllerPromptControllerCreateUpdatePromptTemplateBody } from '@sap-ai-sdk/prompt-registry/internal.js';
import { getOrchestrationDeploymentId } from './deployment-resolver.js';
import { OrchestrationStream } from './orchestration-stream.js';
import { OrchestrationStreamResponse } from './orchestration-stream-response.js';
import { OrchestrationResponse } from './orchestration-response.js';
import { constructCompletionPostRequest, constructCompletionPostRequestFromJsonModuleConfig } from './util/index.js';
const logger = createLogger({
package: 'orchestration',
messageContext: 'orchestration-client'
});
/**
* Get the orchestration client.
*/
export class OrchestrationClient {
config;
deploymentConfig;
destination;
/**
* Creates an instance of the orchestration client.
* @param config - Orchestration module configuration. This can either be an `OrchestrationModuleConfig` object or a JSON string obtained from AI Launchpad.
* @param deploymentConfig - Deployment configuration.
* @param destination - The destination to use for the request.
*/
constructor(config, deploymentConfig, destination) {
this.config = config;
this.deploymentConfig = deploymentConfig;
this.destination = destination;
if (typeof config === 'string') {
this.validateJsonConfig(config);
}
else {
this.config =
typeof config.promptTemplating.prompt === 'string'
? this.parseAndMergeTemplating(config) // parse and assign if templating is a string
: config;
}
}
async chatCompletion(request, requestConfig) {
const response = await this.executeRequest({
request,
requestConfig,
stream: false
});
return new OrchestrationResponse(response);
}
async stream(request, signal, options, requestConfig) {
const controller = new AbortController();
if (signal) {
signal.addEventListener('abort', () => {
controller.abort();
});
}
try {
if (typeof this.config === 'string' && options) {
logger.warn('Stream options are not supported when using a JSON module config.');
}
return await this.createStreamResponse({
request,
requestConfig,
stream: true,
streamOptions: options
}, controller);
}
catch (error) {
controller.abort();
throw error;
}
}
async executeRequest(options) {
const { request, requestConfig, stream, streamOptions } = options;
const body = typeof this.config === 'string'
? constructCompletionPostRequestFromJsonModuleConfig(JSON.parse(this.config), request, stream)
: constructCompletionPostRequest(this.config, request, stream, streamOptions);
const deploymentId = await getOrchestrationDeploymentId(this.deploymentConfig ?? {}, this.destination);
if (!deploymentId) {
throw new Error('Failed to resolve deployment ID');
}
return executeRequest({
url: `/inference/deployments/${deploymentId}/v2/completion`,
...(this.deploymentConfig ?? {})
}, body, requestConfig, this.destination);
}
async createStreamResponse(options, controller) {
const response = new OrchestrationStreamResponse();
const streamResponse = await this.executeRequest({
...options,
requestConfig: {
...options.requestConfig,
responseType: 'stream',
signal: controller.signal
}
});
const stream = OrchestrationStream._create(streamResponse, controller);
response.stream = stream
._pipe(OrchestrationStream._processChunk)
._pipe(OrchestrationStream._processOrchestrationStreamChunkResponse, response)
._pipe(OrchestrationStream._processStreamEnd, response);
return response;
}
/**
* Validate if a string is valid JSON.
* @param config - The JSON string to validate.
*/
validateJsonConfig(config) {
try {
JSON.parse(config);
}
catch (error) {
throw new Error(`Could not parse JSON: ${error}`);
}
}
/**
* Parse and merge templating into the config object.
* @param config - The orchestration module configuration with templating either as object or string.
* @returns The updated and merged orchestration module configuration.
* @throws Error if the YAML parsing fails or if the parsed object does not conform to the expected schema.
*/
parseAndMergeTemplating(config) {
let parsedObject;
if (typeof config.promptTemplating.prompt === 'string' &&
!config.promptTemplating.prompt.trim()) {
throw new Error('Templating YAML string must be non-empty.');
}
try {
parsedObject = yaml.parse(config.promptTemplating.prompt);
}
catch (error) {
throw new Error(`Error parsing YAML: ${error}`);
}
const result = registryControllerPromptControllerCreateUpdatePromptTemplateBody.safeParse(parsedObject);
if (!result.success) {
throw new Error(`Prompt Template YAML does not conform to the defined type. Validation errors: ${result.error}`);
}
const { template, defaults, response_format, tools } = result.data.spec;
return {
...config,
promptTemplating: {
...config.promptTemplating,
prompt: {
template: template,
...(defaults && { defaults }),
...(response_format && { response_format }),
...(tools && { tools })
}
}
};
}
}
//# sourceMappingURL=orchestration-client.js.map