@endlessriver/optimaiz
Version:
SDK for LLM observability - track costs, latency, and usage across OpenAI, Anthropic, Gemini, and more
504 lines (499 loc) • 16 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
OptimaizAuthenticationError: () => OptimaizAuthenticationError,
OptimaizClient: () => OptimaizClient,
OptimaizError: () => OptimaizError,
OptimaizNetworkError: () => OptimaizNetworkError,
OptimaizServerError: () => OptimaizServerError,
OptimaizValidationError: () => OptimaizValidationError,
default: () => OptimaizClient,
fromAnthropicFormat: () => fromAnthropicFormat,
fromMistralFormat: () => fromMistralFormat,
fromOpenAIFormat: () => fromOpenAIFormat,
fromProviderFormat: () => fromProviderFormat,
isAuthenticationError: () => isAuthenticationError,
isNetworkError: () => isNetworkError,
isOptimaizError: () => isOptimaizError,
isServerError: () => isServerError,
isValidationError: () => isValidationError,
toAnthropicFormat: () => toAnthropicFormat,
toMistralFormat: () => toMistralFormat,
toOpenAIFormat: () => toOpenAIFormat,
toProviderFormat: () => toProviderFormat,
validateTools: () => validateTools
});
module.exports = __toCommonJS(index_exports);
// src/errors.ts
var OptimaizError = class extends Error {
constructor(message, status, details, type) {
super(message);
this.name = "OptimaizError";
this.status = status;
this.details = details;
this.type = type;
}
};
var OptimaizAuthenticationError = class extends OptimaizError {
constructor(message, details) {
super(message, 401, details, "AUTHENTICATION_ERROR");
this.name = "OptimaizAuthenticationError";
}
};
var OptimaizValidationError = class extends OptimaizError {
constructor(message, details) {
super(message, 400, details, "VALIDATION_ERROR");
this.name = "OptimaizValidationError";
}
};
var OptimaizServerError = class extends OptimaizError {
constructor(message, details) {
super(message, 500, details, "SERVER_ERROR");
this.name = "OptimaizServerError";
}
};
var OptimaizNetworkError = class extends OptimaizError {
constructor(message, details) {
super(message, 0, details, "NETWORK_ERROR");
this.name = "OptimaizNetworkError";
}
};
function isOptimaizError(error) {
return error instanceof OptimaizError;
}
function isAuthenticationError(error) {
return error instanceof OptimaizAuthenticationError || error?.type === "AUTHENTICATION_ERROR";
}
function isValidationError(error) {
return error instanceof OptimaizValidationError || error?.type === "VALIDATION_ERROR";
}
function isServerError(error) {
return error instanceof OptimaizServerError || error?.type === "SERVER_ERROR";
}
function isNetworkError(error) {
return error instanceof OptimaizNetworkError || error?.type === "NETWORK_ERROR";
}
// src/tools.ts
function validateTools(tools) {
const errors = [];
if (!Array.isArray(tools)) {
errors.push("Tools must be an array");
return { valid: false, errors };
}
for (const tool of tools) {
if (!tool.name || typeof tool.name !== "string") {
errors.push(`Tool must have a valid name: ${tool.name}`);
}
if (!tool.description || typeof tool.description !== "string") {
errors.push(`Tool ${tool.name} must have a valid description`);
}
if (!tool.parameters || typeof tool.parameters !== "object") {
errors.push(`Tool ${tool.name} must have valid parameters`);
} else {
if (tool.parameters.type !== "object") {
errors.push(`Tool ${tool.name} parameters must have type 'object'`);
}
if (!tool.parameters.properties || typeof tool.parameters.properties !== "object") {
errors.push(`Tool ${tool.name} must have properties defined`);
}
}
}
const names = tools.map((t) => t.name);
const duplicates = names.filter((name, i) => names.indexOf(name) !== i);
if (duplicates.length > 0) {
errors.push(`Duplicate tool names found: ${duplicates.join(", ")}`);
}
return { valid: errors.length === 0, errors };
}
function toOpenAIFormat(tools) {
return tools.map((tool) => ({
type: "function",
function: {
name: tool.name,
description: tool.description,
parameters: tool.parameters
}
}));
}
function toAnthropicFormat(tools) {
return tools.map((tool) => ({
name: tool.name,
description: tool.description,
input_schema: {
type: "object",
properties: tool.parameters.properties,
required: tool.parameters.required
}
}));
}
function toMistralFormat(tools) {
return tools.map((tool) => ({
type: "function",
function: {
name: tool.name,
description: tool.description,
parameters: tool.parameters
}
}));
}
function toProviderFormat(tools, provider) {
const lowerProvider = provider.toLowerCase();
if (lowerProvider.includes("openai") || lowerProvider.includes("gpt")) {
return toOpenAIFormat(tools);
}
if (lowerProvider.includes("anthropic") || lowerProvider.includes("claude")) {
return toAnthropicFormat(tools);
}
if (lowerProvider.includes("mistral")) {
return toMistralFormat(tools);
}
return toOpenAIFormat(tools);
}
function fromOpenAIFormat(tools) {
return tools.map((tool) => ({
name: tool.function.name,
description: tool.function.description,
parameters: tool.function.parameters
}));
}
function fromAnthropicFormat(tools) {
return tools.map((tool) => ({
name: tool.name,
description: tool.description,
parameters: {
type: "object",
properties: tool.input_schema.properties,
required: tool.input_schema.required
}
}));
}
function fromMistralFormat(tools) {
return tools.map((tool) => ({
name: tool.function.name,
description: tool.function.description,
parameters: tool.function.parameters
}));
}
function fromProviderFormat(tools, provider) {
const lowerProvider = provider.toLowerCase();
if (lowerProvider.includes("openai") || lowerProvider.includes("gpt")) {
return fromOpenAIFormat(tools);
}
if (lowerProvider.includes("anthropic") || lowerProvider.includes("claude")) {
return fromAnthropicFormat(tools);
}
if (lowerProvider.includes("mistral")) {
return fromMistralFormat(tools);
}
if (tools.length > 0) {
const first = tools[0];
if (first.function) {
return fromOpenAIFormat(tools);
}
if (first.input_schema) {
return fromAnthropicFormat(tools);
}
}
return tools;
}
// src/client.ts
var DEFAULT_BASE_URL = "http://localhost:3456";
var DEFAULT_TIMEOUT = 3e4;
var DEFAULT_RETRIES = 0;
function generateId() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
const r = Math.random() * 16 | 0;
const v = c === "x" ? r : r & 3 | 8;
return v.toString(16);
});
}
var OptimaizClient = class {
constructor(config) {
if (!config.token || typeof config.token !== "string") {
throw new OptimaizValidationError("OptimaizClient requires a valid token");
}
this.token = config.token;
this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;
this.timeout = config.timeout || DEFAULT_TIMEOUT;
this.retries = config.retries || DEFAULT_RETRIES;
this.debug = config.debug || false;
}
log(message, data) {
if (this.debug) {
console.log(`[Optimaiz] ${message}`, data || "");
}
}
async request(path, body, retriesLeft = this.retries) {
const url = `${this.baseUrl}${path}`;
this.log(`POST ${path}`, { bodyKeys: Object.keys(body) });
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${this.token}`
},
body: JSON.stringify(body),
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) {
let errorMessage;
let errorDetails;
try {
const errorData = await response.json();
errorMessage = errorData.error || `HTTP ${response.status}`;
errorDetails = errorData.details;
} catch {
errorMessage = `HTTP ${response.status}`;
}
if (response.status === 401) {
throw new OptimaizAuthenticationError(errorMessage, errorDetails);
} else if (response.status === 400) {
throw new OptimaizValidationError(errorMessage, errorDetails);
} else if (response.status >= 500) {
throw new OptimaizServerError(errorMessage, errorDetails);
} else {
throw new OptimaizError(errorMessage, response.status, errorDetails);
}
}
return response.json();
} catch (error) {
if (error.name === "AbortError") {
throw new OptimaizNetworkError(`Request timeout after ${this.timeout}ms`);
}
if (error.name === "TypeError" && error.message.includes("fetch")) {
if (retriesLeft > 0) {
this.log(`Retrying request (${retriesLeft} retries left)`);
await new Promise((resolve) => setTimeout(resolve, 1e3));
return this.request(path, body, retriesLeft - 1);
}
throw new OptimaizNetworkError(`Unable to connect to ${this.baseUrl}`);
}
throw error;
}
}
/**
* Start a new trace
*/
async startTrace(data) {
if (!data.promptTemplate && !data.promptVariables) {
throw new OptimaizValidationError(
"Either promptTemplate or promptVariables must be provided"
);
}
if (data.tools) {
const validation = validateTools(data.tools);
if (!validation.valid) {
throw new OptimaizValidationError(
`Invalid tool definitions: ${validation.errors.join(", ")}`
);
}
}
const traceId = data.traceId || generateId();
this.request("/api/v1/interactions/start", {
...data,
traceId
}).catch((err) => this.log("Failed to start trace in background", err));
return { success: true, traceId };
}
/**
* Append LLM response to a trace
*/
async appendResponse(data) {
if (!data.traceId) {
throw new OptimaizValidationError("traceId is required");
}
this.request("/api/v1/interactions/append", data).catch((err) => this.log("Failed to append response in background", err));
return { success: true, traceId: data.traceId };
}
/**
* Finalize a trace (calculate latency, etc.)
*/
async finalizeTrace(traceId) {
if (!traceId) {
throw new OptimaizValidationError("traceId is required");
}
this.request("/api/v1/interactions/finalize", { traceId }).catch((err) => this.log("Failed to finalize trace in background", err));
return { success: true, traceId };
}
/**
* Log an error to a trace
*/
async logError(traceId, error) {
if (!traceId) {
throw new OptimaizValidationError("traceId is required");
}
this.request("/api/v1/interactions/error", { traceId, error }).catch((err) => this.log("Failed to log error in background", err));
return { success: true, traceId };
}
/**
* Send feedback for a trace
*/
async sendFeedback(traceId, feedback) {
if (!traceId) {
throw new OptimaizValidationError("traceId is required");
}
this.request("/api/v1/interactions/feedback", {
traceId,
feedback
}).catch((err) => this.log("Failed to send feedback in background", err));
return { success: true, traceId };
}
/**
* Add tool execution record
*/
async addToolExecution(data) {
if (!data.traceId) {
throw new OptimaizValidationError("traceId is required");
}
this.request("/api/v1/interactions/tool-execution", {
...data,
executionTime: data.executionTime || /* @__PURE__ */ new Date()
}).catch((err) => this.log("Failed to add tool execution in background", err));
return { success: true, traceId: data.traceId };
}
/**
* Add tool results to a trace
*/
async addToolResults(data) {
if (!data.traceId) {
throw new OptimaizValidationError("traceId is required");
}
if (!Array.isArray(data.toolResults) || data.toolResults.length === 0) {
throw new OptimaizValidationError("toolResults must be a non-empty array");
}
this.request("/api/v1/interactions/tool-results", data).catch((err) => this.log("Failed to add tool results in background", err));
return { success: true, traceId: data.traceId };
}
/**
* Compose prompts from templates and variables
*/
composePrompts(templates, variables) {
if (!Array.isArray(templates)) {
throw new OptimaizValidationError("Templates must be an array");
}
const prompts = [];
const promptTemplate = [];
for (const { role, content, type = "text" } of templates) {
const resolved = Object.entries(variables).reduce(
(acc, [key, value]) => acc.replace(new RegExp(`\\{${key}\\}`, "g"), String(value || "")),
content
);
prompts.push({ type, role, value: resolved });
promptTemplate.push({ type, role, value: content });
}
return { prompts, promptTemplate, promptVariables: variables };
}
/**
* High-level wrapper for LLM calls with automatic tracing
*/
async wrapLLMCall({
traceId = generateId(),
agentId,
flowId,
threadId,
sessionId,
userId,
promptTemplate,
promptVariables,
tools,
provider,
model,
modelParams,
metadata,
tags,
call
}) {
this.startTrace({
traceId,
agentId,
flowId,
threadId,
sessionId,
userId,
promptTemplate,
promptVariables,
tools,
provider,
model,
modelParams,
metadata,
tags
}).catch((err) => this.log("Failed to start trace", err));
try {
const response = await call();
this.appendResponse({ traceId, rawResponse: response, provider, model }).catch((err) => this.log("Failed to append response", err));
this.finalizeTrace(traceId).catch((err) => this.log("Failed to finalize trace", err));
return { response, traceId };
} catch (err) {
this.logError(traceId, {
message: err.message,
code: err.code || "unknown_error",
details: err.stack
}).catch((logErr) => this.log("Failed to log error", logErr));
throw err;
}
}
/**
* Convert tools to provider-specific format
*/
convertToolsToProvider(tools, provider) {
return toProviderFormat(tools, provider);
}
/**
* Convert provider-specific tools to unified format
*/
convertToolsFromProvider(tools, provider) {
return fromProviderFormat(tools, provider);
}
/**
* Validate tool definitions
*/
validateTools(tools) {
return validateTools(tools);
}
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
OptimaizAuthenticationError,
OptimaizClient,
OptimaizError,
OptimaizNetworkError,
OptimaizServerError,
OptimaizValidationError,
fromAnthropicFormat,
fromMistralFormat,
fromOpenAIFormat,
fromProviderFormat,
isAuthenticationError,
isNetworkError,
isOptimaizError,
isServerError,
isValidationError,
toAnthropicFormat,
toMistralFormat,
toOpenAIFormat,
toProviderFormat,
validateTools
});