first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled

This commit is contained in:
2026-03-17 16:22:57 +03:30
commit 3d5eaf9445
15349 changed files with 2847338 additions and 0 deletions
@@ -0,0 +1,267 @@
import type { IExecuteFunctions } from 'n8n-workflow';
import { mockDeep } from 'jest-mock-extended';
import { apiRequest } from '.';
describe('Anthropic transport', () => {
const executeFunctionsMock = mockDeep<IExecuteFunctions>();
beforeEach(() => {
jest.clearAllMocks();
});
it('should call httpRequestWithAuthentication with correct parameters', async () => {
executeFunctionsMock.getCredentials.mockResolvedValue({
url: 'https://custom-url.com',
});
await apiRequest.call(executeFunctionsMock, 'GET', '/v1/messages', {
headers: {
'Content-Type': 'application/json',
},
body: {
model: 'claude-sonnet-4-20250514',
messages: [{ role: 'user', content: 'Hello' }],
},
qs: {
test: 123,
},
});
expect(executeFunctionsMock.helpers.httpRequestWithAuthentication).toHaveBeenCalledWith(
'anthropicApi',
{
method: 'GET',
url: 'https://custom-url.com/v1/messages',
json: true,
body: {
model: 'claude-sonnet-4-20250514',
messages: [{ role: 'user', content: 'Hello' }],
},
qs: {
test: 123,
},
headers: {
'anthropic-version': '2023-06-01',
'anthropic-beta': 'files-api-2025-04-14',
'Content-Type': 'application/json',
},
},
);
});
it('should use the default url if no custom url is provided', async () => {
executeFunctionsMock.getCredentials.mockResolvedValue({});
await apiRequest.call(executeFunctionsMock, 'GET', '/v1/messages');
expect(executeFunctionsMock.helpers.httpRequestWithAuthentication).toHaveBeenCalledWith(
'anthropicApi',
{
method: 'GET',
url: 'https://api.anthropic.com/v1/messages',
json: true,
headers: {
'anthropic-version': '2023-06-01',
'anthropic-beta': 'files-api-2025-04-14',
},
},
);
});
it('should override the values with `option`', async () => {
executeFunctionsMock.getCredentials.mockResolvedValue({});
await apiRequest.call(executeFunctionsMock, 'GET', '', {
option: {
url: 'https://override-url.com',
returnFullResponse: true,
},
});
expect(executeFunctionsMock.helpers.httpRequestWithAuthentication).toHaveBeenCalledWith(
'anthropicApi',
{
method: 'GET',
url: 'https://override-url.com',
json: true,
returnFullResponse: true,
headers: {
'anthropic-version': '2023-06-01',
'anthropic-beta': 'files-api-2025-04-14',
},
},
);
});
it('should include prompt-tools beta when enableAnthropicBetas.promptTools is true', async () => {
executeFunctionsMock.getCredentials.mockResolvedValue({});
await apiRequest.call(executeFunctionsMock, 'POST', '/v1/messages', {
enableAnthropicBetas: {
promptTools: true,
},
});
expect(executeFunctionsMock.helpers.httpRequestWithAuthentication).toHaveBeenCalledWith(
'anthropicApi',
{
method: 'POST',
url: 'https://api.anthropic.com/v1/messages',
json: true,
headers: {
'anthropic-version': '2023-06-01',
'anthropic-beta': 'files-api-2025-04-14,prompt-tools-2025-04-02',
},
},
);
});
it('should include code-execution beta when enableAnthropicBetas.codeExecution is true', async () => {
executeFunctionsMock.getCredentials.mockResolvedValue({});
await apiRequest.call(executeFunctionsMock, 'POST', '/v1/messages', {
enableAnthropicBetas: {
codeExecution: true,
},
});
expect(executeFunctionsMock.helpers.httpRequestWithAuthentication).toHaveBeenCalledWith(
'anthropicApi',
{
method: 'POST',
url: 'https://api.anthropic.com/v1/messages',
json: true,
headers: {
'anthropic-version': '2023-06-01',
'anthropic-beta': 'files-api-2025-04-14,code-execution-2025-05-22',
},
},
);
});
it('should include both beta features when both are enabled', async () => {
executeFunctionsMock.getCredentials.mockResolvedValue({});
await apiRequest.call(executeFunctionsMock, 'POST', '/v1/messages', {
enableAnthropicBetas: {
promptTools: true,
codeExecution: true,
},
});
expect(executeFunctionsMock.helpers.httpRequestWithAuthentication).toHaveBeenCalledWith(
'anthropicApi',
{
method: 'POST',
url: 'https://api.anthropic.com/v1/messages',
json: true,
headers: {
'anthropic-version': '2023-06-01',
'anthropic-beta':
'files-api-2025-04-14,prompt-tools-2025-04-02,code-execution-2025-05-22',
},
},
);
});
it('should add custom header when header toggle is enabled', async () => {
executeFunctionsMock.getCredentials.mockResolvedValue({
header: true,
headerName: 'X-Custom-Header',
headerValue: 'custom-value-123',
});
await apiRequest.call(executeFunctionsMock, 'POST', '/v1/messages');
expect(executeFunctionsMock.helpers.httpRequestWithAuthentication).toHaveBeenCalledWith(
'anthropicApi',
{
method: 'POST',
url: 'https://api.anthropic.com/v1/messages',
json: true,
headers: {
'anthropic-version': '2023-06-01',
'anthropic-beta': 'files-api-2025-04-14',
'X-Custom-Header': 'custom-value-123',
},
},
);
});
it('should not add custom header when header toggle is disabled', async () => {
executeFunctionsMock.getCredentials.mockResolvedValue({
header: false,
headerName: 'X-Custom-Header',
headerValue: 'custom-value-123',
});
await apiRequest.call(executeFunctionsMock, 'POST', '/v1/messages');
expect(executeFunctionsMock.helpers.httpRequestWithAuthentication).toHaveBeenCalledWith(
'anthropicApi',
{
method: 'POST',
url: 'https://api.anthropic.com/v1/messages',
json: true,
headers: {
'anthropic-version': '2023-06-01',
'anthropic-beta': 'files-api-2025-04-14',
},
},
);
});
it('should add custom header along with other headers', async () => {
executeFunctionsMock.getCredentials.mockResolvedValue({
header: true,
headerName: 'X-Custom-Header',
headerValue: 'custom-value-123',
});
await apiRequest.call(executeFunctionsMock, 'POST', '/v1/messages', {
headers: {
'Content-Type': 'application/json',
},
});
expect(executeFunctionsMock.helpers.httpRequestWithAuthentication).toHaveBeenCalledWith(
'anthropicApi',
{
method: 'POST',
url: 'https://api.anthropic.com/v1/messages',
json: true,
headers: {
'anthropic-version': '2023-06-01',
'anthropic-beta': 'files-api-2025-04-14',
'Content-Type': 'application/json',
'X-Custom-Header': 'custom-value-123',
},
},
);
});
it('should handle custom header with custom URL', async () => {
executeFunctionsMock.getCredentials.mockResolvedValue({
url: 'https://custom-url.com',
header: true,
headerName: 'X-Custom-Header',
headerValue: 'custom-value-123',
});
await apiRequest.call(executeFunctionsMock, 'POST', '/v1/messages');
expect(executeFunctionsMock.helpers.httpRequestWithAuthentication).toHaveBeenCalledWith(
'anthropicApi',
{
method: 'POST',
url: 'https://custom-url.com/v1/messages',
json: true,
headers: {
'anthropic-version': '2023-06-01',
'anthropic-beta': 'files-api-2025-04-14',
'X-Custom-Header': 'custom-value-123',
},
},
);
});
});
@@ -0,0 +1,70 @@
import type FormData from 'form-data';
import type {
IDataObject,
IExecuteFunctions,
IHttpRequestMethods,
ILoadOptionsFunctions,
} from 'n8n-workflow';
type RequestParameters = {
headers?: IDataObject;
body?: IDataObject | string | FormData;
qs?: IDataObject;
option?: IDataObject;
enableAnthropicBetas?: {
promptTools?: boolean;
codeExecution?: boolean;
};
};
export async function apiRequest(
this: IExecuteFunctions | ILoadOptionsFunctions,
method: IHttpRequestMethods,
endpoint: string,
parameters?: RequestParameters,
) {
const { body, qs, option, headers } = parameters ?? {};
const credentials = await this.getCredentials('anthropicApi');
const baseUrl = credentials.url ?? 'https://api.anthropic.com';
const url = `${baseUrl}${endpoint}`;
const betas = ['files-api-2025-04-14'];
if (parameters?.enableAnthropicBetas?.promptTools) {
betas.push('prompt-tools-2025-04-02');
}
if (parameters?.enableAnthropicBetas?.codeExecution) {
betas.push('code-execution-2025-05-22');
}
const requestHeaders: IDataObject = {
'anthropic-version': '2023-06-01',
'anthropic-beta': betas.join(','),
...headers,
};
if (
credentials.header &&
typeof credentials.headerName === 'string' &&
credentials.headerName &&
typeof credentials.headerValue === 'string'
) {
requestHeaders[credentials.headerName] = credentials.headerValue;
}
const options = {
headers: requestHeaders,
method,
body,
qs,
url,
json: true,
};
if (option && Object.keys(option).length !== 0) {
Object.assign(options, option);
}
return await this.helpers.httpRequestWithAuthentication.call(this, 'anthropicApi', options);
}