Skip to content

Commit

Permalink
Merge branch 'main' into feat/azure-assistant-v2-filesearch
Browse files Browse the repository at this point in the history
  • Loading branch information
adnandothussain committed Dec 19, 2024
2 parents 5a2bb03 + d68c874 commit 1383bd4
Show file tree
Hide file tree
Showing 35 changed files with 889 additions and 158 deletions.
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,10 @@ BINGAI_TOKEN=user_provided
#============#

GOOGLE_KEY=user_provided

# GOOGLE_REVERSE_PROXY=
# Some reverse proxies do not support the X-goog-api-key header, uncomment to pass the API key in Authorization header instead.
# GOOGLE_AUTH_HEADER=true

# Gemini API (AI Studio)
# GOOGLE_MODELS=gemini-2.0-flash-exp,gemini-exp-1121,gemini-exp-1114,gemini-1.5-flash-latest,gemini-1.0-pro,gemini-1.0-pro-001,gemini-1.0-pro-latest,gemini-1.0-pro-vision-latest,gemini-1.5-pro-latest,gemini-pro,gemini-pro-vision
Expand Down Expand Up @@ -167,6 +170,7 @@ GOOGLE_KEY=user_provided
# GOOGLE_SAFETY_HATE_SPEECH=BLOCK_ONLY_HIGH
# GOOGLE_SAFETY_HARASSMENT=BLOCK_ONLY_HIGH
# GOOGLE_SAFETY_DANGEROUS_CONTENT=BLOCK_ONLY_HIGH
# GOOGLE_SAFETY_CIVIC_INTEGRITY=BLOCK_ONLY_HIGH

#============#
# OpenAI #
Expand Down
36 changes: 27 additions & 9 deletions api/app/clients/GoogleClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ const BaseClient = require('./BaseClient');

const loc = process.env.GOOGLE_LOC || 'us-central1';
const publisher = 'google';
const endpointPrefix = `https://${loc}-aiplatform.googleapis.com`;
// const apiEndpoint = loc + '-aiplatform.googleapis.com';
const endpointPrefix = `${loc}-aiplatform.googleapis.com`;
const tokenizersCache = {};

const settings = endpointSettings[EModelEndpoint.google];
Expand All @@ -58,6 +57,10 @@ class GoogleClient extends BaseClient {

this.apiKey = creds[AuthKeys.GOOGLE_API_KEY];

this.reverseProxyUrl = options.reverseProxyUrl;

this.authHeader = options.authHeader;

if (options.skipSetOptions) {
return;
}
Expand All @@ -66,7 +69,7 @@ class GoogleClient extends BaseClient {

/* Google specific methods */
constructUrl() {
return `${endpointPrefix}/v1/projects/${this.project_id}/locations/${loc}/publishers/${publisher}/models/${this.modelOptions.model}:serverStreamingPredict`;
return `https://${endpointPrefix}/v1/projects/${this.project_id}/locations/${loc}/publishers/${publisher}/models/${this.modelOptions.model}:serverStreamingPredict`;
}

async getClient() {
Expand Down Expand Up @@ -595,7 +598,21 @@ class GoogleClient extends BaseClient {
createLLM(clientOptions) {
const model = clientOptions.modelName ?? clientOptions.model;
clientOptions.location = loc;
clientOptions.endpoint = `${loc}-aiplatform.googleapis.com`;
clientOptions.endpoint = endpointPrefix;

let requestOptions = null;
if (this.reverseProxyUrl) {
requestOptions = {
baseUrl: this.reverseProxyUrl,
};

if (this.authHeader) {
requestOptions.customHeaders = {
Authorization: `Bearer ${this.apiKey}`,
};
}
}

if (this.project_id && this.isTextModel) {
logger.debug('Creating Google VertexAI client');
return new GoogleVertexAI(clientOptions);
Expand All @@ -607,10 +624,7 @@ class GoogleClient extends BaseClient {
return new ChatVertexAI(clientOptions);
} else if (!EXCLUDED_GENAI_MODELS.test(model)) {
logger.debug('Creating GenAI client');
return new GenAI(this.apiKey).getGenerativeModel({
...clientOptions,
model,
});
return new GenAI(this.apiKey).getGenerativeModel({ ...clientOptions, model }, requestOptions);
}

logger.debug('Creating Chat Google Generative AI client');
Expand Down Expand Up @@ -683,7 +697,7 @@ class GoogleClient extends BaseClient {
promptPrefix = `${promptPrefix ?? ''}\n${this.options.artifactsPrompt}`.trim();
}

if (this.options?.promptPrefix?.length) {
if (promptPrefix.length) {
requestOptions.systemInstruction = {
parts: [
{
Expand Down Expand Up @@ -901,6 +915,10 @@ class GoogleClient extends BaseClient {
threshold:
process.env.GOOGLE_SAFETY_DANGEROUS_CONTENT || 'HARM_BLOCK_THRESHOLD_UNSPECIFIED',
},
{
category: 'HARM_CATEGORY_CIVIC_INTEGRITY',
threshold: process.env.GOOGLE_SAFETY_CIVIC_INTEGRITY || 'HARM_BLOCK_THRESHOLD_UNSPECIFIED',
},
];
}

Expand Down
11 changes: 8 additions & 3 deletions api/app/clients/OpenAIClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ class OpenAIClient extends BaseClient {
this.checkVisionRequest(this.options.attachments);
}

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

const { OPENROUTER_API_KEY, OPENAI_FORCE_PROMPT } = process.env ?? {};
if (OPENROUTER_API_KEY && !this.azure) {
Expand Down Expand Up @@ -147,7 +148,7 @@ class OpenAIClient extends BaseClient {
const { model } = this.modelOptions;

this.isChatCompletion =
/\bo1\b/i.test(model) || model.includes('gpt') || this.useOpenRouter || !!reverseProxy;
o1Pattern.test(model) || model.includes('gpt') || this.useOpenRouter || !!reverseProxy;
this.isChatGptModel = this.isChatCompletion;
if (
model.includes('text-davinci') ||
Expand Down Expand Up @@ -1325,7 +1326,11 @@ ${convo}
/** @type {(value: void | PromiseLike<void>) => void} */
let streamResolve;

if (this.isO1Model === true && this.azure && modelOptions.stream) {
if (
this.isO1Model === true &&
(this.azure || /o1(?!-(?:mini|preview)).*$/.test(modelOptions.model)) &&
modelOptions.stream
) {
delete modelOptions.stream;
delete modelOptions.stop;
}
Expand Down
90 changes: 90 additions & 0 deletions api/models/convoStructure.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,4 +220,94 @@ describe('Conversation Structure Tests', () => {
}
expect(currentNode.children.length).toBe(0); // Last message should have no children
});

test('Random order dates between parent and children messages', async () => {
const userId = 'testUser';
const conversationId = 'testConversation';

// Create messages with deliberately out-of-order timestamps but sequential creation
const messages = [
{
messageId: 'parent',
parentMessageId: null,
text: 'Parent Message',
createdAt: new Date('2023-01-01T00:00:00Z'), // Make parent earliest
},
{
messageId: 'child1',
parentMessageId: 'parent',
text: 'Child Message 1',
createdAt: new Date('2023-01-01T00:01:00Z'),
},
{
messageId: 'child2',
parentMessageId: 'parent',
text: 'Child Message 2',
createdAt: new Date('2023-01-01T00:02:00Z'),
},
{
messageId: 'grandchild1',
parentMessageId: 'child1',
text: 'Grandchild Message 1',
createdAt: new Date('2023-01-01T00:03:00Z'),
},
];

// Add common properties to all messages
messages.forEach((msg) => {
msg.conversationId = conversationId;
msg.user = userId;
msg.isCreatedByUser = false;
msg.error = false;
msg.unfinished = false;
});

// Save messages with overrideTimestamp set to true
await bulkSaveMessages(messages, true);

// Retrieve messages
const retrievedMessages = await getMessages({ conversationId, user: userId });

// Debug log to see what's being returned
console.log(
'Retrieved Messages:',
retrievedMessages.map((msg) => ({
messageId: msg.messageId,
parentMessageId: msg.parentMessageId,
createdAt: msg.createdAt,
})),
);

// Build tree
const tree = buildTree({ messages: retrievedMessages });

// Debug log to see the tree structure
console.log(
'Tree structure:',
tree.map((root) => ({
messageId: root.messageId,
children: root.children.map((child) => ({
messageId: child.messageId,
children: child.children.map((grandchild) => ({
messageId: grandchild.messageId,
})),
})),
})),
);

// Verify the structure before making assertions
expect(retrievedMessages.length).toBe(4); // Should have all 4 messages

// Check if messages are properly linked
const parentMsg = retrievedMessages.find((msg) => msg.messageId === 'parent');
expect(parentMsg.parentMessageId).toBeNull(); // Parent should have null parentMessageId

const childMsg1 = retrievedMessages.find((msg) => msg.messageId === 'child1');
expect(childMsg1.parentMessageId).toBe('parent');

// Then check tree structure
expect(tree.length).toBe(1); // Should have only one root message
expect(tree[0].messageId).toBe('parent');
expect(tree[0].children.length).toBe(2); // Should have two children
});
});
2 changes: 1 addition & 1 deletion api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"@langchain/community": "^0.3.14",
"@langchain/core": "^0.3.18",
"@langchain/google-genai": "^0.1.4",
"@langchain/google-vertexai": "^0.1.2",
"@langchain/google-vertexai": "^0.1.4",
"@langchain/textsplitters": "^0.1.0",
"@librechat/agents": "^1.8.8",
"axios": "^1.7.7",
Expand Down
20 changes: 18 additions & 2 deletions api/server/routes/convos.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ const multer = require('multer');
const express = require('express');
const { CacheKeys, EModelEndpoint } = require('librechat-data-provider');
const { getConvosByPage, deleteConvos, getConvo, saveConvo } = require('~/models/Conversation');
const { forkConversation, duplicateConversation } = require('~/server/utils/import/fork');
const { storage, importFileFilter } = require('~/server/routes/files/multer');
const requireJwtAuth = require('~/server/middleware/requireJwtAuth');
const { forkConversation } = require('~/server/utils/import/fork');
const { importConversations } = require('~/server/utils/import');
const { createImportLimiters } = require('~/server/middleware');
const { deleteToolCalls } = require('~/models/ToolCall');
Expand Down Expand Up @@ -182,9 +182,25 @@ router.post('/fork', async (req, res) => {

res.json(result);
} catch (error) {
logger.error('Error forking conversation', error);
logger.error('Error forking conversation:', error);
res.status(500).send('Error forking conversation');
}
});

router.post('/duplicate', async (req, res) => {
const { conversationId, title } = req.body;

try {
const result = await duplicateConversation({
userId: req.user.id,
conversationId,
title,
});
res.status(201).json(result);
} catch (error) {
logger.error('Error duplicating conversation:', error);
res.status(500).send('Error duplicating conversation');
}
});

module.exports = router;
20 changes: 13 additions & 7 deletions api/server/services/Endpoints/agents/initialize.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,15 @@ const initializeAgentOptions = async ({
agent.endpoint = provider.toLowerCase();
}

const model_parameters = agent.model_parameters ?? { model: agent.model };
const _endpointOption = isInitialAgent
? endpointOption
: {
model_parameters,
};
const model_parameters = Object.assign(
{},
agent.model_parameters ?? { model: agent.model },
isInitialAgent === true ? endpointOption?.model_parameters : {},
);
const _endpointOption =
isInitialAgent === true
? Object.assign({}, endpointOption, { model_parameters })
: { model_parameters };

const options = await getOptions({
req,
Expand All @@ -122,13 +125,16 @@ const initializeAgentOptions = async ({
agent.model_parameters.model = agent.model;
}

const tokensModel =
agent.provider === EModelEndpoint.azureOpenAI ? agent.model : agent.model_parameters.model;

return {
...agent,
tools,
toolContextMap,
maxContextTokens:
agent.max_context_tokens ??
getModelMaxTokens(agent.model_parameters.model, providerEndpointMap[provider]) ??
getModelMaxTokens(tokensModel, providerEndpointMap[provider]) ??
4000,
};
};
Expand Down
9 changes: 8 additions & 1 deletion api/server/services/Endpoints/google/initialize.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
const { EModelEndpoint, AuthKeys } = require('librechat-data-provider');
const { getUserKey, checkUserKeyExpiry } = require('~/server/services/UserService');
const { GoogleClient } = require('~/app');
const { isEnabled } = require('~/server/utils');

const initializeClient = async ({ req, res, endpointOption }) => {
const { GOOGLE_KEY, GOOGLE_REVERSE_PROXY, PROXY } = process.env;
const {
GOOGLE_KEY,
GOOGLE_REVERSE_PROXY,
GOOGLE_AUTH_HEADER,
PROXY,
} = process.env;
const isUserProvided = GOOGLE_KEY === 'user_provided';
const { key: expiresAt } = req.body;

Expand Down Expand Up @@ -46,6 +52,7 @@ const initializeClient = async ({ req, res, endpointOption }) => {
req,
res,
reverseProxyUrl: GOOGLE_REVERSE_PROXY ?? null,
authHeader: isEnabled(GOOGLE_AUTH_HEADER) ?? null,
proxy: PROXY ?? null,
...clientOptions,
...endpointOption,
Expand Down
Loading

0 comments on commit 1383bd4

Please sign in to comment.