Skip to main content
Version: v5.2

Custom tool

You can create your own tool on the platform and make API, RAG, and database calls from the tool.

Tool class object

{
"_name": "GetAssetsTool",
"_description": "The tool gets the asset based on the given criteria",
"_userType": "GetAssetsTool",
"_namespaces": ["Namespace_EleSnXBw"],
"_toolClass": "GetAssetsTool"
}
PropertyTypeDescription
_nameStringThe tool name. The _name, _userType, and _toolClass properties should have the same value.
_descriptionStringEnter a description for the tool.
_userTypeStringThe _name, _userType, and _toolClass properties should have the same value.
_toolClassStringThe class name in your tool script used to extend the abstract class. The _name, _userType, and _toolClass properties should have the same value.

AbstractTool and Zod

AbstractTool

You can write custom tools by extending the AbstractTool class provided in the @dtplatform/agent-core platform library.

Zod

Use Zod, a schema validation library for TypeScript, to define the schema for the arguments that the tool's call methods receive.

Permissions

Only platform managers can upload script content to a custom tool. App developers must send script content to them for upload.

Language

Only TypeScript or JavaScript​ scripts are accepted.

Creating a custom tool

  1. Create the tool resource using IafAISvc.createTools:

    const tool = await IafAISvc.createTools([
    {
    "_name": "GetAssetsTool",
    "_description": "The tool gets the asset based on the given criteria",
    "_userType": "GetAssetsTool",
    "_namespaces": ["Namespace_EleSnXBw"],
    "_toolClass": "GetAssetsTool"
    }
    ])
  2. Write the tool script content:

    i. Import AbstractTool, zod, and the platform APIs you require:

    import { AbstractTool } from '@dtplatform/agent-core';
    import { z } from 'zod';
    import * as PlatformAPI from '@dtplatform/platform-api';
    const { IafItemSvc } = PlatformAPI;

    ii. Define the schema for the Tool input parameters using zod:

    const toolSchema = z.object({
    name: z.string().optional().nullable().describe('Filter by asset name. Supports regex'),
    dtCategory: z.string().optional().nullable().describe('Filter by the dtCategory property. Supports regex'),
    dtType: z.string().optional().nullable().describe('Filter by dtType property. Supports regex'),
    _pageSize: z.string().optional().nullable().describe('Sets the number of results to return per Page in the response'),
    _offset: z.string().optional().nullable().describe('Sets the number of results to skip in the Page response. For example, set to `10` to skip the first 10 results.')
    });

    iii. Create a your tool class that extends the AbstractTool class, then initialize the properties and schema:

    export default class GetAssetsTool extends AbstractTool {
    name: string = 'GetAssetsTool';
    description: string = 'Helps to retrieve Assets based on the provided criteria.';
    requestContext: any = {};
    schema: any = toolSchema;

    constructor(...args: any[]) {
    super(...args);
    const [{ requestContext }] = args;
    this.requestContext = requestContext;
    }
    ...
    }

    iv. Create a _call method, which serves as a placeholder where you can place your API, RAG, or database calls. You can also add private class methods outside the _call method:

    ...
    async _call(args: z.infer<typeof toolSchema>) {
    let criteria: any = { '$or': [] };
    let options: any = {};
    let response: any = {};
    if (args.name || args.dtCategory || args.dtType) {
    if (args.name) {
    criteria['$or'].push({
    'Asset Name': {
    '$regex': this.toRegexString(args.name),
    '$options': 'i'
    }
    });
    }
    if (args.dtCategory) {
    criteria['$or'].push({
    'properties.dtCategory.val': {
    '$regex': this.toRegexString(args.dtCategory),
    '$options': 'i'
    }
    });
    }
    if (args.dtType) {
    criteria['$or'].push({
    'properties.dtType.val': {
    '$regex': this.toRegexString(args.dtType),
    '$options': 'i'
    }
    });
    }
    if (args._pageSize) {
    options['_pageSize'] = parseInt(args._pageSize, 10);
    }
    if (args._offset) {
    options['_offset'] = parseInt(args._offset, 10);
    }
    let colQuery = {
    query: {
    _userType: 'iaf_ext_asset_coll',
    _itemClass: 'NamedUserCollection'
    }
    };
    const colOptions = {
    project: { _userType: 1, _itemClass: 1 },
    sort: { _name: 1 },
    page: { _offset: 0, _pageSize: 1 }
    };
    const colResponse = await IafItemSvc.getNamedUserItems(
    colQuery,
    this.requestContext,
    colOptions
    );
    let assetCollection: any = null;
    if (colResponse && Array.isArray(colResponse._list) && colResponse._list.length > 0) {
    assetCollection = colResponse._list[0];
    if (assetCollection && assetCollection._id) {
    response = await IafItemSvc.getRelatedItems(
    assetCollection._id,
    {query: criteria},
    this.requestContext,
    options
    );
    }
    }
    }
    return response;
    }

    //private class methods
    toRegexString(str) {
    const quoted = str.match(/^'(.*)'$/);
    if (quoted) {
    const escaped = quoted[1].replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    return `.*(${escaped}).*`;
    }
    const terms = str
    .split(/\s+/)
    .map(term => term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
    return `.*(${terms.join('|')}).*`;
    }

  3. The Platform Manager uploads the source code using IafAISvc.uploadToolSourceCode:

    //code as text
    await IafAISvc.uploadToolSourceCode({
    "_content": "Your script goes here"
    });
    //or file
    await IafAISvc.uploadToolSourceCode({
    "_file": .ts file
    });