@rocketnew/llm-sdk
Version:
Universal LLM SDK for JavaScript/TypeScript - OpenAI, Anthropic, Gemini, Perplexity and more
329 lines (325 loc) • 10.6 kB
JavaScript
import {
DEFAULT_MAX_TOKENS,
RESPONSE_FORMAT_TOOL_NAME,
__esm,
__export,
init_constants
} from "./chunk-DDRQKMV4.mjs";
// src/llms/base_llm/base_utils.ts
function updateRefs(schema, refTemplate) {
const stack = [[schema, []]];
const visited = /* @__PURE__ */ new Set();
while (stack.length > 0) {
const [obj, path] = stack.pop();
if (visited.has(obj)) {
continue;
}
visited.add(obj);
if (obj && typeof obj === "object") {
if ("$ref" in obj) {
const refPath = obj["$ref"];
const modelName = refPath.split("/").pop();
obj["$ref"] = refTemplate.replace("{model}", modelName || "");
}
if (Array.isArray(obj)) {
for (let i = 0; i < obj.length; i++) {
if (obj[i] && typeof obj[i] === "object") {
stack.push([obj[i], [...path, i]]);
}
}
} else {
for (const [k, v] of Object.entries(obj)) {
if (v && typeof v === "object") {
stack.push([v, [...path, k]]);
}
}
}
}
}
}
function dictToResponseFormatHelper(responseFormat, refTemplate) {
if (refTemplate && responseFormat.type === "json_schema") {
const modifiedFormat = JSON.parse(JSON.stringify(responseFormat));
const schema = modifiedFormat.json_schema?.schema;
if (schema) {
updateRefs(schema, refTemplate);
}
return modifiedFormat;
}
return responseFormat;
}
function typeToResponseFormatParam(responseFormat, refTemplate) {
if (responseFormat === null || responseFormat === void 0) {
return null;
}
if (typeof responseFormat === "object") {
return dictToResponseFormatHelper(responseFormat, refTemplate);
}
throw new TypeError(`Unsupported response_format type - ${typeof responseFormat}`);
}
function mapDeveloperRoleToSystemRole(messages) {
const newMessages = [];
for (const m of messages) {
if (m.role === "developer") {
newMessages.push({
role: "system",
content: m.content
});
} else {
newMessages.push(m);
}
}
return newMessages;
}
var init_base_utils = __esm({
"src/llms/base_llm/base_utils.ts"() {
"use strict";
}
});
// src/llms/base_llm/chat/transformation.ts
var transformation_exports = {};
__export(transformation_exports, {
BaseConfig: () => BaseConfig,
BaseLLMException: () => BaseLLMException
});
var BaseLLMException, BaseConfig;
var init_transformation = __esm({
"src/llms/base_llm/chat/transformation.ts"() {
"use strict";
init_constants();
init_base_utils();
BaseLLMException = class extends Error {
constructor(options) {
super(options.message);
this.name = "BaseLLMException";
this.statusCode = options.statusCode;
this.message = options.message;
this.headers = options.headers;
this.request = options.request;
this.response = options.response;
this.body = options.body;
}
};
BaseConfig = class {
constructor() {
}
/**
* Get configuration as a dictionary
*
* Returns all non-private, non-function class attributes.
*/
static getConfig() {
const config = {};
for (const [key, value] of Object.entries(this)) {
if (!key.startsWith("_") && typeof value !== "function" && value !== null && value !== void 0) {
config[key] = value;
}
}
return config;
}
/**
* Get JSON schema from response format
*
* Converts a response format object to the appropriate JSON schema format.
*/
getJsonSchemaFromResponseFormat(responseFormat) {
return typeToResponseFormatParam(responseFormat);
}
/**
* Check if thinking/reasoning is enabled in the parameters
*/
isThinkingEnabled(nonDefaultParams) {
return nonDefaultParams.thinking?.type === "enabled" || nonDefaultParams.reasoning_effort !== void 0 && nonDefaultParams.reasoning_effort !== null;
}
/**
* Check if max_tokens is specified in the request
*
* OpenAI spec allows max_tokens or max_completion_tokens to be specified.
*/
isMaxTokensInRequest(nonDefaultParams) {
return "max_tokens" in nonDefaultParams || "max_completion_tokens" in nonDefaultParams;
}
/**
* Update optional params with thinking tokens
*
* Handles scenario where max tokens is not specified. For anthropic models,
* this requires having the max tokens being set and being greater than
* the thinking token budget.
*
* If 'thinking' is enabled and 'max_tokens' is not specified,
* set 'max_tokens' to the thinking token budget + DEFAULT_MAX_TOKENS
*/
updateOptionalParamsWithThinkingTokens(nonDefaultParams, optionalParams) {
const isThinkingEnabled = this.isThinkingEnabled(optionalParams);
if (isThinkingEnabled && !("max_tokens" in nonDefaultParams)) {
const thinkingTokenBudget = optionalParams.thinking?.budget_tokens;
if (thinkingTokenBudget !== null && thinkingTokenBudget !== void 0) {
optionalParams.max_tokens = thinkingTokenBudget + DEFAULT_MAX_TOKENS;
}
}
}
/**
* Returns True if the model/provider should fake stream
*
* Some providers don't support native streaming, so we need to
* simulate it by making a non-streaming request and yielding chunks.
*/
shouldFakeStream(model, stream, customLLMProvider) {
return false;
}
/**
* Helper util to add tools to optional_params
*/
addToolsToOptionalParams(optionalParams, tools) {
if (!("tools" in optionalParams)) {
optionalParams.tools = tools;
} else {
optionalParams.tools = [...optionalParams.tools, ...tools];
}
return optionalParams;
}
/**
* Translate `developer` role to `system` role for non-OpenAI providers.
*
* Overridden by OpenAI/Azure to preserve the developer role.
*/
translateDeveloperRoleToSystemRole(messages) {
return mapDeveloperRoleToSystemRole(messages);
}
/**
* Returns True if the model/provider should retry the LLM API on HTTP error
*
* Overridden by providers where different models support different parameters.
*/
shouldRetryLLMApiInsideLLMTranslationOnHttpError(error, rocketllmParams) {
return false;
}
/**
* Transform the request data on UnprocessableEntityError
*/
transformRequestOnUnprocessableEntityError(error, requestData) {
return requestData;
}
/**
* Returns the max retry count for UnprocessableEntityError
*
* Used if `shouldRetryLLMApiInsideLLMTranslationOnHttpError` returns true.
*/
get maxRetryOnUnprocessableEntityError() {
return 0;
}
/**
* Add response format to tools
*
* Follow similar approach to anthropic - translate to a single tool call.
* This is used to translate response_format to a tool call, for models/APIs
* that don't support response_format directly.
*/
addResponseFormatToTools(optionalParams, value, isResponseFormatSupported, enforceToolChoice = true) {
let jsonSchema = null;
if ("response_schema" in value) {
jsonSchema = value.response_schema;
} else if ("json_schema" in value) {
jsonSchema = value.json_schema?.schema;
}
if (jsonSchema && !isResponseFormatSupported) {
const toolChoice = {
type: "function",
function: {
name: RESPONSE_FORMAT_TOOL_NAME
}
};
const tool = {
type: "function",
function: {
name: RESPONSE_FORMAT_TOOL_NAME,
parameters: jsonSchema
}
};
if (!optionalParams.tools) {
optionalParams.tools = [];
}
optionalParams.tools.push(tool);
if (enforceToolChoice) {
optionalParams.tool_choice = toolChoice;
}
optionalParams.json_mode = true;
} else if (isResponseFormatSupported) {
optionalParams.response_format = value;
}
return optionalParams;
}
/**
* Sign the request
*
* Some providers like Bedrock require signing the request.
* The sign request function needs access to `requestData` and `completeUrl`.
*
* @returns Tuple of [signed headers, signed body (if any)]
*/
signRequest(headers, optionalParams, requestData, apiBase, apiKey, model, stream, fakeStream) {
return [headers, void 0];
}
/**
* Get the complete URL for the request
*
* Some providers need `model` in `api_base`.
*/
getCompleteUrl(apiBase, apiKey, model, optionalParams, rocketllmParams, stream) {
if (apiBase === null) {
throw new Error("api_base is required");
}
return apiBase;
}
/**
* Async version of transform request
*
* Override to allow for http requests on async calls - e.g. converting url to base64.
*/
async asyncTransformRequest(model, messages, optionalParams, rocketllmParams, headers) {
return this.transformRequest(
model,
messages,
optionalParams,
rocketllmParams,
headers
);
}
/**
* Get model response iterator for streaming
*
* Returns an iterator that yields streaming chunks from the response.
*/
getModelResponseIterator(streamingResponse, syncStream, jsonMode) {
return null;
}
/**
* Custom LLM provider identifier
*/
get customLLMProvider() {
return null;
}
/**
* Whether this provider has a custom stream wrapper
*/
get hasCustomStreamWrapper() {
return false;
}
/**
* Whether this provider supports the stream parameter in the request body
*
* Some providers like Bedrock invoke do not support the stream parameter
* in the request body. By default, this is true for almost all providers.
*/
get supportsStreamParamInRequestBody() {
return true;
}
};
}
});
export {
BaseLLMException,
BaseConfig,
transformation_exports,
init_transformation
};