diff --git a/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/assistant/create.operation.ts b/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/assistant/create.operation.ts index 341a17712a..b9a0dee535 100644 --- a/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/assistant/create.operation.ts +++ b/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/assistant/create.operation.ts @@ -9,7 +9,7 @@ import { apiRequest } from '../../transport'; import { modelRLC } from '../descriptions'; const properties: INodeProperties[] = [ - modelRLC, + modelRLC('modelSearch'), { displayName: 'Name', name: 'name', diff --git a/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/assistant/update.operation.ts b/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/assistant/update.operation.ts index 061bb0165a..8a997aa99d 100644 --- a/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/assistant/update.operation.ts +++ b/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/assistant/update.operation.ts @@ -67,7 +67,7 @@ const properties: INodeProperties[] = [ description: 'Whether to augments the assistant with knowledge from outside its model, such as proprietary product information or documents, find more here', }, - { ...modelRLC, required: false }, + { ...modelRLC('modelSearch'), required: false }, { displayName: 'Name', name: 'name', diff --git a/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/descriptions.ts b/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/descriptions.ts index 8416e794d2..2f27ba6414 100644 --- a/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/descriptions.ts +++ b/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/descriptions.ts @@ -1,6 +1,6 @@ import type { INodeProperties } from 'n8n-workflow'; -export const modelRLC: INodeProperties = { +export const modelRLC = (searchListMethod: string = 'modelSearch'): INodeProperties => ({ displayName: 'Model', name: 'modelId', type: 'resourceLocator', @@ -12,7 +12,7 @@ export const modelRLC: INodeProperties = { name: 'list', type: 'list', typeOptions: { - searchListMethod: 'modelSearch', + searchListMethod, searchable: true, }, }, @@ -23,7 +23,7 @@ export const modelRLC: INodeProperties = { placeholder: 'e.g. gpt-4', }, ], -}; +}); export const assistantRLC: INodeProperties = { displayName: 'Assistant', diff --git a/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/image/analyze.operation.ts b/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/image/analyze.operation.ts index b29019602a..07a91ab82d 100644 --- a/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/image/analyze.operation.ts +++ b/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/image/analyze.operation.ts @@ -6,8 +6,13 @@ import type { } from 'n8n-workflow'; import { updateDisplayOptions, NodeOperationError } from 'n8n-workflow'; import { apiRequest } from '../../transport'; +import { modelRLC } from '../descriptions'; const properties: INodeProperties[] = [ + { + ...modelRLC('imageModelSearch'), + displayOptions: { show: { '@version': [{ _cnd: { gte: 1.4 } }] } }, + }, { displayName: 'Text Input', name: 'text', @@ -123,7 +128,11 @@ const displayOptions = { export const description = updateDisplayOptions(displayOptions, properties); export async function execute(this: IExecuteFunctions, i: number): Promise { - const model = 'gpt-4-vision-preview'; + let model = 'gpt-4-vision-preview'; + if (this.getNode().typeVersion >= 1.4) { + model = this.getNodeParameter('modelId', i, 'gpt-4o', { extractValue: true }) as string; + } + const text = this.getNodeParameter('text', i, '') as string; const inputType = this.getNodeParameter('inputType', i) as string; const options = this.getNodeParameter('options', i, {}); diff --git a/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/text/message.operation.ts b/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/text/message.operation.ts index ddcb250d26..4cf72e9f5f 100644 --- a/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/text/message.operation.ts +++ b/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/text/message.operation.ts @@ -14,7 +14,7 @@ import { getConnectedTools } from '../../../../../utils/helpers'; import { MODELS_NOT_SUPPORT_FUNCTION_CALLS } from '../../helpers/constants'; const properties: INodeProperties[] = [ - modelRLC, + modelRLC('modelSearch'), { displayName: 'Messages', name: 'messages', diff --git a/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/versionDescription.ts b/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/versionDescription.ts index 2ed98f89f9..45954f333b 100644 --- a/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/versionDescription.ts +++ b/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/actions/versionDescription.ts @@ -69,7 +69,7 @@ export const versionDescription: INodeTypeDescription = { name: 'openAi', icon: { light: 'file:openAi.svg', dark: 'file:openAi.dark.svg' }, group: ['transform'], - version: [1, 1.1, 1.2, 1.3], + version: [1, 1.1, 1.2, 1.3, 1.4], subtitle: `={{(${prettifyOperation})($parameter.resource, $parameter.operation)}}`, description: 'Message an assistant or GPT, analyze images, generate audio, etc.', defaults: { diff --git a/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/methods/listSearch.ts b/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/methods/listSearch.ts index 8ec3c53a2b..9aa3633d45 100644 --- a/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/methods/listSearch.ts +++ b/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/methods/listSearch.ts @@ -5,6 +5,8 @@ import type { INodeListSearchResult, } from 'n8n-workflow'; +import type { Model } from 'openai/resources/models'; +import type { Assistant } from 'openai/resources/beta/assistants'; import { apiRequest } from '../transport'; export async function fileSearch( @@ -38,37 +40,52 @@ export async function fileSearch( } } +const getModelSearch = + (filterCondition: (model: Model) => boolean) => + async (ctx: ILoadOptionsFunctions, filter?: string): Promise => { + let { data } = (await apiRequest.call(ctx, 'GET', '/models')) as { data: Model[] }; + + data = data?.filter((model) => filterCondition(model)); + + let results: INodeListSearchItems[] = []; + + if (filter) { + for (const model of data || []) { + if (model.id?.toLowerCase().includes(filter.toLowerCase())) { + results.push({ + name: model.id.toUpperCase(), + value: model.id, + }); + } + } + } else { + results = (data || []).map((model) => ({ + name: model.id.toUpperCase(), + value: model.id, + })); + } + + results = results.sort((a, b) => a.name.localeCompare(b.name)); + + return { + results, + }; + }; + export async function modelSearch( this: ILoadOptionsFunctions, filter?: string, ): Promise { - let { data } = await apiRequest.call(this, 'GET', '/models'); + return await getModelSearch((model) => model.id.startsWith('gpt-'))(this, filter); +} - data = data?.filter((model: IDataObject) => (model.id as string).startsWith('gpt-')); - - let results: INodeListSearchItems[] = []; - - if (filter) { - for (const model of data || []) { - if ((model.id as string)?.toLowerCase().includes(filter.toLowerCase())) { - results.push({ - name: (model.id as string).toUpperCase(), - value: model.id as string, - }); - } - } - } else { - results = (data || []).map((model: IDataObject) => ({ - name: (model.id as string).toUpperCase(), - value: model.id as string, - })); - } - - results = results.sort((a, b) => a.name.localeCompare(b.name)); - - return { - results, - }; +export async function imageModelSearch( + this: ILoadOptionsFunctions, + filter?: string, +): Promise { + return await getModelSearch( + (model) => model.id.includes('vision') || model.id.includes('gpt-4o'), + )(this, filter); } export async function assistantSearch( @@ -76,7 +93,7 @@ export async function assistantSearch( filter?: string, paginationToken?: string, ): Promise { - const { data, has_more, last_id } = await apiRequest.call(this, 'GET', '/assistants', { + const { data, has_more, last_id } = (await apiRequest.call(this, 'GET', '/assistants', { headers: { 'OpenAI-Beta': 'assistants=v2', }, @@ -84,9 +101,14 @@ export async function assistantSearch( limit: 100, after: paginationToken, }, - }); + })) as { + data: Assistant[]; + has_more: boolean; + last_id: string; + first_id: string; + }; - if (has_more === true) { + if (has_more) { paginationToken = last_id; } else { paginationToken = undefined; @@ -96,10 +118,10 @@ export async function assistantSearch( const results: INodeListSearchItems[] = []; for (const assistant of data || []) { - if ((assistant.name as string)?.toLowerCase().includes(filter.toLowerCase())) { + if (assistant.name?.toLowerCase().includes(filter.toLowerCase())) { results.push({ - name: assistant.name as string, - value: assistant.id as string, + name: assistant.name, + value: assistant.id, }); } } @@ -109,9 +131,9 @@ export async function assistantSearch( }; } else { return { - results: (data || []).map((assistant: IDataObject) => ({ - name: assistant.name as string, - value: assistant.id as string, + results: (data || []).map((assistant) => ({ + name: assistant.name ?? assistant.id, + value: assistant.id, })), paginationToken, };