@utcp/sdk
Version:
Universal Tool Calling Protocol SDK
1,289 lines (1,263 loc) • 69.4 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
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 __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
ApiKeyAuthSerializer: () => ApiKeyAuthSerializer,
AuthSchema: () => AuthSchema,
AuthSerializer: () => AuthSerializer,
BasicAuthSerializer: () => BasicAuthSerializer,
CallTemplateSchema: () => CallTemplateSchema,
CallTemplateSerializer: () => CallTemplateSerializer,
CommunicationProtocol: () => CommunicationProtocol,
ConcurrentToolRepositoryConfigSerializer: () => ConcurrentToolRepositoryConfigSerializer,
ConcurrentToolRepositorySchema: () => ConcurrentToolRepositorySchema,
DefaultVariableSubstitutor: () => DefaultVariableSubstitutor,
FilterDictPostProcessor: () => FilterDictPostProcessor,
FilterDictPostProcessorSerializer: () => FilterDictPostProcessorSerializer,
InMemConcurrentToolRepository: () => InMemConcurrentToolRepository,
InMemConcurrentToolRepositorySerializer: () => InMemConcurrentToolRepositorySerializer,
JsonSchemaSchema: () => JsonSchemaSchema,
JsonSchemaSerializer: () => JsonSchemaSerializer,
JsonTypeSchema: () => JsonTypeSchema,
LimitStringsPostProcessor: () => LimitStringsPostProcessor,
LimitStringsPostProcessorSerializer: () => LimitStringsPostProcessorSerializer,
OAuth2AuthSerializer: () => OAuth2AuthSerializer,
Serializer: () => Serializer,
TagSearchStrategy: () => TagSearchStrategy,
TagSearchStrategyConfigSerializer: () => TagSearchStrategyConfigSerializer,
ToolPostProcessorConfigSerializer: () => ToolPostProcessorConfigSerializer,
ToolPostProcessorSchema: () => ToolPostProcessorSchema,
ToolSchema: () => ToolSchema,
ToolSearchStrategyConfigSerializer: () => ToolSearchStrategyConfigSerializer,
ToolSearchStrategySchema: () => ToolSearchStrategySchema,
ToolSerializer: () => ToolSerializer,
UTCP_PACKAGE_VERSION: () => UTCP_PACKAGE_VERSION,
UtcpClient: () => UtcpClient,
UtcpClientConfigSchema: () => UtcpClientConfigSchema,
UtcpClientConfigSerializer: () => UtcpClientConfigSerializer,
UtcpManualSchema: () => UtcpManualSchema,
UtcpManualSerializer: () => UtcpManualSerializer,
VariableLoaderSchema: () => VariableLoaderSchema,
VariableLoaderSerializer: () => VariableLoaderSerializer,
ensureCorePluginsInitialized: () => ensureCorePluginsInitialized,
setPluginInitializer: () => setPluginInitializer
});
module.exports = __toCommonJS(index_exports);
// src/data/call_template.ts
var import_zod = require("zod");
// src/interfaces/serializer.ts
var ensurePluginsInitialized = null;
function setPluginInitializer(fn) {
ensurePluginsInitialized = fn;
}
var Serializer = class {
constructor() {
if (ensurePluginsInitialized) {
ensurePluginsInitialized();
}
}
copy(obj) {
return this.validateDict(this.toDict(obj));
}
};
// src/data/call_template.ts
var CallTemplateSerializer = class _CallTemplateSerializer extends Serializer {
static serializers = {};
// No need for the whole plugin registry. Plugins just need to call this to register a new call template type
static registerCallTemplate(callTemplateType, serializer, override = false) {
if (!override && _CallTemplateSerializer.serializers[callTemplateType]) {
return false;
}
_CallTemplateSerializer.serializers[callTemplateType] = serializer;
return true;
}
toDict(obj) {
const serializer = _CallTemplateSerializer.serializers[obj.call_template_type];
if (!serializer) {
throw new Error(`No serializer found for call_template_type: ${obj.call_template_type}`);
}
return serializer.toDict(obj);
}
validateDict(obj) {
const serializer = _CallTemplateSerializer.serializers[obj.call_template_type];
if (!serializer) {
throw new Error(`Invalid call_template_type: ${obj.call_template_type}`);
}
return serializer.validateDict(obj);
}
};
var CallTemplateSchema = import_zod.z.custom((obj) => {
try {
const validated = new CallTemplateSerializer().validateDict(obj);
return validated;
} catch (e) {
return false;
}
}, {
message: "Invalid CallTemplate object"
});
// src/data/utcp_manual.ts
var import_zod3 = require("zod");
// src/data/tool.ts
var import_zod2 = require("zod");
var JsonTypeSchema = import_zod2.z.lazy(() => import_zod2.z.union([
import_zod2.z.string(),
import_zod2.z.number(),
import_zod2.z.boolean(),
import_zod2.z.null(),
import_zod2.z.record(import_zod2.z.string(), JsonTypeSchema),
import_zod2.z.array(JsonTypeSchema)
]));
var JsonSchemaSchema = import_zod2.z.lazy(() => import_zod2.z.object({
$schema: import_zod2.z.string().optional().describe("JSON Schema version URI."),
$id: import_zod2.z.string().optional().describe("A URI for the schema."),
title: import_zod2.z.string().optional().describe("A short explanation about the purpose of the data described by this schema."),
description: import_zod2.z.string().optional().describe("A more lengthy explanation about the purpose of the data described by this schema."),
type: import_zod2.z.union([
import_zod2.z.literal("string"),
import_zod2.z.literal("number"),
import_zod2.z.literal("integer"),
import_zod2.z.literal("boolean"),
import_zod2.z.literal("object"),
import_zod2.z.literal("array"),
import_zod2.z.literal("null"),
import_zod2.z.array(import_zod2.z.string())
]).optional(),
properties: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.lazy(() => JsonSchemaSchema)).optional(),
items: import_zod2.z.union([import_zod2.z.lazy(() => JsonSchemaSchema), import_zod2.z.array(import_zod2.z.lazy(() => JsonSchemaSchema))]).optional(),
required: import_zod2.z.array(import_zod2.z.string()).optional(),
enum: import_zod2.z.array(JsonTypeSchema).optional(),
const: JsonTypeSchema.optional(),
default: JsonTypeSchema.optional(),
format: import_zod2.z.string().optional(),
additionalProperties: import_zod2.z.union([import_zod2.z.boolean(), import_zod2.z.lazy(() => JsonSchemaSchema)]).optional(),
pattern: import_zod2.z.string().optional(),
minimum: import_zod2.z.number().optional(),
maximum: import_zod2.z.number().optional(),
minLength: import_zod2.z.number().optional(),
maxLength: import_zod2.z.number().optional()
}).catchall(import_zod2.z.unknown()));
var ToolSchema = import_zod2.z.object({
name: import_zod2.z.string().describe('Unique identifier for the tool, typically in format "manual_name.tool_name".'),
description: import_zod2.z.string().default("").describe("Human-readable description of what the tool does."),
inputs: JsonSchemaSchema.default({}).describe("JSON Schema defining the tool's input parameters."),
outputs: JsonSchemaSchema.default({}).describe("JSON Schema defining the tool's return value structure."),
tags: import_zod2.z.array(import_zod2.z.string()).default([]).describe("List of tags for categorization and search."),
average_response_size: import_zod2.z.number().optional().describe("Optional hint about typical response size in bytes."),
tool_call_template: CallTemplateSchema.describe("CallTemplate configuration for accessing this tool.")
}).strict();
var JsonSchemaSerializer = class extends Serializer {
toDict(obj) {
return { ...obj };
}
validateDict(obj) {
return JsonSchemaSchema.parse(obj);
}
};
var ToolSerializer = class extends Serializer {
toDict(obj) {
return {
name: obj.name,
description: obj.description,
inputs: obj.inputs,
outputs: obj.outputs,
tags: obj.tags,
...obj.average_response_size !== void 0 && { average_response_size: obj.average_response_size },
tool_call_template: obj.tool_call_template
};
}
validateDict(obj) {
return ToolSchema.parse(obj);
}
};
// src/version.ts
var _VERSION = "1.1.0";
var LIB_VERSION = _VERSION === "1.1.0" ? "1.0.0" : _VERSION;
// src/data/utcp_manual.ts
var UTCP_PACKAGE_VERSION = LIB_VERSION;
var UtcpManualSchema = import_zod3.z.object({
// Use .optional() to allow missing in input, then .default() to satisfy the interface.
utcp_version: import_zod3.z.string().optional().default(UTCP_PACKAGE_VERSION).describe("UTCP protocol version supported by the provider."),
manual_version: import_zod3.z.string().optional().default("1.0.0").describe("Version of this specific manual."),
tools: import_zod3.z.array(ToolSchema).describe("List of available tools with their complete configurations.")
}).strict();
var UtcpManualSerializer = class extends Serializer {
toDict(obj) {
return {
utcp_version: obj.utcp_version,
manual_version: obj.manual_version,
tools: obj.tools
};
}
validateDict(obj) {
return UtcpManualSchema.parse(obj);
}
};
// src/interfaces/communication_protocol.ts
var CommunicationProtocol = class {
/**
* Mapping of communication protocol types to their respective implementations.
*/
static communicationProtocols = {};
/**
* Closes any persistent connections or resources held by the communication protocol.
* This is a cleanup method that should be called when the client is shut down.
*/
async close() {
}
};
// src/client/utcp_client_config.ts
var import_zod16 = require("zod");
// src/data/auth.ts
var import_zod4 = require("zod");
var AuthSerializer = class _AuthSerializer extends Serializer {
static serializers = {};
// No need for the whole plugin registry. Plugins just need to call this to register a new auth type
static registerAuth(authType, serializer, override = false) {
if (!override && _AuthSerializer.serializers[authType]) {
return false;
}
_AuthSerializer.serializers[authType] = serializer;
return true;
}
toDict(obj) {
const serializer = _AuthSerializer.serializers[obj.auth_type];
if (!serializer) {
throw new Error(`No serializer found for auth_type: ${obj.auth_type}`);
}
return serializer.toDict(obj);
}
validateDict(obj) {
const serializer = _AuthSerializer.serializers[obj.auth_type];
if (!serializer) {
throw new Error(`Invalid auth_type: ${obj.auth_type}`);
}
return serializer.validateDict(obj);
}
};
var AuthSchema = import_zod4.z.custom((obj) => {
try {
const validated = new AuthSerializer().validateDict(obj);
return validated;
} catch (e) {
return false;
}
}, {
message: "Invalid Auth object"
});
// src/data/auth_implementations/api_key_auth.ts
var import_zod5 = require("zod");
var ApiKeyAuthSerializer = class extends Serializer {
toDict(obj) {
return { ...obj };
}
validateDict(obj) {
return ApiKeyAuthSchema.parse(obj);
}
};
var ApiKeyAuthSchema = import_zod5.z.object({
auth_type: import_zod5.z.literal("api_key"),
api_key: import_zod5.z.string(),
var_name: import_zod5.z.string().default("X-Api-Key"),
location: import_zod5.z.enum(["header", "query", "cookie"]).default("header")
});
// src/data/auth_implementations/basic_auth.ts
var import_zod6 = require("zod");
var BasicAuthSerializer = class extends Serializer {
toDict(obj) {
return { ...obj };
}
validateDict(obj) {
return BasicAuthSchema.parse(obj);
}
};
var BasicAuthSchema = import_zod6.z.object({
auth_type: import_zod6.z.literal("basic"),
username: import_zod6.z.string().describe("The username for basic authentication. Recommended to use injected variables."),
password: import_zod6.z.string().describe("The password for basic authentication. Recommended to use injected variables.")
}).strict();
// src/data/auth_implementations/oauth2_auth.ts
var import_zod7 = require("zod");
var OAuth2AuthSerializer = class extends Serializer {
toDict(obj) {
return { ...obj };
}
validateDict(obj) {
return OAuth2AuthSchema.parse(obj);
}
};
var OAuth2AuthSchema = import_zod7.z.object({
auth_type: import_zod7.z.literal("oauth2"),
token_url: import_zod7.z.string().describe("The URL to fetch the OAuth2 access token from. Recommended to use injected variables."),
client_id: import_zod7.z.string().describe("The OAuth2 client ID. Recommended to use injected variables."),
client_secret: import_zod7.z.string().describe("The OAuth2 client secret. Recommended to use injected variables."),
scope: import_zod7.z.string().optional().describe("Optional scope parameter to limit the access token's permissions.")
}).strict();
// src/implementations/in_mem_concurrent_tool_repository.ts
var import_zod8 = require("zod");
var InMemConcurrentToolRepository = class {
tool_repository_type = "in_memory";
_config;
// Store the config to return in toDict
_toolsByName = /* @__PURE__ */ new Map();
_manuals = /* @__PURE__ */ new Map();
_manualCallTemplates = /* @__PURE__ */ new Map();
_writeMutex = new AsyncMutex();
/**
* The constructor must accept the config type to match the factory signature,
* even if the implementation does not use it.
*/
constructor(config = { tool_repository_type: "in_memory" }) {
this._config = config;
}
/**
* Converts the repository instance's configuration to a dictionary.
*/
toDict() {
return this._config;
}
/**
* Saves a manual's call template and its associated tools in the repository.
* This operation replaces any existing manual with the same name.
* @param manualCallTemplate The call template associated with the manual to save.
* @param manual The complete UTCP Manual object to save.
*/
async saveManual(manualCallTemplate, manual) {
const release = await this._writeMutex.acquire();
try {
const manualName = manualCallTemplate.name;
const oldManual = this._manuals.get(manualName);
if (oldManual) {
for (const tool of oldManual.tools) {
this._toolsByName.delete(tool.name);
}
}
this._manualCallTemplates.set(manualName, { ...manualCallTemplate });
this._manuals.set(manualName, { ...manual, tools: manual.tools.map((t) => ({ ...t })) });
for (const tool of manual.tools) {
this._toolsByName.set(tool.name, { ...tool });
}
} finally {
release();
}
}
/**
* Removes a manual and its tools from the repository.
* @param manualName The name of the manual to remove.
* @returns True if the manual was removed, False otherwise.
*/
async removeManual(manualName) {
const release = await this._writeMutex.acquire();
try {
const oldManual = this._manuals.get(manualName);
if (!oldManual) {
return false;
}
for (const tool of oldManual.tools) {
this._toolsByName.delete(tool.name);
}
this._manuals.delete(manualName);
this._manualCallTemplates.delete(manualName);
return true;
} finally {
release();
}
}
/**
* Removes a specific tool from the repository.
* Note: This also attempts to remove the tool from any associated manual.
* @param toolName The full namespaced name of the tool to remove.
* @returns True if the tool was removed, False otherwise.
*/
async removeTool(toolName) {
const release = await this._writeMutex.acquire();
try {
const toolRemoved = this._toolsByName.delete(toolName);
if (!toolRemoved) {
return false;
}
const manualName = toolName.split(".")[0];
if (manualName) {
const manual = this._manuals.get(manualName);
if (manual) {
manual.tools = manual.tools.filter((t) => t.name !== toolName);
}
}
return true;
} finally {
release();
}
}
/**
* Retrieves a tool by its full namespaced name.
* @param toolName The full namespaced name of the tool to retrieve.
* @returns The tool if found, otherwise undefined.
*/
async getTool(toolName) {
const tool = this._toolsByName.get(toolName);
return tool ? { ...tool } : void 0;
}
/**
* Retrieves all tools from the repository.
* @returns A list of all registered tools.
*/
async getTools() {
return Array.from(this._toolsByName.values()).map((t) => ({ ...t }));
}
/**
* Retrieves all tools associated with a specific manual.
* @param manualName The name of the manual.
* @returns A list of tools associated with the manual, or undefined if the manual is not found.
*/
async getToolsByManual(manualName) {
const manual = this._manuals.get(manualName);
return manual ? manual.tools.map((t) => ({ ...t })) : void 0;
}
/**
* Retrieves a complete UTCP Manual object by its name.
* @param manualName The name of the manual to retrieve.
* @returns The manual if found, otherwise undefined.
*/
async getManual(manualName) {
const manual = this._manuals.get(manualName);
return manual ? { ...manual, tools: manual.tools.map((t) => ({ ...t })) } : void 0;
}
/**
* Retrieves all registered manuals from the repository.
* @returns A list of all registered UtcpManual objects.
*/
async getManuals() {
return Array.from(this._manuals.values()).map((m) => ({ ...m, tools: m.tools.map((t) => ({ ...t })) }));
}
/**
* Retrieves a manual's CallTemplate by its name.
* @param manualCallTemplateName The name of the manual's CallTemplate to retrieve.
* @returns The CallTemplate if found, otherwise undefined.
*/
async getManualCallTemplate(manualCallTemplateName) {
const template = this._manualCallTemplates.get(manualCallTemplateName);
return template ? { ...template } : void 0;
}
/**
* Retrieves all registered manual CallTemplates from the repository.
* @returns A list of all registered CallTemplateBase objects.
*/
async getManualCallTemplates() {
return Array.from(this._manualCallTemplates.values()).map((t) => ({ ...t }));
}
};
var InMemConcurrentToolRepositorySerializer = class extends Serializer {
toDict(obj) {
return {
tool_repository_type: obj.tool_repository_type
};
}
validateDict(data) {
try {
return new InMemConcurrentToolRepository(InMemConcurrentToolRepositoryConfigSchema.parse(data));
} catch (e) {
if (e instanceof import_zod8.z.ZodError) {
throw new Error(`Invalid configuration: ${e.message}`);
}
throw new Error("Unexpected error during validation");
}
}
};
var AsyncMutex = class {
queue = [];
locked = false;
/**
* Acquires the mutex. If the mutex is already locked, waits until it's released.
* @returns A function to call to release the mutex.
*/
async acquire() {
if (!this.locked) {
this.locked = true;
return this._release.bind(this);
} else {
return new Promise((resolve) => {
this.queue.push(() => {
this.locked = true;
resolve(this._release.bind(this));
});
});
}
}
/**
* Releases the mutex, allowing the next queued operation (if any) to proceed.
*/
_release() {
this.locked = false;
if (this.queue.length > 0) {
const next = this.queue.shift();
if (next) {
next();
}
}
}
};
var InMemConcurrentToolRepositoryConfigSchema = import_zod8.z.object({
tool_repository_type: import_zod8.z.literal("in_memory")
}).passthrough();
// src/interfaces/concurrent_tool_repository.ts
var import_zod9 = __toESM(require("zod"), 1);
var ConcurrentToolRepositoryConfigSerializer = class _ConcurrentToolRepositoryConfigSerializer extends Serializer {
static implementations = {};
static default_strategy = "in_memory";
// No need for the whole plugin registry. Plugins just need to call this to register a new repository
static registerRepository(type, serializer, override = false) {
if (!override && _ConcurrentToolRepositoryConfigSerializer.implementations[type]) {
return false;
}
_ConcurrentToolRepositoryConfigSerializer.implementations[type] = serializer;
return true;
}
toDict(obj) {
const serializer = _ConcurrentToolRepositoryConfigSerializer.implementations[obj.tool_repository_type];
if (!serializer) throw new Error(`No serializer for type: ${obj.tool_repository_type}`);
return serializer.toDict(obj);
}
validateDict(data) {
const serializer = _ConcurrentToolRepositoryConfigSerializer.implementations[data["tool_repository_type"]];
if (!serializer) throw new Error(`Invalid tool repository type: ${data["tool_repository_type"]}`);
return serializer.validateDict(data);
}
};
var ConcurrentToolRepositorySchema = import_zod9.default.custom((obj) => {
try {
const validated = new ConcurrentToolRepositoryConfigSerializer().validateDict(obj);
return validated;
} catch (e) {
return false;
}
}, {
message: "Invalid ConcurrentToolRepository object"
});
// src/implementations/tag_search_strategy.ts
var import_zod10 = require("zod");
var TagSearchStrategy = class {
tool_search_strategy_type = "tag_and_description_word_match";
descriptionWeight;
tagWeight;
_config;
/**
* Creates an instance of TagSearchStrategy.
*
* @param descriptionWeight The weight to apply to words found in the tool's description.
* @param tagWeight The weight to apply to words found in the tool's tags.
*/
constructor(config) {
this._config = TagSearchStrategyConfigSchema.parse(config);
this.descriptionWeight = this._config.description_weight;
this.tagWeight = this._config.tag_weight;
}
/**
* Converts the search strategy instance's configuration to a dictionary.
*/
toDict() {
return this._config;
}
/**
* Searches for tools by matching tags and description content against a query.
*
* @param concurrentToolRepository The repository to search for tools.
* @param query The search query string.
* @param limit The maximum number of tools to return. If 0, all matched tools are returned.
* @param anyOfTagsRequired Optional list of tags where one of them must be present in the tool's tags.
* @returns A promise that resolves to a list of tools ordered by relevance.
*/
async searchTools(concurrentToolRepository, query, limit = 10, anyOfTagsRequired) {
const queryLower = query.toLowerCase();
const queryWords = new Set(queryLower.match(/\w+/g) || []);
let tools = await concurrentToolRepository.getTools();
if (anyOfTagsRequired && anyOfTagsRequired.length > 0) {
const requiredTagsLower = new Set(anyOfTagsRequired.map((tag) => tag.toLowerCase()));
tools = tools.filter(
(tool) => tool.tags && tool.tags.some((tag) => requiredTagsLower.has(tag.toLowerCase()))
);
}
const toolScores = tools.map((tool) => {
let score = 0;
const toolNameLower = tool.name.toLowerCase();
const toolNameOnly = toolNameLower.includes(".") ? toolNameLower.split(".").pop() || toolNameLower : toolNameLower;
if (queryLower === toolNameOnly || queryLower.includes(toolNameOnly) || toolNameOnly.includes(queryLower)) {
score += this.tagWeight * 2;
}
const toolNameWords = new Set(toolNameOnly.match(/\w+/g) || []);
for (const word of toolNameWords) {
if (queryWords.has(word)) {
score += this.tagWeight;
}
}
if (tool.tags) {
for (const tag of tool.tags) {
const tagLower = tag.toLowerCase();
if (queryLower.includes(tagLower) || tagLower.includes(queryLower)) {
score += this.tagWeight;
}
const tagWords = new Set(tagLower.match(/\w+/g) || []);
for (const word of tagWords) {
if (queryWords.has(word)) {
score += this.tagWeight * 0.5;
}
}
for (const queryWord of queryWords) {
if (queryWord.length > 2) {
for (const tagWord of tagWords) {
if (tagWord.length > 2 && (tagWord.includes(queryWord) || queryWord.includes(tagWord))) {
score += this.tagWeight * 0.3;
break;
}
}
}
}
}
}
if (tool.description) {
const descriptionLower = tool.description.toLowerCase();
const descriptionWords = new Set(
descriptionLower.match(/\w+/g) || []
);
for (const word of descriptionWords) {
if (queryWords.has(word) && word.length > 2) {
score += this.descriptionWeight;
}
}
for (const queryWord of queryWords) {
if (queryWord.length > 2) {
for (const descWord of descriptionWords) {
if (descWord.length > 2 && (descWord.includes(queryWord) || queryWord.includes(descWord))) {
score += this.descriptionWeight * 0.5;
break;
}
}
}
}
}
return { tool, score };
});
const sortedTools = toolScores.sort((a, b) => b.score - a.score).map((item) => item.tool);
return limit > 0 ? sortedTools.slice(0, limit) : sortedTools;
}
};
var TagSearchStrategyConfigSerializer = class extends Serializer {
toDict(obj) {
return {
tool_search_strategy_type: obj.tool_search_strategy_type,
description_weight: obj.descriptionWeight,
tag_weight: obj.tagWeight
};
}
validateDict(data) {
try {
return new TagSearchStrategy(TagSearchStrategyConfigSchema.parse(data));
} catch (e) {
if (e instanceof import_zod10.z.ZodError) {
throw new Error(`Invalid configuration: ${e.message}`);
}
throw new Error("Unexpected error during validation");
}
}
};
var TagSearchStrategyConfigSchema = import_zod10.z.object({
tool_search_strategy_type: import_zod10.z.literal("tag_and_description_word_match"),
description_weight: import_zod10.z.number().optional().default(1),
tag_weight: import_zod10.z.number().optional().default(3)
}).passthrough();
// src/interfaces/tool_search_strategy.ts
var import_zod11 = __toESM(require("zod"), 1);
var ToolSearchStrategyConfigSerializer = class _ToolSearchStrategyConfigSerializer extends Serializer {
static implementations = {};
static default_strategy = "tag_and_description_word_match";
// No need for the whole plugin registry. Plugins just need to call this to register a new strategy
static registerStrategy(type, serializer, override = false) {
if (!override && _ToolSearchStrategyConfigSerializer.implementations[type]) {
return false;
}
_ToolSearchStrategyConfigSerializer.implementations[type] = serializer;
return true;
}
toDict(obj) {
const serializer = _ToolSearchStrategyConfigSerializer.implementations[obj.tool_search_strategy_type];
if (!serializer) throw new Error(`No serializer for type: ${obj.tool_search_strategy_type}`);
return serializer.toDict(obj);
}
validateDict(data) {
const serializer = _ToolSearchStrategyConfigSerializer.implementations[data["tool_search_strategy_type"]];
if (!serializer) throw new Error(`Invalid tool search strategy type: ${data["tool_search_strategy_type"]}`);
return serializer.validateDict(data);
}
};
var ToolSearchStrategySchema = import_zod11.default.custom((obj) => {
try {
const validated = new ToolSearchStrategyConfigSerializer().validateDict(obj);
return validated;
} catch (e) {
return false;
}
}, {
message: "Invalid ToolSearchStrategy object"
});
// src/implementations/post_processors/filter_dict_post_processor.ts
var import_zod12 = require("zod");
var FilterDictPostProcessor = class {
tool_post_processor_type = "filter_dict";
excludeKeys;
onlyIncludeKeys;
excludeTools;
onlyIncludeTools;
excludeManuals;
onlyIncludeManuals;
_config;
constructor(config) {
this._config = FilterDictPostProcessorConfigSchema.parse(config);
this.excludeKeys = config.exclude_keys ? new Set(config.exclude_keys) : void 0;
this.onlyIncludeKeys = config.only_include_keys ? new Set(config.only_include_keys) : void 0;
this.excludeTools = config.exclude_tools ? new Set(config.exclude_tools) : void 0;
this.onlyIncludeTools = config.only_include_tools ? new Set(config.only_include_tools) : void 0;
this.excludeManuals = config.exclude_manuals ? new Set(config.exclude_manuals) : void 0;
this.onlyIncludeManuals = config.only_include_manuals ? new Set(config.only_include_manuals) : void 0;
if (this.excludeKeys && this.onlyIncludeKeys) {
console.warn("FilterDictPostProcessor configured with both 'exclude_keys' and 'only_include_keys'. 'exclude_keys' will be ignored.");
}
if (this.excludeTools && this.onlyIncludeTools) {
console.warn("FilterDictPostProcessor configured with both 'exclude_tools' and 'only_include_tools'. 'exclude_tools' will be ignored.");
}
if (this.excludeManuals && this.onlyIncludeManuals) {
console.warn("FilterDictPostProcessor configured with both 'exclude_manuals' and 'only_include_manuals'. 'exclude_manuals' will be ignored.");
}
}
/**
* Converts the post-processor instance's configuration to a dictionary.
*/
toDict() {
return this._config;
}
/**
* Processes the result of a tool call, applying filtering logic.
* @param caller The UTCP client instance.
* @param tool The Tool object that was called.
* @param manualCallTemplate The CallTemplateBase object of the manual that owns the tool.
* @param result The raw result returned by the tool's communication protocol.
* @returns The processed result.
*/
postProcess(caller, tool, manualCallTemplate, result) {
if (this.shouldSkipProcessing(tool, manualCallTemplate)) {
return result;
}
if (this.onlyIncludeKeys) {
return this._filterDictOnlyIncludeKeys(result);
}
if (this.excludeKeys) {
return this._filterDictExcludeKeys(result);
}
return result;
}
/**
* Determines if processing should be skipped based on tool and manual filters.
* @param tool The Tool object.
* @param manualCallTemplate The CallTemplateBase object of the manual.
* @returns True if processing should be skipped, false otherwise.
*/
shouldSkipProcessing(tool, manualCallTemplate) {
if (this.onlyIncludeTools && !this.onlyIncludeTools.has(tool.name)) {
return true;
}
if (this.excludeTools && this.excludeTools.has(tool.name)) {
return true;
}
const manualName = manualCallTemplate.name;
if (manualName) {
if (this.onlyIncludeManuals && !this.onlyIncludeManuals.has(manualName)) {
return true;
}
if (this.excludeManuals && this.excludeManuals.has(manualName)) {
return true;
}
}
return false;
}
/**
* Recursively filters a dictionary, keeping only specified keys.
* @param data The data to filter.
* @returns The filtered data.
*/
_filterDictOnlyIncludeKeys(data) {
if (typeof data !== "object" || data === null) {
return data;
}
if (Array.isArray(data)) {
return data.map((item) => this._filterDictOnlyIncludeKeys(item)).filter((item) => {
if (typeof item === "object" && item !== null) {
if (Array.isArray(item)) return item.length > 0;
return Object.keys(item).length > 0;
}
return true;
});
}
const newObject = {};
for (const key in data) {
if (Object.prototype.hasOwnProperty.call(data, key)) {
if (this.onlyIncludeKeys?.has(key)) {
newObject[key] = this._filterDictOnlyIncludeKeys(data[key]);
} else {
const processedValue = this._filterDictOnlyIncludeKeys(data[key]);
if (typeof processedValue === "object" && processedValue !== null) {
if (Array.isArray(processedValue) && processedValue.length > 0) {
newObject[key] = processedValue;
} else if (Object.keys(processedValue).length > 0) {
newObject[key] = processedValue;
}
}
}
}
}
return newObject;
}
/**
* Recursively filters a dictionary, excluding specified keys.
* @param data The data to filter.
* @returns The filtered data.
*/
_filterDictExcludeKeys(data) {
if (typeof data !== "object" || data === null) {
return data;
}
if (Array.isArray(data)) {
return data.map((item) => this._filterDictExcludeKeys(item)).filter((item) => {
if (typeof item === "object" && item !== null) {
if (Array.isArray(item)) return item.length > 0;
return Object.keys(item).length > 0;
}
return true;
});
}
const newObject = {};
for (const key in data) {
if (Object.prototype.hasOwnProperty.call(data, key)) {
if (!this.excludeKeys?.has(key)) {
newObject[key] = this._filterDictExcludeKeys(data[key]);
}
}
}
return newObject;
}
};
var FilterDictPostProcessorConfigSchema = import_zod12.z.object({
tool_post_processor_type: import_zod12.z.literal("filter_dict"),
exclude_keys: import_zod12.z.array(import_zod12.z.string()).optional(),
only_include_keys: import_zod12.z.array(import_zod12.z.string()).optional(),
exclude_tools: import_zod12.z.array(import_zod12.z.string()).optional(),
only_include_tools: import_zod12.z.array(import_zod12.z.string()).optional(),
exclude_manuals: import_zod12.z.array(import_zod12.z.string()).optional(),
only_include_manuals: import_zod12.z.array(import_zod12.z.string()).optional()
}).passthrough();
var FilterDictPostProcessorSerializer = class extends Serializer {
toDict(obj) {
const filterDictConfig = obj.toDict();
return {
tool_post_processor_type: filterDictConfig.tool_post_processor_type,
exclude_keys: filterDictConfig.exclude_keys,
only_include_keys: filterDictConfig.only_include_keys,
exclude_tools: filterDictConfig.exclude_tools,
only_include_tools: filterDictConfig.only_include_tools,
exclude_manuals: filterDictConfig.exclude_manuals,
only_include_manuals: filterDictConfig.only_include_manuals
};
}
validateDict(data) {
try {
return new FilterDictPostProcessor(FilterDictPostProcessorConfigSchema.parse(data));
} catch (e) {
if (e instanceof import_zod12.z.ZodError) {
throw new Error(`Invalid configuration: ${e.message}`);
}
throw new Error("Unexpected error during validation");
}
}
};
// src/implementations/post_processors/limit_strings_post_processor.ts
var import_zod13 = require("zod");
var LimitStringsPostProcessor = class {
tool_post_processor_type = "limit_strings";
limit;
excludeTools;
onlyIncludeTools;
excludeManuals;
onlyIncludeManuals;
_config;
constructor(config) {
this._config = LimitStringsPostProcessorConfigSchema.parse(config);
this.limit = config.limit;
this.excludeTools = config.exclude_tools ? new Set(config.exclude_tools) : void 0;
this.onlyIncludeTools = config.only_include_tools ? new Set(config.only_include_tools) : void 0;
this.excludeManuals = config.exclude_manuals ? new Set(config.exclude_manuals) : void 0;
this.onlyIncludeManuals = config.only_include_manuals ? new Set(config.only_include_manuals) : void 0;
if (this.excludeTools && this.onlyIncludeTools) {
console.warn("LimitStringsPostProcessor configured with both 'exclude_tools' and 'only_include_tools'. 'exclude_tools' will be ignored.");
}
if (this.excludeManuals && this.onlyIncludeManuals) {
console.warn("LimitStringsPostProcessor configured with both 'exclude_manuals' and 'only_include_manuals'. 'exclude_manuals' will be ignored.");
}
}
/**
* Converts the post-processor instance's configuration to a dictionary.
*/
toDict() {
return this._config;
}
/**
* Processes the result of a tool call, truncating string values if applicable.
* @param caller The UTCP client instance.
* @param tool The Tool object that was called.
* @param manualCallTemplate The CallTemplateBase object of the manual that owns the tool.
* @param result The raw result returned by the tool's communication protocol.
* @returns The processed result.
*/
postProcess(caller, tool, manualCallTemplate, result) {
if (this.shouldSkipProcessing(tool, manualCallTemplate)) {
return result;
}
return this._processObject(result);
}
/**
* Determines if processing should be skipped based on tool and manual filters.
* @param tool The Tool object.
* @param manualCallTemplate The CallTemplateBase object of the manual.
* @returns True if processing should be skipped, false otherwise.
*/
shouldSkipProcessing(tool, manualCallTemplate) {
if (this.onlyIncludeTools && !this.onlyIncludeTools.has(tool.name)) {
return true;
}
if (this.excludeTools && this.excludeTools.has(tool.name)) {
return true;
}
const manualName = manualCallTemplate.name;
if (manualName) {
if (this.onlyIncludeManuals && !this.onlyIncludeManuals.has(manualName)) {
return true;
}
if (this.excludeManuals && this.excludeManuals.has(manualName)) {
return true;
}
}
return false;
}
/**
* Recursively processes an object, truncating strings.
* @param obj The object to process.
* @returns The processed object.
*/
_processObject(obj) {
if (typeof obj === "string") {
return obj.length > this.limit ? obj.substring(0, this.limit) : obj;
}
if (Array.isArray(obj)) {
return obj.map((item) => this._processObject(item));
}
if (typeof obj === "object" && obj !== null) {
const newObj = {};
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
newObj[key] = this._processObject(obj[key]);
}
}
return newObj;
}
return obj;
}
};
var LimitStringsPostProcessorConfigSchema = import_zod13.z.object({
tool_post_processor_type: import_zod13.z.literal("limit_strings"),
limit: import_zod13.z.number().int().positive().default(1e4),
exclude_tools: import_zod13.z.array(import_zod13.z.string()).optional(),
only_include_tools: import_zod13.z.array(import_zod13.z.string()).optional(),
exclude_manuals: import_zod13.z.array(import_zod13.z.string()).optional(),
only_include_manuals: import_zod13.z.array(import_zod13.z.string()).optional()
}).passthrough();
var LimitStringsPostProcessorSerializer = class extends Serializer {
toDict(obj) {
const limitStringsConfig = obj.toDict();
return {
tool_post_processor_type: limitStringsConfig.tool_post_processor_type,
limit: limitStringsConfig.limit,
exclude_tools: limitStringsConfig.exclude_tools,
only_include_tools: limitStringsConfig.only_include_tools,
exclude_manuals: limitStringsConfig.exclude_manuals,
only_include_manuals: limitStringsConfig.only_include_manuals
};
}
validateDict(data) {
try {
return new LimitStringsPostProcessor(LimitStringsPostProcessorConfigSchema.parse(data));
} catch (e) {
if (e instanceof import_zod13.z.ZodError) {
throw new Error(`Invalid configuration: ${e.message}`);
}
throw new Error("Unexpected error during validation");
}
}
};
// src/interfaces/tool_post_processor.ts
var import_zod14 = __toESM(require("zod"), 1);
var ToolPostProcessorConfigSerializer = class _ToolPostProcessorConfigSerializer extends Serializer {
static implementations = {};
// No need for the whole plugin registry. Plugins just need to call this to register a new post-processor
static registerPostProcessor(type, serializer, override = false) {
if (!override && _ToolPostProcessorConfigSerializer.implementations[type]) {
return false;
}
_ToolPostProcessorConfigSerializer.implementations[type] = serializer;
return true;
}
toDict(obj) {
const serializer = _ToolPostProcessorConfigSerializer.implementations[obj.tool_post_processor_type];
if (!serializer) throw new Error(`No serializer for type: ${obj.tool_post_processor_type}`);
return serializer.toDict(obj);
}
validateDict(data) {
const serializer = _ToolPostProcessorConfigSerializer.implementations[data["tool_post_processor_type"]];
if (!serializer) throw new Error(`Invalid tool post-processor type: ${data["tool_post_processor_type"]}`);
return serializer.validateDict(data);
}
};
var ToolPostProcessorSchema = import_zod14.default.custom((obj) => {
try {
const validated = new ToolPostProcessorConfigSerializer().validateDict(obj);
return validated;
} catch (e) {
return false;
}
}, {
message: "Invalid ToolPostProcessor object"
});
// src/plugins/plugin_loader.ts
var corePluginsInitialized = false;
var initializing = false;
setPluginInitializer(() => ensureCorePluginsInitialized());
function _registerCorePlugins() {
AuthSerializer.registerAuth("api_key", new ApiKeyAuthSerializer());
AuthSerializer.registerAuth("basic", new BasicAuthSerializer());
AuthSerializer.registerAuth("oauth2", new OAuth2AuthSerializer());
ConcurrentToolRepositoryConfigSerializer.registerRepository("in_memory", new InMemConcurrentToolRepositorySerializer());
ToolSearchStrategyConfigSerializer.registerStrategy("tag_and_description_word_match", new TagSearchStrategyConfigSerializer());
ToolPostProcessorConfigSerializer.registerPostProcessor("filter_dict", new FilterDictPostProcessorSerializer());
ToolPostProcessorConfigSerializer.registerPostProcessor("limit_strings", new LimitStringsPostProcessorSerializer());
}
function ensureCorePluginsInitialized() {
if (!corePluginsInitialized && !initializing) {
initializing = true;
_registerCorePlugins();
corePluginsInitialized = true;
initializing = false;
}
}
// src/data/variable_loader.ts
var import_zod15 = require("zod");
var VariableLoaderSerializer = class _VariableLoaderSerializer extends Serializer {
static serializers = {};
/**
* Registers a variable loader serializer for a specific type.
* @param type The variable_loader_type identifier
* @param serializer The serializer instance for this type
* @param override Whether to override an existing registration
* @returns true if registration succeeded, false if already exists and override is false
*/
static registerVariableLoader(type, serializer, override = false) {
if (!override && _VariableLoaderSerializer.serializers[type]) {
return false;
}
_VariableLoaderSerializer.serializers[type] = serializer;
return true;
}
toDict(obj) {
const serializer = _VariableLoaderSerializer.serializers[obj.variable_loader_type];
if (!serializer) {
throw new Error(`No serializer found for variable_loader_type: ${obj.variable_loader_type}`);
}
return serializer.toDict(obj);
}
validateDict(obj) {
const serializer = _VariableLoaderSerializer.serializers[obj.variable_loader_type];
if (!serializer) {
throw new Error(`Invalid variable_loader_type: ${obj.variable_loader_type}`);
}
return serializer.validateDict(obj);
}
};
var VariableLoaderSchema = import_zod15.z.custom((obj) => {
try {
const validated = new VariableLoaderSerializer().validateDict(obj);
return validated;
} catch (e) {
return false;
}
}, {
message: "Invalid VariableLoader object"
});
// src/client/utcp_client_config.ts
ensureCorePluginsInitialized();
var UtcpClientConfigSchema = import_zod16.z.object({
variables: import_zod16.z.record(import_zod16.z.string(), import_zod16.z.string()).optional().default({}),
load_variables_from: import_zod16.z.array(VariableLoaderSchema).nullable().optional().default(null).transform((val) => {
if (val === null) return null;
return val.map((item) => {
if ("variable_loader_type" in item) {
return new VariableLoaderSerializer().validateDict(item);
}
return item;
});
}),
tool_repository: import_zod16.z.any().transform((val) => {
if (typeof val === "object" && val !== null && "tool_repository_type" in val) {
return new ConcurrentToolRepositoryConfigSerializer().validateDict(val);
}
return val;
}).optional().default(new ConcurrentToolRepositoryConfigSerializer().validateDict({
tool_repository_type: ConcurrentToolRepositoryConfigSerializer.default_strategy
})),
tool_search_strategy: import_zod16.z.any().transform((val) => {
if (typeof val === "object" && val !== null && "tool_search_strategy_type" in val) {
return new ToolSearchStrategyConfigSerializer().validateDict(val);
}
return val;
}).optional().default(new ToolSearchStrategyConfigSerializer().validateDict({
tool_search_strategy_type: ToolSearchStrategyConfigSerializer.default_strategy
})),
post_processing: import_zod16.z.array(import_zod16.z.any()).transform((val) => {
return val.map((item) => {
if (typeof item === "object" && item !== null && "tool_post_processor_type" in item) {
return new ToolPostProcessorConfigSerializer().validateDict(item);
}
return item;
});
}).optional().default([]),
manual_call_templates: import_zod16.z.array(CallTemplateSchema).transform((val) => {
return val.map((item) => {
if (typeof item === "object" && item !== null && "call_template_type" in item) {
return new CallTemplateSerializer().validateDict(item);
}
return item;
});
}).optional().default([])
}).strict();
var UtcpClientConfigSerializer = class extends Serializer {
/**
* REQUIRED
* Convert a UtcpClientConfig object to a dictionary.
*
* @param obj The UtcpClientConfig object to convert.
* @returns The dictionary converted from the UtcpClientConfig object.
*/
toDict(obj) {
return {
variables: obj.variables,
load_variables_from: obj.load_variables_from === null ? null : obj.load_variables_from?.map((item) => new VariableLoaderSerializer().toDict(item)),
tool_repository: new ConcurrentToolRepositoryConfigSerializer().toDict(obj.tool_repository),
tool_search_strategy: new ToolSearchStrategyConfigSerializer().toDict(obj.tool_search_strategy),
post_processing: obj.post_processing.map((item) => new ToolPostProcessorConfigSerializer().toDict(item)),
manual_call_templates: obj.manual_call_templates.map((item) => new CallTemplateSerializer().toDict(item))
};
}
/**
* REQUIRED
* Validate a dictionary and convert it to a UtcpClientConfig object.
*
* @param data The dictionary to validate and convert.
* @returns The UtcpClientConfig object converted from the dictionary.
* @throws Error if validation fails
*/
validateDict(data) {
try {
return UtcpClientConfigSchema.parse(data);
} catch (e) {
throw new Error(`Invalid UtcpClientConfig: ${e.message}
${e.stack || ""}`);
}
}
};
// src/exceptions/utcp_variable_not_found_error.ts
var UtcpVariableNotFoundError = class extends Error {
variableName;
/**
* Initializes the exception with the missing variable name.
*
* @param variableName The name of the variable that could not be found.
*/
constructor(variableName) {
super(
`Variable '${variableName}' referenced in call template configuration not found. Please ensure it's defined in client.config.variables, environment variables, or a configured variable loader.`
);
this.variableName = variableName;
this.name = "UtcpVariableNotFoundError";
}
};
// src/implementations/default_variable_substitutor.ts
var DefaultVariableSubstitutor = class {
/**
* Retrieves a variable value from configured sources, respecting namespaces.
*
* @param key The variable name to look up (without namespace prefix).
* @param config The UTCP client configuration.
* @param namespace An optional namespace to prepend to the variable name for lookup.
* @returns The resolved variable value.
* @throws UtcpVariableNotFoundError if the variable cannot be found.
*/
async _getVariable(key, config, namespace) {
let effectiveKey = key;
if (namespace) {
effectiveKey = namespace.replace(/_/g, "!").replace(/!/g, "__") + "_" + key;
}
if (config.variables && effectiveKey in config.variables) {
return config.variables[effectiveKey];
}
if (config.load_variables_from) {
for (const varLoader of config.load_variables_from) {
const varValue = await varLoader.get(effectiveKey);
if (varValue) {
return varValue;
}
}
}
try {
const envVar = process.env[effectiveKey];
if (envVar) {
return envVar;
}
} catch (e) {
}
throw new UtcpVariableNotFoundError(effectiveKey);
}
/**
* Recursively substitutes variables in the given object.
*
* @param obj The object (can be string, array, or object) containing potential variable references to substitute.
* @param config The UTCP client configuration containin