Merge branch 'main' into feat/model-spec-group-icons

This commit is contained in:
Odrec 2025-12-10 14:05:12 +01:00 committed by GitHub
commit 94819c7d0c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 12319 additions and 35 deletions

View file

@ -317,14 +317,22 @@ const loadTools = async ({
requestedTools[tool] = async () => { requestedTools[tool] = async () => {
toolContextMap[tool] = `# \`${tool}\`: toolContextMap[tool] = `# \`${tool}\`:
Current Date & Time: ${replaceSpecialVars({ text: '{{iso_datetime}}' })} Current Date & Time: ${replaceSpecialVars({ text: '{{iso_datetime}}' })}
1. **Execute immediately without preface** when using \`${tool}\`.
2. **After the search, begin with a brief summary** that directly addresses the query without headers or explaining your process. **Execute immediately without preface.** After search, provide a brief summary addressing the query directly, then structure your response with clear Markdown formatting (## headers, lists, tables). Cite sources properly, tailor tone to query type, and provide comprehensive details.
3. **Structure your response clearly** using Markdown formatting (Level 2 headers for sections, lists for multiple points, tables for comparisons).
4. **Cite sources properly** according to the citation anchor format, utilizing group anchors when appropriate. **CITATION FORMAT - INVISIBLE UNICODE ANCHORS ONLY:**
5. **Tailor your approach to the query type** (academic, news, coding, etc.) while maintaining an expert, journalistic, unbiased tone. Use these Unicode characters: \\ue202 (before each anchor), \\ue200 (group start), \\ue201 (group end), \\ue203 (highlight start), \\ue204 (highlight end)
6. **Provide comprehensive information** with specific details, examples, and as much relevant context as possible from search results.
7. **Avoid moralizing language.** Anchor pattern: turn{N}{type}{index} where N=turn number, type=search|news|image|ref, index=0,1,2...
`.trim();
**Examples:**
- Single: "Statement.\\ue202turn0search0"
- Multiple: "Statement.\\ue202turn0search0\\ue202turn0news1"
- Group: "Statement. \\ue200\\ue202turn0search0\\ue202turn0news1\\ue201"
- Highlight: "\\ue203Cited text.\\ue204\\ue202turn0search0"
- Image: "See photo\\ue202turn0image0."
**CRITICAL:** Place anchors AFTER punctuation. Cite every non-obvious fact/quote. NEVER use markdown links, [1], footnotes, or HTML tags.`.trim();
return createSearchTool({ return createSearchTool({
...result.authResult, ...result.authResult,
onSearchResults, onSearchResults,

View file

@ -47,7 +47,7 @@
"@langchain/google-genai": "^0.2.13", "@langchain/google-genai": "^0.2.13",
"@langchain/google-vertexai": "^0.2.13", "@langchain/google-vertexai": "^0.2.13",
"@langchain/textsplitters": "^0.1.0", "@langchain/textsplitters": "^0.1.0",
"@librechat/agents": "^3.0.36", "@librechat/agents": "^3.0.50",
"@librechat/api": "*", "@librechat/api": "*",
"@librechat/data-schemas": "*", "@librechat/data-schemas": "*",
"@microsoft/microsoft-graph-client": "^3.0.7", "@microsoft/microsoft-graph-client": "^3.0.7",

View file

@ -3,7 +3,8 @@ const fs = require('fs').promises;
const FormData = require('form-data'); const FormData = require('form-data');
const { Readable } = require('stream'); const { Readable } = require('stream');
const { logger } = require('@librechat/data-schemas'); const { logger } = require('@librechat/data-schemas');
const { genAzureEndpoint } = require('@librechat/api'); const { HttpsProxyAgent } = require('https-proxy-agent');
const { genAzureEndpoint, logAxiosError } = require('@librechat/api');
const { extractEnvVariable, STTProviders } = require('librechat-data-provider'); const { extractEnvVariable, STTProviders } = require('librechat-data-provider');
const { getAppConfig } = require('~/server/services/Config'); const { getAppConfig } = require('~/server/services/Config');
@ -34,6 +35,34 @@ const MIME_TO_EXTENSION_MAP = {
'audio/x-flac': 'flac', 'audio/x-flac': 'flac',
}; };
/**
* Validates and extracts ISO-639-1 language code from a locale string.
* @param {string} language - The language/locale string (e.g., "en-US", "en", "zh-CN")
* @returns {string|null} The ISO-639-1 language code (e.g., "en") or null if invalid
*/
function getValidatedLanguageCode(language) {
try {
if (!language) {
return null;
}
const normalizedLanguage = language.toLowerCase();
const isValidLocaleCode = /^[a-z]{2}(-[a-z]{2})?$/.test(normalizedLanguage);
if (isValidLocaleCode) {
return normalizedLanguage.split('-')[0];
}
logger.warn(
`[STT] Invalid language format "${language}". Expected ISO-639-1 locale code like "en-US" or "en". Skipping language parameter.`,
);
return null;
} catch (error) {
logger.error(`[STT] Error validating language code "${language}":`, error);
return null;
}
}
/** /**
* Gets the file extension from the MIME type. * Gets the file extension from the MIME type.
* @param {string} mimeType - The MIME type. * @param {string} mimeType - The MIME type.
@ -172,10 +201,9 @@ class STTService {
model: sttSchema.model, model: sttSchema.model,
}; };
if (language) { const validLanguage = getValidatedLanguageCode(language);
/** Converted locale code (e.g., "en-US") to ISO-639-1 format (e.g., "en") */ if (validLanguage) {
const isoLanguage = language.split('-')[0]; data.language = validLanguage;
data.language = isoLanguage;
} }
const headers = { const headers = {
@ -220,10 +248,9 @@ class STTService {
contentType: audioFile.mimetype, contentType: audioFile.mimetype,
}); });
if (language) { const validLanguage = getValidatedLanguageCode(language);
/** Converted locale code (e.g., "en-US") to ISO-639-1 format (e.g., "en") */ if (validLanguage) {
const isoLanguage = language.split('-')[0]; formData.append('language', validLanguage);
formData.append('language', isoLanguage);
} }
const headers = { const headers = {
@ -266,8 +293,14 @@ class STTService {
language, language,
); );
const options = { headers };
if (process.env.PROXY) {
options.httpsAgent = new HttpsProxyAgent(process.env.PROXY);
}
try { try {
const response = await axios.post(url, data, { headers }); const response = await axios.post(url, data, options);
if (response.status !== 200) { if (response.status !== 200) {
throw new Error('Invalid response from the STT API'); throw new Error('Invalid response from the STT API');
@ -279,7 +312,7 @@ class STTService {
return response.data.text.trim(); return response.data.text.trim();
} catch (error) { } catch (error) {
logger.error(`STT request failed for provider ${provider}:`, error); logAxiosError({ message: `STT request failed for provider ${provider}:`, error });
throw error; throw error;
} }
} }
@ -309,7 +342,7 @@ class STTService {
const text = await this.sttRequest(provider, sttSchema, { audioBuffer, audioFile, language }); const text = await this.sttRequest(provider, sttSchema, { audioBuffer, audioFile, language });
res.json({ text }); res.json({ text });
} catch (error) { } catch (error) {
logger.error('An error occurred while processing the audio:', error); logAxiosError({ message: 'An error occurred while processing the audio:', error });
res.sendStatus(500); res.sendStatus(500);
} finally { } finally {
try { try {

View file

@ -1,6 +1,7 @@
const axios = require('axios'); const axios = require('axios');
const { logger } = require('@librechat/data-schemas'); const { logger } = require('@librechat/data-schemas');
const { genAzureEndpoint } = require('@librechat/api'); const { HttpsProxyAgent } = require('https-proxy-agent');
const { genAzureEndpoint, logAxiosError } = require('@librechat/api');
const { extractEnvVariable, TTSProviders } = require('librechat-data-provider'); const { extractEnvVariable, TTSProviders } = require('librechat-data-provider');
const { getRandomVoiceId, createChunkProcessor, splitTextIntoChunks } = require('./streamAudio'); const { getRandomVoiceId, createChunkProcessor, splitTextIntoChunks } = require('./streamAudio');
const { getAppConfig } = require('~/server/services/Config'); const { getAppConfig } = require('~/server/services/Config');
@ -266,10 +267,14 @@ class TTSService {
const options = { headers, responseType: stream ? 'stream' : 'arraybuffer' }; const options = { headers, responseType: stream ? 'stream' : 'arraybuffer' };
if (process.env.PROXY) {
options.httpsAgent = new HttpsProxyAgent(process.env.PROXY);
}
try { try {
return await axios.post(url, data, options); return await axios.post(url, data, options);
} catch (error) { } catch (error) {
logger.error(`TTS request failed for provider ${provider}:`, error); logAxiosError({ message: `TTS request failed for provider ${provider}:`, error });
throw error; throw error;
} }
} }
@ -325,7 +330,10 @@ class TTSService {
break; break;
} }
} catch (innerError) { } catch (innerError) {
logger.error('Error processing manual update:', chunk, innerError); logAxiosError({
message: `[TTS] Error processing manual update for chunk: ${chunk?.text?.substring(0, 50)}...`,
error: innerError,
});
if (!res.headersSent) { if (!res.headersSent) {
return res.status(500).end(); return res.status(500).end();
} }
@ -337,7 +345,7 @@ class TTSService {
res.end(); res.end();
} }
} catch (error) { } catch (error) {
logger.error('Error creating the audio stream:', error); logAxiosError({ message: '[TTS] Error creating the audio stream:', error });
if (!res.headersSent) { if (!res.headersSent) {
return res.status(500).send('An error occurred'); return res.status(500).send('An error occurred');
} }
@ -407,7 +415,10 @@ class TTSService {
break; break;
} }
} catch (innerError) { } catch (innerError) {
logger.error('Error processing audio stream update:', update, innerError); logAxiosError({
message: `[TTS] Error processing audio stream update: ${update?.text?.substring(0, 50)}...`,
error: innerError,
});
if (!res.headersSent) { if (!res.headersSent) {
return res.status(500).end(); return res.status(500).end();
} }
@ -424,7 +435,7 @@ class TTSService {
res.end(); res.end();
} }
} catch (error) { } catch (error) {
logger.error('Failed to fetch audio:', error); logAxiosError({ message: '[TTS] Failed to fetch audio:', error });
if (!res.headersSent) { if (!res.headersSent) {
res.status(500).end(); res.status(500).end();
} }

12229
bun.lock Normal file

File diff suppressed because it is too large Load diff

BIN
bun.lockb

Binary file not shown.

10
package-lock.json generated
View file

@ -61,7 +61,7 @@
"@langchain/google-genai": "^0.2.13", "@langchain/google-genai": "^0.2.13",
"@langchain/google-vertexai": "^0.2.13", "@langchain/google-vertexai": "^0.2.13",
"@langchain/textsplitters": "^0.1.0", "@langchain/textsplitters": "^0.1.0",
"@librechat/agents": "^3.0.36", "@librechat/agents": "^3.0.50",
"@librechat/api": "*", "@librechat/api": "*",
"@librechat/data-schemas": "*", "@librechat/data-schemas": "*",
"@microsoft/microsoft-graph-client": "^3.0.7", "@microsoft/microsoft-graph-client": "^3.0.7",
@ -16281,9 +16281,9 @@
} }
}, },
"node_modules/@librechat/agents": { "node_modules/@librechat/agents": {
"version": "3.0.36", "version": "3.0.50",
"resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-3.0.36.tgz", "resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-3.0.50.tgz",
"integrity": "sha512-52+uNiG0X2B4TZX03ldFRvqtJrExnntEFQV5UfA38+2sNbYgPm4lcdKyAHr9OTPdmmtbmDY/gKKguRiUzLVL2g==", "integrity": "sha512-oovj3BsP/QoxPbWFAc71Ddplwd9BT8ucfYs+n+kiR37aCWtvxdvL9/XldRYfnaq9boNE324njQJyqc8v8AAPFQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@langchain/anthropic": "^0.3.26", "@langchain/anthropic": "^0.3.26",
@ -46276,7 +46276,7 @@
"@azure/storage-blob": "^12.27.0", "@azure/storage-blob": "^12.27.0",
"@keyv/redis": "^4.3.3", "@keyv/redis": "^4.3.3",
"@langchain/core": "^0.3.79", "@langchain/core": "^0.3.79",
"@librechat/agents": "^3.0.36", "@librechat/agents": "^3.0.50",
"@librechat/data-schemas": "*", "@librechat/data-schemas": "*",
"@modelcontextprotocol/sdk": "^1.21.0", "@modelcontextprotocol/sdk": "^1.21.0",
"axios": "^1.12.1", "axios": "^1.12.1",

View file

@ -67,10 +67,11 @@
"b:api-inspect": "NODE_ENV=production bun --inspect run api/server/index.js", "b:api-inspect": "NODE_ENV=production bun --inspect run api/server/index.js",
"b:api:dev": "NODE_ENV=production bun run --watch api/server/index.js", "b:api:dev": "NODE_ENV=production bun run --watch api/server/index.js",
"b:data": "cd packages/data-provider && bun run b:build", "b:data": "cd packages/data-provider && bun run b:build",
"b:mcp": "cd packages/api && bun run b:build", "b:build:client-package": "cd packages/client && bun run b:build",
"b:data-schemas": "cd packages/data-schemas && bun run b:build", "b:data-schemas": "cd packages/data-schemas && bun run b:build",
"b:build:api": "cd packages/api && bun run b:build", "b:build:api": "cd packages/api && bun run b:build",
"b:client": "bun --bun run b:data && bun --bun run b:mcp && bun --bun run b:data-schemas && cd client && bun --bun run b:build", "b:build:client": "cd client && bun --bun run b:build",
"b:client": "bun --bun run b:data && bun --bun run b:data-schemas && bun --bun run b:build:api && bun --bun run b:build:client-package && bun --bun run b:build:client",
"b:client:dev": "cd client && bun run b:dev", "b:client:dev": "cd client && bun run b:dev",
"b:test:client": "cd client && bun run b:test", "b:test:client": "cd client && bun run b:test",
"b:test:api": "cd api && bun run b:test", "b:test:api": "cd api && bun run b:test",

View file

@ -84,7 +84,7 @@
"@azure/storage-blob": "^12.27.0", "@azure/storage-blob": "^12.27.0",
"@keyv/redis": "^4.3.3", "@keyv/redis": "^4.3.3",
"@langchain/core": "^0.3.79", "@langchain/core": "^0.3.79",
"@librechat/agents": "^3.0.36", "@librechat/agents": "^3.0.50",
"@librechat/data-schemas": "*", "@librechat/data-schemas": "*",
"@modelcontextprotocol/sdk": "^1.21.0", "@modelcontextprotocol/sdk": "^1.21.0",
"axios": "^1.12.1", "axios": "^1.12.1",

View file

@ -17,7 +17,9 @@
], ],
"scripts": { "scripts": {
"clean": "rimraf dist", "clean": "rimraf dist",
"b:clean": "bun run rimraf dist",
"build": "npm run clean && rollup -c --bundleConfigAsCjs", "build": "npm run clean && rollup -c --bundleConfigAsCjs",
"b:build": "bun run b:clean && bun run rollup -c --silent --bundleConfigAsCjs",
"build:watch": "rollup -c -w --bundleConfigAsCjs", "build:watch": "rollup -c -w --bundleConfigAsCjs",
"dev": "rollup -c -w --bundleConfigAsCjs" "dev": "rollup -c -w --bundleConfigAsCjs"
}, },