@future-agi/sdk
Version:
We help GenAI teams maintain high-accuracy for their Models in production.
602 lines • 30.1 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Prompt = void 0;
const auth_1 = require("../api/auth");
const types_1 = require("../api/types");
const routes_1 = require("../utils/routes");
const types_2 = require("./types");
const labels_1 = require("./labels");
const errors_1 = require("../utils/errors");
/**
* Simple JSON handler – returns parsed JSON on success, handles common auth/not-found errors.
*/
class SimpleJsonResponseHandler extends auth_1.ResponseHandler {
static _parseSuccess(response) {
return response.data;
}
static _handleError(response) {
if (response.status === 403) {
throw new errors_1.InvalidAuthError();
}
if (response.status === 404) {
throw new errors_1.TemplateNotFound('unknown');
}
// Fallback – use generic logic from base class
return super._handleError(response);
}
}
/**
* Response handler for prompt-template related endpoints. Converts backend payloads into
* strongly-typed `PromptTemplate` instances where appropriate.
*/
class PromptResponseHandler extends auth_1.ResponseHandler {
static _buildModelConfig(cfgSrc) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _o;
if (!cfgSrc)
return new types_2.ModelConfig();
return new types_2.ModelConfig({
model_name: (_a = cfgSrc.modelName) !== null && _a !== void 0 ? _a : cfgSrc.model,
temperature: (_b = cfgSrc.temperature) !== null && _b !== void 0 ? _b : 0,
frequency_penalty: (_d = (_c = cfgSrc.frequencyPenalty) !== null && _c !== void 0 ? _c : cfgSrc.frequency_penalty) !== null && _d !== void 0 ? _d : 0,
presence_penalty: (_f = (_e = cfgSrc.presencePenalty) !== null && _e !== void 0 ? _e : cfgSrc.presence_penalty) !== null && _f !== void 0 ? _f : 0,
max_tokens: (_g = cfgSrc.maxTokens) !== null && _g !== void 0 ? _g : cfgSrc.max_tokens,
top_p: (_j = (_h = cfgSrc.topP) !== null && _h !== void 0 ? _h : cfgSrc.top_p) !== null && _j !== void 0 ? _j : 0,
response_format: (_k = cfgSrc.responseFormat) !== null && _k !== void 0 ? _k : cfgSrc.response_format,
tool_choice: (_l = cfgSrc.toolChoice) !== null && _l !== void 0 ? _l : cfgSrc.tool_choice,
tools: (_o = cfgSrc.tools) !== null && _o !== void 0 ? _o : null,
});
}
static _toPromptTemplate(data) {
var _a, _b, _c, _d, _e;
const promptConfigRaw = data.promptConfig || data.prompt_config;
let modelConfiguration;
let messages = [];
if (promptConfigRaw && Array.isArray(promptConfigRaw) && promptConfigRaw.length > 0) {
const pc = promptConfigRaw[0];
modelConfiguration = this._buildModelConfig(pc.configuration || {});
messages = pc.messages || [];
}
return new types_2.PromptTemplate(Object.assign({ id: data.id, name: data.name, description: (_a = data.description) !== null && _a !== void 0 ? _a : '', messages, model_configuration: modelConfiguration, variable_names: (_c = (_b = data.variableNames) !== null && _b !== void 0 ? _b : data.variable_names) !== null && _c !== void 0 ? _c : {}, version: data.version, is_default: (_d = data.isDefault) !== null && _d !== void 0 ? _d : true, evaluation_configs: (_e = data.evaluationConfigs) !== null && _e !== void 0 ? _e : [], status: data.status, error_message: data.errorMessage }, (Array.isArray(data.labels) ? { labels: data.labels } : {})));
}
static _parseSuccess(response) {
var _a, _b, _c, _d, _e;
const { data } = response;
const url = (_a = response.config.url) !== null && _a !== void 0 ? _a : '';
const method = (_c = (_b = response.config.method) === null || _b === void 0 ? void 0 : _b.toUpperCase()) !== null && _c !== void 0 ? _c : 'GET';
// Search endpoint: /prompt-templates/?search=<name>
if (url.includes('search=')) {
const results = (_d = data.results) !== null && _d !== void 0 ? _d : [];
const name = url.split('search=')[1];
const found = results.find((it) => it.name === name);
if (found)
return found.id;
throw new errors_1.SDKException(`No template found with the given name: ${name}`);
}
// GET template by ID endpoint
if (method === types_1.HttpMethod.GET && !url.endsWith('/')) {
// Heuristic: treat as single-template retrieval by ID
return this._toPromptTemplate(data);
}
// GET template by name endpoint – keep parity with Python SDK behaviour
if (method === types_1.HttpMethod.GET && url.includes(routes_1.Routes.get_template_by_name)) {
return this._toPromptTemplate(data);
}
// POST create template endpoint returns { result: {...} }
if (method === types_1.HttpMethod.POST && url.endsWith(routes_1.Routes.create_template)) {
return (_e = data.result) !== null && _e !== void 0 ? _e : data;
}
// Fallback to raw payload
return data;
}
static _handleError(response) {
var _a, _b, _c;
// Optional debug output
if (process.env.FI_SDK_DEBUG === '1' || process.env.FI_SDK_DEBUG === 'true') {
/* eslint-disable no-console */
console.error('[PromptResponseHandler] HTTP', response.status, 'payload:', response.data);
/* eslint-enable no-console */
}
if (response.status === 403) {
throw new errors_1.InvalidAuthError();
}
if (response.status === 404) {
// Attempt to parse the template name from query string if present
const url = (_a = response.config.url) !== null && _a !== void 0 ? _a : '';
const match = url.match(/name=([^&]+)/);
const name = match ? decodeURIComponent(match[1]) : 'unknown';
throw new errors_1.TemplateNotFound(name);
}
if (response.status === 400) {
const detail = (_b = response.data) !== null && _b !== void 0 ? _b : {};
const errorCode = detail.errorCode;
// Map "template not found" phrasing used by backend to our TemplateNotFound exception
if ((detail === null || detail === void 0 ? void 0 : detail.result) && typeof detail.result === 'string') {
const lowercase = detail.result.toLowerCase();
if (lowercase.includes('no prompttemplate matches') || lowercase.includes('failed to retrieve template')) {
throw new errors_1.TemplateNotFound('unknown');
}
}
if (errorCode === 'TEMPLATE_ALREADY_EXIST') {
throw new errors_1.TemplateAlreadyExists((_c = detail.name) !== null && _c !== void 0 ? _c : '<unknown>');
}
const msg = detail.result ||
detail.message ||
detail.error ||
(() => {
try {
return JSON.stringify(detail);
}
catch (_a) {
return undefined;
}
})() ||
'Bad request – please verify request body.';
throw new errors_1.SDKException(msg);
}
return super._handleError(response);
}
}
/**
* Main Prompt client – allows programmatic CRUD and execution of prompt templates.
*/
class Prompt extends auth_1.APIKeyAuth {
// ---------- Static helpers ----------
/**
* List all prompt templates (raw JSON payload).
*/
static listTemplates() {
return __awaiter(this, arguments, void 0, function* (options = {}) {
var _a;
const client = new auth_1.APIKeyAuth(options);
try {
const res = (yield client.request({
method: types_1.HttpMethod.GET,
url: `${client.baseUrl}/${routes_1.Routes.list_templates}`,
}));
return (_a = res.data) !== null && _a !== void 0 ? _a : res;
}
finally {
yield client.close();
}
});
}
/**
* Convenience: fetch template by exact name.
*/
static getTemplateByName(name_1) {
return __awaiter(this, arguments, void 0, function* (name, _a = {}) {
var { label = 'production', version } = _a, options = __rest(_a, ["label", "version"]);
const client = new auth_1.APIKeyAuth(options);
try {
const params = { name };
if (version)
params.version = version;
else if (label)
params.label = label;
return (yield client.request({
method: types_1.HttpMethod.GET,
url: `${client.baseUrl}/${routes_1.Routes.prompt_label_get_by_name}`,
params,
}, PromptResponseHandler));
}
finally {
yield client.close();
}
});
}
/**
* Delete template by name (helper).
*/
static deleteTemplateByName(name_1) {
return __awaiter(this, arguments, void 0, function* (name, options = {}) {
var _a;
const client = new auth_1.APIKeyAuth(options);
try {
const tmpl = (yield client.request({
method: types_1.HttpMethod.GET,
url: `${client.baseUrl}/${routes_1.Routes.prompt_label_get_by_name}`,
params: { name },
}, PromptResponseHandler));
yield client.request({
method: types_1.HttpMethod.DELETE,
url: `${client.baseUrl}/${routes_1.Routes.delete_template.replace('{template_id}', (_a = tmpl.id) !== null && _a !== void 0 ? _a : '')}`,
});
return true;
}
finally {
yield client.close();
}
});
}
/** Set default version for a template by name (class helper). */
static setDefaultVersion(templateName_1, version_1) {
return __awaiter(this, arguments, void 0, function* (templateName, version, options = {}) {
return (0, labels_1.setDefaultVersion)(templateName, version, options);
});
}
/** List versions and labels for a template by name or id. */
static getTemplateLabels() {
return __awaiter(this, arguments, void 0, function* (args = {}) {
return (0, labels_1.getTemplateLabels)(args);
});
}
/** Assign label to template/version using only names. */
static assignLabelToTemplateVersion(templateName_1, version_1, label_1) {
return __awaiter(this, arguments, void 0, function* (templateName, version, label, options = {}) {
return (0, labels_1.assignLabelToTemplateVersion)(templateName, version, label, options);
});
}
/** Remove label by name from template/version using only names. */
static removeLabelFromTemplateVersion(templateName_1, version_1, label_1) {
return __awaiter(this, arguments, void 0, function* (templateName, version, label, options = {}) {
return (0, labels_1.removeLabelFromTemplateVersion)(templateName, version, label, options);
});
}
// ---------- Instance ----------
constructor(template, options = {}) {
super(options);
if (template) {
this.template = template;
}
}
/**
* Create a new draft prompt template.
*/
open() {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
if (!this.template) {
throw new errors_1.SDKException('template must be set');
}
// If template already has an ID it's already created – just return the client.
if (this.template.id) {
return this;
}
// Attempt to fetch existing template by name; propagate any errors.
if (this.template.name) {
try {
const remote = yield Prompt.getTemplateByName(this.template.name, {
fiApiKey: this.fiApiKey,
fiSecretKey: this.fiSecretKey,
fiBaseUrl: this.baseUrl,
});
// Found existing template – adopt it and return immediately
this.template = remote;
return this;
}
catch (err) {
// In production, treat any error during lookup as "not found" and proceed to create
// This handles cases where the lookup endpoint is unstable but create works
// Template truly does not exist – proceed to create below
}
}
// Transform messages into backend-friendly format
const messages = this._prepareMessages();
const jsonPayload = {
name: this.template.name,
prompt_config: [
{
messages,
configuration: {
model: (_b = (_a = this.template.model_configuration) === null || _a === void 0 ? void 0 : _a.model_name) !== null && _b !== void 0 ? _b : 'gpt-4o-mini',
temperature: (_c = this.template.model_configuration) === null || _c === void 0 ? void 0 : _c.temperature,
max_tokens: (_d = this.template.model_configuration) === null || _d === void 0 ? void 0 : _d.max_tokens,
top_p: (_e = this.template.model_configuration) === null || _e === void 0 ? void 0 : _e.top_p,
frequency_penalty: (_f = this.template.model_configuration) === null || _f === void 0 ? void 0 : _f.frequency_penalty,
presence_penalty: (_g = this.template.model_configuration) === null || _g === void 0 ? void 0 : _g.presence_penalty,
},
},
],
variable_names: this.template.variable_names,
evaluation_configs: (_h = this.template.evaluation_configs) !== null && _h !== void 0 ? _h : [],
};
let response;
try {
response = yield this.request({
method: types_1.HttpMethod.POST,
url: `${this.baseUrl}/${routes_1.Routes.create_template}`,
json: jsonPayload,
timeout: 60000, // 60 second timeout for create operations
}, PromptResponseHandler);
}
catch (error) {
throw error;
}
// Update template with returned info
this.template.id = response.id;
this.template.name = response.name;
this.template.version = (_k = (_j = response.templateVersion) !== null && _j !== void 0 ? _j : response.createdVersion) !== null && _k !== void 0 ? _k : 'v1';
return this;
});
}
_createNewDraft() {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
if (!this.template || !this.template.id) {
throw new errors_1.SDKException('Template must be created before creating a new version.');
}
const messages = this._prepareMessages();
const modelConfig = {
model: (_b = (_a = this.template.model_configuration) === null || _a === void 0 ? void 0 : _a.model_name) !== null && _b !== void 0 ? _b : 'gpt-4o-mini',
temperature: (_c = this.template.model_configuration) === null || _c === void 0 ? void 0 : _c.temperature,
max_tokens: (_d = this.template.model_configuration) === null || _d === void 0 ? void 0 : _d.max_tokens,
top_p: (_e = this.template.model_configuration) === null || _e === void 0 ? void 0 : _e.top_p,
frequency_penalty: (_f = this.template.model_configuration) === null || _f === void 0 ? void 0 : _f.frequency_penalty,
presence_penalty: (_g = this.template.model_configuration) === null || _g === void 0 ? void 0 : _g.presence_penalty,
};
const body = {
new_prompts: [
{
prompt_config: [{ messages, configuration: modelConfig }],
variable_names: this.template.variable_names,
evaluation_configs: (_h = this.template.evaluation_configs) !== null && _h !== void 0 ? _h : [],
},
],
};
const url = `${this.baseUrl}/${routes_1.Routes.add_new_draft.replace('{template_id}', this.template.id)}`;
const resp = yield this.request({
method: types_1.HttpMethod.POST,
url,
json: body,
timeout: 60000, // 60 second timeout for create operations
}, PromptResponseHandler);
// The backend typically returns { result: [ { templateVersion: 'vX', ... } ] }
if (resp && typeof resp === 'object' && resp.result && Array.isArray(resp.result) && resp.result.length > 0) {
const newVersion = (_j = resp.result[0]) === null || _j === void 0 ? void 0 : _j.templateVersion;
if (newVersion && this.template) {
this.template.version = newVersion;
}
}
});
}
/* ------------------------------------------------------------------
* Version-history helpers (parity with Python SDK)
* ------------------------------------------------------------------ */
/** Fetch raw version history (array of objects) */
_fetchTemplateVersionHistory() {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b;
if (!this.template || !this.template.id) {
throw new errors_1.SDKException('Template must be created before fetching history.');
}
const res = (yield this.request({
method: types_1.HttpMethod.GET,
url: `${this.baseUrl}/${routes_1.Routes.get_template_version_history}`,
params: { template_id: this.template.id },
timeout: 60000, // 60 second timeout for history operations
}));
return (_b = ((_a = res.data) !== null && _a !== void 0 ? _a : res).results) !== null && _b !== void 0 ? _b : [];
});
}
/** Public: list full version history */
listTemplateVersions() {
return __awaiter(this, void 0, void 0, function* () {
return this._fetchTemplateVersionHistory();
});
}
/** Internal draft-status helper */
_currentVersionIsDraft() {
return __awaiter(this, void 0, void 0, function* () {
const history = yield this._fetchTemplateVersionHistory();
return history.some((e) => { var _a; return e.templateVersion === ((_a = this.template) === null || _a === void 0 ? void 0 : _a.version) && e.isDraft === true; });
});
}
/** Apply selected fields from another PromptTemplate to current */
_applyTemplateUpdates(tpl) {
if (!this.template)
return;
const mutable = [
'messages',
'description',
'variable_names',
'model_configuration',
'evaluation_configs',
];
for (const field of mutable) {
const val = tpl[field];
if (val !== undefined)
this.template[field] = val;
}
}
/** Save current draft state to backend */
saveCurrentDraft() {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b, _c, _d, _e, _f, _g, _h;
if (!this.template || !this.template.id) {
throw new errors_1.SDKException('Template must be created before it can be updated.');
}
if (!(yield this._currentVersionIsDraft())) {
throw new errors_1.SDKException('Current version is already committed; create a new draft version first.');
}
const messages = this._prepareMessages();
const modelCfg = {
model: (_b = (_a = this.template.model_configuration) === null || _a === void 0 ? void 0 : _a.model_name) !== null && _b !== void 0 ? _b : 'gpt-4o-mini',
temperature: (_c = this.template.model_configuration) === null || _c === void 0 ? void 0 : _c.temperature,
max_tokens: (_d = this.template.model_configuration) === null || _d === void 0 ? void 0 : _d.max_tokens,
top_p: (_e = this.template.model_configuration) === null || _e === void 0 ? void 0 : _e.top_p,
frequency_penalty: (_f = this.template.model_configuration) === null || _f === void 0 ? void 0 : _f.frequency_penalty,
presence_penalty: (_g = this.template.model_configuration) === null || _g === void 0 ? void 0 : _g.presence_penalty,
};
const body = {
is_run: 'draft',
is_sdk: true,
version: this.template.version,
prompt_config: [{ messages, configuration: modelCfg }],
variable_names: this.template.variable_names,
evaluation_configs: (_h = this.template.evaluation_configs) !== null && _h !== void 0 ? _h : [],
};
yield this.request({
method: types_1.HttpMethod.POST,
url: `${this.baseUrl}/${routes_1.Routes.run_template.replace('{template_id}', this.template.id)}`,
json: body,
timeout: 60000, // 60 second timeout for save operations
}, SimpleJsonResponseHandler);
return true;
});
}
/** Commit current draft version */
commitCurrentVersion() {
return __awaiter(this, arguments, void 0, function* (message = '', set_default = false) {
var _a;
if (!this.template || !this.template.id || !this.template.version) {
throw new errors_1.SDKException('Template and version must be set before commit.');
}
const body = {
version_name: this.template.version,
message,
set_default,
is_draft: false,
};
yield this.request({
method: types_1.HttpMethod.POST,
url: `${this.baseUrl}/${routes_1.Routes.commit_template.replace('{template_id}', this.template.id)}`,
json: body,
timeout: 60000, // 60 second timeout for commit operations
}, SimpleJsonResponseHandler);
// If a label was queued during draft, assign it now post-commit
if (this._pendingLabel && ((_a = this.template) === null || _a === void 0 ? void 0 : _a.id) && this.template.version) {
try {
yield this.labels().assign(this._pendingLabel);
}
catch (_b) {
// swallow assignment errors; commit already succeeded
}
finally {
this._pendingLabel = undefined;
}
}
return true;
});
}
/** Commit current draft if needed then open a new draft version */
createNewVersion() {
return __awaiter(this, arguments, void 0, function* ({ template, commit_message = 'Auto-commit via SDK', set_default = false, } = {}) {
if (!this.template)
throw new errors_1.SDKException('Client.template must be set');
if (template)
this._applyTemplateUpdates(template);
if (yield this._currentVersionIsDraft()) {
yield this.commitCurrentVersion(commit_message, set_default);
}
yield this._createNewDraft();
return this;
});
}
/** Accessor: label service for instance operations */
labels() {
return new labels_1.PromptLabels(this);
}
/* ------------------------------------------------------------------
* Deprecated execution method
* ------------------------------------------------------------------ */
/**
* Execution support removed – this SDK variant focuses solely on template management.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
/**
* Delete the current template.
*/
delete() {
return __awaiter(this, void 0, void 0, function* () {
if (!this.template || !this.template.id) {
throw new errors_1.SDKException('Template ID missing; cannot delete.');
}
yield this.request({
method: types_1.HttpMethod.DELETE,
url: `${this.baseUrl}/${routes_1.Routes.delete_template.replace('{template_id}', this.template.id)}`,
timeout: 60000, // 60 second timeout for delete operations
});
// Clear local ref
this.template = undefined;
return true;
});
}
// Helper to transform messages into backend format (adds variable_names if missing for user role)
_prepareMessages() {
var _a;
if (!this.template)
return [];
const varNames = Object.keys((_a = this.template.variable_names) !== null && _a !== void 0 ? _a : {});
return this.template.messages.map((msg) => {
const obj = Object.assign({}, msg);
if (typeof obj.content === 'string') {
obj.content = [{ type: 'text', text: obj.content }];
}
if (obj.role === 'user') {
// Ensure variable_names field exists for user messages
if (!obj.variable_names || obj.variable_names.length === 0) {
obj.variable_names = varNames;
}
}
return obj;
});
}
/** Compile the template messages with provided variables.
* Replaces {{var}} occurrences in string contents; preserves structured contents.
*/
compile(variables = {}) {
if (!this.template || !this.template.messages) {
throw new errors_1.SDKException('Template must be loaded before compilation.');
}
const substitute = (text) => text.replace(/\{\{\s*(\w+)\s*\}\}/g, (_m, name) => Object.prototype.hasOwnProperty.call(variables, name) ? String(variables[name]) : `{{${name}}}`);
const compiled = [];
for (const msg of this.template.messages) {
// Placeholder message support: { type: 'placeholder', name: 'messagesVar' }
if (msg.type === 'placeholder') {
const ph = msg;
const arr = variables === null || variables === void 0 ? void 0 : variables[ph.name];
if (Array.isArray(arr)) {
for (const it of arr) {
const r = it.role;
const c = it.content;
if (Array.isArray(c)) {
const parts = c.map((part) => typeof (part === null || part === void 0 ? void 0 : part.text) === 'string' ? Object.assign(Object.assign({}, part), { text: substitute(part.text) }) : part);
compiled.push({ role: r, content: parts });
}
else {
compiled.push({ role: r, content: substitute(String(c)) });
}
}
continue;
}
// if not provided, keep placeholder as-is
compiled.push({ role: 'placeholder', content: [{ type: 'text', text: ph.name }] });
continue;
}
const role = msg.role;
const content = msg.content;
if (Array.isArray(content)) {
// structured content – substitute only text fields
const newParts = content.map((part) => typeof (part === null || part === void 0 ? void 0 : part.text) === 'string' ? Object.assign(Object.assign({}, part), { text: substitute(part.text) }) : part);
compiled.push({ role, content: newParts });
}
else {
compiled.push({ role, content: substitute(String(content)) });
}
}
return compiled;
}
}
exports.Prompt = Prompt;
exports.default = { Prompt };
//# sourceMappingURL=client.js.map