UNPKG

@future-agi/sdk

Version:

We help GenAI teams maintain high-accuracy for their Models in production.

602 lines 30.1 kB
"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