Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

replace {{current_date}} server side for OpenAI, Anthropic and Assistants #4335

Closed
wants to merge 35 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
4e82416
replace {{current_date}} server side for OpenAI, Anthropic and Assist…
olivierhub Oct 3, 2024
4b329e4
Merge branch 'main' into main
owengo Oct 4, 2024
f7343cf
Update AnthropicClient.js
owengo Oct 7, 2024
2ed430c
💬 fix: adjust regex in ModelService to recognize o1 models
juwalter Oct 7, 2024
24750b3
🔨 fix(ToolCall): Check output string type before performing `.toLower…
hanna-daoud Oct 8, 2024
d36ef94
🧠 feat: Implement O1 Model Support for Max Tokens Handling (#4376)
danny-avila Oct 10, 2024
8de7f56
🔨 fix(AzureOpenAI): o1 model, `stream` parameter check (#4381)
ABHIJITH-EA Oct 10, 2024
4d57369
🤖 feat: Enhance Assistant Model Handling for Model Specs (#4390)
danny-avila Oct 11, 2024
794921f
🪙 feat: Update token value for gpt-4o (#4387)
hongkai-neu Oct 11, 2024
a134889
* (feature) global presets in database
olivierhub Oct 11, 2024
384c8f3
* (wip) pass custom header x-openai-thread-id for action requests
olivierhub Oct 13, 2024
434b085
✨ feat: Add `GOOGLE_LOC` environment variable (#4395)
berry-13 Oct 15, 2024
f270cf3
🕒 feat: Add 5-second timeout for Fetching Model Lists (#4423)
danny-avila Oct 15, 2024
1838848
📦 chore: npm package audit (#4424)
danny-avila Oct 16, 2024
2296836
⬆️ feat: Cancel chat file uploads; fix: Assistant uploads (#4433)
danny-avila Oct 16, 2024
b1f6e25
🤖 fix: Minor Assistants Issues (#4436)
danny-avila Oct 16, 2024
53b217d
🐋 chore: remove Docker version syntax as its no longer (#4375)
matsubo Oct 19, 2024
1302d62
🔃 refactor: rename all server endpoints to use same file names (#4364)
adrianfagerland Oct 19, 2024
eefd4be
🎨 refactor: UI stlye (#4438)
berry-13 Oct 19, 2024
ade4f76
🖼️ feat: Add dat.gui to Artifacts UI libs (#4344)
lx-0 Oct 19, 2024
f6a81ad
🧪 chore: raise max temperature to 2 for OpenAI/Custom Endpoints
danny-avila Oct 19, 2024
bd249ec
🎨 style: UI Style Enhancements and Refactor for Improved Consistency …
berry-13 Oct 20, 2024
13b9734
🔐 refactor: Unverified User Verification Logic (#4482)
danny-avila Oct 21, 2024
3f0cafa
chore: bump librechat-data-provider
danny-avila Oct 21, 2024
5848d36
🤖 fix: Address Minor Agent Issues (#4483)
danny-avila Oct 21, 2024
dc7613a
🖼️ fix: Avatar Handling for Agents and Assistants (#4507)
danny-avila Oct 22, 2024
37530f0
🤖 feat: Add support for `claude-3-5-sonnet-20241022` (#4510)
danny-avila Oct 22, 2024
785d240
👓 fix: Assistants Vision Prompt Error Handling (legacy) (#4529)
danny-avila Oct 23, 2024
a6f9773
🔘 a11y: Switch Contrast and File Input Key Events to WCAG (#4536)
danny-avila Oct 24, 2024
1ddc005
🎚️ fix: Google `top_k` Slider Step to Integers (#4537)
danny-avila Oct 24, 2024
8677080
🌏 i18n: modify username min length in Ko.ts (3→2) (#4532)
Kim-Jaemin420 Oct 24, 2024
2a7e33b
🍎 fix: Update "Enter to send" behavior for Mac users (#4539)
danny-avila Oct 24, 2024
ff7d953
🌏 i18n: Added Missing Localizations (Ar, De, Es, Fr, It, Jp, Ko, Ru, …
danny-avila Oct 24, 2024
1d021d3
🛡️ fix: Minor Vulnerabilities (#4543)
danny-avila Oct 24, 2024
073c3d9
✨ v0.7.5 (#4541)
danny-avila Oct 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: "3.8"

services:
app:
build:
Expand Down
4 changes: 3 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ PROXY=
#============#

ANTHROPIC_API_KEY=user_provided
# ANTHROPIC_MODELS=claude-3-5-sonnet-20240620,claude-3-opus-20240229,claude-3-sonnet-20240229,claude-3-haiku-20240307,claude-2.1,claude-2,claude-1.2,claude-1,claude-1-100k,claude-instant-1,claude-instant-1-100k
# ANTHROPIC_MODELS=claude-3-5-sonnet-20241022,claude-3-5-sonnet-latest,claude-3-5-sonnet-20240620,claude-3-opus-20240229,claude-3-sonnet-20240229,claude-3-haiku-20240307,claude-2.1,claude-2,claude-1.2,claude-1,claude-1-100k,claude-instant-1,claude-instant-1-100k
# ANTHROPIC_REVERSE_PROXY=

#============#
Expand Down Expand Up @@ -146,6 +146,8 @@ GOOGLE_KEY=user_provided

# GOOGLE_TITLE_MODEL=gemini-pro

# GOOGLE_LOC=us-central1

# Google Safety Settings
# NOTE: These settings apply to both Vertex AI and Gemini API (AI Studio)
#
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# v0.7.5-rc2
# v0.7.5

# Base node image
FROM node:20-alpine AS node
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile.multi
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Dockerfile.multi
# v0.7.5-rc2
# v0.7.5

# Base for all builds
FROM node:20-alpine AS base
Expand Down
7 changes: 5 additions & 2 deletions api/app/clients/AnthropicClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const {
parseParamFromPrompt,
createContextHandlers,
} = require('./prompts');
const { getModelMaxTokens, getModelMaxOutputTokens, matchModelName } = require('~/utils');
const { getModelMaxTokens, getModelMaxOutputTokens, matchModelName, replaceSpecialVars} = require('~/utils');
const { spendTokens, spendStructuredTokens } = require('~/models/spendTokens');
const { sleep } = require('~/server/utils');
const BaseClient = require('./BaseClient');
Expand Down Expand Up @@ -494,7 +494,7 @@ class AnthropicClient extends BaseClient {
identityPrefix = `${identityPrefix}\nYou are ${this.options.modelLabel}`;
}

let promptPrefix = (this.options.promptPrefix ?? '').trim();
let promptPrefix = replaceSpecialVars((this.options.promptPrefix ?? '').trim());
if (typeof this.options.artifactsPrompt === 'string' && this.options.artifactsPrompt) {
promptPrefix = `${promptPrefix ?? ''}\n${this.options.artifactsPrompt}`.trim();
}
Expand Down Expand Up @@ -682,6 +682,9 @@ class AnthropicClient extends BaseClient {
*/
checkPromptCacheSupport(modelName) {
const modelMatch = matchModelName(modelName, EModelEndpoint.anthropic);
if (modelMatch.includes('claude-3-5-sonnet-latest')) {
return false;
}
if (
modelMatch === 'claude-3-5-sonnet' ||
modelMatch === 'claude-3-haiku' ||
Expand Down
4 changes: 3 additions & 1 deletion api/app/clients/GoogleClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const {
} = require('./prompts');
const BaseClient = require('./BaseClient');

const loc = 'us-central1';
const loc = process.env.GOOGLE_LOC || 'us-central1';
const publisher = 'google';
const endpointPrefix = `https://${loc}-aiplatform.googleapis.com`;
// const apiEndpoint = loc + '-aiplatform.googleapis.com';
Expand Down Expand Up @@ -593,6 +593,8 @@ class GoogleClient extends BaseClient {

createLLM(clientOptions) {
const model = clientOptions.modelName ?? clientOptions.model;
clientOptions.location = loc;
clientOptions.endpoint = `${loc}-aiplatform.googleapis.com`;
if (this.project_id && this.isTextModel) {
logger.debug('Creating Google VertexAI client');
return new GoogleVertexAI(clientOptions);
Expand Down
4 changes: 3 additions & 1 deletion api/app/clients/OllamaClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ class OllamaClient {
try {
const ollamaEndpoint = deriveBaseURL(baseURL);
/** @type {Promise<AxiosResponse<OllamaListResponse>>} */
const response = await axios.get(`${ollamaEndpoint}/api/tags`);
const response = await axios.get(`${ollamaEndpoint}/api/tags`, {
timeout: 5000,
});
models = response.data.models.map((tag) => tag.name);
return models;
} catch (error) {
Expand Down
45 changes: 27 additions & 18 deletions api/app/clients/OpenAIClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const {
getModelMaxTokens,
genAzureChatCompletion,
getModelMaxOutputTokens,
replaceSpecialVars,
} = require('~/utils');
const {
truncateText,
Expand Down Expand Up @@ -68,6 +69,8 @@ class OpenAIClient extends BaseClient {

/** @type {OpenAIUsageMetadata | undefined} */
this.usage;
/** @type {boolean|undefined} */
this.isO1Model;
}

// TODO: PluginsClient calls this 3x, unneeded
Expand Down Expand Up @@ -98,6 +101,8 @@ class OpenAIClient extends BaseClient {
this.options.modelOptions,
);

this.isO1Model = /\bo1\b/i.test(this.modelOptions.model);

this.defaultVisionModel = this.options.visionModel ?? 'gpt-4-vision-preview';
if (typeof this.options.attachments?.then === 'function') {
this.options.attachments.then((attachments) => this.checkVisionRequest(attachments));
Expand Down Expand Up @@ -545,9 +550,8 @@ class OpenAIClient extends BaseClient {
promptPrefix = this.augmentedPrompt + promptPrefix;
}

const isO1Model = /\bo1\b/i.test(this.modelOptions.model);
if (promptPrefix && !isO1Model) {
promptPrefix = `Instructions:\n${promptPrefix.trim()}`;
if (promptPrefix && this.isO1Model !== true) {
promptPrefix = `Instructions:\n${replaceSpecialVars(promptPrefix.trim())}`;
instructions = {
role: 'system',
name: 'instructions',
Expand Down Expand Up @@ -575,7 +579,7 @@ class OpenAIClient extends BaseClient {
};

/** EXPERIMENTAL */
if (promptPrefix && isO1Model) {
if (promptPrefix && this.isO1Model === true) {
const lastUserMessageIndex = payload.findLastIndex((message) => message.role === 'user');
if (lastUserMessageIndex !== -1) {
payload[
Expand Down Expand Up @@ -839,27 +843,27 @@ class OpenAIClient extends BaseClient {
}

const titleChatCompletion = async () => {
modelOptions.model = model;
try {
modelOptions.model = model;

if (this.azure) {
modelOptions.model = process.env.AZURE_OPENAI_DEFAULT_MODEL ?? modelOptions.model;
this.azureEndpoint = genAzureChatCompletion(this.azure, modelOptions.model, this);
}
if (this.azure) {
modelOptions.model = process.env.AZURE_OPENAI_DEFAULT_MODEL ?? modelOptions.model;
this.azureEndpoint = genAzureChatCompletion(this.azure, modelOptions.model, this);
}

const instructionsPayload = [
{
role: this.options.titleMessageRole ?? (this.isOllama ? 'user' : 'system'),
content: `Please generate ${titleInstruction}
const instructionsPayload = [
{
role: this.options.titleMessageRole ?? (this.isOllama ? 'user' : 'system'),
content: `Please generate ${titleInstruction}

${convo}

||>Title:`,
},
];
},
];

const promptTokens = this.getTokenCountForMessage(instructionsPayload[0]);
const promptTokens = this.getTokenCountForMessage(instructionsPayload[0]);

try {
let useChatCompletion = true;

if (this.options.reverseProxyUrl === CohereConstants.API_URL) {
Expand Down Expand Up @@ -1227,6 +1231,11 @@ ${convo}
opts.defaultHeaders = { ...opts.defaultHeaders, 'api-key': this.apiKey };
}

if (this.isO1Model === true && modelOptions.max_tokens != null) {
modelOptions.max_completion_tokens = modelOptions.max_tokens;
delete modelOptions.max_tokens;
}

if (process.env.OPENAI_ORGANIZATION) {
opts.organization = process.env.OPENAI_ORGANIZATION;
}
Expand Down Expand Up @@ -1301,7 +1310,7 @@ ${convo}
/** @type {(value: void | PromiseLike<void>) => void} */
let streamResolve;

if (modelOptions.stream && /\bo1\b/i.test(modelOptions.model)) {
if (modelOptions.stream && this.isO1Model) {
delete modelOptions.stream;
delete modelOptions.stop;
}
Expand Down
30 changes: 27 additions & 3 deletions api/app/clients/specs/AnthropicClient.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,10 +201,10 @@ describe('AnthropicClient', () => {
);
});

it('should add beta header for claude-3-5-sonnet model', () => {
it('should add "max-tokens" & "prompt-caching" beta header for claude-3-5-sonnet model', () => {
const client = new AnthropicClient('test-api-key');
const modelOptions = {
model: 'claude-3-5-sonnet-20240307',
model: 'claude-3-5-sonnet-20241022',
};
client.setOptions({ modelOptions, promptCache: true });
const anthropicClient = client.getClient(modelOptions);
Expand All @@ -215,7 +215,7 @@ describe('AnthropicClient', () => {
);
});

it('should add beta header for claude-3-haiku model', () => {
it('should add "prompt-caching" beta header for claude-3-haiku model', () => {
const client = new AnthropicClient('test-api-key');
const modelOptions = {
model: 'claude-3-haiku-2028',
Expand All @@ -229,6 +229,30 @@ describe('AnthropicClient', () => {
);
});

it('should add "prompt-caching" beta header for claude-3-opus model', () => {
const client = new AnthropicClient('test-api-key');
const modelOptions = {
model: 'claude-3-opus-2028',
};
client.setOptions({ modelOptions, promptCache: true });
const anthropicClient = client.getClient(modelOptions);
expect(anthropicClient._options.defaultHeaders).toBeDefined();
expect(anthropicClient._options.defaultHeaders).toHaveProperty('anthropic-beta');
expect(anthropicClient._options.defaultHeaders['anthropic-beta']).toBe(
'prompt-caching-2024-07-31',
);
});

it('should not add beta header for claude-3-5-sonnet-latest model', () => {
const client = new AnthropicClient('test-api-key');
const modelOptions = {
model: 'anthropic/claude-3-5-sonnet-latest',
};
client.setOptions({ modelOptions, promptCache: true });
const anthropicClient = client.getClient(modelOptions);
expect(anthropicClient.defaultHeaders).not.toHaveProperty('anthropic-beta');
});

it('should not add beta header for other models', () => {
const client = new AnthropicClient('test-api-key');
client.setOptions({
Expand Down
49 changes: 45 additions & 4 deletions api/models/Preset.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ const { logger } = require('~/config');

const getPreset = async (user, presetId) => {
try {
return await Preset.findOne({ user, presetId }).lean();
// Try to find user-specific preset
let preset = await Preset.findOne({ user, presetId }).lean();
if (!preset) {
// If not found, try to find global preset
preset = await Preset.findOne({ presetId, isGlobal: true }).lean();
}
return preset;
} catch (error) {
logger.error('[getPreset] Error getting single preset', error);
return { message: 'Error getting single preset' };
Expand All @@ -15,7 +21,19 @@ module.exports = {
getPreset,
getPresets: async (user, filter) => {
try {
const presets = await Preset.find({ ...filter, user }).lean();

// Fetch user-specific presets
const userPresetsPromise = Preset.find({ ...filter, user }).lean();
// Fetch global presets
const globalPresetsPromise = Preset.find({ ...filter, isGlobal: true }).lean();

const [userPresets, globalPresets] = await Promise.all([
userPresetsPromise,
globalPresetsPromise,
]);

// Combine global and user-specific presets
let presets = [...globalPresets, ...userPresets];
const defaultValue = 10000;

presets.sort((a, b) => {
Expand All @@ -28,7 +46,6 @@ module.exports = {

return b.updatedAt - a.updatedAt;
});

return presets;
} catch (error) {
logger.error('[getPresets] Error getting presets', error);
Expand All @@ -37,8 +54,32 @@ module.exports = {
},
savePreset: async (user, { presetId, newPresetId, defaultPreset, ...preset }) => {
try {
// Check if presetId corresponds to a global preset
if (presetId && !newPresetId) {
const existingPreset = await Preset.findOne({ presetId, isGlobal: true });
if (existingPreset) {
// User is attempting to edit a global preset
// Create a copy for the user
presetId = `user-${user}-${presetId}-${Date.now()}`; // Generate a unique presetId for the user

const { _id, createdAt, updatedAt, _v,isGlobal, ...presetWithoutIdAndTimestamps } = preset;

// Prepare the new preset data
const newPreset = {
presetId: presetId,
user: user,
isGlobal: false,
...presetWithoutIdAndTimestamps,
};
// Assign the modified preset back
preset = newPreset;

}
}

const setter = { $set: {} };
const update = { presetId, ...preset };
const { user: _, ...cleanPreset } = preset;
const update = { presetId, ...cleanPreset };
if (preset.tools && Array.isArray(preset.tools)) {
update.tools =
preset.tools
Expand Down
5 changes: 3 additions & 2 deletions api/models/Prompt.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const {
removeGroupFromAllProjects,
} = require('./Project');
const { Prompt, PromptGroup } = require('./schema/promptSchema');
const { escapeRegExp } = require('~/server/utils');
const { logger } = require('~/config');

/**
Expand Down Expand Up @@ -106,7 +107,7 @@ const getAllPromptGroups = async (req, filter) => {
let searchShared = true;
let searchSharedOnly = false;
if (name) {
query.name = new RegExp(name, 'i');
query.name = new RegExp(escapeRegExp(name), 'i');
}
if (!query.category) {
delete query.category;
Expand Down Expand Up @@ -159,7 +160,7 @@ const getPromptGroups = async (req, filter) => {
let searchShared = true;
let searchSharedOnly = false;
if (name) {
query.name = new RegExp(name, 'i');
query.name = new RegExp(escapeRegExp(name), 'i');
}
if (!query.category) {
delete query.category;
Expand Down
5 changes: 5 additions & 0 deletions api/models/schema/presetSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ const presetSchema = mongoose.Schema(
type: String,
default: null,
},
isGlobal: {
type: Boolean,
default: false,
index: true,
},
defaultPreset: {
type: Boolean,
},
Expand Down
8 changes: 4 additions & 4 deletions api/models/tx.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ const tokenValues = Object.assign(
'o1-preview': { prompt: 15, completion: 60 },
'o1-mini': { prompt: 3, completion: 12 },
o1: { prompt: 15, completion: 60 },
'gpt-4o-2024-08-06': { prompt: 2.5, completion: 10 },
'gpt-4o-mini': { prompt: 0.15, completion: 0.6 },
'gpt-4o': { prompt: 5, completion: 15 },
'gpt-4o': { prompt: 2.5, completion: 10 },
'gpt-4o-2024-05-13': { prompt: 5, completion: 15 },
'gpt-4-1106': { prompt: 10, completion: 30 },
'gpt-3.5-turbo-0125': { prompt: 0.5, completion: 1.5 },
'claude-3-opus': { prompt: 15, completion: 75 },
Expand Down Expand Up @@ -104,8 +104,8 @@ const getValueKey = (model, endpoint) => {
return 'o1-mini';
} else if (modelName.includes('o1')) {
return 'o1';
} else if (modelName.includes('gpt-4o-2024-08-06')) {
return 'gpt-4o-2024-08-06';
} else if (modelName.includes('gpt-4o-2024-05-13')) {
return 'gpt-4o-2024-05-13';
} else if (modelName.includes('gpt-4o-mini')) {
return 'gpt-4o-mini';
} else if (modelName.includes('gpt-4o')) {
Expand Down
Loading