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,
|
INodeExecutionData,
|
||||||
IDataObject,
|
IDataObject,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { NodeOperationError, updateDisplayOptions } from 'n8n-workflow';
|
import { ApplicationError, NodeOperationError, updateDisplayOptions } from 'n8n-workflow';
|
||||||
import { apiRequest } from '../../transport';
|
import { apiRequest } from '../../transport';
|
||||||
import { assistantRLC, modelRLC } from '../descriptions';
|
import { assistantRLC, modelRLC } from '../descriptions';
|
||||||
|
|
||||||
|
@ -116,6 +116,18 @@ const displayOptions = {
|
||||||
|
|
||||||
export const description = updateDisplayOptions(displayOptions, properties);
|
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[]> {
|
export async function execute(this: IExecuteFunctions, i: number): Promise<INodeExecutionData[]> {
|
||||||
const assistantId = this.getNodeParameter('assistantId', i, '', { extractValue: true }) as string;
|
const assistantId = this.getNodeParameter('assistantId', i, '', { extractValue: true }) as string;
|
||||||
const options = this.getNodeParameter('options', i, {});
|
const options = this.getNodeParameter('options', i, {});
|
||||||
|
@ -137,11 +149,8 @@ export async function execute(this: IExecuteFunctions, i: number): Promise<INode
|
||||||
const body: IDataObject = {};
|
const body: IDataObject = {};
|
||||||
|
|
||||||
if (file_ids) {
|
if (file_ids) {
|
||||||
let files = file_ids;
|
const files = getFileIds(file_ids);
|
||||||
if (typeof files === 'string') {
|
if (files.length > 20) {
|
||||||
files = files.split(',').map((file_id) => file_id.trim());
|
|
||||||
}
|
|
||||||
if ((file_ids as IDataObject[]).length > 20) {
|
|
||||||
throw new NodeOperationError(
|
throw new NodeOperationError(
|
||||||
this.getNode(),
|
this.getNode(),
|
||||||
'The maximum number of files that can be attached to the assistant is 20',
|
'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 = {
|
||||||
...((body.tool_resources as object) ?? {}),
|
...((body.tool_resources as object) ?? {}),
|
||||||
code_interpreter: {
|
code_interpreter: {
|
||||||
file_ids,
|
file_ids: files,
|
||||||
},
|
|
||||||
file_search: {
|
|
||||||
vector_stores: [
|
|
||||||
{
|
|
||||||
file_ids,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
|
// 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: {
|
code_interpreter: {
|
||||||
file_ids: [],
|
file_ids: [],
|
||||||
},
|
},
|
||||||
file_search: {
|
},
|
||||||
vector_stores: [
|
tools: [{ type: 'existing_tool' }, { type: 'code_interpreter' }, { type: 'file_search' }],
|
||||||
{
|
},
|
||||||
file_ids: [],
|
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' }],
|
tools: [{ type: 'existing_tool' }, { type: 'code_interpreter' }, { type: 'file_search' }],
|
||||||
|
|
Loading…
Reference in a new issue