Compare commits
9 Commits
fix/appcon
...
feat/add-b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9d0442531 | ||
|
|
274987712c | ||
|
|
888e3a31cf | ||
|
|
7ea23c5a7d | ||
|
|
f4833b6b25 | ||
|
|
d37db43e29 | ||
|
|
eec10bf745 | ||
|
|
3508839d6d | ||
|
|
a8babbcebf |
@@ -653,8 +653,10 @@ class OpenAIClient extends BaseClient {
|
||||
if (headers && typeof headers === 'object' && !Array.isArray(headers)) {
|
||||
configOptions.baseOptions = {
|
||||
headers: resolveHeaders({
|
||||
...headers,
|
||||
...configOptions?.baseOptions?.headers,
|
||||
headers: {
|
||||
...headers,
|
||||
...configOptions?.baseOptions?.headers,
|
||||
},
|
||||
}),
|
||||
};
|
||||
}
|
||||
@@ -749,7 +751,7 @@ class OpenAIClient extends BaseClient {
|
||||
groupMap,
|
||||
});
|
||||
|
||||
this.options.headers = resolveHeaders(headers);
|
||||
this.options.headers = resolveHeaders({ headers });
|
||||
this.options.reverseProxyUrl = baseURL ?? null;
|
||||
this.langchainProxy = extractBaseURL(this.options.reverseProxyUrl);
|
||||
this.apiKey = azureOptions.azureOpenAIApiKey;
|
||||
@@ -1181,7 +1183,7 @@ ${convo}
|
||||
modelGroupMap,
|
||||
groupMap,
|
||||
});
|
||||
opts.defaultHeaders = resolveHeaders(headers);
|
||||
opts.defaultHeaders = resolveHeaders({ headers });
|
||||
this.langchainProxy = extractBaseURL(baseURL);
|
||||
this.apiKey = azureOptions.azureOpenAIApiKey;
|
||||
|
||||
|
||||
@@ -109,14 +109,15 @@ const initializeClient = async ({ req, res, version, endpointOption, initAppClie
|
||||
|
||||
apiKey = azureOptions.azureOpenAIApiKey;
|
||||
opts.defaultQuery = { 'api-version': azureOptions.azureOpenAIApiVersion };
|
||||
opts.defaultHeaders = resolveHeaders(
|
||||
{
|
||||
opts.defaultHeaders = resolveHeaders({
|
||||
headers: {
|
||||
...headers,
|
||||
'api-key': apiKey,
|
||||
'OpenAI-Beta': `assistants=${version}`,
|
||||
},
|
||||
req.user,
|
||||
);
|
||||
user: req.user,
|
||||
body: req.body,
|
||||
});
|
||||
opts.model = azureOptions.azureOpenAIApiDeploymentName;
|
||||
|
||||
if (initAppClient) {
|
||||
|
||||
@@ -28,7 +28,11 @@ const initializeClient = async ({ req, res, endpointOption, optionsOnly, overrid
|
||||
const CUSTOM_API_KEY = extractEnvVariable(endpointConfig.apiKey);
|
||||
const CUSTOM_BASE_URL = extractEnvVariable(endpointConfig.baseURL);
|
||||
|
||||
let resolvedHeaders = resolveHeaders(endpointConfig.headers, req.user);
|
||||
let resolvedHeaders = resolveHeaders({
|
||||
headers: endpointConfig.headers,
|
||||
user: req.user,
|
||||
body: req.body,
|
||||
});
|
||||
|
||||
if (CUSTOM_API_KEY.match(envVarRegex)) {
|
||||
throw new Error(`Missing API Key for ${endpoint}.`);
|
||||
|
||||
@@ -64,13 +64,14 @@ describe('custom/initializeClient', () => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('calls resolveHeaders with headers and user', async () => {
|
||||
it('calls resolveHeaders with headers, user, and body for body placeholder support', async () => {
|
||||
const { resolveHeaders } = require('@librechat/api');
|
||||
await initializeClient({ req: mockRequest, res: mockResponse, optionsOnly: true });
|
||||
expect(resolveHeaders).toHaveBeenCalledWith(
|
||||
{ 'x-user': '{{LIBRECHAT_USER_ID}}', 'x-email': '{{LIBRECHAT_USER_EMAIL}}' },
|
||||
{ id: 'user-123', email: 'test@example.com' },
|
||||
);
|
||||
expect(resolveHeaders).toHaveBeenCalledWith({
|
||||
headers: { 'x-user': '{{LIBRECHAT_USER_ID}}', 'x-email': '{{LIBRECHAT_USER_EMAIL}}' },
|
||||
user: { id: 'user-123', email: 'test@example.com' },
|
||||
body: { endpoint: 'test-endpoint' }, // body - supports {{LIBRECHAT_BODY_*}} placeholders
|
||||
});
|
||||
});
|
||||
|
||||
it('throws if endpoint config is missing', async () => {
|
||||
|
||||
@@ -81,10 +81,11 @@ const initializeClient = async ({
|
||||
serverless = _serverless;
|
||||
|
||||
clientOptions.reverseProxyUrl = baseURL ?? clientOptions.reverseProxyUrl;
|
||||
clientOptions.headers = resolveHeaders(
|
||||
{ ...headers, ...(clientOptions.headers ?? {}) },
|
||||
req.user,
|
||||
);
|
||||
clientOptions.headers = resolveHeaders({
|
||||
headers: { ...headers, ...(clientOptions.headers ?? {}) },
|
||||
user: req.user,
|
||||
body: req.body,
|
||||
});
|
||||
|
||||
clientOptions.titleConvo = azureConfig.titleConvo;
|
||||
clientOptions.titleModel = azureConfig.titleModel;
|
||||
|
||||
@@ -189,6 +189,7 @@ async function createMCPTool({ req, res, toolKey, provider: _provider }) {
|
||||
},
|
||||
oauthStart,
|
||||
oauthEnd,
|
||||
body: req.body,
|
||||
});
|
||||
|
||||
if (isAssistantsEndpoint(provider) && Array.isArray(result)) {
|
||||
|
||||
@@ -259,6 +259,8 @@ endpoints:
|
||||
# recommended environment variables:
|
||||
apiKey: '${OPENROUTER_KEY}'
|
||||
baseURL: 'https://openrouter.ai/api/v1'
|
||||
headers:
|
||||
x-librechat-body-parentmessageid: '{{LIBRECHAT_BODY_PARENTMESSAGEID}}'
|
||||
models:
|
||||
default: ['meta-llama/llama-3-70b-instruct']
|
||||
fetch: true
|
||||
|
||||
@@ -87,10 +87,10 @@ export const initializeOpenAI = async ({
|
||||
});
|
||||
|
||||
clientOptions.reverseProxyUrl = configBaseURL ?? clientOptions.reverseProxyUrl;
|
||||
clientOptions.headers = resolveHeaders(
|
||||
{ ...headers, ...(clientOptions.headers ?? {}) },
|
||||
req.user,
|
||||
);
|
||||
clientOptions.headers = resolveHeaders({
|
||||
headers: { ...headers, ...(clientOptions.headers ?? {}) },
|
||||
user: req.user,
|
||||
});
|
||||
|
||||
const groupName = modelGroupMap[modelName || '']?.group;
|
||||
if (groupName && groupMap[groupName]) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { CallToolResultSchema, ErrorCode, McpError } from '@modelcontextprotocol
|
||||
import type { OAuthClientInformation } from '@modelcontextprotocol/sdk/shared/auth.js';
|
||||
import type { RequestOptions } from '@modelcontextprotocol/sdk/shared/protocol.js';
|
||||
import type { TokenMethods } from '@librechat/data-schemas';
|
||||
import type { TUser } from 'librechat-data-provider';
|
||||
import type { TUser, RequestBody } from 'librechat-data-provider';
|
||||
import type { MCPOAuthTokens, MCPOAuthFlowMetadata } from './oauth/types';
|
||||
import type { FlowStateManager } from '~/flow/manager';
|
||||
import type { JsonSchemaType } from '~/types/zod';
|
||||
@@ -373,6 +373,7 @@ export class MCPManager {
|
||||
oauthEnd,
|
||||
signal,
|
||||
returnOnOAuth = false,
|
||||
body,
|
||||
}: {
|
||||
user: TUser;
|
||||
serverName: string;
|
||||
@@ -383,6 +384,7 @@ export class MCPManager {
|
||||
oauthEnd?: () => Promise<void>;
|
||||
signal?: AbortSignal;
|
||||
returnOnOAuth?: boolean;
|
||||
body?: RequestBody;
|
||||
}): Promise<MCPConnection> {
|
||||
const userId = user.id;
|
||||
if (!userId) {
|
||||
@@ -432,7 +434,7 @@ export class MCPManager {
|
||||
);
|
||||
}
|
||||
|
||||
config = { ...(processMCPEnv(config, user, customUserVars) ?? {}) };
|
||||
config = { ...(processMCPEnv(config, user, customUserVars, body) ?? {}) };
|
||||
/** If no in-memory tokens, tokens from persistent storage */
|
||||
let tokens: MCPOAuthTokens | null = null;
|
||||
if (tokenMethods?.findToken) {
|
||||
@@ -859,6 +861,7 @@ export class MCPManager {
|
||||
oauthStart,
|
||||
oauthEnd,
|
||||
customUserVars,
|
||||
body,
|
||||
}: {
|
||||
user?: TUser;
|
||||
serverName: string;
|
||||
@@ -871,6 +874,7 @@ export class MCPManager {
|
||||
flowManager: FlowStateManager<MCPOAuthTokens | null>;
|
||||
oauthStart?: (authURL: string) => Promise<void>;
|
||||
oauthEnd?: () => Promise<void>;
|
||||
body?: RequestBody;
|
||||
}): Promise<t.FormattedToolResponse> {
|
||||
/** User-specific connection */
|
||||
let connection: MCPConnection | undefined;
|
||||
@@ -890,6 +894,7 @@ export class MCPManager {
|
||||
oauthEnd,
|
||||
signal: options?.signal,
|
||||
customUserVars,
|
||||
body,
|
||||
});
|
||||
} else {
|
||||
/** App-level connection */
|
||||
|
||||
@@ -36,12 +36,14 @@ describe('resolveHeaders', () => {
|
||||
});
|
||||
|
||||
it('should return empty object when headers is null', () => {
|
||||
const result = resolveHeaders(null as unknown as Record<string, string> | undefined);
|
||||
const result = resolveHeaders({
|
||||
headers: null as unknown as Record<string, string>,
|
||||
});
|
||||
expect(result).toEqual({});
|
||||
});
|
||||
|
||||
it('should return empty object when headers is empty', () => {
|
||||
const result = resolveHeaders({});
|
||||
const result = resolveHeaders({ headers: {} });
|
||||
expect(result).toEqual({});
|
||||
});
|
||||
|
||||
@@ -52,7 +54,7 @@ describe('resolveHeaders', () => {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
const result = resolveHeaders(headers);
|
||||
const result = resolveHeaders({ headers });
|
||||
|
||||
expect(result).toEqual({
|
||||
Authorization: 'test-api-key-value',
|
||||
@@ -68,7 +70,7 @@ describe('resolveHeaders', () => {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
const result = resolveHeaders(headers, user);
|
||||
const result = resolveHeaders({ headers, user });
|
||||
|
||||
expect(result).toEqual({
|
||||
'User-Id': 'test-user-123',
|
||||
@@ -82,7 +84,7 @@ describe('resolveHeaders', () => {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
const result = resolveHeaders(headers);
|
||||
const result = resolveHeaders({ headers });
|
||||
|
||||
expect(result).toEqual({
|
||||
'User-Id': '{{LIBRECHAT_USER_ID}}',
|
||||
@@ -97,7 +99,7 @@ describe('resolveHeaders', () => {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
const result = resolveHeaders(headers, user);
|
||||
const result = resolveHeaders({ headers, user });
|
||||
|
||||
expect(result).toEqual({
|
||||
'User-Id': '{{LIBRECHAT_USER_ID}}',
|
||||
@@ -123,7 +125,7 @@ describe('resolveHeaders', () => {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
const result = resolveHeaders(headers, user);
|
||||
const result = resolveHeaders({ headers, user });
|
||||
|
||||
expect(result).toEqual({
|
||||
'User-Email': 'test@example.com',
|
||||
@@ -148,7 +150,7 @@ describe('resolveHeaders', () => {
|
||||
'Non-Existent': '{{LIBRECHAT_USER_NONEXISTENT}}',
|
||||
};
|
||||
|
||||
const result = resolveHeaders(headers, user);
|
||||
const result = resolveHeaders({ headers, user });
|
||||
|
||||
expect(result).toEqual({
|
||||
'User-Email': 'test@example.com',
|
||||
@@ -171,7 +173,7 @@ describe('resolveHeaders', () => {
|
||||
'X-User-Id': '{{LIBRECHAT_USER_ID}}',
|
||||
};
|
||||
|
||||
const result = resolveHeaders(headers, user, customUserVars);
|
||||
const result = resolveHeaders({ headers, user, customUserVars });
|
||||
|
||||
expect(result).toEqual({
|
||||
Authorization: 'Bearer user-specific-token',
|
||||
@@ -194,7 +196,7 @@ describe('resolveHeaders', () => {
|
||||
'Test-Email': '{{LIBRECHAT_USER_EMAIL}}',
|
||||
};
|
||||
|
||||
const result = resolveHeaders(headers, user, customUserVars);
|
||||
const result = resolveHeaders({ headers, user, customUserVars });
|
||||
|
||||
expect(result).toEqual({
|
||||
'Test-Email': 'custom-email@example.com',
|
||||
@@ -213,7 +215,7 @@ describe('resolveHeaders', () => {
|
||||
'User-Id': '{{LIBRECHAT_USER_ID}}',
|
||||
};
|
||||
|
||||
const result = resolveHeaders(headers, user);
|
||||
const result = resolveHeaders({ headers, user });
|
||||
|
||||
expect(result).toEqual({
|
||||
'User-Role': 'admin',
|
||||
@@ -233,7 +235,7 @@ describe('resolveHeaders', () => {
|
||||
'Backup-Email': '{{LIBRECHAT_USER_EMAIL}}',
|
||||
};
|
||||
|
||||
const result = resolveHeaders(headers, user);
|
||||
const result = resolveHeaders({ headers, user });
|
||||
|
||||
expect(result).toEqual({
|
||||
'Primary-Email': 'test@example.com',
|
||||
@@ -259,7 +261,7 @@ describe('resolveHeaders', () => {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
const result = resolveHeaders(headers, user, customUserVars);
|
||||
const result = resolveHeaders({ headers, user, customUserVars });
|
||||
|
||||
expect(result).toEqual({
|
||||
Authorization: 'Bearer secret-token',
|
||||
@@ -277,7 +279,7 @@ describe('resolveHeaders', () => {
|
||||
};
|
||||
const user = { id: 'user-123' };
|
||||
|
||||
const result = resolveHeaders(originalHeaders, user);
|
||||
const result = resolveHeaders({ headers: originalHeaders, user });
|
||||
|
||||
// Verify the result is processed
|
||||
expect(result).toEqual({
|
||||
@@ -306,7 +308,7 @@ describe('resolveHeaders', () => {
|
||||
'Dot-Header': '{{CUSTOM.VAR}}',
|
||||
};
|
||||
|
||||
const result = resolveHeaders(headers, user, customUserVars);
|
||||
const result = resolveHeaders({ headers, user, customUserVars });
|
||||
|
||||
expect(result).toEqual({
|
||||
'Dash-Header': 'dash-value',
|
||||
@@ -357,7 +359,7 @@ describe('resolveHeaders', () => {
|
||||
'X-User-TermsAccepted': '{{LIBRECHAT_USER_TERMSACCEPTED}}',
|
||||
};
|
||||
|
||||
const result = resolveHeaders(headers, user);
|
||||
const result = resolveHeaders({ headers, user });
|
||||
|
||||
expect(result['X-User-ID']).toBe('abc');
|
||||
expect(result['X-User-Name']).toBe('Test User');
|
||||
@@ -384,7 +386,7 @@ describe('resolveHeaders', () => {
|
||||
'X-Multi': 'User: {{LIBRECHAT_USER_ID}}, Env: ${TEST_API_KEY}, Custom: {{MY_CUSTOM}}',
|
||||
};
|
||||
const customVars = { MY_CUSTOM: 'custom-value' };
|
||||
const result = resolveHeaders(headers, user, customVars);
|
||||
const result = resolveHeaders({ headers, user, customUserVars: customVars });
|
||||
expect(result['X-Multi']).toBe('User: abc, Env: test-api-key-value, Custom: custom-value');
|
||||
});
|
||||
|
||||
@@ -394,7 +396,7 @@ describe('resolveHeaders', () => {
|
||||
'X-Unknown': '{{SOMETHING_NOT_RECOGNIZED}}',
|
||||
'X-Known': '{{LIBRECHAT_USER_ID}}',
|
||||
};
|
||||
const result = resolveHeaders(headers, user);
|
||||
const result = resolveHeaders({ headers, user });
|
||||
expect(result['X-Unknown']).toBe('{{SOMETHING_NOT_RECOGNIZED}}');
|
||||
expect(result['X-Known']).toBe('abc');
|
||||
});
|
||||
@@ -416,7 +418,7 @@ describe('resolveHeaders', () => {
|
||||
'X-Boolean': '{{LIBRECHAT_USER_EMAILVERIFIED}}',
|
||||
};
|
||||
const customVars = { MY_CUSTOM: 'custom-value' };
|
||||
const result = resolveHeaders(headers, user, customVars);
|
||||
const result = resolveHeaders({ headers, user, customUserVars: customVars });
|
||||
|
||||
expect(result['X-User']).toBe('abc');
|
||||
expect(result['X-Env']).toBe('test-api-key-value');
|
||||
@@ -426,4 +428,15 @@ describe('resolveHeaders', () => {
|
||||
expect(result['X-Empty']).toBe('');
|
||||
expect(result['X-Boolean']).toBe('true');
|
||||
});
|
||||
|
||||
it('should process LIBRECHAT_BODY placeholders', () => {
|
||||
const body = {
|
||||
conversationId: 'conv-123',
|
||||
parentMessageId: 'parent-456',
|
||||
messageId: 'msg-789',
|
||||
};
|
||||
const headers = { 'X-Conversation': '{{LIBRECHAT_BODY_CONVERSATIONID}}' };
|
||||
const result = resolveHeaders({ headers, body });
|
||||
expect(result['X-Conversation']).toBe('conv-123');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { extractEnvVariable } from 'librechat-data-provider';
|
||||
import type { TUser, MCPOptions } from 'librechat-data-provider';
|
||||
import type { TUser, MCPOptions, RequestBody } from 'librechat-data-provider';
|
||||
|
||||
/**
|
||||
* List of allowed user fields that can be used in MCP environment variables.
|
||||
@@ -25,6 +25,12 @@ const ALLOWED_USER_FIELDS = [
|
||||
'termsAccepted',
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* List of allowed request body fields that can be used in header placeholders.
|
||||
* These are common fields from the request body that are safe to expose in headers.
|
||||
*/
|
||||
const ALLOWED_BODY_FIELDS = ['conversationId', 'parentMessageId', 'messageId'] as const;
|
||||
|
||||
/**
|
||||
* Processes a string value to replace user field placeholders
|
||||
* @param value - The string value to process
|
||||
@@ -61,21 +67,48 @@ function processUserPlaceholders(value: string, user?: TUser): string {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces request body field placeholders within a string.
|
||||
* Recognized placeholders: `{{LIBRECHAT_BODY_<FIELD>}}` where `<FIELD>` ∈ ALLOWED_BODY_FIELDS.
|
||||
* If a body field is absent or null/undefined, it is replaced with an empty string.
|
||||
*
|
||||
* @param value - The string value to process
|
||||
* @param body - The request body object
|
||||
* @returns The processed string with placeholders replaced
|
||||
*/
|
||||
function processBodyPlaceholders(value: string, body: RequestBody): string {
|
||||
for (const field of ALLOWED_BODY_FIELDS) {
|
||||
const placeholder = `{{LIBRECHAT_BODY_${field.toUpperCase()}}}`;
|
||||
if (!value.includes(placeholder)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const fieldValue = body[field];
|
||||
const replacementValue = fieldValue == null ? '' : String(fieldValue);
|
||||
value = value.replace(new RegExp(placeholder, 'g'), replacementValue);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a single string value by replacing various types of placeholders
|
||||
* @param originalValue - The original string value to process
|
||||
* @param customUserVars - Optional custom user variables to replace placeholders
|
||||
* @param user - Optional user object for replacing user field placeholders
|
||||
* @param body - Optional request body object for replacing body field placeholders
|
||||
* @returns The processed string with all placeholders replaced
|
||||
*/
|
||||
function processSingleValue({
|
||||
originalValue,
|
||||
customUserVars,
|
||||
user,
|
||||
body = undefined,
|
||||
}: {
|
||||
originalValue: string;
|
||||
customUserVars?: Record<string, string>;
|
||||
user?: TUser;
|
||||
body?: RequestBody;
|
||||
}): string {
|
||||
let value = originalValue;
|
||||
|
||||
@@ -92,7 +125,12 @@ function processSingleValue({
|
||||
// 2. Replace user field placeholders (e.g., {{LIBRECHAT_USER_EMAIL}}, {{LIBRECHAT_USER_ID}})
|
||||
value = processUserPlaceholders(value, user);
|
||||
|
||||
// 3. Replace system environment variables
|
||||
// 3. Replace body field placeholders (e.g., {{LIBRECHAT_BODY_CONVERSATIONID}}, {{LIBRECHAT_BODY_PARENTMESSAGEID}})
|
||||
if (body) {
|
||||
value = processBodyPlaceholders(value, body);
|
||||
}
|
||||
|
||||
// 4. Replace system environment variables
|
||||
value = extractEnvVariable(value);
|
||||
|
||||
return value;
|
||||
@@ -103,12 +141,14 @@ function processSingleValue({
|
||||
* @param obj - The object to process
|
||||
* @param user - The user object containing all user fields
|
||||
* @param customUserVars - vars that user set in settings
|
||||
* @param body - the body of the request that is being processed
|
||||
* @returns - The processed object with environment variables replaced
|
||||
*/
|
||||
export function processMCPEnv(
|
||||
obj: Readonly<MCPOptions>,
|
||||
user?: TUser,
|
||||
customUserVars?: Record<string, string>,
|
||||
body?: RequestBody,
|
||||
): MCPOptions {
|
||||
if (obj === null || obj === undefined) {
|
||||
return obj;
|
||||
@@ -119,7 +159,7 @@ export function processMCPEnv(
|
||||
if ('env' in newObj && newObj.env) {
|
||||
const processedEnv: Record<string, string> = {};
|
||||
for (const [key, originalValue] of Object.entries(newObj.env)) {
|
||||
processedEnv[key] = processSingleValue({ originalValue, customUserVars, user });
|
||||
processedEnv[key] = processSingleValue({ originalValue, customUserVars, user, body });
|
||||
}
|
||||
newObj.env = processedEnv;
|
||||
}
|
||||
@@ -127,7 +167,7 @@ export function processMCPEnv(
|
||||
if ('args' in newObj && newObj.args) {
|
||||
const processedArgs: string[] = [];
|
||||
for (const originalValue of newObj.args) {
|
||||
processedArgs.push(processSingleValue({ originalValue, customUserVars, user }));
|
||||
processedArgs.push(processSingleValue({ originalValue, customUserVars, user, body }));
|
||||
}
|
||||
newObj.args = processedArgs;
|
||||
}
|
||||
@@ -137,39 +177,47 @@ export function processMCPEnv(
|
||||
if ('headers' in newObj && newObj.headers) {
|
||||
const processedHeaders: Record<string, string> = {};
|
||||
for (const [key, originalValue] of Object.entries(newObj.headers)) {
|
||||
processedHeaders[key] = processSingleValue({ originalValue, customUserVars, user });
|
||||
processedHeaders[key] = processSingleValue({ originalValue, customUserVars, user, body });
|
||||
}
|
||||
newObj.headers = processedHeaders;
|
||||
}
|
||||
|
||||
// Process URL if it exists (for WebSocket, SSE, StreamableHTTP types)
|
||||
if ('url' in newObj && newObj.url) {
|
||||
newObj.url = processSingleValue({ originalValue: newObj.url, customUserVars, user });
|
||||
newObj.url = processSingleValue({ originalValue: newObj.url, customUserVars, user, body });
|
||||
}
|
||||
|
||||
return newObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves header values by replacing user placeholders, custom variables, and environment variables
|
||||
* @param headers - The headers object to process
|
||||
* @param user - Optional user object for replacing user field placeholders (can be partial with just id)
|
||||
* @param customUserVars - Optional custom user variables to replace placeholders
|
||||
* @returns - The processed headers with all placeholders replaced
|
||||
* Resolves header values by replacing user placeholders, body variables, custom variables, and environment variables.
|
||||
*
|
||||
* @param options - Optional configuration object.
|
||||
* @param options.headers - The headers object to process.
|
||||
* @param options.user - Optional user object for replacing user field placeholders (can be partial with just id).
|
||||
* @param options.body - Optional request body object for replacing body field placeholders.
|
||||
* @param options.customUserVars - Optional custom user variables to replace placeholders.
|
||||
* @returns The processed headers with all placeholders replaced.
|
||||
*/
|
||||
export function resolveHeaders(
|
||||
headers: Record<string, string> | undefined,
|
||||
user?: Partial<TUser> | { id: string },
|
||||
customUserVars?: Record<string, string>,
|
||||
) {
|
||||
const resolvedHeaders = { ...(headers ?? {}) };
|
||||
export function resolveHeaders(options?: {
|
||||
headers: Record<string, string> | undefined;
|
||||
user?: Partial<TUser> | { id: string };
|
||||
body?: RequestBody;
|
||||
customUserVars?: Record<string, string>;
|
||||
}) {
|
||||
const { headers, user, body, customUserVars } = options ?? {};
|
||||
const inputHeaders = headers ?? {};
|
||||
|
||||
if (headers && typeof headers === 'object' && !Array.isArray(headers)) {
|
||||
Object.keys(headers).forEach((key) => {
|
||||
const resolvedHeaders: Record<string, string> = { ...inputHeaders };
|
||||
|
||||
if (inputHeaders && typeof inputHeaders === 'object' && !Array.isArray(inputHeaders)) {
|
||||
Object.keys(inputHeaders).forEach((key) => {
|
||||
resolvedHeaders[key] = processSingleValue({
|
||||
originalValue: headers[key],
|
||||
originalValue: inputHeaders[key],
|
||||
customUserVars,
|
||||
user: user as TUser,
|
||||
body,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ export * from './types/mutations';
|
||||
export * from './types/queries';
|
||||
export * from './types/runs';
|
||||
export * from './types/web';
|
||||
export * from './types/http';
|
||||
/* query/mutation keys */
|
||||
export * from './keys';
|
||||
/* api call helpers */
|
||||
|
||||
6
packages/data-provider/src/types/http.ts
Normal file
6
packages/data-provider/src/types/http.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export interface RequestBody {
|
||||
parentMessageId: string;
|
||||
messageId: string;
|
||||
conversationId?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
Reference in New Issue
Block a user