mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix(OpenAI Node): Allow updating assistant files (#12042)
This commit is contained in:
parent
0ad3871141
commit
7b20f8aaa8
|
@ -4,7 +4,7 @@ import type {
|
|||
INodeExecutionData,
|
||||
IDataObject,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeOperationError, updateDisplayOptions } from 'n8n-workflow';
|
||||
import { ApplicationError, NodeOperationError, updateDisplayOptions } from 'n8n-workflow';
|
||||
import { apiRequest } from '../../transport';
|
||||
import { assistantRLC, modelRLC } from '../descriptions';
|
||||
|
||||
|
@ -116,6 +116,18 @@ const displayOptions = {
|
|||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
function getFileIds(file_ids: unknown): string[] {
|
||||
if (Array.isArray(file_ids)) {
|
||||
return file_ids;
|
||||
}
|
||||
|
||||
if (typeof file_ids === 'string') {
|
||||
return file_ids.split(',').map((file_id) => file_id.trim());
|
||||
}
|
||||
|
||||
throw new ApplicationError('Invalid file_ids type');
|
||||
}
|
||||
|
||||
export async function execute(this: IExecuteFunctions, i: number): Promise<INodeExecutionData[]> {
|
||||
const assistantId = this.getNodeParameter('assistantId', i, '', { extractValue: true }) as string;
|
||||
const options = this.getNodeParameter('options', i, {});
|
||||
|
@ -137,11 +149,8 @@ export async function execute(this: IExecuteFunctions, i: number): Promise<INode
|
|||
const body: IDataObject = {};
|
||||
|
||||
if (file_ids) {
|
||||
let files = file_ids;
|
||||
if (typeof files === 'string') {
|
||||
files = files.split(',').map((file_id) => file_id.trim());
|
||||
}
|
||||
if ((file_ids as IDataObject[]).length > 20) {
|
||||
const files = getFileIds(file_ids);
|
||||
if (files.length > 20) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
'The maximum number of files that can be attached to the assistant is 20',
|
||||
|
@ -152,15 +161,12 @@ export async function execute(this: IExecuteFunctions, i: number): Promise<INode
|
|||
body.tool_resources = {
|
||||
...((body.tool_resources as object) ?? {}),
|
||||
code_interpreter: {
|
||||
file_ids,
|
||||
},
|
||||
file_search: {
|
||||
vector_stores: [
|
||||
{
|
||||
file_ids,
|
||||
},
|
||||
],
|
||||
file_ids: files,
|
||||
},
|
||||
// updating file_ids for file_search directly is not supported by OpenAI API
|
||||
// only updating vector_store_ids for file_search is supported
|
||||
// support for this to be added as part of ADO-2968
|
||||
// https://platform.openai.com/docs/api-reference/assistants/modifyAssistant
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -210,12 +210,89 @@ describe('OpenAi, Assistant resource', () => {
|
|||
code_interpreter: {
|
||||
file_ids: [],
|
||||
},
|
||||
file_search: {
|
||||
vector_stores: [
|
||||
{
|
||||
file_ids: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
tools: [{ type: 'existing_tool' }, { type: 'code_interpreter' }, { type: 'file_search' }],
|
||||
},
|
||||
headers: { 'OpenAI-Beta': 'assistants=v2' },
|
||||
});
|
||||
});
|
||||
|
||||
it('update => should call apiRequest with file_ids as an array for search', async () => {
|
||||
(transport.apiRequest as jest.Mock).mockResolvedValueOnce({
|
||||
tools: [{ type: 'existing_tool' }],
|
||||
});
|
||||
(transport.apiRequest as jest.Mock).mockResolvedValueOnce({});
|
||||
|
||||
await assistant.update.execute.call(
|
||||
createExecuteFunctionsMock({
|
||||
assistantId: 'assistant-id',
|
||||
options: {
|
||||
modelId: 'gpt-model',
|
||||
name: 'name',
|
||||
instructions: 'some instructions',
|
||||
codeInterpreter: true,
|
||||
knowledgeRetrieval: true,
|
||||
file_ids: ['1234'],
|
||||
removeCustomTools: false,
|
||||
},
|
||||
}),
|
||||
0,
|
||||
);
|
||||
|
||||
expect(transport.apiRequest).toHaveBeenCalledTimes(2);
|
||||
expect(transport.apiRequest).toHaveBeenCalledWith('GET', '/assistants/assistant-id', {
|
||||
headers: { 'OpenAI-Beta': 'assistants=v2' },
|
||||
});
|
||||
expect(transport.apiRequest).toHaveBeenCalledWith('POST', '/assistants/assistant-id', {
|
||||
body: {
|
||||
instructions: 'some instructions',
|
||||
model: 'gpt-model',
|
||||
name: 'name',
|
||||
tool_resources: {
|
||||
code_interpreter: {
|
||||
file_ids: ['1234'],
|
||||
},
|
||||
},
|
||||
tools: [{ type: 'existing_tool' }, { type: 'code_interpreter' }, { type: 'file_search' }],
|
||||
},
|
||||
headers: { 'OpenAI-Beta': 'assistants=v2' },
|
||||
});
|
||||
});
|
||||
|
||||
it('update => should call apiRequest with file_ids as strings for search', async () => {
|
||||
(transport.apiRequest as jest.Mock).mockResolvedValueOnce({
|
||||
tools: [{ type: 'existing_tool' }],
|
||||
});
|
||||
(transport.apiRequest as jest.Mock).mockResolvedValueOnce({});
|
||||
|
||||
await assistant.update.execute.call(
|
||||
createExecuteFunctionsMock({
|
||||
assistantId: 'assistant-id',
|
||||
options: {
|
||||
modelId: 'gpt-model',
|
||||
name: 'name',
|
||||
instructions: 'some instructions',
|
||||
codeInterpreter: true,
|
||||
knowledgeRetrieval: true,
|
||||
file_ids: '1234, 5678, 90',
|
||||
removeCustomTools: false,
|
||||
},
|
||||
}),
|
||||
0,
|
||||
);
|
||||
|
||||
expect(transport.apiRequest).toHaveBeenCalledTimes(2);
|
||||
expect(transport.apiRequest).toHaveBeenCalledWith('GET', '/assistants/assistant-id', {
|
||||
headers: { 'OpenAI-Beta': 'assistants=v2' },
|
||||
});
|
||||
expect(transport.apiRequest).toHaveBeenCalledWith('POST', '/assistants/assistant-id', {
|
||||
body: {
|
||||
instructions: 'some instructions',
|
||||
model: 'gpt-model',
|
||||
name: 'name',
|
||||
tool_resources: {
|
||||
code_interpreter: {
|
||||
file_ids: ['1234', '5678', '90'],
|
||||
},
|
||||
},
|
||||
tools: [{ type: 'existing_tool' }, { type: 'code_interpreter' }, { type: 'file_search' }],
|
||||
|
|
Loading…
Reference in a new issue