Skip to content

Commit 4b46bf9

Browse files
committed
🔧 refactor: Improve Params Handling, Remove Legacy Items, & Update Configs (#6074)
* chore: include all assets for service worker, remove unused tsconfig.node.json, eslint ignore vite config * chore: exclude image files from service worker caching * refactor: simplify googleSchema transformation and error handling * fix: max output tokens cap for 3.7 models * fix: skip index fixing in CI, development, and test environments * ci: add maxOutputTokens handling tests for Claude models * refactor: drop top_k and top_p parameters for claude-3.7 in AnthropicClient and add tests for new behavior * refactor: conditionally include top_k and top_p parameters for non-claude-3.7 models * ci: add unit tests for getLLMConfig function with various model options * chore: remove all OPENROUTER_API_KEY legacy logic * refactor: optimize stream chunk handling * feat: reset model parameters button * refactor: remove unused examples field from convoSchema and presetSchema * chore: update librechat-data-provider version to 0.7.6993 * refactor: move excludedKeys set to data-provider for better reusability * feat: enhance saveMessageToDatabase to handle unset fields and fetched conversation state * feat: add 'iconURL' and 'greeting' to excludedKeys in data provider config * fix: add optional chaining to user ID retrieval in getConvo call
1 parent 0490e40 commit 4b46bf9

25 files changed

+560
-234
lines changed

‎.env.example

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -209,12 +209,6 @@ ASSISTANTS_API_KEY=user_provided
209209
# More info, including how to enable use of Assistants with Azure here:
210210
# https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/azure#using-assistants-with-azure
211211

212-
#============#
213-
# OpenRouter #
214-
#============#
215-
# !!!Warning: Use the variable above instead of this one. Using this one will override the OpenAI endpoint
216-
# OPENROUTER_API_KEY=
217-
218212
#============#
219213
# Plugins #
220214
#============#

‎api/app/clients/AnthropicClient.js

Lines changed: 24 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ const {
77
getResponseSender,
88
validateVisionModel,
99
} = require('librechat-data-provider');
10-
const { SplitStreamHandler, GraphEvents } = require('@librechat/agents');
11-
const { encodeAndFormat } = require('~/server/services/Files/images/encode');
10+
const { SplitStreamHandler: _Handler, GraphEvents } = require('@librechat/agents');
1211
const {
1312
truncateText,
1413
formatMessage,
@@ -24,6 +23,7 @@ const {
2423
} = require('~/server/services/Endpoints/anthropic/helpers');
2524
const { getModelMaxTokens, getModelMaxOutputTokens, matchModelName } = require('~/utils');
2625
const { spendTokens, spendStructuredTokens } = require('~/models/spendTokens');
26+
const { encodeAndFormat } = require('~/server/services/Files/images/encode');
2727
const Tokenizer = require('~/server/services/Tokenizer');
2828
const { logger, sendEvent } = require('~/config');
2929
const { sleep } = require('~/server/utils');
@@ -32,6 +32,15 @@ const BaseClient = require('./BaseClient');
3232
const HUMAN_PROMPT = '\n\nHuman:';
3333
const AI_PROMPT = '\n\nAssistant:';
3434

35+
class SplitStreamHandler extends _Handler {
36+
getDeltaContent(chunk) {
37+
return (chunk?.delta?.text ?? chunk?.completion) || '';
38+
}
39+
getReasoningDelta(chunk) {
40+
return chunk?.delta?.thinking || '';
41+
}
42+
}
43+
3544
/** Helper function to introduce a delay before retrying */
3645
function delayBeforeRetry(attempts, baseDelay = 1000) {
3746
return new Promise((resolve) => setTimeout(resolve, baseDelay * attempts));
@@ -105,7 +114,9 @@ class AnthropicClient extends BaseClient {
105114

106115
const modelMatch = matchModelName(this.modelOptions.model, EModelEndpoint.anthropic);
107116
this.isClaude3 = modelMatch.includes('claude-3');
108-
this.isLegacyOutput = !modelMatch.includes('claude-3-5-sonnet');
117+
this.isLegacyOutput = !(
118+
/claude-3[-.]5-sonnet/.test(modelMatch) || /claude-3[-.]7/.test(modelMatch)
119+
);
109120
this.supportsCacheControl = this.options.promptCache && checkPromptCacheSupport(modelMatch);
110121

111122
if (
@@ -733,10 +744,17 @@ class AnthropicClient extends BaseClient {
733744
stop_sequences,
734745
temperature,
735746
metadata,
736-
top_p,
737-
top_k,
738747
};
739748

749+
if (!/claude-3[-.]7/.test(model)) {
750+
if (top_p !== undefined) {
751+
requestOptions.top_p = top_p;
752+
}
753+
if (top_k !== undefined) {
754+
requestOptions.top_k = top_k;
755+
}
756+
}
757+
740758
if (this.useMessages) {
741759
requestOptions.messages = payload;
742760
requestOptions.max_tokens =
@@ -798,50 +816,16 @@ class AnthropicClient extends BaseClient {
798816
}
799817
});
800818

801-
/** @param {string} chunk */
802-
const handleChunk = (chunk) => {
803-
this.streamHandler.handle({
804-
choices: [
805-
{
806-
delta: {
807-
content: chunk,
808-
},
809-
},
810-
],
811-
});
812-
};
813-
/** @param {string} chunk */
814-
const handleReasoningChunk = (chunk) => {
815-
this.streamHandler.handle({
816-
choices: [
817-
{
818-
delta: {
819-
reasoning_content: chunk,
820-
},
821-
},
822-
],
823-
});
824-
};
825-
826819
for await (const completion of response) {
827-
// Handle each completion as before
828820
const type = completion?.type ?? '';
829821
if (tokenEventTypes.has(type)) {
830822
logger.debug(`[AnthropicClient] ${type}`, completion);
831823
this[type] = completion;
832824
}
833-
if (completion?.delta?.thinking) {
834-
handleReasoningChunk(completion.delta.thinking);
835-
} else if (completion?.delta?.text) {
836-
handleChunk(completion.delta.text);
837-
} else if (completion.completion) {
838-
handleChunk(completion.completion);
839-
}
840-
825+
this.streamHandler.handle(completion);
841826
await sleep(streamRate);
842827
}
843828

844-
// Successful processing, exit loop
845829
break;
846830
} catch (error) {
847831
attempts += 1;

‎api/app/clients/BaseClient.js

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ const {
55
isAgentsEndpoint,
66
isParamEndpoint,
77
EModelEndpoint,
8+
excludedKeys,
89
ErrorTypes,
910
Constants,
1011
} = require('librechat-data-provider');
11-
const { getMessages, saveMessage, updateMessage, saveConvo } = require('~/models');
12+
const { getMessages, saveMessage, updateMessage, saveConvo, getConvo } = require('~/models');
1213
const { addSpaceIfNeeded, isEnabled } = require('~/server/utils');
1314
const { truncateToolCallOutputs } = require('./prompts');
1415
const checkBalance = require('~/models/checkBalance');
@@ -55,6 +56,10 @@ class BaseClient {
5556
* Flag to determine if the client re-submitted the latest assistant message.
5657
* @type {boolean | undefined} */
5758
this.continued;
59+
/**
60+
* Flag to determine if the client has already fetched the conversation while saving new messages.
61+
* @type {boolean | undefined} */
62+
this.fetchedConvo;
5863
/** @type {TMessage[]} */
5964
this.currentMessages = [];
6065
/** @type {import('librechat-data-provider').VisionModes | undefined} */
@@ -863,16 +868,39 @@ class BaseClient {
863868
return { message: savedMessage };
864869
}
865870

866-
const conversation = await saveConvo(
867-
this.options.req,
868-
{
869-
conversationId: message.conversationId,
870-
endpoint: this.options.endpoint,
871-
endpointType: this.options.endpointType,
872-
...endpointOptions,
873-
},
874-
{ context: 'api/app/clients/BaseClient.js - saveMessageToDatabase #saveConvo' },
875-
);
871+
const fieldsToKeep = {
872+
conversationId: message.conversationId,
873+
endpoint: this.options.endpoint,
874+
endpointType: this.options.endpointType,
875+
...endpointOptions,
876+
};
877+
878+
const existingConvo =
879+
this.fetchedConvo === true
880+
? null
881+
: await getConvo(this.options.req?.user?.id, message.conversationId);
882+
883+
const unsetFields = {};
884+
if (existingConvo != null) {
885+
this.fetchedConvo = true;
886+
for (const key in existingConvo) {
887+
if (!key) {
888+
continue;
889+
}
890+
if (excludedKeys.has(key)) {
891+
continue;
892+
}
893+
894+
if (endpointOptions?.[key] === undefined) {
895+
unsetFields[key] = 1;
896+
}
897+
}
898+
}
899+
900+
const conversation = await saveConvo(this.options.req, fieldsToKeep, {
901+
context: 'api/app/clients/BaseClient.js - saveMessageToDatabase #saveConvo',
902+
unsetFields,
903+
});
876904

877905
return { message: savedMessage, conversation };
878906
}

‎api/app/clients/OpenAIClient.js

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,7 @@ class OpenAIClient extends BaseClient {
109109
const omniPattern = /\b(o1|o3)\b/i;
110110
this.isOmni = omniPattern.test(this.modelOptions.model);
111111

112-
const { OPENROUTER_API_KEY, OPENAI_FORCE_PROMPT } = process.env ?? {};
113-
if (OPENROUTER_API_KEY && !this.azure) {
114-
this.apiKey = OPENROUTER_API_KEY;
115-
this.useOpenRouter = true;
116-
}
117-
112+
const { OPENAI_FORCE_PROMPT } = process.env ?? {};
118113
const { reverseProxyUrl: reverseProxy } = this.options;
119114

120115
if (!this.useOpenRouter && reverseProxy && reverseProxy.includes(KnownEndpoints.openrouter)) {

0 commit comments

Comments
 (0)