Skip to content

Commit 1321e60

Browse files
authored
Merge pull request #15 from intelequia/preview
Merge from Preview branch: fixes Azure Agents, balance and local permissions file
2 parents 58339fd + 3d09f06 commit 1321e60

File tree

9 files changed

+144
-81
lines changed

9 files changed

+144
-81
lines changed

api/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"@aws-sdk/client-s3": "^3.758.0",
3939
"@aws-sdk/s3-request-presigner": "^3.758.0",
4040
"@azure/ai-projects": "^1.0.0",
41-
"@azure/identity": "^4.7.0",
41+
"@azure/identity": "^4.11.0",
4242
"@azure/search-documents": "^12.0.0",
4343
"@azure/storage-blob": "^12.27.0",
4444
"@google/generative-ai": "^0.24.0",

api/server/controllers/azureAgents/helpers.js

Lines changed: 47 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -72,56 +72,59 @@ const _listAssistants = async ({ req, res, version, query }) => {
7272
* @returns {Promise<Array<Assistant>>} A promise that resolves to the response from the `openai.beta.assistants.list` method call.
7373
*/
7474
const listAllAzureAgents = async ({ req, res, version, query }) => {
75-
/** @type {{ azureAgentClient: 'AIProjectsClient' }} */
76-
const azureAgentClient = await getOpenAIClient({ req, res, version });
77-
const allAzureAgents = [];
78-
79-
let first_id;
80-
let last_id;
81-
let afterToken = query.after;
82-
let hasMore = true;
83-
84-
let permissionsNodeName = "azureAgentPermissions";
85-
if (global.myCache.get(permissionsNodeName)) {
86-
let agents = [];
87-
88-
for await (const agent of azureAgentClient.listAgents()){
75+
const client = await getOpenAIClient({ req, res, version });
76+
77+
const limit = Number(query?.limit) > 0 ? Number(query.limit) : undefined;
78+
const after = query?.after; // continuation token
79+
const permissionsNodeName = 'azureAgentPermissions';
80+
81+
/** @type {any[]} */
82+
let agents = [];
83+
/** @type {string | undefined} */
84+
let nextContinuationToken;
85+
86+
// If 'after' or 'limit' are provided, use byPage to fetch a single page
87+
if (after || limit) {
88+
const pageIter = client.listAgents().byPage({
89+
maxPageSize: limit ?? 100,
90+
continuationToken: after,
91+
});
92+
const { value: page = [] } = await pageIter.next();
93+
agents = [...page];
94+
// Azure SDK attaches continuationToken on the page array
95+
nextContinuationToken = page && page.continuationToken ? page.continuationToken : undefined;
96+
} else {
97+
// Otherwise, iterate all agents
98+
let count = 0;
99+
for await (const agent of client.listAgents()) {
89100
agents.push(agent);
101+
if (limit && ++count >= limit) break;
90102
}
103+
}
91104

92-
const allowedAzureAgents = await checkGroupPermissions(req.user._id.toString(), agents, permissionsNodeName);
93-
94-
allowedAzureAgents.forEach((azureAgent) => allAzureAgents.push(azureAgent));
95-
} else {
96-
while (hasMore) {
97-
let agents = [];
98-
99-
for await (const agent of azureAgentClient.listAgents()){
100-
agents.push(agent);
101-
}
102-
103-
allAzureAgents.push(agents);
104-
hasMore = body.has_more;
105-
106-
if (!first_id) {
107-
first_id = body.first_id;
108-
}
109-
110-
if (hasMore) {
111-
afterToken = body.last_id;
112-
} else {
113-
last_id = body.last_id;
114-
}
115-
}
105+
// Optional permission filtering
106+
if (global.myCache.get(permissionsNodeName)) {
107+
agents = await checkGroupPermissions(
108+
req.user._id.toString(),
109+
agents,
110+
permissionsNodeName,
111+
);
116112
}
117113

114+
// Compute metadata similar to OpenAI-style list responses
115+
const first_id = agents[0]?.id;
116+
const last_id = agents[agents.length - 1]?.id || nextContinuationToken;
117+
const has_more = Boolean(nextContinuationToken);
118+
118119
return {
119-
data: allAzureAgents,
120+
data: agents,
120121
body: {
121-
data: allAzureAgents,
122-
has_more: false,
122+
data: agents,
123+
has_more,
123124
first_id,
124125
last_id,
126+
// Use last_id as the token to pass back in `after` for the next page
127+
next_after: nextContinuationToken,
125128
},
126129
};
127130
};
@@ -147,13 +150,10 @@ const listAgentsForAzure = async ({ req, res, version, azureConfig = {}, query }
147150
const groupModelTuples = [];
148151
const promises = [];
149152
/** @type {Array<TAzureGroup>} */
150-
const groups = [];
151153

152-
const { groupMap, assistantGroups } = azureConfig;
154+
const { groups } = azureConfig;
153155

154-
for (const groupName of assistantGroups) {
155-
const group = groupMap[groupName];
156-
groups.push(group);
156+
for (const group of groups) {
157157

158158
const currentModelTuples = Object.entries(group?.models);
159159
groupModelTuples.push(currentModelTuples);
@@ -237,7 +237,7 @@ const fetchAssistants = async ({ req, res, overrideEndpoint }) => {
237237
/** @type {AssistantListResponse} */
238238
let body;
239239

240-
const azureConfig = req.app.locals[EModelEndpoint.azureOpenAI];
240+
const azureConfig = req.app.locals[EModelEndpoint.azureAgents];
241241
body = await listAgentsForAzure({ req, res, version, azureConfig, query });
242242

243243
if (req.user.role === SystemRoles.ADMIN) {

api/server/services/Endpoints/azureAgents/initialize.js

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,11 @@ const {
55
EModelEndpoint,
66
} = require('librechat-data-provider');
77

8-
const { DefaultAzureCredential } = require('@azure/identity');
8+
const { ClientSecretCredential, DefaultAzureCredential } = require('@azure/identity');
99
const { isUserProvided } = require('~/server/utils');
1010
const { truncateText, titleInstruction } = require('~/app/clients/prompts/index');
1111

1212

13-
14-
1513
class Files {
1614
constructor(client) {
1715
this._client = client;
@@ -74,16 +72,30 @@ const initializeClient = async ({ req, res, initAppClient = false }) => {
7472
name: EModelEndpoint.azureAgents,
7573
});
7674
}
77-
78-
79-
const credentials = new DefaultAzureCredential()
75+
// Prefer explicit client credentials from environment; fallback to DefaultAzureCredential
76+
const { AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET } = process.env;
77+
let credentials;
78+
try {
79+
if (AZURE_TENANT_ID && AZURE_CLIENT_ID && AZURE_CLIENT_SECRET) {
80+
credentials = new ClientSecretCredential(
81+
AZURE_TENANT_ID,
82+
AZURE_CLIENT_ID,
83+
AZURE_CLIENT_SECRET,
84+
);
85+
} else {
86+
credentials = new DefaultAzureCredential();
87+
}
88+
} catch (e) {
89+
console.log('Azure credential initialization failed:', e);
90+
throw e;
91+
}
8092

8193

8294
/** @type {TAzureConfig | undefined} */
8395
const azureConfig = req.app.locals[EModelEndpoint.azureOpenAI];
8496

8597
const client = new AgentsClient(AZURE_AI_PROJECT_ENDPOINT, credentials);
86-
98+
8799
client.options = {}
88100

89101
if(azureConfig && azureConfig.assistants){

api/utils/intelequia/intelequiaConfigLoader.js

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ const { ClientSecretCredential } = require('@azure/identity');
55
const { logger } = require('~/config');
66
const jwtDecode = require('jsonwebtoken/decode');
77
const { load } = require('mime');
8+
const path = require('path');
9+
const fs = require('fs');
10+
11+
812

913
/**
1014
* Manage all permission operations, loads, saves parameters and configure users role
@@ -17,8 +21,8 @@ async function intelequiaConfigLoader() {
1721
const url = process.env.REMOTE_CONFIG_FILE_URL;
1822

1923
if (url && url != "") {
20-
logger.info("[IntelequiaConfigLoader] Loading GraphClient and Permissions files ...")
21-
const result = await loadPermissionConfigFiles(url)
24+
logger.info(`[IntelequiaConfigLoader] Loading GraphClient and Permissions files from ${url} ...`);
25+
const result = await loadPermissionConfigFiles(url);
2226
const { permissions, functions, assistantAdminRole, agentPermissions, azureAgentPermissions } = result;
2327

2428
global.myCache.set("functions", functions);
@@ -78,9 +82,18 @@ async function loadPermissionConfigFiles(url) {
7882

7983
logger.info(`[validateTools] fetch remote configuration file: Fetching remote configuration file at URL "${url}"`);
8084
try {
81-
const response = await fetch(url);
82-
if (response.ok) {
83-
return await response.json();
85+
if (url.startsWith('http')) {
86+
const response = await fetch(url);
87+
if (response.ok) {
88+
return await response.json();
89+
}
90+
}
91+
else {
92+
const filePath = path.join(__dirname, '../../..', url);
93+
if (fs.existsSync(filePath)) {
94+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
95+
}
96+
throw new Error(`[validateTools] fetch remote configuration file: File not found at "${filePath}"`);
8497
}
8598
} catch (e) {
8699
console.log(e)

client/src/components/Nav/AccountSettings.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ function AccountSettings() {
1616
const { user, isAuthenticated, logout } = useAuthContext();
1717
const { data: startupConfig } = useGetStartupConfig();
1818
const balanceQuery = useGetUserBalance({
19-
enabled: !!isAuthenticated && startupConfig?.balance?.enabled,
19+
enabled: !!isAuthenticated && startupConfig?.balance?.enabled === true,
2020
});
2121
const [showSettings, setShowSettings] = useState(false);
2222
const [showFiles, setShowFiles] = useRecoilState(store.showFiles);

client/src/components/Nav/SettingsTabs/Balance/Balance.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ function Balance() {
1010
const { data: startupConfig } = useGetStartupConfig();
1111

1212
const balanceQuery = useGetUserBalance({
13-
enabled: !!isAuthenticated && !!startupConfig?.balance?.enabled,
13+
enabled: !!isAuthenticated && startupConfig?.balance?.enabled === true,
1414
});
1515
const balanceData = balanceQuery.data;
1616

client/src/hooks/SSE/useSSE.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export default function useSSE(
8686

8787
const { data: startupConfig } = useGetStartupConfig();
8888
const balanceQuery = useGetUserBalance({
89-
enabled: !!isAuthenticated && startupConfig?.balance?.enabled,
89+
enabled: !!isAuthenticated && startupConfig?.balance?.enabled === true,
9090
});
9191

9292
useEffect(() => {

package-lock.json

Lines changed: 55 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/data-provider/src/config.ts

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -876,23 +876,7 @@ export const alternateName = {
876876
const azureAgentsModels =[
877877
'gpt-4o-mini',
878878
'gpt-4o',
879-
'gpt-4.5-preview',
880-
'gpt-4.5-preview-2025-02-27',
881-
'gpt-3.5-turbo',
882-
'gpt-3.5-turbo-0125',
883-
'gpt-4-turbo',
884-
'gpt-4-turbo-2024-04-09',
885-
'gpt-4-0125-preview',
886-
'gpt-4-turbo-preview',
887-
'gpt-4-1106-preview',
888-
'gpt-3.5-turbo-1106',
889-
'gpt-3.5-turbo-16k-0613',
890-
'gpt-3.5-turbo-16k',
891-
'gpt-4',
892-
'gpt-4-0314',
893-
'gpt-4-32k-0314',
894-
'gpt-4-0613',
895-
'gpt-3.5-turbo-0613',
879+
'gpt-4.1',
896880
]
897881
const sharedOpenAIModels = [
898882
'gpt-4o-mini',

0 commit comments

Comments
 (0)