diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a2ece08..42dc152 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,4 +19,4 @@ jobs: # TODO change this to bun test - name: Run tests - run: bun test -u + run: bun test:unit -u diff --git a/README.md b/README.md index 1eaa7ea..ceabd21 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ await generateText({ maxSteps: 3, }); ``` + > [!NOTE] > The workflow planner is in closed beta and only available to design partners. Apply for the waitlist [here](https://www.stackone.com/demo). @@ -275,50 +276,80 @@ const toolset = new StackOneToolSet({ baseUrl: "https://api.example-dev.com" }); [View full example](examples/custom-base-url.ts) -### Parameter Transformations +### Advanced Parameter Transformations + +**⚠️ EXPERIMENTAL**: For advanced use cases, you can dynamically transform tool schemas and parameters using the experimental schema override and preExecute functionality. -You can derive multiple parameters from a single source parameter. +This two-stage transformation approach allows you to: -This is particularly useful for features like file uploads, where you can derive file content, name, and format from a file path, or for user data, where you can derive multiple user attributes from a user ID by doing a database lookup. +1. **Schema Override**: Change the tool's input schema at creation time +2. **PreExecute**: Transform parameters from the override schema back to the original API format at execution time -You can also define your own transformations for any type of parameter: +This is particularly powerful for document handling, where you can simplify complex file upload parameters: ```typescript -import { OpenAPIToolSet } from "@stackone/ai"; +import { StackOneToolSet } from "@stackone/ai"; +import type { + Experimental_SchemaOverride, + Experimental_PreExecuteFunction, +} from "@stackone/ai"; + +// 1. Schema Override: Simplify the input schema +const documentSchemaOverride: Experimental_SchemaOverride = ( + originalSchema +) => { + // Replace complex file parameters with simple doc_id + const newProperties = { ...originalSchema.properties }; + delete newProperties.content; + delete newProperties.name; + delete newProperties.file_format; + + newProperties.doc_id = { + type: "string", + description: "Document path or identifier", + }; + + return { ...originalSchema, properties: newProperties }; +}; -// Define a custom transformation configuration for user data -const userTransforms = { - transforms: { - first_name: (userId) => { - // Fetch user data and return first name - return getUserFirstName(userId); - }, - last_name: (userId) => { - // Fetch user data and return last name - return getUserLastName(userId); - }, - email: (userId) => { - // Fetch user data and return email - return getUserEmail(userId); - }, - }, - derivedParameters: ["first_name", "last_name", "email"], +// 2. PreExecute: Transform the simplified parameters back to API format +const documentPreExecute: Experimental_PreExecuteFunction = async (params) => { + const { doc_id, ...otherParams } = params; + + // Read file and convert to required API format + const fileContent = readFileSync(doc_id); + const base64Content = fileContent.toString("base64"); + const fileName = basename(doc_id); + const extension = extname(doc_id).slice(1); + + return { + ...otherParams, + content: base64Content, + name: fileName, + file_format: { value: extension }, + }; }; -// Initialize the toolset with custom transformation config -const toolset = new OpenAPIToolSet({ - filePath: "/path/to/openapi.json", - transformers: { - user_id: userTransforms, - }, +// Use the experimental transformation +const toolset = new StackOneToolSet(); +const tools = toolset.getStackOneTools("hris_*", "account_id"); + +const documentTool = tools.getTool("hris_upload_employee_document", { + experimental_schemaOverride: documentSchemaOverride, + experimental_preExecute: documentPreExecute, }); -// Execute with just the user_id parameter -// The first_name, last_name, and email will be derived automatically -const result = await tool.execute({ user_id: "user123" }); +// Now you can use the simplified schema +const result = await documentTool.execute({ + doc_id: "/path/to/document.pdf", // Simplified input + id: "employee_123", + category: { value: "shared" }, +}); ``` -[View full example](examples/openapi-transformations.ts) +⚠️ **Important**: This is experimental functionality and the API may change in future versions. + +[View full example](examples/experimental-document-handling.ts) ### Testing with dryRun diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..0691be8 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,305 @@ +# StackOne AI SDK Examples + +This directory contains practical examples demonstrating how to use the StackOne AI SDK across different use cases and integrations. + +## Quick Start + +### Clone the repository + +```bash +git clone https://github.com/StackOneHQ/stackone-ai-node.git +``` + +### 2. Authentication + +Set your StackOne API key as an environment variable: + +```bash +export STACKONE_API_KEY=your_api_key_here +``` + +Or create a `.env` file in the project root: + +```env +STACKONE_API_KEY=your_api_key_here +``` + +Optional: Set an OpenAI API key as an environment variable: + +```bash +export OPENAI_API_KEY=your_api_key_here +``` + +Or create a `.env` file in the project root: + +```env +OPENAI_API_KEY=your_api_key_here +``` + +### 3. Configure Account IDs + +Update the account IDs in [`constants.ts`](./constants.ts) with your actual integration account IDs: + +```typescript +export const ACCOUNT_IDS = { + // Human Resources Information System + HRIS: "your_hris_account_id", + + // Applicant Tracking System + ATS: "your_ats_account_id", + + // Customer Relationship Management + CRM: "your_crm_account_id", + + // Document Management System + DOCUMENTS: "your_documents_account_id", + + // Test account IDs (used in examples that don't make real API calls) + TEST: { + VALID: "test_account_id", + OVERRIDE: "test_account_id_override", + DIRECT: "test_account_id_direct", + INVALID: "invalid_test_account_id", + }, +}; +``` + +## Running Examples + +### Run Individual Examples + +```bash +# Run a specific example +bun run examples/ai-sdk-integration.ts + +# Or with Node.js +npx tsx examples/ai-sdk-integration.ts +``` + +### Run All Examples + +```bash +# Test all examples +bun test examples/ +``` + +## Examples Overview + +### Core Integration Examples + +#### [`index.ts`](./index.ts) - Quickstart Guide + +Basic example showing how to initialize the toolset and make your first API call. + +- **Account ID**: HRIS +- **API Calls**: Yes +- **Key Features**: Basic tool usage, employee listing + +#### [`ai-sdk-integration.ts`](./ai-sdk-integration.ts) - AI SDK Integration + +Demonstrates integration with Vercel's AI SDK for building AI agents. + +- **Account ID**: HRIS +- **API Calls**: Via AI agent +- **Key Features**: AI SDK tools conversion, automated agent workflows + +#### [`openai-integration.ts`](./openai-integration.ts) - OpenAI Integration + +Shows how to use StackOne tools with OpenAI's function calling. + +- **Account ID**: HRIS +- **API Calls**: Via OpenAI function calls +- **Key Features**: OpenAI tools format, function calling + +### Configuration Examples + +#### [`account-id-usage.ts`](./account-id-usage.ts) - Account ID Management + +Demonstrates different ways to set and manage account IDs. + +- **Account ID**: TEST (multiple) +- **API Calls**: No (dry run) +- **Key Features**: Account ID precedence, override patterns + +#### [`custom-base-url.ts`](./custom-base-url.ts) - Custom Base URL + +Shows how to use custom base URLs for development or self-hosted instances. + +- **Account ID**: None +- **API Calls**: No (dry run) +- **Key Features**: Custom API endpoints, development setup + +### Advanced Features + +#### [`experimental-document-handling.ts`](./experimental-document-handling.ts) - Document Processing + +**⚠️ EXPERIMENTAL**: Advanced document handling with schema overrides. + +- **Account ID**: HRIS +- **API Calls**: No (dry run) +- **Key Features**: Schema transformation, file processing, multi-source documents + +#### [`filters.ts`](./filters.ts) - Advanced Filtering + +Demonstrates complex filtering and query parameter serialization. + +- **Account ID**: TEST +- **API Calls**: No (dry run) +- **Key Features**: Deep object serialization, complex filters, proxy parameters + +#### [`human-in-the-loop.ts`](./human-in-the-loop.ts) - Human Validation + +Shows how to implement human-in-the-loop workflows for validation. + +- **Account ID**: HRIS +- **API Calls**: Conditional +- **Key Features**: Manual approval workflows, UI integration patterns + +### OpenAPI Toolset Examples + +#### [`openapi-toolset.ts`](./openapi-toolset.ts) - OpenAPI Integration + +Demonstrates loading and using OpenAPI specifications directly. + +- **Account ID**: None +- **API Calls**: No (dry run) +- **Key Features**: File loading, URL loading, OpenAPI parsing + +### Planning Module (Beta) + +#### [`planning.ts`](./planning.ts) - Workflow Planning + +**🚧 CLOSED BETA**: Advanced workflow planning with StackOne's planning agent. + +- **Account ID**: ATS, HRIS +- **API Calls**: Planning API +- **Key Features**: Multi-step workflows, caching, complex business processes + +### Error Handling + +#### [`error-handling.ts`](./error-handling.ts) - Error Management + +Comprehensive error handling patterns and best practices. + +- **Account ID**: TEST (invalid) +- **API Calls**: Intentionally failing calls +- **Key Features**: Error types, validation, graceful degradation + +## Example Categories + +### 🟢 Production Ready + +Examples that are stable and recommended for production use: + +- `index.ts` +- `ai-sdk-integration.ts` +- `openai-integration.ts` +- `account-id-usage.ts` +- `custom-base-url.ts` +- `filters.ts` +- `error-handling.ts` +- `openapi-toolset.ts` + +### 🟡 Advanced/Experimental + +Examples showcasing advanced or experimental features: + +- `experimental-document-handling.ts` (⚠️ API may change) +- `human-in-the-loop.ts` + +### 🔵 Beta/Limited Access + +Examples requiring special access: + +- `planning.ts` (🚧 Closed beta only) + +## Common Patterns + +### Dry Run for Testing + +Most examples support dry run mode to inspect requests without making API calls: + +```typescript +const result = await tool.execute(params, { dryRun: true }); +console.log(result.url); // The URL that would be called +console.log(result.method); // HTTP method +console.log(result.headers); // Request headers +console.log(result.body); // Request body +``` + +### Error Handling + +All production examples include proper error handling: + +```typescript +try { + const result = await tool.execute(params); + // Handle success +} catch (error) { + if (error instanceof StackOneAPIError) { + console.error("API Error:", error.statusCode, error.responseBody); + } else { + console.error("Unexpected error:", error.message); + } +} +``` + +### Account ID Patterns + +Examples demonstrate different ways to provide account IDs: + +```typescript +// 1. At toolset initialization +const toolset = new StackOneToolSet({ accountId: "account_123" }); + +// 2. When getting tools +const tools = toolset.getStackOneTools("hris_*", "account_123"); + +// 3. Directly on individual tools +tool.setAccountId("account_123"); +``` + +## Testing Examples + +The examples include a comprehensive test suite: + +```bash +# Run all example tests +bun test examples/ + +# Run with verbose output +bun test examples/ --verbose + +# Run specific test +bun test examples/examples.spec.ts +``` + +## Troubleshooting + +### Common Issues + +1. **Authentication Errors**: Ensure `STACKONE_API_KEY` is set correctly +2. **Account ID Errors**: Update account IDs in `constants.ts` with your actual values +3. **Network Errors**: Check if you're behind a proxy or firewall +4. **TypeScript Errors**: Ensure you're using compatible Node.js and TypeScript versions + +### Getting Help + +- Check the [main README](../README.md) for general setup instructions +- Review the [StackOne documentation](https://docs.stackone.com) +- Open an issue on GitHub for bug reports or feature requests + +## Contributing + +When adding new examples: + +1. Follow the existing naming convention +2. Add the example to this README +3. Include proper error handling +4. Add TypeScript types +5. Test with the examples test suite +6. Update `constants.ts` if new account IDs are needed + +## License + +These examples are part of the StackOne AI SDK and are subject to the same license terms. diff --git a/examples/account-id-usage.ts b/examples/account-id-usage.ts index b64c9b5..1b6684e 100644 --- a/examples/account-id-usage.ts +++ b/examples/account-id-usage.ts @@ -16,39 +16,40 @@ import assert from 'node:assert'; import { StackOneToolSet } from '../src'; +import { ACCOUNT_IDS } from './constants'; const accountIdUsage = async (): Promise => { /* * Set account ID on toolset initialization */ - const toolset = new StackOneToolSet({ accountId: 'initial-account-id' }); + const toolset = new StackOneToolSet({ accountId: ACCOUNT_IDS.TEST.VALID }); const tools = toolset.getTools('hris_*'); const employeeTool = tools.getStackOneTool('hris_list_employees'); assert( - employeeTool.getAccountId() === 'initial-account-id', + employeeTool.getAccountId() === ACCOUNT_IDS.TEST.VALID, 'Account ID should match what was set' ); /* * Setting account ID when getting tools (overrides toolset account ID) */ - const toolsWithOverride = toolset.getStackOneTools('hris_*', 'override-account-id'); + const toolsWithOverride = toolset.getStackOneTools('hris_*', ACCOUNT_IDS.TEST.OVERRIDE); const employeeToolWithOverride = toolsWithOverride.getStackOneTool('hris_list_employees'); assert( - employeeToolWithOverride?.getAccountId() === 'override-account-id', + employeeToolWithOverride?.getAccountId() === ACCOUNT_IDS.TEST.OVERRIDE, 'Account ID should match what was set' ); /* * Set the account ID directly on the tool */ - employeeTool.setAccountId('direct-account-id'); + employeeTool.setAccountId(ACCOUNT_IDS.TEST.DIRECT); assert( - employeeTool.getAccountId() === 'direct-account-id', + employeeTool.getAccountId() === ACCOUNT_IDS.TEST.DIRECT, 'Account ID should match what was set' ); }; diff --git a/examples/ai-sdk-integration.ts b/examples/ai-sdk-integration.ts index 59aad38..d51eed7 100644 --- a/examples/ai-sdk-integration.ts +++ b/examples/ai-sdk-integration.ts @@ -6,11 +6,12 @@ import assert from 'node:assert'; import { openai } from '@ai-sdk/openai'; import { generateText } from 'ai'; import { StackOneToolSet } from '../src'; +import { ACCOUNT_IDS } from './constants'; const aiSdkIntegration = async (): Promise => { // Initialize StackOne const toolset = new StackOneToolSet(); - const accountId = '45072196112816593343'; + const accountId = ACCOUNT_IDS.HRIS; // Get HRIS tools const tools = toolset.getStackOneTools('hris_get_*', accountId); @@ -20,13 +21,13 @@ const aiSdkIntegration = async (): Promise => { // Use max steps to automatically call the tool if it's needed const { text } = await generateText({ - model: openai('gpt-4o-mini'), + model: openai('gpt-4.1-mini'), tools: aiSdkTools, prompt: 'Get all details about employee with id: c28xIQaWQ6MzM5MzczMDA2NzMzMzkwNzIwNA', maxSteps: 3, }); - assert(text.includes('Isacc Newton'), 'Expected employee name to be included in the response'); + assert(text.includes('Michael'), 'Expected employee name to be included in the response'); }; aiSdkIntegration(); diff --git a/examples/constants.ts b/examples/constants.ts new file mode 100644 index 0000000..ae132d7 --- /dev/null +++ b/examples/constants.ts @@ -0,0 +1,26 @@ +/** + * Centralized account IDs for StackOne examples + * + * These account IDs are organized by vertical and can be reused across examples. + * Update these values with your actual account IDs for each integration. + */ +export const ACCOUNT_IDS = { + // Human Resources Information System + HRIS: '46132201201510402136', + + // Applicant Tracking System + ATS: '46132127373317208518', + + // Customer Relationship Management + CRM: '46132129512514182883', + + // Document Management System + DOCUMENTS: '46132143471913690795', + + TEST: { + VALID: 'test_account_id', + OVERRIDE: 'test_account_id_override', + DIRECT: 'test_account_id_direct', + INVALID: 'invalid_test_account_id', + }, +} as const; diff --git a/examples/error-handling.ts b/examples/error-handling.ts index b653530..fa7f6f4 100644 --- a/examples/error-handling.ts +++ b/examples/error-handling.ts @@ -6,6 +6,7 @@ import assert from 'node:assert'; import { StackOneAPIError, StackOneError, StackOneToolSet, ToolSetConfigError } from '../src'; +import { ACCOUNT_IDS } from './constants'; const errorHandling = async (): Promise => { // Example 1: Handle initialization errors @@ -32,7 +33,7 @@ const errorHandling = async (): Promise => { // Example 2: Handle API errors const testApiErrors = async (): Promise => { const toolset = new StackOneToolSet(); - const accountId = 'invalid-account-id'; // Invalid account ID to force an error + const accountId = ACCOUNT_IDS.TEST.INVALID; // Invalid account ID to force an error try { const tools = toolset.getStackOneTools('hris_*', accountId); diff --git a/examples/examples.spec.ts b/examples/examples.spec.ts new file mode 100644 index 0000000..f22bb47 --- /dev/null +++ b/examples/examples.spec.ts @@ -0,0 +1,61 @@ +import { describe, expect, it } from 'bun:test'; +import { $ } from 'bun'; +import { directoryExists, joinPaths, listFilesInDirectory } from '../src/utils/file'; + +describe('Examples', () => { + it( + 'should run all example files without errors', + async () => { + const examplesDir = joinPaths(process.cwd(), 'examples'); + + if (!directoryExists(examplesDir)) { + throw new Error('Examples directory not found'); + } + + const exampleFiles = listFilesInDirectory( + examplesDir, + (fileName) => fileName.endsWith('.ts') && !fileName.includes('.spec.') + ); + + expect(exampleFiles.length).toBeGreaterThan(0); + + const results = await Promise.all( + exampleFiles.map(async (file) => { + const filePath = joinPaths(examplesDir, file); + + try { + const result = await $`bun run ${filePath}`.quiet(); + return { + file, + success: result.exitCode === 0, + exitCode: result.exitCode, + stdout: result.stdout?.toString() || '', + stderr: result.stderr?.toString() || '', + }; + } catch (error) { + return { + file, + success: false, + exitCode: 1, + stdout: '', + stderr: error instanceof Error ? error.message : String(error), + }; + } + }) + ); + + const failedExamples = results.filter((result) => !result.success); + + if (failedExamples.length > 0) { + const errorMessage = failedExamples + .map(({ file, exitCode, stderr }) => `${file} (exit code: ${exitCode}): ${stderr}`) + .join('\n'); + + throw new Error(`Examples failed:\n${errorMessage}`); + } + + expect(results.every((result) => result.success)).toBe(true); + }, + { timeout: 30000 } + ); +}); diff --git a/examples/experimental-document-handling.ts b/examples/experimental-document-handling.ts new file mode 100644 index 0000000..c9a53d9 --- /dev/null +++ b/examples/experimental-document-handling.ts @@ -0,0 +1,388 @@ +/** + * EXPERIMENTAL: Document Handling with Schema Override + PreExecute + * + * This example demonstrates the new experimental schema override + preExecute functionality + * for handling documents from various sources (local files, URLs, databases, etc.) + * + * The new API provides two-stage transformation: + * 1. Schema Override: Changes the tool's input schema at creation time + * 2. PreExecute: Transforms from override schema back to original API format at execution time + * + * This is an experimental feature and the API may change in future versions. + * + * Run this example with: + * bun run examples/experimental-document-handling.ts + */ + +import assert from 'node:assert'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import type { JSONSchema7Definition } from 'json-schema'; +import { + type Experimental_PreExecuteFunction, + type Experimental_SchemaOverride, + StackOneToolSet, +} from '../src'; +import { ACCOUNT_IDS } from './constants'; + +const accountId = ACCOUNT_IDS.HRIS; + +interface FileFormatParam { + value: string; +} + +interface DocumentParams { + content: string; + name: string; + file_format: FileFormatParam; + [key: string]: unknown; +} + +/** + * EXPERIMENTAL: Schema override for document upload - changes from complex schema to simple doc_id + */ +const createDocumentSchemaOverride = (): Experimental_SchemaOverride => { + return (originalSchema) => { + // Extract only the category from original schema, replace file-related params with doc_id + const newProperties: Record = {}; + + // Keep non-file parameters from original schema + for (const [key, value] of Object.entries(originalSchema.properties)) { + if (!['content', 'name', 'file_format'].includes(key)) { + newProperties[key] = value; + } + } + + // Add simplified document ID parameter + newProperties.doc_id = { + type: 'string', + description: 'Document identifier or file path', + }; + + return { + type: 'object', + properties: newProperties, + required: [ + 'doc_id', + ...(originalSchema.required?.filter( + (r) => !['content', 'name', 'file_format'].includes(r) + ) || []), + ], + }; + }; +}; + +/** + * EXPERIMENTAL: PreExecute function that transforms doc_id back to original file parameters + */ +const createDocumentPreExecute = (allowedPaths: string[]): Experimental_PreExecuteFunction => { + return async (params) => { + const { doc_id, ...otherParams } = params; + + if (typeof doc_id !== 'string') { + throw new Error('doc_id must be a string'); + } + + // Security check: only allow certain paths + const isAllowed = allowedPaths.some((allowedPath) => doc_id.startsWith(allowedPath)); + + if (!isAllowed) { + throw new Error(`Document path not allowed: ${doc_id}`); + } + + if (!fs.existsSync(doc_id)) { + throw new Error(`Document not found: ${doc_id}`); + } + + // Read file and convert to base64 + const fileContent = fs.readFileSync(doc_id); + const base64Content = fileContent.toString('base64'); + const fileName = path.basename(doc_id); + const extension = path.extname(doc_id).slice(1); + + // Transform back to original API format + return { + ...otherParams, + content: base64Content, + name: fileName, + file_format: { value: extension }, + }; + }; +}; + +/** + * EXPERIMENTAL: Schema override for external document references + */ +const createExternalDocumentSchemaOverride = (): Experimental_SchemaOverride => { + return (originalSchema) => { + const newProperties: Record = {}; + + // Keep non-file parameters from original schema + for (const [key, value] of Object.entries(originalSchema.properties)) { + if (!['content', 'name', 'file_format'].includes(key)) { + newProperties[key] = value; + } + } + + // Add external document reference parameter + newProperties.document_reference = { + type: 'string', + description: 'External document reference (S3 key, database ID, etc.)', + }; + + return { + type: 'object', + properties: newProperties, + required: [ + 'document_reference', + ...(originalSchema.required?.filter( + (r) => !['content', 'name', 'file_format'].includes(r) + ) || []), + ], + }; + }; +}; + +/** + * EXPERIMENTAL: PreExecute function for external document fetching + */ +const createExternalDocumentPreExecute = (): Experimental_PreExecuteFunction => { + return async (params) => { + const { document_reference, ...otherParams } = params; + + if (typeof document_reference !== 'string') { + throw new Error('document_reference must be a string'); + } + + // Simulate fetching from external source (S3, database, etc.) + console.log(`Fetching document from external source: ${document_reference}`); + + // In a real implementation, this would fetch from S3, database, etc. + const mockDocumentContent = 'This is a mock document fetched from external source'; + const base64Content = Buffer.from(mockDocumentContent).toString('base64'); + + // Transform back to original API format + return { + ...otherParams, + content: base64Content, + name: `external-doc-${document_reference}.txt`, + file_format: { value: 'txt' }, + }; + }; +}; + +/** + * EXPERIMENTAL: Schema override for multi-source documents (supports both local and external) + */ +const createMultiSourceSchemaOverride = (): Experimental_SchemaOverride => { + return (originalSchema) => { + const newProperties: Record = {}; + + // Keep non-file parameters from original schema + for (const [key, value] of Object.entries(originalSchema.properties)) { + if (!['content', 'name', 'file_format'].includes(key)) { + newProperties[key] = value; + } + } + + // Add both document parameters (user can provide either) + newProperties.doc_id = { + type: 'string', + description: 'Local document path (takes precedence if both provided)', + }; + + newProperties.document_reference = { + type: 'string', + description: 'External document reference (used if doc_id not provided)', + }; + + return { + type: 'object', + properties: newProperties, + required: [ + ...(originalSchema.required?.filter( + (r) => !['content', 'name', 'file_format'].includes(r) + ) || []), + ], + }; + }; +}; + +/** + * EXPERIMENTAL: PreExecute function for multi-source document handling with fallback + */ +const createMultiSourcePreExecute = (localPaths: string[]): Experimental_PreExecuteFunction => { + const localPreExecute = createDocumentPreExecute(localPaths); + const externalPreExecute = createExternalDocumentPreExecute(); + + return async (params) => { + // Try local file first if doc_id is provided + if (params.doc_id) { + try { + return await localPreExecute(params); + } catch (error) { + console.warn(`Local file handler failed: ${error}`); + } + } + + // Fallback to external handler if document_reference is provided + if (params.document_reference) { + return await externalPreExecute(params); + } + + // No document parameters provided + throw new Error('Either doc_id or document_reference must be provided'); + }; +}; + +const experimentalDocumentHandling = async (): Promise => { + // Create a sample file for testing + const sampleFilePath = path.join(__dirname, 'sample-document.txt'); + fs.writeFileSync(sampleFilePath, 'This is an experimental document handling test file.'); + + try { + // Initialize the StackOne toolset + const toolset = new StackOneToolSet(); + + // Get base tools for documents + const tools = toolset.getStackOneTools('hris_*', accountId); + + console.log('🧪 Testing EXPERIMENTAL schema override + preExecute for local files...'); + + // EXPERIMENTAL: Create a tool with schema override and preExecute for local files + const localDocumentTool = tools.getTool('hris_upload_employee_document', { + experimental_schemaOverride: createDocumentSchemaOverride(), + experimental_preExecute: createDocumentPreExecute([__dirname]), + }); + + assert(localDocumentTool !== undefined, 'Local document tool not found'); + + // Use the new simplified schema (doc_id instead of content/name/file_format) + const localFileResult = await localDocumentTool.execute( + { + doc_id: sampleFilePath, // Simplified schema - just document ID + id: 'c28xIQaWQ6MzM5MzczMDA2NzMzMzkwNzIwNA', + category: { value: 'shared' }, + }, + { + dryRun: true, + } + ); + + console.log('✅ Local file schema override + preExecute successful'); + const localParams = localFileResult.mappedParams as Record; + const localDocumentParams = localParams as DocumentParams & Record; + assert( + localDocumentParams.file_format?.value === 'txt', + 'File format was not transformed correctly' + ); + assert( + localDocumentParams.name === 'sample-document.txt', + 'File name was not transformed correctly' + ); + assert( + typeof localDocumentParams.content === 'string', + 'File content was not transformed correctly' + ); + + console.log('🧪 Testing EXPERIMENTAL schema override + preExecute for external documents...'); + + // EXPERIMENTAL: Create a tool for external document references + const externalDocumentTool = tools.getTool('hris_upload_employee_document', { + experimental_schemaOverride: createExternalDocumentSchemaOverride(), + experimental_preExecute: createExternalDocumentPreExecute(), + }); + + assert(externalDocumentTool !== undefined, 'External document tool not found'); + + const externalResult = await externalDocumentTool.execute( + { + document_reference: 'external-doc-123', // Simplified schema - just reference + id: 'c28xIQaWQ6MzM5MzczMDA2NzMzMzkwNzIwNA', + category: { value: 'shared' }, + }, + { + dryRun: true, + } + ); + + console.log('✅ External document schema override + preExecute successful'); + const externalParams = externalResult.mappedParams as Record; + const externalDocumentParams = externalParams as DocumentParams & Record; + assert( + externalDocumentParams.name.includes('external-doc-123'), + 'External document name was not transformed correctly' + ); + + console.log('🧪 Testing EXPERIMENTAL multi-source schema override + preExecute...'); + + // EXPERIMENTAL: Create a tool that supports both local and external documents + const multiSourceTool = tools.getTool('hris_upload_employee_document', { + experimental_schemaOverride: createMultiSourceSchemaOverride(), + experimental_preExecute: createMultiSourcePreExecute([__dirname]), + }); + + assert(multiSourceTool !== undefined, 'Multi-source tool not found'); + + // Test with local file + const multiSourceLocalResult = await multiSourceTool.execute( + { + doc_id: sampleFilePath, // Local file takes precedence + id: 'c28xIQaWQ6MzM5MzczMDA2NzMzMzkwNzIwNA', + category: { value: 'shared' }, + }, + { + dryRun: true, + } + ); + + console.log('✅ Multi-source (local) schema override + preExecute successful'); + const multiLocalParams = multiSourceLocalResult.mappedParams as Record; + const multiLocalDocumentParams = multiLocalParams as DocumentParams & Record; + assert( + multiLocalDocumentParams.name === 'sample-document.txt', + 'Multi-source local document name was not transformed correctly' + ); + + // Test with external reference + const multiSourceExternalResult = await multiSourceTool.execute( + { + document_reference: 'external-doc-456', // Fallback to external + id: 'c28xIQaWQ6MzM5MzczMDA2NzMzMzkwNzIwNA', + category: { value: 'shared' }, + }, + { + dryRun: true, + } + ); + + console.log('✅ Multi-source (external) schema override + preExecute successful'); + const multiExternalParams = multiSourceExternalResult.mappedParams as Record; + const multiExternalDocumentParams = multiExternalParams as DocumentParams & + Record; + assert( + multiExternalDocumentParams.name.includes('external-doc-456'), + 'Multi-source external document name was not transformed correctly' + ); + + console.log('🎉 All EXPERIMENTAL schema override + preExecute tests passed!'); + console.log(''); + console.log('📋 API Summary:'); + console.log(' 1. experimental_schemaOverride: Changes tool input schema at creation time'); + console.log( + ' 2. experimental_preExecute: Transforms from override schema to original API format' + ); + console.log(' 3. Two-stage transformation: Schema definition → Parameter transformation'); + console.log(''); + console.log('⚠️ IMPORTANT: This is experimental functionality.'); + console.log(' The API may change in future versions.'); + console.log(' Use at your own risk in production environments.'); + } finally { + // Clean up the sample file + if (fs.existsSync(sampleFilePath)) { + fs.unlinkSync(sampleFilePath); + } + } +}; + +experimentalDocumentHandling(); diff --git a/examples/file-uploads.ts b/examples/file-uploads.ts deleted file mode 100644 index 5e9148f..0000000 --- a/examples/file-uploads.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Example showing how to upload files using the StackOne SDK. - * - * This example demonstrates how to upload files using the simplified file_path parameter, - * which is the only parameter needed for file uploads. The SDK automatically derives - * the necessary file parameters (content, name, file_format) from the file_path. - * - * Run this example with: - * bun run examples/file-uploads.ts - */ - -import assert from 'node:assert'; -import * as fs from 'node:fs'; -import * as path from 'node:path'; -import { StackOneToolSet } from '../src'; - -const accountId = '45072196112816593343'; - -const fileUploads = async (): Promise => { - // Create a sample file for testing - const sampleFilePath = path.join(__dirname, 'sample-file.txt'); - fs.writeFileSync(sampleFilePath, 'This is a sample file for testing file uploads.'); - - try { - // Initialize the StackOne toolset - const toolset = new StackOneToolSet(); - - // Get tools for documents - const tools = toolset.getStackOneTools('hris_*', accountId); - - // Get the upload file tool - const uploadTool = tools.getTool('hris_upload_employee_document'); - - // Check if upload tool exists - assert(uploadTool !== undefined, 'Upload document tool not found'); - - /* - * Upload a file using the file_path parameter - * The SDK will automatically derive content, name, and file_format from the file_path - */ - // Use dry run to check parameter mapping - const dryRunResult = await uploadTool.execute( - { - file_path: sampleFilePath, - id: 'c28xIQaWQ6MzM5MzczMDA2NzMzMzkwNzIwNA', - category: { value: 'shared' }, - }, - { dryRun: true } - ); - - assert( - (dryRunResult.mappedParams as Record).file_format.value === 'txt', - 'File format was not mapped correctly' - ); - } finally { - // Clean up the sample file - if (fs.existsSync(sampleFilePath)) { - fs.unlinkSync(sampleFilePath); - } - } -}; - -fileUploads(); diff --git a/examples/filters.ts b/examples/filters.ts index 7a6e6e6..481d0bc 100644 --- a/examples/filters.ts +++ b/examples/filters.ts @@ -21,13 +21,14 @@ import assert from 'node:assert'; import { StackOneToolSet } from '../src'; +import { ACCOUNT_IDS } from './constants'; type DryRunResult = { url: string }; const hriseEmployeeFilters = async (): Promise => { // Initialize the toolset const toolset = new StackOneToolSet(); - const accountId = 'test-account-id'; + const accountId = ACCOUNT_IDS.TEST.VALID; // Get the HRIS tools with account ID const tools = toolset.getStackOneTools('hris_*', accountId); diff --git a/examples/human-in-the-loop.ts b/examples/human-in-the-loop.ts index aa36def..eeaf6a8 100644 --- a/examples/human-in-the-loop.ts +++ b/examples/human-in-the-loop.ts @@ -11,6 +11,7 @@ import { openai } from '@ai-sdk/openai'; import { generateText } from 'ai'; import { StackOneToolSet } from '../src'; import type { JsonDict } from '../src/types'; +import { ACCOUNT_IDS } from './constants'; interface ToolCall { toolName: string; @@ -20,7 +21,7 @@ interface ToolCall { const humanInTheLoopExample = async (): Promise => { // Create a toolset const toolset = new StackOneToolSet(); - const hrisAccountId = 'workday_account_id'; + const hrisAccountId = ACCOUNT_IDS.HRIS; // Get the create employee tool const createEmployeeTool = toolset.getTool('hris_create_employee', { diff --git a/examples/index.ts b/examples/index.ts index 6c1e464..3dcfb3d 100644 --- a/examples/index.ts +++ b/examples/index.ts @@ -33,10 +33,12 @@ dotenv.config(); * StackOne uses account IDs to identify different integrations. * See the example in the README for more details. * - * This example will hardcode the account ID: + * This example will use the centralized account ID: */ -const accountId = '45072196112816593343'; +import { ACCOUNT_IDS } from './constants'; + +const accountId = ACCOUNT_IDS.HRIS; /** * # Quickstart @@ -59,9 +61,9 @@ const quickstart = async (): Promise => { assert(employeeTool !== undefined, 'Expected to find hris_list_employees tool'); // Execute the tool and verify the response - const employees = await employeeTool.execute(); - assert(Array.isArray(employees), 'Expected employees to be an array'); - assert(employees.length > 0, 'Expected to find at least one employee'); + const result = await employeeTool.execute(); + assert(Array.isArray(result.data), 'Expected employees to be an array'); + assert(result.data.length > 0, 'Expected to find at least one employee'); }; // Run the example @@ -75,7 +77,7 @@ quickstart(); * - [OpenAI Integration](openai-integration.md) * - [AI SDK Integration](ai-sdk-integration.md) * - [Error Handling](error-handling.md) - * - [File Uploads](file-uploads.md) + * - [EXPERIMENTAL: Document Handling](experimental-document-handling.md) * - [Custom Base URL](custom-base-url.md) * - [Account ID Usage](account-id-usage.md) */ diff --git a/examples/openai-integration.ts b/examples/openai-integration.ts index e9bff1b..d2cae53 100644 --- a/examples/openai-integration.ts +++ b/examples/openai-integration.ts @@ -5,11 +5,12 @@ import assert from 'node:assert'; import OpenAI from 'openai'; import { StackOneToolSet } from '../src'; +import { ACCOUNT_IDS } from './constants'; const openaiIntegration = async (): Promise => { // Initialize StackOne const toolset = new StackOneToolSet(); - const accountId = '45072196112816593343'; + const accountId = ACCOUNT_IDS.HRIS; // Get the correct tool const tools = toolset.getStackOneTools('hris_get_*', accountId); diff --git a/examples/openapi-transformations.ts b/examples/openapi-transformations.ts deleted file mode 100644 index 61ebda7..0000000 --- a/examples/openapi-transformations.ts +++ /dev/null @@ -1,215 +0,0 @@ -/** - * OpenAPI with Parameter Transformations Example - * - * This example demonstrates how to: - * 1. Create custom parameter transformers - * 2. Use them with OpenAPI tools to derive additional parameters - * 3. Execute tools with minimal input, letting the transformers handle the rest - */ - -import assert from 'node:assert'; -import fs from 'node:fs'; -import path from 'node:path'; -import { OpenAPIToolSet } from '../src/toolsets/openapi'; -import type { ParameterTransformer } from '../src/types'; - -/** - * Create a mock OpenAPI specification for testing - */ -const createMockOpenAPISpec = (): string => { - // Create a simple OpenAPI spec with two operations - const spec = { - openapi: '3.0.0', - info: { - title: 'Parameter Transformation API', - version: '1.0.0', - }, - paths: { - '/upload': { - post: { - operationId: 'upload_file', - description: 'Upload a file', - requestBody: { - content: { - 'multipart/form-data': { - schema: { - type: 'object', - properties: { - file_content: { - type: 'string', - description: 'Base64-encoded file content', - }, - file_name: { - type: 'string', - description: 'Name of the file', - }, - file_format: { - type: 'string', - description: 'Format of the file', - }, - }, - required: ['file_content', 'file_name', 'file_format'], - }, - }, - }, - }, - responses: { - '200': { - description: 'File uploaded successfully', - }, - }, - }, - }, - '/users/{user_id}': { - put: { - operationId: 'update_user', - description: 'Update user details', - parameters: [ - { - name: 'user_id', - in: 'path', - required: true, - schema: { - type: 'string', - }, - }, - ], - requestBody: { - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - user_name: { - type: 'string', - description: 'User name', - }, - user_email: { - type: 'string', - description: 'User email', - }, - user_role: { - type: 'string', - description: 'User role', - }, - }, - }, - }, - }, - }, - responses: { - '200': { - description: 'User updated successfully', - }, - }, - }, - }, - }, - }; - - // Write the spec to a temporary file - const tempFile = path.join( - process.env.TMPDIR || '/tmp', - `parameter-transformation-spec-${Date.now()}.json` - ); - fs.writeFileSync(tempFile, JSON.stringify(spec, null, 2)); - - return tempFile; -}; - -/** - * Create a file transformer - * This transformer extracts file_content, file_name, and file_format from file_path - */ -const createFileTransformer = (): ParameterTransformer => { - return { - transforms: { - // Extract file content as base64 - file_content: (filePath: unknown): string => { - if (typeof filePath !== 'string') { - throw new Error('file_path must be a string'); - } - - if (!fs.existsSync(filePath)) { - throw new Error(`File not found: ${filePath}`); - } - - return fs.readFileSync(filePath).toString('base64'); - }, - - // Extract file name - file_name: (filePath: unknown): string => { - if (typeof filePath !== 'string') { - throw new Error('file_path must be a string'); - } - - return path.basename(filePath); - }, - - // Extract file format (extension) - file_format: (filePath: unknown): string => { - if (typeof filePath !== 'string') { - throw new Error('file_path must be a string'); - } - - const extension = path.extname(filePath).slice(1); - return extension || ''; - }, - }, - }; -}; - -/** - * Example of using parameter transformations with OpenAPI - */ -async function main(): Promise { - const specFilePath = createMockOpenAPISpec(); - const fileTransformer = createFileTransformer(); - - // Create a transformers map for the toolset - const transformers = new Map(); - transformers.set('file_path', fileTransformer); - - // Create the toolset with transformers - const toolset = new OpenAPIToolSet({ - filePath: specFilePath, - transformers, - }); - - // Get the tools - const tools = toolset.getTools(); - const fileUploadTool = tools.getTool('upload_file'); - - assert(fileUploadTool, 'Expected to find upload_file tool'); - - const tempFilePath = path.join(__dirname, 'temp.txt'); - fs.writeFileSync(tempFilePath, 'Hello, world!'); - - try { - // Execute with just file_path - other parameters will be transformed - const fileUploadResult = await fileUploadTool.execute( - { file_path: tempFilePath }, - { dryRun: true } - ); - - const fileParams = fileUploadResult.mappedParams as Record; - - // Assertions to validate the transformations worked - assert(fileParams.file_name === 'temp.txt', 'Expected file_name to be transformed correctly'); - assert(fileParams.file_format === 'txt', 'Expected file_format to be transformed correctly'); - assert( - typeof fileParams.file_content === 'string' && fileParams.file_content.length > 0, - 'Expected file_content to be transformed correctly' - ); - } finally { - try { - fs.unlinkSync(tempFilePath); - fs.unlinkSync(specFilePath); - console.log('Cleaned up temporary files'); - } catch (error) { - console.error('Error cleaning up temporary files:', error); - } - } -} - -main(); diff --git a/examples/planning.ts b/examples/planning.ts index 4b86346..b9bdd96 100644 --- a/examples/planning.ts +++ b/examples/planning.ts @@ -6,36 +6,35 @@ * For example, onboard a new hire from your ATS to your HRIS. */ -import { StackOneToolSet } from '../src'; - -const toolset = new StackOneToolSet(); - -const onboardWorkflow = await toolset.plan({ - key: 'custom_onboarding', - input: 'Onboard the last new hire from Teamtailor to Workday', - model: 'stackone-planner-latest', - tools: ['hris_*', 'ats_*'], - accountIds: ['teamtailor_account_id', 'workday_account_id'], - cache: true, // saves the plan to $HOME/.stackone/plans -}); - -await onboardWorkflow.execute(); - -/** - * or use as part of a larger agent (using AI SDK by Vercel) - */ - import { openai } from '@ai-sdk/openai'; import { generateText } from 'ai'; - -await generateText({ - model: openai('gpt-4o'), - prompt: 'You are a workplace agent, onboard the latest hires to our systems', - tools: onboardWorkflow.toAISDK(), - maxSteps: 3, -}); - -/* - * The planning model is in closed beta and only available to design partners. - * Apply for the waitlist [here](https://www.stackone.com/demo). - */ +import { StackOneToolSet } from '../src'; +import { ACCOUNT_IDS } from './constants'; + +export const planningModule = async (): Promise => { + const toolset = new StackOneToolSet(); + + const onboardWorkflow = await toolset.plan({ + key: 'custom_onboarding', + input: 'Onboard the last new hire from Teamtailor to Workday', + model: 'stackone-planner-latest', + tools: ['hris_*', 'ats_*'], + accountIds: [ACCOUNT_IDS.ATS, ACCOUNT_IDS.HRIS], + cache: true, // saves the plan to $HOME/.stackone/plans + }); + + await onboardWorkflow.execute(); + + /** + * or use as part of a larger agent (using AI SDK by Vercel) + */ + await generateText({ + model: openai('gpt-4o'), + prompt: 'You are a workplace agent, onboard the latest hires to our systems', + tools: onboardWorkflow.toAISDK(), + maxSteps: 3, + }); +}; + +console.log('Planning module is in closed beta and only available to design partners.'); +console.log('Apply for the waitlist [here](https://www.stackone.com/demo).'); diff --git a/package.json b/package.json index b5a647e..fff8b2c 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,10 @@ "build": "bun build ./src/index.ts --outdir ./dist --target node && tsc --emitDeclarationOnly", "rebuild": "bun run fetch:specs && bun run build", "prepublishOnly": "bun run rebuild", - "test": "bun test", + "test:unit": "bun test src", + "test:examples": "bun test examples", + "test:scripts": "bun test scripts", + "test": "bun run test:unit && bun run test:examples && bun run test:scripts", "fetch:specs": "bun run ./scripts/fetch-specs.ts && bun run format", "build:docs": "bun run ./scripts/build-docs.ts", "docs:serve": "mkdocs serve", diff --git a/scripts/build-docs.test.ts b/scripts/build-docs.spec.ts similarity index 100% rename from scripts/build-docs.test.ts rename to scripts/build-docs.spec.ts diff --git a/scripts/rename-derivation-to-transform.ts b/scripts/rename-derivation-to-transform.ts deleted file mode 100755 index 3927cab..0000000 --- a/scripts/rename-derivation-to-transform.ts +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/env bun - -/** - * Script to rename derivation terminology to parameter transformation terminology - * - * This script will: - * 1. Rename DerivationConfig to ParameterTransformer - * 2. Rename derivationFunctions to transforms - * 3. Rename deriveParameters to transformParameter - * 4. Rename derivationConfigs to transformers - * 5. Update all related variable names and comments - */ - -import { execSync } from 'node:child_process'; -import fs from 'node:fs'; -import path from 'node:path'; - -// Define the replacements -const replacements = [ - // Type and interface names - { from: /DerivationConfig/g, to: 'ParameterTransformer' }, - { from: /DerivationFunction/g, to: 'TransformFunction' }, - { from: /DerivationFunctions/g, to: 'TransformFunctions' }, - { from: /DerivationConfigMap/g, to: 'ParameterTransformerMap' }, - - // Property and method names - { from: /derivationFunctions/g, to: 'transforms' }, - { from: /derivationConfigs/g, to: 'transformers' }, - { from: /derivationConfig/g, to: 'transformer' }, - { from: /deriveParameters/g, to: 'transformParameter' }, - { from: /addDerivationConfig/g, to: 'addTransformer' }, - { from: /getDerivationConfig/g, to: 'getTransformer' }, - { from: /getDefaultDerivationConfigs/g, to: 'getDefaultTransformers' }, - - // Variable names - { from: /sourceParameter/g, to: 'sourceParameter' }, // Keep this the same - - // Comments and documentation - { from: /parameter derivation/g, to: 'parameter transformation' }, - { from: /Parameter Derivation/g, to: 'Parameter Transformation' }, - { from: /derived parameter/g, to: 'transformed parameter' }, - { from: /derive parameter/g, to: 'transform parameter' }, - { from: /Derive parameter/g, to: 'Transform parameter' }, -]; - -// Get all TypeScript files in the src directory -const getAllTsFiles = (dir: string): string[] => { - const files: string[] = []; - const items = fs.readdirSync(dir); - - for (const item of items) { - const fullPath = path.join(dir, item); - const stat = fs.statSync(fullPath); - - if (stat.isDirectory()) { - files.push(...getAllTsFiles(fullPath)); - } else if (item.endsWith('.ts')) { - files.push(fullPath); - } - } - - return files; -}; - -// Process a file with the replacements -const processFile = (filePath: string): void => { - console.log(`Processing ${filePath}...`); - let content = fs.readFileSync(filePath, 'utf-8'); - let changed = false; - - for (const replacement of replacements) { - const newContent = content.replace(replacement.from, replacement.to); - if (newContent !== content) { - content = newContent; - changed = true; - } - } - - if (changed) { - fs.writeFileSync(filePath, content); - console.log(`Updated ${filePath}`); - } -}; - -// Main function -const main = (): void => { - // Process src directory - const srcFiles = getAllTsFiles(path.join(process.cwd(), 'src')); - for (const file of srcFiles) { - processFile(file); - } - - // Process examples directory - const examplesFiles = getAllTsFiles(path.join(process.cwd(), 'examples')); - for (const file of examplesFiles) { - processFile(file); - } - - // Run tests to verify everything still works - console.log('\nRunning tests to verify changes...'); - try { - execSync('bun test', { stdio: 'inherit' }); - console.log('\nAll tests passed! The renaming was successful.'); - } catch (error) { - console.error('\nTests failed after renaming. Please check the errors above.'); - process.exit(1); - } -}; - -// Run the script -main(); \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 29e859d..cef2c09 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,9 +23,10 @@ export { export type { ExecuteConfig, ExecuteOptions, + Experimental_PreExecuteFunction, + Experimental_SchemaOverride, + Experimental_ToolCreationOptions, JsonDict, ParameterLocation, - ParameterTransformer, - ParameterTransformerMap, ToolDefinition, } from './types'; diff --git a/src/modules/parameterMapper.ts b/src/modules/parameterMapper.ts deleted file mode 100644 index 531bacb..0000000 --- a/src/modules/parameterMapper.ts +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Parameter derivation functions for StackOne tools - * - * This file contains functions to transform parameter values from other parameters, - * particularly for file uploads where we want to extract multiple values from a file path. - */ - -import type { JsonDict, ParameterTransformer, ParameterTransformerMap } from '../types'; -import { StackOneError } from '../utils/errors'; - -/** - * Handles parameter mapping and transformation - */ -export class ParameterMapper { - private transformers: ParameterTransformerMap; - - constructor(transformers?: ParameterTransformerMap) { - this.transformers = transformers || new Map(); - } - - /** - * Add a transformer for a parameter - */ - addTransformer(sourceParam: string, transformer: ParameterTransformer): void { - this.transformers.set(sourceParam, transformer); - } - - /** - * Get a transformer for a parameter - */ - getTransformer(sourceParam: string): ParameterTransformer | undefined { - return this.transformers.get(sourceParam); - } - - /** - * Map parameters from user input to API parameters - */ - mapParameters(userParams: JsonDict | string | undefined): JsonDict { - // If no parameters provided, return empty object - if (!userParams) return {}; - - // If parameters are provided as a string, parse them as JSON - const params = typeof userParams === 'string' ? JSON.parse(userParams) : userParams; - - // Create a copy of the parameters to avoid modifying the original - const mappedParams: JsonDict = { ...params }; - - // Process transformed parameters - for (const [sourceParam, config] of this.transformers.entries()) { - // Skip if source parameter is not present - if (!(sourceParam in params)) continue; - - // Get the source parameter value - const sourceValue = params[sourceParam]; - - // Process each derivation function - for (const targetParam of Object.keys(config.transforms)) { - try { - // Derive the parameter value - const derivedValues = transformParameter(sourceValue, targetParam, sourceParam, config); - - // Add derived values to mapped parameters - Object.assign(mappedParams, derivedValues); - } catch (error) { - // Log error but continue processing other parameters - console.error(`Error deriving parameter ${targetParam}:`, error); - } - } - - // Always remove source parameters after transformation - delete mappedParams[sourceParam]; - } - - return mappedParams; - } -} - -/** - * Apply derivation functions to derive a parameter from a source parameter - * - * @param sourceValue Value of the source parameter - * @param targetParam Name of the parameter to derive - * @param sourceParam Name of the source parameter - * @param transformer The derivation configuration containing derivation functions - * @returns Object with the transformed parameter value - */ -export const transformParameter = ( - sourceValue: unknown, - targetParam: string, - sourceParam: string, - transformer: ParameterTransformer -): JsonDict => { - const result: JsonDict = {}; - - // Get the derivation function for the target parameter - const deriveFn = transformer.transforms[targetParam]; - if (!deriveFn) return result; - - try { - const derivedValue = deriveFn(sourceValue); - if (derivedValue !== null) { - result[targetParam] = derivedValue; - } - } catch (error) { - throw new StackOneError( - `Error deriving parameter ${targetParam} from ${sourceParam}: ${error instanceof Error ? error.message : 'Unknown error'}` - ); - } - - return result; -}; diff --git a/src/modules/tests/parameterMapper.spec.ts b/src/modules/tests/parameterMapper.spec.ts deleted file mode 100644 index 6a85c71..0000000 --- a/src/modules/tests/parameterMapper.spec.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { describe, expect, it } from 'bun:test'; -import type { ParameterTransformer } from '../../types'; -import { StackOneError } from '../../utils/errors'; -import { ParameterMapper, transformParameter } from '../parameterMapper'; - -describe('ParameterMapper', () => { - it('should initialize with no transformers', () => { - const mapper = new ParameterMapper(); - expect(mapper).toBeDefined(); - }); - - it('should initialize with provided transformers', () => { - const transformers = new Map(); - transformers.set('sourceParam', { - transforms: { - targetParam: (value) => `transformed-${value}`, - }, - }); - - const mapper = new ParameterMapper(transformers); - expect(mapper).toBeDefined(); - expect(mapper.getTransformer('sourceParam')).toBeDefined(); - }); - - it('should add a transformer', () => { - const mapper = new ParameterMapper(); - - const transformer: ParameterTransformer = { - transforms: { - targetParam: (value) => `transformed-${value}`, - }, - }; - - mapper.addTransformer('sourceParam', transformer); - - const retrievedTransformer = mapper.getTransformer('sourceParam'); - expect(retrievedTransformer).toBe(transformer); - }); - - it('should map parameters without transformations', () => { - const mapper = new ParameterMapper(); - const params = { param1: 'value1', param2: 'value2' }; - - const result = mapper.mapParameters(params); - - expect(result).toEqual(params); - }); - - it('should map parameters with transformations', () => { - const transformers = new Map(); - transformers.set('sourceParam', { - transforms: { - targetParam: (value) => `transformed-${value}`, - }, - }); - - const mapper = new ParameterMapper(transformers); - const params = { sourceParam: 'value', otherParam: 'not-transformed' }; - - const result = mapper.mapParameters(params); - - expect(result).toEqual({ - otherParam: 'not-transformed', - targetParam: 'transformed-value', - }); - }); - - it('should handle parameters provided as a JSON string', () => { - const mapper = new ParameterMapper(); - const paramsString = JSON.stringify({ param1: 'value1', param2: 'value2' }); - - const result = mapper.mapParameters(paramsString); - - expect(result).toEqual({ param1: 'value1', param2: 'value2' }); - }); - - it('should handle undefined parameters', () => { - const mapper = new ParameterMapper(); - const result = mapper.mapParameters(undefined); - - expect(result).toEqual({}); - }); - - it('should skip transformation if source parameter is not present', () => { - const transformers = new Map(); - transformers.set('sourceParam', { - transforms: { - targetParam: (value) => `transformed-${value}`, - }, - }); - - const mapper = new ParameterMapper(transformers); - const params = { otherParam: 'value' }; - - const result = mapper.mapParameters(params); - - expect(result).toEqual({ otherParam: 'value' }); - expect(result).not.toHaveProperty('targetParam'); - }); - - it('should handle multiple target parameters from a single source', () => { - const transformers = new Map(); - transformers.set('sourceParam', { - transforms: { - targetParam1: (value) => `${value}-1`, - targetParam2: (value) => `${value}-2`, - }, - }); - - const mapper = new ParameterMapper(transformers); - const params = { sourceParam: 'value' }; - - const result = mapper.mapParameters(params); - - expect(result).toEqual({ - targetParam1: 'value-1', - targetParam2: 'value-2', - }); - }); - - it('should handle multiple source parameters with transformations', () => { - const transformers = new Map(); - - transformers.set('sourceParam1', { - transforms: { - targetParam1: (value) => `${value}-1`, - }, - }); - - transformers.set('sourceParam2', { - transforms: { - targetParam2: (value) => `${value}-2`, - }, - }); - - const mapper = new ParameterMapper(transformers); - const params = { sourceParam1: 'value1', sourceParam2: 'value2' }; - - const result = mapper.mapParameters(params); - - expect(result).toEqual({ - targetParam1: 'value1-1', - targetParam2: 'value2-2', - }); - }); -}); - -describe('transformParameter', () => { - it('should transform a parameter correctly', () => { - const sourceValue = 'test'; - const targetParam = 'target'; - const sourceParam = 'source'; - const transformer: ParameterTransformer = { - transforms: { - target: (value) => `transformed-${value}`, - }, - }; - - const result = transformParameter(sourceValue, targetParam, sourceParam, transformer); - - expect(result).toEqual({ target: 'transformed-test' }); - }); - - it('should return empty object if transformer for target param does not exist', () => { - const sourceValue = 'test'; - const targetParam = 'nonExistentTarget'; - const sourceParam = 'source'; - const transformer: ParameterTransformer = { - transforms: { - target: (value) => `transformed-${value}`, - }, - }; - - const result = transformParameter(sourceValue, targetParam, sourceParam, transformer); - - expect(result).toEqual({}); - }); - - it('should handle null return from transformation function', () => { - const sourceValue = 'test'; - const targetParam = 'target'; - const sourceParam = 'source'; - const transformer: ParameterTransformer = { - transforms: { - target: () => null, - }, - }; - - const result = transformParameter(sourceValue, targetParam, sourceParam, transformer); - - expect(result).toEqual({}); - }); - - it('should throw StackOneError when transformation function throws', () => { - const sourceValue = 'test'; - const targetParam = 'target'; - const sourceParam = 'source'; - const transformer: ParameterTransformer = { - transforms: { - target: () => { - throw new Error('Transformation error'); - }, - }, - }; - - expect(() => { - transformParameter(sourceValue, targetParam, sourceParam, transformer); - }).toThrow(StackOneError); - }); -}); diff --git a/src/tests/transformations.spec.ts b/src/tests/transformations.spec.ts deleted file mode 100644 index 1458c70..0000000 --- a/src/tests/transformations.spec.ts +++ /dev/null @@ -1,539 +0,0 @@ -/** - * Tests for parameter transformation functions - */ - -import { - type Mock, - afterAll, - afterEach, - beforeAll, - beforeEach, - describe, - expect, - it, - mock, - spyOn, -} from 'bun:test'; -import fs from 'node:fs'; -import os from 'node:os'; -import path from 'node:path'; -import { transformParameter } from '../modules/parameterMapper'; -import type { Tools } from '../tool'; -import { OpenAPIToolSet } from '../toolsets'; -import type { ParameterTransformer } from '../types'; -import { mockFetch } from './utils/fetch-mock'; - -describe('Parameter Transformations', () => { - // Create a test file for derivation tests - const testFileContent = 'Test file content'; - const testFilePath = path.join(import.meta.dir, 'test-file.txt'); - - // Create the test file before tests - beforeAll(() => { - fs.writeFileSync(testFilePath, testFileContent); - }); - - // Remove the test file after tests - afterAll(() => { - fs.unlinkSync(testFilePath); - }); - - // Create a test derivation config - const testParameterTransformer: ParameterTransformer = { - transforms: { - derived_param1: (value: unknown): string => { - if (typeof value !== 'string') { - throw new Error('Value must be a string'); - } - return `derived_${value}`; - }, - derived_param2: (value: unknown): string => { - if (typeof value !== 'string') { - throw new Error('Value must be a string'); - } - return `${value}_derived`; - }, - }, - }; - - describe('transformParameter', () => { - it('should derive multiple parameters from a source parameter', () => { - // Test data - const sourceParam = 'source_param'; - const sourceValue = 'test_value'; - - // Transform parameters for derived_param1 - const result1 = transformParameter( - sourceValue, - 'derived_param1', - sourceParam, - testParameterTransformer - ); - - // Transform parameters for derived_param2 - const result2 = transformParameter( - sourceValue, - 'derived_param2', - sourceParam, - testParameterTransformer - ); - - // Verify derived parameters - expect(result1).toHaveProperty('derived_param1', 'derived_test_value'); - expect(result2).toHaveProperty('derived_param2', 'test_value_derived'); - }); - - it('should handle unknown parameters gracefully', () => { - // Test with a parameter that doesn't exist - const result = transformParameter( - 'test_value', - 'nonexistent_param', - 'source_param', - testParameterTransformer - ); - - // Verify no parameters were added - expect(Object.keys(result).length).toBe(0); - }); - - it('should handle errors in derivation functions', () => { - // Create a derivation config with a function that throws - const errorConfig: ParameterTransformer = { - transforms: { - error_param: (_value: unknown): string => { - throw new Error('Test error'); - }, - success_param: (value: unknown): string => { - if (typeof value !== 'string') { - throw new Error('Value must be a string'); - } - return `success_${value}`; - }, - }, - }; - - // Test data - const sourceValue = 'test_value'; - const sourceParam = 'source_param'; - - // Transform parameters for success_param - const successResult = transformParameter( - sourceValue, - 'success_param', - sourceParam, - errorConfig - ); - - // Verify success parameter is present - expect(successResult).toHaveProperty('success_param', 'success_test_value'); - - // Verify error parameter throws - expect(() => - transformParameter(sourceValue, 'error_param', sourceParam, errorConfig) - ).toThrow(); - }); - }); -}); - -describe('Parameter Transformation Edge Cases', () => { - // Create a temporary directory and file for tests - let tempDir: string; - let tempSpecFile: string; - let fetchMock: ReturnType; - let mockTool: { - execute: Mock< - ( - params: Record, - options?: unknown - ) => Promise<{ - mappedParams: Record; - url: string; - method: string; - headers: Record; - body: unknown; - originalParams: Record; - }> - >; - }; - - // Set up before each test - beforeEach(() => { - // Create a temporary directory - tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'openapi-test-')); - - // Create a temporary spec file - tempSpecFile = path.join(tempDir, 'test-spec.json'); - - // Write a minimal OpenAPI spec to the file - fs.writeFileSync( - tempSpecFile, - JSON.stringify({ - openapi: '3.0.0', - info: { - title: 'Test API', - version: '1.0.0', - }, - paths: { - '/test': { - post: { - operationId: 'test_derivation', - parameters: [ - { - name: 'source_param', - in: 'query', - schema: { - type: 'string', - }, - }, - ], - responses: { - '200': { - description: 'OK', - }, - }, - }, - }, - }, - }) - ); - - // Set up fetch mock - fetchMock = mockFetch({ - defaultResponse: { - ok: true, - json: async () => ({ success: true }), - }, - }); - - // Create a mock tool with an execute method - mockTool = { - execute: mock(async (params, _options) => { - return { - mappedParams: params, - url: 'https://example.com/api', - method: 'POST', - headers: {}, - body: null, - originalParams: params, - }; - }), - }; - - // Mock the OpenAPIToolSet.getTools method - spyOn(OpenAPIToolSet.prototype, 'getTools').mockImplementation(() => { - return { - getTool: mock(() => mockTool), - } as unknown as Tools; - }); - }); - - // Clean up after each test - afterEach(() => { - // Restore fetch mock - fetchMock.restore(); - - // Clean up temporary files - try { - fs.unlinkSync(tempSpecFile); - fs.rmdirSync(tempDir, { recursive: true }); - } catch (error) { - console.error('Error cleaning up temp files:', error); - } - }); - - describe('Empty derivation configs', () => { - it('should handle empty derivation configs map', async () => { - // Create OpenAPIToolSet with empty derivation configs - const toolset = new OpenAPIToolSet({ - filePath: tempSpecFile, - transformers: new Map(), - }); - - // Get test tool - const tools = toolset.getTools(); - const testTool = tools.getTool('test_derivation'); - - expect(testTool).toBeDefined(); - if (!testTool) return; - - // Execute tool with dry run - await testTool.execute({ source_param: 'test_value' }, { dryRun: true }); - - // Verify the execute method was called with the correct parameters - expect(mockTool.execute).toHaveBeenCalledWith( - { source_param: 'test_value' }, - { dryRun: true } - ); - }); - - it('should handle derivation config with no derivation functions', async () => { - // Create a transformation config with no transformation functions - const emptyConfig: ParameterTransformer = { - transforms: {}, - }; - - // Create a map of transformation configs - const transformers = new Map(); - transformers.set('source_param', emptyConfig); - - // Create OpenAPIToolSet with transformation configs - const toolset = new OpenAPIToolSet({ - filePath: tempSpecFile, - transformers, - }); - - // Get test tool - const tools = toolset.getTools(); - const testTool = tools.getTool('test_derivation'); - - expect(testTool).toBeDefined(); - if (!testTool) return; - - // Execute tool with dry run - await testTool.execute({ source_param: 'test_value' }, { dryRun: true }); - - // Verify the execute method was called with the correct parameters - expect(mockTool.execute).toHaveBeenCalledWith( - { source_param: 'test_value' }, - { dryRun: true } - ); - }); - }); - - describe('Invalid transformation configs', () => { - it('should handle transformation config with invalid source parameter', async () => { - // Create a transformation config with a non-existent source parameter - const invalidConfig: ParameterTransformer = { - transforms: { - derived_param1: (value: unknown): string => { - if (typeof value !== 'string') { - throw new Error('Value must be a string'); - } - return `derived_${value}`; - }, - }, - }; - - // Create a map of transformation configs - const transformers = new Map(); - transformers.set('non_existent_param', invalidConfig); - - // Create OpenAPIToolSet with transformation configs - const toolset = new OpenAPIToolSet({ - filePath: tempSpecFile, - transformers, - }); - - // Get test tool - const tools = toolset.getTools(); - const testTool = tools.getTool('test_derivation'); - - expect(testTool).toBeDefined(); - if (!testTool) return; - - // Execute tool with dry run - await testTool.execute({ source_param: 'test_value' }, { dryRun: true }); - - // Verify the execute method was called with the correct parameters - expect(mockTool.execute).toHaveBeenCalledWith( - { source_param: 'test_value' }, - { dryRun: true } - ); - }); - }); - - describe('Error handling in transformation functions', () => { - it('should handle one transformation function failing while others succeed', async () => { - // Create a transformation config with mixed success/failure - const mixedConfig: ParameterTransformer = { - transforms: { - derived_param1: (_value: unknown): string => { - throw new Error('Error in derived_param1'); - }, - derived_param2: (_value: unknown): string => { - return 'derived_value'; - }, - }, - }; - - // Create a map of transformation configs - const transformers = new Map(); - transformers.set('source_param', mixedConfig); - - // Create OpenAPIToolSet with transformation configs - const toolset = new OpenAPIToolSet({ - filePath: tempSpecFile, - transformers, - }); - - // Get test tool - const tools = toolset.getTools(); - const testTool = tools.getTool('test_derivation'); - - expect(testTool).toBeDefined(); - if (!testTool) return; - - // Execute tool with dry run - await testTool.execute({ source_param: 'test_value' }, { dryRun: true }); - - // Verify the execute method was called with the correct parameters - expect(mockTool.execute).toHaveBeenCalledWith( - { source_param: 'test_value' }, - { dryRun: true } - ); - }); - - it('should handle all transformation functions failing', async () => { - // Create a transformation config with all functions that throw - const errorConfig: ParameterTransformer = { - transforms: { - derived_param1: (_value: unknown): string => { - throw new Error('Error in derived_param1'); - }, - derived_param2: (_value: unknown): string => { - throw new Error('Error in derived_param2'); - }, - }, - }; - - // Create a map of transformation configs - const transformers = new Map(); - transformers.set('source_param', errorConfig); - - // Create OpenAPIToolSet with transformation configs - const toolset = new OpenAPIToolSet({ - filePath: tempSpecFile, - transformers, - }); - - // Get test tool - const tools = toolset.getTools(); - const testTool = tools.getTool('test_derivation'); - - expect(testTool).toBeDefined(); - if (!testTool) return; - - // Execute tool with dry run - await testTool.execute({ source_param: 'test_value' }, { dryRun: true }); - - // Verify the execute method was called with the correct parameters - expect(mockTool.execute).toHaveBeenCalledWith( - { source_param: 'test_value' }, - { dryRun: true } - ); - }); - }); - - describe('Nested derivations', () => { - it('should handle nested derivations', async () => { - // Create a first-level derivation config - const firstLevelConfig: ParameterTransformer = { - transforms: { - nested_source: (value: unknown): string => { - if (typeof value !== 'string') { - throw new Error('Value must be a string'); - } - return `nested_${value}`; - }, - }, - }; - - // Create a second-level derivation config - const secondLevelConfig: ParameterTransformer = { - transforms: { - nested_derived: (value: unknown): string => { - if (typeof value !== 'string') { - throw new Error('Value must be a string'); - } - return `derived_from_${value}`; - }, - }, - }; - - // Create a map of derivation configs - const transformers = new Map(); - transformers.set('source_param', firstLevelConfig); - transformers.set('nested_source', secondLevelConfig); - - // Create a mock OpenAPIToolSet with the transformers - const toolset = new OpenAPIToolSet({ - filePath: tempSpecFile, - transformers, - }); - - // Get test tool - const tools = toolset.getTools(); - const testTool = tools.getTool('test_derivation'); - - expect(testTool).toBeDefined(); - if (!testTool) return; - - // Execute tool with dry run - await testTool.execute({ source_param: 'test_value' }, { dryRun: true }); - - // Verify the execute method was called with the correct parameters - expect(mockTool.execute).toHaveBeenCalledWith( - { source_param: 'test_value' }, - { dryRun: true } - ); - }); - }); - - describe('Conflicting derivations', () => { - it('should handle conflicting derivation configs', async () => { - // Create a derivation config for the first parameter - const config1: ParameterTransformer = { - transforms: { - derived_param: (value: unknown): string => { - if (typeof value !== 'string') { - throw new Error('Value must be a string'); - } - return `derived_from_source_${value}`; - }, - }, - }; - - // Create a derivation config for the second parameter - const config2: ParameterTransformer = { - transforms: { - derived_param: (value: unknown): string => { - if (typeof value !== 'string') { - throw new Error('Value must be a string'); - } - return `derived_from_other_${value}`; - }, - }, - }; - - // Create a map of derivation configs - const transformers = new Map(); - transformers.set('source_param', config1); - transformers.set('other_param', config2); - - // Create a mock OpenAPIToolSet with the transformers - const toolset = new OpenAPIToolSet({ - filePath: tempSpecFile, - transformers, - }); - - // Get test tool - const tools = toolset.getTools(); - const testTool = tools.getTool('test_derivation'); - - expect(testTool).toBeDefined(); - if (!testTool) return; - - // Execute tool with dry run - await testTool.execute( - { source_param: 'test_value', other_param: 'other_value' }, - { dryRun: true } - ); - - // Verify the execute method was called with the correct parameters - expect(mockTool.execute).toHaveBeenCalledWith( - { source_param: 'test_value', other_param: 'other_value' }, - { dryRun: true } - ); - }); - }); -}); diff --git a/src/tool.ts b/src/tool.ts index 5c230e2..1672c40 100644 --- a/src/tool.ts +++ b/src/tool.ts @@ -1,13 +1,12 @@ import { type ToolSet, jsonSchema } from 'ai'; import type { ChatCompletionTool } from 'openai/resources/chat/completions'; -import { ParameterMapper } from './modules/parameterMapper'; import { RequestBuilder } from './modules/requestBuilder'; import type { ExecuteConfig, ExecuteOptions, + Experimental_PreExecuteFunction, + Experimental_ToolCreationOptions, JsonDict, - ParameterTransformer, - ParameterTransformerMap, ToolParameters, } from './types'; import { StackOneError } from './utils/errors'; @@ -21,8 +20,8 @@ export class BaseTool { description: string; parameters: ToolParameters; executeConfig: ExecuteConfig; - protected parameterMapper: ParameterMapper; protected requestBuilder: RequestBuilder; + protected experimental_preExecute?: Experimental_PreExecuteFunction; constructor( name: string, @@ -30,28 +29,14 @@ export class BaseTool { parameters: ToolParameters, executeConfig: ExecuteConfig, headers?: Record, - transformers?: ParameterTransformerMap + experimental_preExecute?: Experimental_PreExecuteFunction ) { this.name = name; this.description = description; this.parameters = parameters; this.executeConfig = executeConfig; - this.parameterMapper = new ParameterMapper(transformers); this.requestBuilder = new RequestBuilder(executeConfig, headers); - } - - /** - * Add a parameter transformer - */ - public setParameterTransformer(sourceParam: string, config: ParameterTransformer): void { - this.parameterMapper.addTransformer(sourceParam, config); - } - - /** - * Get a parameter transformer - */ - public getParameterTransformer(sourceParam: string): ParameterTransformer | undefined { - return this.parameterMapper.getTransformer(sourceParam); + this.experimental_preExecute = experimental_preExecute; } /** @@ -85,11 +70,18 @@ export class BaseTool { ); } - // Map parameters from user input to API parameters - const mappedParams = this.parameterMapper.mapParameters(inputParams); + // Convert string params to object + const params = typeof inputParams === 'string' ? JSON.parse(inputParams) : inputParams || {}; + + // Apply experimental preExecute function (either from tool creation or execution options) + let processedParams = params; + + if (this.experimental_preExecute) { + processedParams = await this.experimental_preExecute(params); + } - // Execute the request - return await this.requestBuilder.execute(mappedParams, options); + // Execute the request directly with processed parameters + return await this.requestBuilder.execute(processedParams, options); } catch (error) { if (error instanceof StackOneError) { throw error; @@ -187,8 +179,46 @@ export class Tools implements Iterable { /** * Get a tool by name */ - getTool(name: string): BaseTool | undefined { - return this.tools.find((tool) => tool.name === name); + getTool(name: string, options?: Experimental_ToolCreationOptions): BaseTool | undefined { + const originalTool = this.tools.find((tool) => tool.name === name); + if (!originalTool) { + return undefined; + } + + // If no experimental options provided, return original tool + if (!options?.experimental_schemaOverride && !options?.experimental_preExecute) { + return originalTool; + } + + // Create a new tool with experimental schema override and preExecute + let parameters = originalTool.parameters; + + // Apply schema override if provided + if (options.experimental_schemaOverride) { + parameters = options.experimental_schemaOverride(originalTool.parameters); + } + + // Create new tool instance with modified schema and preExecute function + if (originalTool instanceof StackOneTool) { + const newTool = new StackOneTool( + originalTool.name, + originalTool.description, + parameters, + originalTool.executeConfig, + originalTool.getHeaders(), + options.experimental_preExecute + ); + return newTool; + } + const newTool = new BaseTool( + originalTool.name, + originalTool.description, + parameters, + originalTool.executeConfig, + originalTool.getHeaders(), + options.experimental_preExecute + ); + return newTool; } /** diff --git a/src/toolsets/base.ts b/src/toolsets/base.ts index b9e4f87..982dd7a 100644 --- a/src/toolsets/base.ts +++ b/src/toolsets/base.ts @@ -1,10 +1,5 @@ import { type BaseTool, Tools } from '../tool'; -import { - ParameterLocation, - type ParameterTransformer, - type ParameterTransformerMap, - type ToolDefinition, -} from '../types'; +import type { Experimental_ToolCreationOptions } from '../types'; /** * Base exception for toolset errors @@ -56,7 +51,6 @@ export interface BaseToolSetConfig { baseUrl?: string; authentication?: AuthenticationConfig; headers?: Record; - transformers?: ParameterTransformerMap; _oasUrl?: string; } @@ -68,7 +62,6 @@ export abstract class ToolSet { protected authentication?: AuthenticationConfig; protected headers: Record; protected tools: BaseTool[] = []; - protected transformers: ParameterTransformerMap; /** * Initialize a toolset with optional configuration @@ -78,7 +71,6 @@ export abstract class ToolSet { this.baseUrl = config?.baseUrl; this.authentication = config?.authentication; this.headers = config?.headers || {}; - this.transformers = new Map(config?.transformers || []); // Set Authentication headers if provided if (this.authentication) { @@ -113,15 +105,6 @@ export abstract class ToolSet { } } - /** - * Add a parameter transformer to the toolset - * @param sourceParam Source parameter name - * @param config Transformer configuration - */ - public setParameterTransformer(sourceParam: string, config: ParameterTransformer): void { - this.transformers.set(sourceParam, config); - } - /** * Check if a tool name matches a filter pattern * @param toolName Tool name to check @@ -206,74 +189,48 @@ export abstract class ToolSet { * @param headers Optional headers to apply to the tool * @returns Tool instance */ - getTool(name: string, headers?: Record): BaseTool { + getTool(name: string, headers?: Record): BaseTool; + getTool(name: string, options: Experimental_ToolCreationOptions): BaseTool; + getTool( + name: string, + headersOrOptions?: Record | Experimental_ToolCreationOptions + ): BaseTool { const tool = this.tools.find((tool) => tool.name === name); if (!tool) { throw new ToolSetError(`Tool with name ${name} not found`); } - const mergedHeaders = { ...this.headers, ...headers }; - if (mergedHeaders && tool.setHeaders) { - tool.setHeaders(mergedHeaders); - } - return tool; - } - /** - * Process transformed parameters in a tool definition - * @param toolDef Tool definition to process - * @returns Updated tool definition with transformed parameters - */ - protected processDerivedValues(toolDef: ToolDefinition): ToolDefinition { - // Create a copy of the tool definition to avoid modifying the original - const processedDef = { ...toolDef }; - - // Process each parameter in the execute config - for (const param of processedDef.execute.params) { - // Skip parameters that are already derived - if (param.derivedFrom) continue; - - // Check if this parameter is a source for any derivation config - if (this.transformers.has(param.name)) { - const config = this.transformers.get(param.name); - - // Only proceed if config exists - if (config) { - // Add transformed parameters to the tool definition - for (const targetParam of Object.keys(config.transforms)) { - // Skip if the parameter already exists in execute params - if (processedDef.execute.params.some((p) => p.name === targetParam)) continue; - - // Add the transformed parameter to execute params - processedDef.execute.params.push({ - name: targetParam, - location: this.determineParameterLocation(targetParam), - type: param.type, - derivedFrom: param.name, - }); - } - } - } - } + // Determine if the second parameter is headers or experimental options + const isExperimentalOptions = + headersOrOptions && + ('experimental_schemaOverride' in headersOrOptions || + 'experimental_preExecute' in headersOrOptions); - return processedDef; - } + if (isExperimentalOptions) { + const options = headersOrOptions as Experimental_ToolCreationOptions; - /** - * Determine the location of a parameter - * @param paramName Parameter name - * @returns Parameter location (HEADER, QUERY, PATH, or BODY) - */ - protected determineParameterLocation(paramName: string): ParameterLocation { - // Check if the parameter exists in any of the tools - for (const tool of this.tools) { - // Check if the parameter exists in the execute config - const param = tool.executeConfig.params.find((p) => p.name === paramName); - if (param) { - return param.location; + // Get the tools collection and use its getTool method with experimental options + const toolsCollection = new Tools([tool]); + const experimentalTool = toolsCollection.getTool(name, options); + + if (!experimentalTool) { + throw new ToolSetError(`Tool with name ${name} not found`); + } + + // Apply instance headers to the tool + if (this.headers && experimentalTool.setHeaders) { + experimentalTool.setHeaders(this.headers); } + + return experimentalTool; } - // Default to BODY if not found - return ParameterLocation.BODY; + // Traditional headers-based approach + const headers = headersOrOptions as Record | undefined; + const mergedHeaders = { ...this.headers, ...headers }; + if (mergedHeaders && tool.setHeaders) { + tool.setHeaders(mergedHeaders); + } + return tool; } } diff --git a/src/toolsets/openapi.ts b/src/toolsets/openapi.ts index a9a0fb1..45c017e 100644 --- a/src/toolsets/openapi.ts +++ b/src/toolsets/openapi.ts @@ -39,7 +39,6 @@ export class OpenAPIToolSet extends ToolSet { baseUrl: config?.baseUrl, authentication: config?.authentication, headers: config?.headers, - transformers: config?.transformers, }); if ('filePath' in config) { @@ -67,7 +66,6 @@ export class OpenAPIToolSet extends ToolSet { baseUrl: config.baseUrl, authentication: config.authentication, headers: config.headers, - transformers: config.transformers, _oasUrl: config.url, }); @@ -88,11 +86,8 @@ export class OpenAPIToolSet extends ToolSet { // Process each tool for (const [toolName, toolDef] of Object.entries(tools)) { - // Process derived values - const processedDef = this.processDerivedValues(toolDef); - // Create tool - const tool = this.createTool(toolName, processedDef); + const tool = this.createTool(toolName, toolDef); // Add tool to the list this.tools.push(tool); @@ -116,11 +111,8 @@ export class OpenAPIToolSet extends ToolSet { // Process each tool for (const [toolName, toolDef] of Object.entries(tools)) { - // Process derived values - const processedDef = this.processDerivedValues(toolDef); - // Create tool - const tool = this.createTool(toolName, processedDef); + const tool = this.createTool(toolName, toolDef); // Add tool to the list this.tools.push(tool); @@ -141,14 +133,7 @@ export class OpenAPIToolSet extends ToolSet { private createTool(toolName: string, toolDef: ToolDefinition): BaseTool { // Create tool const { description, parameters, execute } = toolDef; - const tool = new BaseTool( - toolName, - description, - parameters, - execute, - this.headers, - this.transformers - ); + const tool = new BaseTool(toolName, description, parameters, execute, this.headers); return tool; } diff --git a/src/toolsets/stackone.ts b/src/toolsets/stackone.ts index 423f3b0..907b5a9 100644 --- a/src/toolsets/stackone.ts +++ b/src/toolsets/stackone.ts @@ -1,9 +1,8 @@ import { loadStackOneSpecs } from '../openapi/loader'; import { StackOneTool, type Tools } from '../tool'; -import type { ParameterTransformer, ToolDefinition } from '../types'; -import { extractFileInfo, isValidFilePath, readFileAsBase64 } from '../utils/file'; +import type { ToolDefinition } from '../types'; import { removeJsonSchemaProperty } from '../utils/schema'; -import { type BaseToolSetConfig, ToolSet, ToolSetConfigError, ToolSetError } from './base'; +import { type BaseToolSetConfig, ToolSet, ToolSetConfigError } from './base'; /** * Configuration for StackOne toolset @@ -76,65 +75,15 @@ export class StackOneToolSet extends ToolSet { baseUrl: config?.baseUrl, authentication, headers, - transformers: config?.transformers, }); this.accountId = accountId; this._removedParams = ['source_value']; - // Add default parameter transformers - const defaultTransformers = StackOneToolSet.getDefaultParameterTransformers(); - for (const [sourceParam, config] of defaultTransformers.entries()) { - this.setParameterTransformer(sourceParam, config); - } - // Load tools this.loadTools(); } - /** - * Get the default derivation configurations for StackOne tools - */ - private static getDefaultParameterTransformers(): Map { - const transformers = new Map(); - - // File path derivation config - transformers.set('file_path', { - transforms: { - content: (filePath: unknown): string => { - if (typeof filePath !== 'string') { - throw new ToolSetError('file_path must be a string'); - } - - if (!isValidFilePath(filePath)) { - throw new ToolSetError(`Invalid file path or file not found: ${filePath}`); - } - - return readFileAsBase64(filePath); - }, - name: (filePath: unknown): string => { - if (typeof filePath !== 'string') { - throw new ToolSetError('file_path must be a string'); - } - - const { fileName } = extractFileInfo(filePath); - return fileName; - }, - file_format: (filePath: unknown): { value: string } => { - if (typeof filePath !== 'string') { - throw new ToolSetError('file_path must be a string'); - } - - // get the file extension - const { extension } = extractFileInfo(filePath); - return { value: extension || '' }; - }, - }, - }); - - return transformers; - } - /** * Get StackOne tools matching a filter pattern * @param filterPattern Optional glob pattern or array of patterns to filter tools @@ -173,25 +122,18 @@ export class StackOneToolSet extends ToolSet { for (const [_, tools] of Object.entries(specs)) { // Process each tool for (const [toolName, toolDef] of Object.entries(tools)) { - // Process derived values - const processedDef = this.processDerivedValues(toolDef); - // Remove account ID parameter if not provided if (!this.accountId) { - this.removeAccountIdParameter(processedDef); + this.removeAccountIdParameter(toolDef); } - // Add transformation source parameters to the tool's parameters schema - this.addTransformationSourceParameters(processedDef); - // Create tool const tool = new StackOneTool( toolName, - processedDef.description, - processedDef.parameters, - processedDef.execute, - this.headers, - this.transformers + toolDef.description, + toolDef.parameters, + toolDef.execute, + this.headers ); // Add tool to the list @@ -217,27 +159,4 @@ export class StackOneToolSet extends ToolSet { ); } } - - /** - * Add transformation source parameters to the tool's parameters schema - * This ensures parameters like file_path are included in the schema for model consumption - * @param toolDef Tool definition to modify - */ - private addTransformationSourceParameters(toolDef: ToolDefinition): void { - // Skip if there are no transformers or no parameters - if (!this.transformers || !toolDef.parameters.properties) return; - - // Add each transformer source parameter to the schema - for (const [sourceParam, _] of this.transformers.entries()) { - // Skip if the parameter is already in the schema - if (sourceParam in toolDef.parameters.properties) continue; - - // Add the parameter to the schema - toolDef.parameters.properties[sourceParam] = { - type: 'string', - description: - 'Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.', - }; - } - } } diff --git a/src/toolsets/tests/__snapshots__/stackone.spec.ts.snap b/src/toolsets/tests/__snapshots__/stackone.spec.ts.snap index dd85573..0f54f11 100644 --- a/src/toolsets/tests/__snapshots__/stackone.spec.ts.snap +++ b/src/toolsets/tests/__snapshots__/stackone.spec.ts.snap @@ -47,20 +47,8 @@ Tools { ], "url": "https://api.stackone.com/unified/hris/companies", }, + "experimental_preExecute": undefined, "name": "hris_list_companies", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -68,10 +56,6 @@ Map { "example": "id,remote_id,name,full_name,display_name,created_at,updated_at", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "filter": { "description": "Filter parameters that allow greater customisation of the list response", "properties": { @@ -186,20 +170,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/companies/{id}", }, + "experimental_preExecute": undefined, "name": "hris_get_company", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -207,10 +179,6 @@ Map { "example": "id,remote_id,name,full_name,display_name,created_at,updated_at", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -310,20 +278,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/custom_field_definitions/employees", }, + "experimental_preExecute": undefined, "name": "hris_list_employee_custom_field_definitions", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -331,10 +287,6 @@ Map { "example": "id,remote_id,name,description,type,options", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "filter": { "description": "Filter parameters that allow greater customisation of the list response", "properties": { @@ -464,20 +416,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/custom_field_definitions/employees/{id}", }, + "experimental_preExecute": undefined, "name": "hris_get_employee_custom_field_definition", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -485,10 +425,6 @@ Map { "example": "id,remote_id,name,description,type,options", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "filter": { "description": "Filter parameters that allow greater customisation of the list response", "properties": { @@ -633,20 +569,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees", }, + "experimental_preExecute": undefined, "name": "hris_list_employees", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "expand": { @@ -659,10 +583,6 @@ Map { "example": "id,remote_id,first_name,last_name,name,display_name,gender,ethnicity,date_of_birth,birthday,marital_status,avatar_url,avatar,personal_email,personal_phone_number,work_email,work_phone_number,job_id,remote_job_id,job_title,job_description,department_id,remote_department_id,department,cost_centers,company,manager_id,remote_manager_id,hire_date,start_date,tenure,work_anniversary,employment_type,employment_contract_type,employment_status,termination_date,company_name,company_id,remote_company_id,preferred_language,citizenships,home_location,work_location,employments,custom_fields,documents,created_at,updated_at,benefits,employee_number,national_identity_number,national_identity_numbers,skills", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "filter": { "description": "HRIS Employees filters", "properties": { @@ -960,20 +880,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees", }, + "experimental_preExecute": undefined, "name": "hris_create_employee", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "avatar": { @@ -1635,10 +1543,6 @@ Map { }, "type": "object", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "first_name": { "description": "The employee first name", "example": "Isaac", @@ -3216,20 +3120,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}", }, + "experimental_preExecute": undefined, "name": "hris_get_employee", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "expand": { @@ -3242,10 +3134,6 @@ Map { "example": "id,remote_id,first_name,last_name,name,display_name,gender,ethnicity,date_of_birth,birthday,marital_status,avatar_url,avatar,personal_email,personal_phone_number,work_email,work_phone_number,job_id,remote_job_id,job_title,job_description,department_id,remote_department_id,department,cost_centers,company,manager_id,remote_manager_id,hire_date,start_date,tenure,work_anniversary,employment_type,employment_contract_type,employment_status,termination_date,company_name,company_id,remote_company_id,preferred_language,citizenships,home_location,work_location,employments,custom_fields,documents,created_at,updated_at,benefits,employee_number,national_identity_number,national_identity_numbers,skills", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -3510,20 +3398,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}", }, + "experimental_preExecute": undefined, "name": "hris_update_employee", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "avatar": { @@ -4163,10 +4039,6 @@ Map { }, "type": "object", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "first_name": { "description": "The employee first name", "example": "Isaac", @@ -5729,26 +5601,10 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/invite", }, + "experimental_preExecute": undefined, "name": "hris_invite_employee", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -5847,20 +5703,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/time_off", }, + "experimental_preExecute": undefined, "name": "hris_list_employee_time_off_requests", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "expand": { @@ -5873,10 +5717,6 @@ Map { "example": "id,remote_id,employee_id,remote_employee_id,approver_id,remote_approver_id,status,type,start_date,end_date,start_half_day,end_half_day,time_off_policy_id,remote_time_off_policy_id,reason,duration,created_at,updated_at,policy", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "filter": { "description": "HRIS Time Off filters", "properties": { @@ -6043,20 +5883,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/time_off", }, + "experimental_preExecute": undefined, "name": "hris_create_employee_time_off_request", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "approver_id": { @@ -6086,10 +5914,6 @@ Map { }, ], }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -6282,20 +6106,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/time_off/{subResourceId}", }, + "experimental_preExecute": undefined, "name": "hris_get_employees_time_off_request", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "expand": { @@ -6308,10 +6120,6 @@ Map { "example": "id,remote_id,employee_id,remote_employee_id,approver_id,remote_approver_id,status,type,start_date,end_date,start_half_day,end_half_day,time_off_policy_id,remote_time_off_policy_id,reason,duration,created_at,updated_at,policy", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -6405,26 +6213,10 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/time_off/{subResourceId}", }, + "experimental_preExecute": undefined, "name": "hris_cancel_employee_time_off_request", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -6534,20 +6326,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/time_off/{subResourceId}", }, + "experimental_preExecute": undefined, "name": "hris_update_employee_time_off_request", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "approver_id": { @@ -6577,10 +6357,6 @@ Map { }, ], }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -6762,26 +6538,10 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/documents/upload/batch", }, + "experimental_preExecute": undefined, "name": "hris_batch_upload_employee_document", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -8187,20 +7947,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/documents/upload", }, + "experimental_preExecute": undefined, "name": "hris_upload_employee_document", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "category": { @@ -9498,10 +9246,6 @@ Map { }, "type": "object", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -9612,20 +9356,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/documents/{subResourceId}/download", }, + "experimental_preExecute": undefined, "name": "hris_download_employee_document", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "export_format": { @@ -9633,10 +9365,6 @@ Map { "example": "text/plain", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "format": { "description": "The format to download the file in", "example": "base64", @@ -9741,20 +9469,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/documents", }, + "experimental_preExecute": undefined, "name": "hris_list_employee_documents", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -9762,10 +9478,6 @@ Map { "example": "id,remote_id,name,path,type,category,category_id,remote_category_id,contents,created_at,updated_at,remote_url,file_format", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "filter": { "description": "Filter parameters that allow greater customisation of the list response", "properties": { @@ -9895,20 +9607,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/documents/{subResourceId}", }, + "experimental_preExecute": undefined, "name": "hris_get_employee_document", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -9916,10 +9616,6 @@ Map { "example": "id,remote_id,name,path,type,category,category_id,remote_category_id,contents,created_at,updated_at,remote_url,file_format", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -10028,20 +9724,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/documents/employee_categories", }, + "experimental_preExecute": undefined, "name": "hris_list_employee_categories", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -10049,10 +9733,6 @@ Map { "example": "id,remote_id,name,active", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "filter": { "description": "Filter parameters that allow greater customisation of the list response", "properties": { @@ -10167,20 +9847,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/documents/employee_categories/{id}", }, + "experimental_preExecute": undefined, "name": "hris_get_employee_document_category", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -10188,10 +9856,6 @@ Map { "example": "id,remote_id,name,active", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -10296,20 +9960,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/work_eligibility", }, + "experimental_preExecute": undefined, "name": "hris_list_employee_work_eligibility", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -10317,10 +9969,6 @@ Map { "example": "id,remote_id,type,sub_type,document,valid_from,valid_to,issued_by,number", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "filter": { "description": "Filter parameters that allow greater customisation of the list response", "properties": { @@ -10470,20 +10118,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/work_eligibility", }, + "experimental_preExecute": undefined, "name": "hris_create_employee_work_eligibility_request", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "document": { @@ -11769,10 +11405,6 @@ Map { }, "type": "object", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -12190,20 +11822,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/work_eligibility/{subResourceId}", }, + "experimental_preExecute": undefined, "name": "hris_get_employees_work_eligibility", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -12211,10 +11831,6 @@ Map { "example": "id,remote_id,type,sub_type,document,valid_from,valid_to,issued_by,number", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -12343,20 +11959,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/work_eligibility/{subResourceId}", }, + "experimental_preExecute": undefined, "name": "hris_update_employee_work_eligibility_request", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "document": { @@ -13642,10 +13246,6 @@ Map { }, "type": "object", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -14087,20 +13687,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/time_off_balances", }, + "experimental_preExecute": undefined, "name": "hris_list_employee_time_off_balances", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "expand": { @@ -14113,10 +13701,6 @@ Map { "example": "id,remote_id,employee_id,remote_employee_id,policy_id,remote_policy_id,policy,current_balance,initial_balance,balance_unit,balance_start_date,balance_expiry_date,updated_at", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "filter": { "description": "HRIS Time Off Balance filters", "properties": { @@ -14264,20 +13848,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/time_off_balances/{subResourceId}", }, + "experimental_preExecute": undefined, "name": "hris_get_employee_time_off_balance", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "expand": { @@ -14290,10 +13862,6 @@ Map { "example": "id,remote_id,employee_id,remote_employee_id,policy_id,remote_policy_id,policy,current_balance,initial_balance,balance_unit,balance_start_date,balance_expiry_date,updated_at", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -14412,20 +13980,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employments", }, + "experimental_preExecute": undefined, "name": "hris_list_employments", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "expand": { @@ -14438,10 +13994,6 @@ Map { "example": "id,remote_id,employee_id,remote_employee_id,job_title,pay_rate,pay_period,pay_frequency,pay_currency,effective_date,end_date,employment_type,employment_contract_type,change_reason,grade,work_time,payroll_code,fte,created_at,updated_at,start_date,active,department,team,cost_center,cost_centers,division,job,type,contract_type,manager", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "filter": { "description": "Filter parameters that allow greater customisation of the list response", "properties": { @@ -14566,20 +14118,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employments/{id}", }, + "experimental_preExecute": undefined, "name": "hris_get_employment", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "expand": { @@ -14592,10 +14132,6 @@ Map { "example": "id,remote_id,employee_id,remote_employee_id,job_title,pay_rate,pay_period,pay_frequency,pay_currency,effective_date,end_date,employment_type,employment_contract_type,change_reason,grade,work_time,payroll_code,fte,created_at,updated_at,start_date,active,department,team,cost_center,cost_centers,division,job,type,contract_type,manager", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -14710,20 +14246,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/employments", }, + "experimental_preExecute": undefined, "name": "hris_list_employee_employments", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "expand": { @@ -14736,10 +14260,6 @@ Map { "example": "id,remote_id,employee_id,remote_employee_id,job_title,pay_rate,pay_period,pay_frequency,pay_currency,effective_date,end_date,employment_type,employment_contract_type,change_reason,grade,work_time,payroll_code,fte,created_at,updated_at,start_date,active,department,team,cost_center,cost_centers,division,job,type,contract_type,manager", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "filter": { "description": "Filter parameters that allow greater customisation of the list response", "properties": { @@ -14919,20 +14439,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/employments", }, + "experimental_preExecute": undefined, "name": "hris_create_employee_employment", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "effective_date": { @@ -14947,10 +14455,6 @@ Map { "format": "date-time", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "grade": { "description": "Represents the employee’s position within the organizational hierarchy.", "properties": { @@ -15241,20 +14745,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/employments/{subResourceId}", }, + "experimental_preExecute": undefined, "name": "hris_get_employee_employment", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "expand": { @@ -15267,10 +14759,6 @@ Map { "example": "id,remote_id,employee_id,remote_employee_id,job_title,pay_rate,pay_period,pay_frequency,pay_currency,effective_date,end_date,employment_type,employment_contract_type,change_reason,grade,work_time,payroll_code,fte,created_at,updated_at,start_date,active,department,team,cost_center,cost_centers,division,job,type,contract_type,manager", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -15424,20 +14912,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/employments/{subResourceId}", }, + "experimental_preExecute": undefined, "name": "hris_update_employee_employment", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "effective_date": { @@ -15452,10 +14928,6 @@ Map { "format": "date-time", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "grade": { "description": "Represents the employee’s position within the organizational hierarchy.", "properties": { @@ -15745,20 +15217,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/locations", }, + "experimental_preExecute": undefined, "name": "hris_list_locations", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -15766,10 +15226,6 @@ Map { "example": "id,remote_id,employee_id,remote_employee_id,name,phone_number,street_1,street_2,city,state,zip_code,country,location_type,created_at,updated_at", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "filter": { "description": "Filter parameters that allow greater customisation of the list response", "properties": { @@ -15884,20 +15340,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/locations/{id}", }, + "experimental_preExecute": undefined, "name": "hris_get_location", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -15905,10 +15349,6 @@ Map { "example": "id,remote_id,employee_id,remote_employee_id,name,phone_number,street_1,street_2,city,state,zip_code,country,location_type,created_at,updated_at", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -16013,20 +15453,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/time_off", }, + "experimental_preExecute": undefined, "name": "hris_list_time_off_requests", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "expand": { @@ -16039,10 +15467,6 @@ Map { "example": "id,remote_id,employee_id,remote_employee_id,approver_id,remote_approver_id,status,type,start_date,end_date,start_half_day,end_half_day,time_off_policy_id,remote_time_off_policy_id,reason,duration,created_at,updated_at,policy", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "filter": { "description": "HRIS Time Off filters", "properties": { @@ -16174,20 +15598,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/time_off/{id}", }, + "experimental_preExecute": undefined, "name": "hris_get_time_off_request", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "expand": { @@ -16200,10 +15612,6 @@ Map { "example": "id,remote_id,employee_id,remote_employee_id,approver_id,remote_approver_id,status,type,start_date,end_date,start_half_day,end_half_day,time_off_policy_id,remote_time_off_policy_id,reason,duration,created_at,updated_at,policy", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -16308,20 +15716,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/time_off_types", }, + "experimental_preExecute": undefined, "name": "hris_list_time_off_types", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -16329,10 +15725,6 @@ Map { "example": "id,remote_id,name,active", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "filter": { "description": "Filter parameters that allow greater customisation of the list response", "properties": { @@ -16447,20 +15839,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/time_off_types/{id}", }, + "experimental_preExecute": undefined, "name": "hris_get_time_off_type", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -16468,10 +15848,6 @@ Map { "example": "id,remote_id,name,active", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -16571,20 +15947,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/time_entries", }, + "experimental_preExecute": undefined, "name": "hris_list_time_entries", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -16592,10 +15956,6 @@ Map { "example": "id,remote_id,employee_id,remote_employee_id,start_time,end_time,hours_worked,break_duration,labor_type,location,status,created_at,updated_at", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "filter": { "description": "HRIS Time Entries filters", "properties": { @@ -16727,20 +16087,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/time_entries/{id}", }, + "experimental_preExecute": undefined, "name": "hris_get_time_entries", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -16748,10 +16096,6 @@ Map { "example": "id,remote_id,employee_id,remote_employee_id,start_time,end_time,hours_worked,break_duration,labor_type,location,status,created_at,updated_at", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -16851,20 +16195,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/benefits", }, + "experimental_preExecute": undefined, "name": "hris_list_benefits", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -16872,10 +16204,6 @@ Map { "example": "id,remote_id,name,benefit_type,provider,description,created_at,updated_at", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "filter": { "description": "Filter parameters that allow greater customisation of the list response", "properties": { @@ -16990,20 +16318,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/benefits/{id}", }, + "experimental_preExecute": undefined, "name": "hris_get_benefit", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -17011,10 +16327,6 @@ Map { "example": "id,remote_id,name,benefit_type,provider,description,created_at,updated_at", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -17114,20 +16426,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/groups", }, + "experimental_preExecute": undefined, "name": "hris_list_groups", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -17135,10 +16435,6 @@ Map { "example": "id,remote_id,name,type,parent_ids,remote_parent_ids,owner_ids,remote_owner_ids,company_id,remote_company_id", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "filter": { "description": "Filter parameters that allow greater customisation of the list response", "properties": { @@ -17263,20 +16559,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/groups/departments", }, + "experimental_preExecute": undefined, "name": "hris_list_department_groups", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -17284,10 +16568,6 @@ Map { "example": "id,remote_id,name", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "filter": { "description": "Filter parameters that allow greater customisation of the list response", "properties": { @@ -17412,20 +16692,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/groups/cost_centers", }, + "experimental_preExecute": undefined, "name": "hris_list_cost_center_groups", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -17433,10 +16701,6 @@ Map { "example": "id,remote_id,name,type,distribution_percentage,parent_ids,remote_parent_ids,owner_ids,remote_owner_ids,company_id,remote_company_id", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "filter": { "description": "Filter parameters that allow greater customisation of the list response", "properties": { @@ -17561,20 +16825,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/groups/teams", }, + "experimental_preExecute": undefined, "name": "hris_list_team_groups", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -17582,10 +16834,6 @@ Map { "example": "id,remote_id,name,type,parent_ids,remote_parent_ids,owner_ids,remote_owner_ids", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "filter": { "description": "Filter parameters that allow greater customisation of the list response", "properties": { @@ -17700,20 +16948,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/groups/{id}", }, + "experimental_preExecute": undefined, "name": "hris_get_group", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -17721,10 +16957,6 @@ Map { "example": "id,remote_id,name,type,parent_ids,remote_parent_ids,owner_ids,remote_owner_ids,company_id,remote_company_id", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -17814,20 +17046,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/groups/departments/{id}", }, + "experimental_preExecute": undefined, "name": "hris_get_department_group", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -17835,10 +17055,6 @@ Map { "example": "id,remote_id,name", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -17928,20 +17144,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/groups/cost_centers/{id}", }, + "experimental_preExecute": undefined, "name": "hris_get_cost_center_group", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -17949,10 +17153,6 @@ Map { "example": "id,remote_id,name,type,distribution_percentage,parent_ids,remote_parent_ids,owner_ids,remote_owner_ids,company_id,remote_company_id", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -18042,20 +17242,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/groups/teams/{id}", }, + "experimental_preExecute": undefined, "name": "hris_get_team_group", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -18063,10 +17251,6 @@ Map { "example": "id,remote_id,name,type,parent_ids,remote_parent_ids,owner_ids,remote_owner_ids", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -18166,20 +17350,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/jobs", }, + "experimental_preExecute": undefined, "name": "hris_list_jobs", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -18187,10 +17359,6 @@ Map { "example": "id,remote_id,name,type,parent_ids,remote_parent_ids,owner_ids,remote_owner_ids,company_id,remote_company_id", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "filter": { "description": "Filter parameters that allow greater customisation of the list response", "properties": { @@ -18305,20 +17473,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/jobs/{id}", }, + "experimental_preExecute": undefined, "name": "hris_get_job", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -18326,10 +17482,6 @@ Map { "example": "id,remote_id,name,type,parent_ids,remote_parent_ids,owner_ids,remote_owner_ids,company_id,remote_company_id", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -18434,20 +17586,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/skills", }, + "experimental_preExecute": undefined, "name": "hris_list_employee_skills", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -18455,10 +17595,6 @@ Map { "example": "id,remote_id,name,active,language,maximum_proficiency,minimum_proficiency", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "filter": { "description": "Filter parameters that allow greater customisation of the list response", "properties": { @@ -18583,26 +17719,10 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/skills", }, + "experimental_preExecute": undefined, "name": "hris_create_employee_skill", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "description": "The ID associated with this skill", "example": "16873-IT345", @@ -18761,20 +17881,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/skills/{subResourceId}", }, + "experimental_preExecute": undefined, "name": "hris_get_employee_skill", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -18782,10 +17890,6 @@ Map { "example": "id,remote_id,name,active,language,maximum_proficiency,minimum_proficiency", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -18894,20 +17998,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/time_off_policies", }, + "experimental_preExecute": undefined, "name": "hris_list_time_off_policies", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -18915,10 +18007,6 @@ Map { "example": "id,remote_id,name,description,type,duration_unit,reasons,updated_at,created_at", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "filter": { "description": "HRIS Time-Off Policies filters", "properties": { @@ -19062,20 +18150,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/time_off_policies/{id}", }, + "experimental_preExecute": undefined, "name": "hris_get_time_off_policy", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -19083,10 +18159,6 @@ Map { "example": "id,remote_id,name,description,type,duration_unit,reasons,updated_at,created_at", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -19191,20 +18263,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/time_off_policies", }, + "experimental_preExecute": undefined, "name": "hris_list_employee_time_off_policies", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "fields": { @@ -19212,10 +18272,6 @@ Map { "example": "id,remote_id,name,description,type,duration_unit,reasons,updated_at,created_at", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "filter": { "description": "HRIS Time-Off Policies filters", "properties": { @@ -19389,20 +18445,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/tasks", }, + "experimental_preExecute": undefined, "name": "hris_list_employee_tasks", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "expand": { @@ -19415,10 +18459,6 @@ Map { "example": "id,remote_id,employee_id,remote_employee_id,name,description,type,status,due_date,completion_date,assigned_by_employee_id,remote_assigned_by_employee_id,assigned_by_employee_name,link_to_task,extracted_links,next_task_id,remote_next_task_id,parent_process_name,comments,attachments,created_at,updated_at", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "filter": { "description": "Filter parameters that allow greater customisation of the list response", "properties": { @@ -19558,20 +18598,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/tasks/{subResourceId}", }, + "experimental_preExecute": undefined, "name": "hris_get_employee_task", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "expand": { @@ -19584,10 +18612,6 @@ Map { "example": "id,remote_id,employee_id,remote_employee_id,name,description,type,status,due_date,completion_date,assigned_by_employee_id,remote_assigned_by_employee_id,assigned_by_employee_name,link_to_task,extracted_links,next_task_id,remote_next_task_id,parent_process_name,comments,attachments,created_at,updated_at", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, @@ -19691,20 +18715,8 @@ Map { ], "url": "https://api.stackone.com/unified/hris/employees/{id}/tasks/{subResourceId}", }, + "experimental_preExecute": undefined, "name": "hris_complete_employee_task", - "parameterMapper": ParameterMapper { - "transformers": -Map { - "file_path" => { - "transforms": { - "content": [Function], - "file_format": [Function], - "name": [Function], - }, - }, - } -, - }, "parameters": { "properties": { "comment": { @@ -19712,10 +18724,6 @@ Map { "example": "All required documents have been submitted", "type": "string", }, - "file_path": { - "description": "Convenience parameter that will be transformed into other parameters. Try and use this parameter in your tool call.", - "type": "string", - }, "id": { "type": "string", }, diff --git a/src/types.ts b/src/types.ts index 54042af..167647a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -25,30 +25,20 @@ export type JsonSchemaProperties = Record; export type JsonSchemaType = JSONSchema7['type']; /** - * Type definition for a derivation function - * Takes a source value and returns a derived value + * EXPERIMENTAL: Function to override the tool schema at creation time + * Takes the original tool parameters and returns a new schema + * @param originalSchema - The original tool parameters schema from OpenAPI + * @returns New schema definition for the tool */ -export type TransformFunction = (sourceValue: unknown) => unknown; +export type Experimental_SchemaOverride = (originalSchema: ToolParameters) => ToolParameters; /** - * Type definition for a map of derivation functions - * Keys are transformed parameter names, values are functions to derive that parameter + * EXPERIMENTAL: Function to preprocess parameters before tool execution + * Transforms parameters from override schema format back to original API format + * @param params - The input parameters in override schema format + * @returns Parameters in original API format */ -export type TransformFunctions = Record; - -/** - * Configuration for parameter transformations - * The key in the derivation configs map is the source parameter name - */ -export type ParameterTransformer = { - transforms: TransformFunctions; -}; - -/** - * Type definition for a map of derivation configurations - * Keys are source parameter names, values are derivation functions - */ -export type ParameterTransformerMap = Map; +export type Experimental_PreExecuteFunction = (params: JsonDict) => Promise | JsonDict; /** * Valid locations for parameters in requests @@ -75,6 +65,23 @@ export interface ExecuteConfig { }[]; // this params are the full list of params used to execute. This should come straight from the OpenAPI spec. } +/** + * EXPERIMENTAL: Options for creating tools with schema overrides and preExecute functions + */ +export interface Experimental_ToolCreationOptions { + /** + * EXPERIMENTAL: Function to override the tool schema at creation time + * Takes the original schema and returns a new schema for the tool + */ + experimental_schemaOverride?: Experimental_SchemaOverride; + + /** + * EXPERIMENTAL: Function to preprocess parameters before execution + * Transforms parameters from override schema format back to original API format + */ + experimental_preExecute?: Experimental_PreExecuteFunction; +} + /** * Options for executing a tool */