Skip to content

Commit 9ba5a0f

Browse files
authored
🔧 Fix: Resolve Anthropic Client Issues 🧠 (danny-avila#1226)
* fix: correct preset title for Anthropic endpoint * fix(Settings/Anthropic): show correct default value for LLM temperature * fix(AnthropicClient): use `getModelMaxTokens` to get the correct LLM max context tokens, correctly set default temperature to 1, use only 2 params for class constructor, use `getResponseSender` to add correct sender to response message * refactor(/api/ask|edit/anthropic): save messages to database after the final response is sent to the client, and do not save conversation from route controller * fix(initializeClient/anthropic): correctly pass client options (endpointOption) to class initialization * feat(ModelService/Anthropic): add claude-1.2
1 parent f8e68b4 commit 9ba5a0f

File tree

9 files changed

+73
-45
lines changed

9 files changed

+73
-45
lines changed

api/app/clients/AnthropicClient.js

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,18 @@
1-
// const { Agent, ProxyAgent } = require('undici');
2-
const BaseClient = require('./BaseClient');
3-
const { encoding_for_model: encodingForModel, get_encoding: getEncoding } = require('tiktoken');
41
const Anthropic = require('@anthropic-ai/sdk');
2+
const { encoding_for_model: encodingForModel, get_encoding: getEncoding } = require('tiktoken');
3+
const { getResponseSender, EModelEndpoint } = require('~/server/routes/endpoints/schemas');
4+
const { getModelMaxTokens } = require('~/utils');
5+
const BaseClient = require('./BaseClient');
56

67
const HUMAN_PROMPT = '\n\nHuman:';
78
const AI_PROMPT = '\n\nAssistant:';
89

910
const tokenizersCache = {};
1011

1112
class AnthropicClient extends BaseClient {
12-
constructor(apiKey, options = {}, cacheOptions = {}, baseURL) {
13-
super(apiKey, options, cacheOptions);
13+
constructor(apiKey, options = {}) {
14+
super(apiKey, options);
1415
this.apiKey = apiKey || process.env.ANTHROPIC_API_KEY;
15-
this.sender = 'Anthropic';
16-
if (baseURL) {
17-
this.baseURL = baseURL;
18-
}
1916
this.userLabel = HUMAN_PROMPT;
2017
this.assistantLabel = AI_PROMPT;
2118
this.setOptions(options);
@@ -43,13 +40,13 @@ class AnthropicClient extends BaseClient {
4340
...modelOptions,
4441
// set some good defaults (check for undefined in some cases because they may be 0)
4542
model: modelOptions.model || 'claude-1',
46-
temperature: typeof modelOptions.temperature === 'undefined' ? 0.7 : modelOptions.temperature, // 0 - 1, 0.7 is recommended
43+
temperature: typeof modelOptions.temperature === 'undefined' ? 1 : modelOptions.temperature, // 0 - 1, 1 is default
4744
topP: typeof modelOptions.topP === 'undefined' ? 0.7 : modelOptions.topP, // 0 - 1, default: 0.7
4845
topK: typeof modelOptions.topK === 'undefined' ? 40 : modelOptions.topK, // 1-40, default: 40
4946
stop: modelOptions.stop, // no stop method for now
5047
};
5148

52-
this.maxContextTokens = this.options.maxContextTokens || 99999;
49+
this.maxContextTokens = getModelMaxTokens(this.modelOptions.model) ?? 100000;
5350
this.maxResponseTokens = this.modelOptions.maxOutputTokens || 1500;
5451
this.maxPromptTokens =
5552
this.options.maxPromptTokens || this.maxContextTokens - this.maxResponseTokens;
@@ -62,6 +59,14 @@ class AnthropicClient extends BaseClient {
6259
);
6360
}
6461

62+
this.sender =
63+
this.options.sender ??
64+
getResponseSender({
65+
model: this.modelOptions.model,
66+
endpoint: EModelEndpoint.anthropic,
67+
modelLabel: this.options.modelLabel,
68+
});
69+
6570
this.startToken = '||>';
6671
this.endToken = '';
6772
this.gptEncoder = this.constructor.getTokenizer('cl100k_base');
@@ -81,16 +86,15 @@ class AnthropicClient extends BaseClient {
8186
}
8287

8388
getClient() {
84-
if (this.baseURL) {
85-
return new Anthropic({
86-
apiKey: this.apiKey,
87-
baseURL: this.baseURL,
88-
});
89-
} else {
90-
return new Anthropic({
91-
apiKey: this.apiKey,
92-
});
89+
const options = {
90+
apiKey: this.apiKey,
91+
};
92+
93+
if (this.options.reverseProxyUrl) {
94+
options.baseURL = this.options.reverseProxyUrl;
9395
}
96+
97+
return new Anthropic(options);
9498
}
9599

96100
async buildMessages(messages, parentMessageId) {

api/server/routes/ask/anthropic.js

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ const {
99
setHeaders,
1010
validateEndpoint,
1111
buildEndpointOption,
12-
} = require('../../middleware');
13-
const { saveMessage, getConvoTitle, saveConvo, getConvo } = require('../../../models');
14-
const { sendMessage, createOnProgress } = require('../../utils');
12+
} = require('~/server/middleware');
13+
const { saveMessage, getConvoTitle, getConvo } = require('~/models');
14+
const { sendMessage, createOnProgress } = require('~/server/utils');
1515

1616
router.post('/abort', handleAbort());
1717

@@ -109,14 +109,6 @@ router.post('/', validateEndpoint, buildEndpointOption, setHeaders, async (req,
109109
response.parentMessageId = overrideParentMessageId;
110110
}
111111

112-
await saveConvo(user, {
113-
...endpointOption,
114-
...endpointOption.modelOptions,
115-
conversationId,
116-
endpoint: 'anthropic',
117-
});
118-
119-
await saveMessage({ ...response, user });
120112
sendMessage(res, {
121113
title: await getConvoTitle(user, conversationId),
122114
final: true,
@@ -126,6 +118,9 @@ router.post('/', validateEndpoint, buildEndpointOption, setHeaders, async (req,
126118
});
127119
res.end();
128120

121+
await saveMessage({ ...response, user });
122+
await saveMessage(userMessage);
123+
129124
// TODO: add anthropic titling
130125
} catch (error) {
131126
const partialText = getPartialText();

api/server/routes/edit/anthropic.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ const {
99
setHeaders,
1010
validateEndpoint,
1111
buildEndpointOption,
12-
} = require('../../middleware');
13-
const { saveMessage, getConvoTitle, getConvo } = require('../../../models');
14-
const { sendMessage, createOnProgress } = require('../../utils');
12+
} = require('~/server/middleware');
13+
const { saveMessage, getConvoTitle, getConvo } = require('~/models');
14+
const { sendMessage, createOnProgress } = require('~/server/utils');
1515

1616
router.post('/abort', handleAbort());
1717

@@ -119,7 +119,6 @@ router.post('/', validateEndpoint, buildEndpointOption, setHeaders, async (req,
119119
response.parentMessageId = overrideParentMessageId;
120120
}
121121

122-
await saveMessage({ ...response, user });
123122
sendMessage(res, {
124123
title: await getConvoTitle(user, conversationId),
125124
final: true,
@@ -129,6 +128,9 @@ router.post('/', validateEndpoint, buildEndpointOption, setHeaders, async (req,
129128
});
130129
res.end();
131130

131+
await saveMessage({ ...response, user });
132+
await saveMessage(userMessage);
133+
132134
// TODO: add anthropic titling
133135
} catch (error) {
134136
const partialText = getPartialText();

api/server/routes/endpoints/anthropic/initializeClient.js

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
const { AnthropicClient } = require('../../../../app');
2-
const { getUserKey, checkUserKeyExpiry } = require('../../../services/UserService');
1+
const { AnthropicClient } = require('~/app');
2+
const { getUserKey, checkUserKeyExpiry } = require('~/server/services/UserService');
33

4-
const initializeClient = async ({ req, res }) => {
5-
const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
4+
const initializeClient = async ({ req, res, endpointOption }) => {
5+
const { ANTHROPIC_API_KEY, ANTHROPIC_REVERSE_PROXY } = process.env;
66
const expiresAt = req.body.key;
77
const isUserProvided = ANTHROPIC_API_KEY === 'user_provided';
88

9-
let anthropicApiKey = isUserProvided ? await getAnthropicUserKey(req.user.id) : ANTHROPIC_API_KEY;
10-
let reverseProxy = process.env.ANTHROPIC_REVERSE_PROXY || undefined;
11-
console.log('ANTHROPIC_REVERSE_PROXY', reverseProxy);
9+
const anthropicApiKey = isUserProvided
10+
? await getAnthropicUserKey(req.user.id)
11+
: ANTHROPIC_API_KEY;
1212

1313
if (expiresAt && isUserProvided) {
1414
checkUserKeyExpiry(
@@ -17,7 +17,12 @@ const initializeClient = async ({ req, res }) => {
1717
);
1818
}
1919

20-
const client = new AnthropicClient(anthropicApiKey, { req, res }, {}, reverseProxy);
20+
const client = new AnthropicClient(anthropicApiKey, {
21+
req,
22+
res,
23+
reverseProxyUrl: ANTHROPIC_REVERSE_PROXY ?? null,
24+
...endpointOption,
25+
});
2126

2227
return {
2328
client,

api/server/services/ModelService.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ const getAnthropicModels = () => {
117117
let models = [
118118
'claude-2.1',
119119
'claude-2',
120+
'claude-1.2',
120121
'claude-1',
121122
'claude-1-100k',
122123
'claude-instant-1',

api/utils/tokens.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ const maxTokensMap = {
5151
'gpt-3.5-turbo-16k-0613': 15999,
5252
'gpt-3.5-turbo-1106': 16380, // -5 from max
5353
'gpt-4-1106': 127995, // -5 from max
54+
'claude-2.1': 200000,
55+
'claude-': 100000,
5456
};
5557

5658
/**

api/utils/tokens.spec.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,25 @@ describe('getModelMaxTokens', () => {
6262
expect(getModelMaxTokens('gpt-4-1106-preview')).toBe(maxTokensMap['gpt-4-1106']);
6363
expect(getModelMaxTokens('gpt-4-1106-vision-preview')).toBe(maxTokensMap['gpt-4-1106']);
6464
});
65+
66+
test('should return correct tokens for Anthropic models', () => {
67+
const models = [
68+
'claude-2.1',
69+
'claude-2',
70+
'claude-1.2',
71+
'claude-1',
72+
'claude-1-100k',
73+
'claude-instant-1',
74+
'claude-instant-1-100k',
75+
];
76+
77+
const claude21MaxTokens = maxTokensMap['claude-2.1'];
78+
const claudeMaxTokens = maxTokensMap['claude-'];
79+
models.forEach((model) => {
80+
const expectedTokens = model === 'claude-2.1' ? claude21MaxTokens : claudeMaxTokens;
81+
expect(getModelMaxTokens(model)).toEqual(expectedTokens);
82+
});
83+
});
6584
});
6685

6786
describe('matchModelName', () => {

client/src/components/Endpoints/Settings/Anthropic.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export default function Settings({ conversation, setOption, models, readonly }:
8686
<div className="flex justify-between">
8787
<Label htmlFor="temp-int" className="text-left text-sm font-medium">
8888
{localize('com_endpoint_temperature')}{' '}
89-
<small className="opacity-40">({localize('com_endpoint_default')}: 0.2)</small>
89+
<small className="opacity-40">({localize('com_endpoint_default')}: 1)</small>
9090
</Label>
9191
<InputNumber
9292
id="temp-int"

client/src/utils/presets.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export const getPresetTitle = (preset: TPreset) => {
2424
if (model) {
2525
_title += `: ${model}`;
2626
}
27-
} else if (endpoint === EModelEndpoint.google) {
27+
} else if (endpoint === EModelEndpoint.google || endpoint === EModelEndpoint.anthropic) {
2828
if (modelLabel) {
2929
_title = modelLabel;
3030
}

0 commit comments

Comments
 (0)