feat(config): add location feature flag and geocoder config

Adds a `location` section to librechat.yaml config with enabled/geocoder
fields, wires loadLocationConfig into AppService, and surfaces the resolved
flag in the startup-config endpoint so the UI can gate location controls.
This commit is contained in:
Marco Beretta 2026-06-15 18:41:42 +02:00
parent baa369a448
commit fdca6184ba
No known key found for this signature in database
GPG key ID: D918033D8E74CC11
6 changed files with 74 additions and 0 deletions

View file

@ -181,6 +181,18 @@ function buildWebSearchConfig(appConfig) {
};
}
function buildLocationStartupConfig(appConfig) {
const loc = appConfig?.location;
if (!loc || loc.enabled === false) {
return undefined;
}
const out = { enabled: true };
if (loc.geocoder?.endpoint) {
out.geocoder = { endpoint: loc.geocoder.endpoint };
}
return out;
}
function buildCloudFrontStartupConfig() {
const config = getCloudFrontConfig();
if (
@ -284,6 +296,11 @@ router.get('/', async function (req, res) {
payload.webSearch = webSearch;
}
const location = buildLocationStartupConfig(appConfig);
if (location) {
payload.location = location;
}
const buildInfo = buildBuildInfoPayload(appConfig?.interfaceConfig);
if (buildInfo) {
payload.buildInfo = buildInfo;

View file

@ -1409,6 +1409,12 @@ export type TStartupConfig = {
scraperProvider?: ScraperProviders;
rerankerType?: RerankerTypes;
};
location?: {
enabled: boolean;
geocoder?: {
endpoint?: string;
};
};
cloudFront?: {
cookieRefresh?: {
endpoint: string;
@ -1561,6 +1567,17 @@ export const webSearchSchema = z.object({
export type TWebSearchConfig = DeepPartial<z.infer<typeof webSearchSchema>>;
export const locationSchema = z.object({
enabled: z.boolean().default(true),
geocoder: z
.object({
endpoint: z.string().url().optional(),
})
.optional(),
});
export type TLocationConfig = z.infer<typeof locationSchema>;
export const ocrSchema = z.object({
mistralModel: z.string().optional(),
apiKey: z.string().optional().default('${OCR_API_KEY}'),
@ -1687,6 +1704,7 @@ export const configSchema = z.object({
cache: z.boolean().default(true),
ocr: ocrSchema.optional(),
webSearch: webSearchSchema.optional(),
location: locationSchema.optional(),
memory: memorySchema.optional(),
summarization: summarizationConfigSchema.optional(),
skillSync: skillSyncConfigSchema,

View file

@ -0,0 +1,19 @@
import { loadLocationConfig } from './location';
describe('loadLocationConfig', () => {
it('defaults to enabled when unset', () => {
expect(loadLocationConfig(undefined)).toEqual({ enabled: true });
});
it('respects an explicit disable', () => {
expect(loadLocationConfig({ enabled: false })).toEqual({ enabled: false });
});
it('passes through a geocoder endpoint', () => {
const result = loadLocationConfig({
enabled: true,
geocoder: { endpoint: 'https://example.com/geo' },
});
expect(result.geocoder?.endpoint).toBe('https://example.com/geo');
});
});

View file

@ -0,0 +1,15 @@
import type { TCustomConfig, TLocationConfig } from 'librechat-data-provider';
/**
* Resolves the `location` section of librechat.yaml into the runtime config.
* Defaults to enabled when the section is omitted.
*/
export function loadLocationConfig(location: TCustomConfig['location']): TLocationConfig {
if (!location) {
return { enabled: true };
}
return {
enabled: location.enabled ?? true,
...(location.geocoder?.endpoint ? { geocoder: { endpoint: location.geocoder.endpoint } } : {}),
};
}

View file

@ -8,6 +8,7 @@ import type { TCustomConfig, FileSources, DeepPartial } from 'librechat-data-pro
import type { AppConfig, FunctionTool } from '~/types/app';
import { loadDefaultInterface } from './interface';
import { loadTurnstileConfig } from './turnstile';
import { loadLocationConfig } from './location';
import { agentsConfigSetup } from './agents';
import { loadWebSearchConfig } from './web';
import { processModelSpecs } from './specs';
@ -97,6 +98,7 @@ export const AppService = async (params?: {
const ocr = loadOCRConfig(config.ocr);
const webSearch = loadWebSearchConfig(config.webSearch);
const location = loadLocationConfig(config.location);
const memory = loadMemoryConfig(config.memory);
const summarization = loadSummarizationConfig(config);
const skillSync = loadSkillSyncConfig(config);
@ -139,6 +141,7 @@ export const AppService = async (params?: {
balance,
skillSync,
webSearch,
location,
mcpSettings,
fileStrategy,
registration,

View file

@ -63,6 +63,8 @@ export interface AppConfig {
summarization?: SummarizationConfig;
/** Web search configuration */
webSearch?: TCustomConfig['webSearch'];
/** Location feature configuration */
location?: TCustomConfig['location'];
/** Message filter configuration (PII and future filter types) */
messageFilter?: TCustomConfig['messageFilter'];
/** Skill sync configuration */