feat(Anthropic Chat Model Node): Add configurable base URL for Anthropic API (#15063)

This commit is contained in:
oleg
2025-05-05 09:57:10 +02:00
committed by GitHub
parent 54499fc6d1
commit 4b5f045281
4 changed files with 56 additions and 6 deletions

View File

@@ -21,6 +21,13 @@ export class AnthropicApi implements ICredentialType {
required: true, required: true,
default: '', default: '',
}, },
{
displayName: 'Base URL',
name: 'url',
type: 'string',
default: 'https://api.anthropic.com',
description: 'Override the default base URL for the API',
},
]; ];
authenticate: IAuthenticateGeneric = { authenticate: IAuthenticateGeneric = {
@@ -34,7 +41,7 @@ export class AnthropicApi implements ICredentialType {
test: ICredentialTestRequest = { test: ICredentialTestRequest = {
request: { request: {
baseURL: 'https://api.anthropic.com', baseURL: '={{$credentials?.url}}',
url: '/v1/messages', url: '/v1/messages',
method: 'POST', method: 'POST',
headers: { headers: {

View File

@@ -266,8 +266,10 @@ export class LmChatAnthropic implements INodeType {
}; };
async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> { async supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {
const credentials = await this.getCredentials('anthropicApi'); const credentials = await this.getCredentials<{ url?: string; apiKey?: string }>(
'anthropicApi',
);
const baseURL = credentials.url ?? 'https://api.anthropic.com';
const version = this.getNode().typeVersion; const version = this.getNode().typeVersion;
const modelName = const modelName =
version >= 1.3 version >= 1.3
@@ -317,8 +319,9 @@ export class LmChatAnthropic implements INodeType {
} }
const model = new ChatAnthropic({ const model = new ChatAnthropic({
anthropicApiKey: credentials.apiKey as string, anthropicApiKey: credentials.apiKey,
modelName, modelName,
anthropicApiUrl: baseURL,
maxTokens: options.maxTokensToSample, maxTokens: options.maxTokensToSample,
temperature: options.temperature, temperature: options.temperature,
topK: options.topK, topK: options.topK,

View File

@@ -40,6 +40,7 @@ describe('searchModels', () => {
beforeEach(() => { beforeEach(() => {
mockContext = { mockContext = {
getCredentials: jest.fn().mockResolvedValue({}),
helpers: { helpers: {
httpRequestWithAuthentication: jest.fn().mockResolvedValue({ httpRequestWithAuthentication: jest.fn().mockResolvedValue({
data: mockModels, data: mockModels,
@@ -50,11 +51,47 @@ describe('searchModels', () => {
afterEach(() => { afterEach(() => {
jest.clearAllMocks(); jest.clearAllMocks();
// Reset the getCredentials mock to its default value
mockContext.getCredentials = jest.fn().mockResolvedValue({});
}); });
it('should fetch models from Anthropic API', async () => { it('should fetch models from default Anthropic API URL when no custom URL is provided', async () => {
const result = await searchModels.call(mockContext); const result = await searchModels.call(mockContext);
expect(mockContext.getCredentials).toHaveBeenCalledWith('anthropicApi');
expect(mockContext.helpers.httpRequestWithAuthentication).toHaveBeenCalledWith('anthropicApi', {
url: 'https://api.anthropic.com/v1/models',
headers: {
'anthropic-version': '2023-06-01',
},
});
expect(result.results).toHaveLength(5);
});
it('should fetch models from custom Anthropic API URL when provided in credentials', async () => {
const customUrl = 'https://custom-anthropic-api.example.com';
// Override the default mock to return credentials with a custom URL
mockContext.getCredentials = jest.fn().mockResolvedValue({ url: customUrl });
const result = await searchModels.call(mockContext);
expect(mockContext.getCredentials).toHaveBeenCalledWith('anthropicApi');
expect(mockContext.helpers.httpRequestWithAuthentication).toHaveBeenCalledWith('anthropicApi', {
url: `${customUrl}/v1/models`,
headers: {
'anthropic-version': '2023-06-01',
},
});
expect(result.results).toHaveLength(5);
});
it('should use default URL when empty URL is provided in credentials', async () => {
// Override the default mock to return credentials with an empty URL
mockContext.getCredentials = jest.fn().mockResolvedValue({ url: null });
const result = await searchModels.call(mockContext);
expect(mockContext.getCredentials).toHaveBeenCalledWith('anthropicApi');
expect(mockContext.helpers.httpRequestWithAuthentication).toHaveBeenCalledWith('anthropicApi', { expect(mockContext.helpers.httpRequestWithAuthentication).toHaveBeenCalledWith('anthropicApi', {
url: 'https://api.anthropic.com/v1/models', url: 'https://api.anthropic.com/v1/models',
headers: { headers: {

View File

@@ -15,8 +15,11 @@ export async function searchModels(
this: ILoadOptionsFunctions, this: ILoadOptionsFunctions,
filter?: string, filter?: string,
): Promise<INodeListSearchResult> { ): Promise<INodeListSearchResult> {
const credentials = await this.getCredentials<{ url?: string }>('anthropicApi');
const baseURL = credentials.url ?? 'https://api.anthropic.com';
const response = (await this.helpers.httpRequestWithAuthentication.call(this, 'anthropicApi', { const response = (await this.helpers.httpRequestWithAuthentication.call(this, 'anthropicApi', {
url: 'https://api.anthropic.com/v1/models', url: `${baseURL}/v1/models`,
headers: { headers: {
'anthropic-version': '2023-06-01', 'anthropic-version': '2023-06-01',
}, },