diff --git a/api/app/clients/tools/AzureAiSearch.js b/api/app/clients/tools/AzureAiSearch.js index 7a5742b175..0a552884df 100644 --- a/api/app/clients/tools/AzureAiSearch.js +++ b/api/app/clients/tools/AzureAiSearch.js @@ -1,56 +1,77 @@ -const { Tool } = require('langchain/tools'); +const { StructuredTool } = require('langchain/tools'); +const { z } = require('zod'); const { SearchClient, AzureKeyCredential } = require('@azure/search-documents'); -class AzureAISearch extends Tool { +class AzureAISearch extends StructuredTool { + // Constants for default values static DEFAULT_API_VERSION = '2023-11-01'; static DEFAULT_QUERY_TYPE = 'simple'; static DEFAULT_TOP = 5; + // Helper function for initializing properties + _initializeField(field, envVar, defaultValue) { + return field || process.env[envVar] || defaultValue; + } + constructor(fields = {}) { super(); - this.initializeProperties(fields); - this.initializeClient(); + + // Initialize properties using helper function + this.serviceEndpoint = this._initializeField(fields.AZURE_AI_SEARCH_SERVICE_ENDPOINT, 'AZURE_AI_SEARCH_SERVICE_ENDPOINT'); + this.indexName = this._initializeField(fields.AZURE_AI_SEARCH_INDEX_NAME, 'AZURE_AI_SEARCH_INDEX_NAME'); + this.apiKey = this._initializeField(fields.AZURE_AI_SEARCH_API_KEY, 'AZURE_AI_SEARCH_API_KEY'); + this.apiVersion = this._initializeField(fields.AZURE_AI_SEARCH_API_VERSION, 'AZURE_AI_SEARCH_API_VERSION', AzureAISearch.DEFAULT_API_VERSION); + this.queryType = this._initializeField(fields.AZURE_AI_SEARCH_SEARCH_OPTION_QUERY_TYPE, 'AZURE_AI_SEARCH_SEARCH_OPTION_QUERY_TYPE', AzureAISearch.DEFAULT_QUERY_TYPE); + this.top = this._initializeField(fields.AZURE_AI_SEARCH_SEARCH_OPTION_TOP, 'AZURE_AI_SEARCH_SEARCH_OPTION_TOP', AzureAISearch.DEFAULT_TOP); + this.select = this._initializeField(fields.AZURE_AI_SEARCH_SEARCH_OPTION_SELECT, 'AZURE_AI_SEARCH_SEARCH_OPTION_SELECT'); + + // Check for required fields + if (!this.serviceEndpoint || !this.indexName || !this.apiKey) { + throw new Error('Missing AZURE_AI_SEARCH_SERVICE_ENDPOINT, AZURE_AI_SEARCH_INDEX_NAME, or AZURE_AI_SEARCH_API_KEY environment variable.'); + } + + // Create SearchClient + this.client = new SearchClient( + this.serviceEndpoint, + this.indexName, + new AzureKeyCredential(this.apiKey), + { apiVersion: this.apiVersion } + ); + + // Define schema + this.schema = z.object({ + query: z.string().describe('Search word or phrase to Azure AI Search'), + }); } - initializeProperties(fields) { - const getValue = (fieldNames, defaultValue) => { - for (const name of fieldNames) { - const value = fields[name] || process.env[name]; - if (value !== undefined && value !== null) return value; - } - return defaultValue; - }; - - this.serviceEndpoint = getValue(['AZURE_AI_SEARCH_SERVICE_ENDPOINT', 'AZURE_COGNITIVE_SEARCH_SERVICE_ENDPOINT'], this.getServiceEndpoint()); - this.indexName = getValue(['AZURE_AI_SEARCH_INDEX_NAME', 'AZURE_COGNITIVE_SEARCH_INDEX_NAME'], this.getIndexName()); - this.apiKey = getValue(['AZURE_AI_SEARCH_API_KEY', 'AZURE_COGNITIVE_SEARCH_API_KEY'], this.getApiKey()); - this.apiVersion = getValue(['AZURE_AI_SEARCH_API_VERSION', 'AZURE_COGNITIVE_SEARCH_API_VERSION'], AzureAISearch.DEFAULT_API_VERSION); - this.queryType = getValue(['AZURE_AI_SEARCH_SEARCH_OPTION_QUERY_TYPE', 'AZURE_COGNITIVE_SEARCH_SEARCH_OPTION_QUERY_TYPE'], AzureAISearch.DEFAULT_QUERY_TYPE); - this.top = getValue(['AZURE_AI_SEARCH_SEARCH_OPTION_TOP', 'AZURE_COGNITIVE_SEARCH_SEARCH_OPTION_TOP'], AzureAISearch.DEFAULT_TOP); - this.select = this.getSelect(); + // Simplified getter methods + get name() { + return 'azure-ai-search'; } - initializeClient() { - this.client = new SearchClient(this.serviceEndpoint, this.indexName, new AzureKeyCredential(this.apiKey), { apiVersion: this.apiVersion }); + get description() { + return 'Use the \'azure-ai-search\' tool to retrieve search results relevant to your input'; } - name = 'azure-ai-search'; - - description = - 'Use the \'azure-ai-search\' tool to retrieve search results relevant to your input'; - - async _call(query) { + // Improved error handling and logging + async _call(data) { + const { query } = data; try { - const searchOptions = { + const searchOption = { queryType: this.queryType, top: this.top, - select: this.select }; - - const searchResults = await this.client.search(query, searchOptions); - return JSON.stringify(searchResults.results.map(result => result.document)); + if (this.select) { + searchOption.select = this.select.split(','); + } + const searchResults = await this.client.search(query, searchOption); + const resultDocuments = []; + for await (const result of searchResults.results) { + resultDocuments.push(result.document); + } + return JSON.stringify(resultDocuments); } catch (error) { - console.error(`Azure AI Search request failed: ${error}`); + console.error(`Azure AI Search request failed: ${error.message}`); return 'There was an error with Azure AI Search.'; } } diff --git a/api/app/clients/tools/structured/AzureAISearch.js b/api/app/clients/tools/structured/AzureAISearch.js index 8d22ced0b1..0a552884df 100644 --- a/api/app/clients/tools/structured/AzureAISearch.js +++ b/api/app/clients/tools/structured/AzureAISearch.js @@ -3,63 +3,75 @@ const { z } = require('zod'); const { SearchClient, AzureKeyCredential } = require('@azure/search-documents'); class AzureAISearch extends StructuredTool { + // Constants for default values static DEFAULT_API_VERSION = '2023-11-01'; static DEFAULT_QUERY_TYPE = 'simple'; static DEFAULT_TOP = 5; + // Helper function for initializing properties + _initializeField(field, envVar, defaultValue) { + return field || process.env[envVar] || defaultValue; + } + constructor(fields = {}) { super(); - this.initializeProperties(fields); - this.initializeClient(); - this.initializeSchema(); - } - initializeProperties(fields) { - const getValue = (fieldNames, defaultValue) => { - for (const name of fieldNames) { - const value = fields[name] || process.env[name]; - if (value !== undefined && value !== null) return value; - } - return defaultValue; - }; + // Initialize properties using helper function + this.serviceEndpoint = this._initializeField(fields.AZURE_AI_SEARCH_SERVICE_ENDPOINT, 'AZURE_AI_SEARCH_SERVICE_ENDPOINT'); + this.indexName = this._initializeField(fields.AZURE_AI_SEARCH_INDEX_NAME, 'AZURE_AI_SEARCH_INDEX_NAME'); + this.apiKey = this._initializeField(fields.AZURE_AI_SEARCH_API_KEY, 'AZURE_AI_SEARCH_API_KEY'); + this.apiVersion = this._initializeField(fields.AZURE_AI_SEARCH_API_VERSION, 'AZURE_AI_SEARCH_API_VERSION', AzureAISearch.DEFAULT_API_VERSION); + this.queryType = this._initializeField(fields.AZURE_AI_SEARCH_SEARCH_OPTION_QUERY_TYPE, 'AZURE_AI_SEARCH_SEARCH_OPTION_QUERY_TYPE', AzureAISearch.DEFAULT_QUERY_TYPE); + this.top = this._initializeField(fields.AZURE_AI_SEARCH_SEARCH_OPTION_TOP, 'AZURE_AI_SEARCH_SEARCH_OPTION_TOP', AzureAISearch.DEFAULT_TOP); + this.select = this._initializeField(fields.AZURE_AI_SEARCH_SEARCH_OPTION_SELECT, 'AZURE_AI_SEARCH_SEARCH_OPTION_SELECT'); - this.serviceEndpoint = getValue(['AZURE_AI_SEARCH_SERVICE_ENDPOINT', 'AZURE_COGNITIVE_SEARCH_SERVICE_ENDPOINT'], this.getServiceEndpoint()); - this.indexName = getValue(['AZURE_AI_SEARCH_INDEX_NAME', 'AZURE_COGNITIVE_SEARCH_INDEX_NAME'], this.getIndexName()); - this.apiKey = getValue(['AZURE_AI_SEARCH_API_KEY', 'AZURE_COGNITIVE_SEARCH_API_KEY'], this.getApiKey()); - this.apiVersion = getValue(['AZURE_AI_SEARCH_API_VERSION', 'AZURE_COGNITIVE_SEARCH_API_VERSION'], AzureAISearch.DEFAULT_API_VERSION); - this.queryType = getValue(['AZURE_AI_SEARCH_SEARCH_OPTION_QUERY_TYPE', 'AZURE_COGNITIVE_SEARCH_SEARCH_OPTION_QUERY_TYPE'], AzureAISearch.DEFAULT_QUERY_TYPE); - this.top = getValue(['AZURE_AI_SEARCH_SEARCH_OPTION_TOP', 'AZURE_COGNITIVE_SEARCH_SEARCH_OPTION_TOP'], AzureAISearch.DEFAULT_TOP); - this.select = this.getSelect(); - } + // Check for required fields + if (!this.serviceEndpoint || !this.indexName || !this.apiKey) { + throw new Error('Missing AZURE_AI_SEARCH_SERVICE_ENDPOINT, AZURE_AI_SEARCH_INDEX_NAME, or AZURE_AI_SEARCH_API_KEY environment variable.'); + } - initializeClient() { - this.client = new SearchClient(this.serviceEndpoint, this.indexName, new AzureKeyCredential(this.apiKey), { apiVersion: this.apiVersion }); - } + // Create SearchClient + this.client = new SearchClient( + this.serviceEndpoint, + this.indexName, + new AzureKeyCredential(this.apiKey), + { apiVersion: this.apiVersion } + ); - initializeSchema() { + // Define schema this.schema = z.object({ query: z.string().describe('Search word or phrase to Azure AI Search'), }); } - name = 'azure-ai-search'; + // Simplified getter methods + get name() { + return 'azure-ai-search'; + } - description = - 'Use the \'azure-ai-search\' tool to retrieve search results relevant to your input'; + get description() { + return 'Use the \'azure-ai-search\' tool to retrieve search results relevant to your input'; + } + // Improved error handling and logging async _call(data) { const { query } = data; try { - const searchOptions = { + const searchOption = { queryType: this.queryType, top: this.top, - select: this.select }; - - const searchResults = await this.client.search(query, searchOptions); - return JSON.stringify(searchResults.results.map(result => result.document)); + if (this.select) { + searchOption.select = this.select.split(','); + } + const searchResults = await this.client.search(query, searchOption); + const resultDocuments = []; + for await (const result of searchResults.results) { + resultDocuments.push(result.document); + } + return JSON.stringify(resultDocuments); } catch (error) { - console.error(`Azure AI Search request failed: ${error}`); + console.error(`Azure AI Search request failed: ${error.message}`); return 'There was an error with Azure AI Search.'; } }