mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-10-09 06:56:30 +00:00
fix(tools): register get_location in availableTools and refine location UX
This commit is contained in:
parent
398661d340
commit
f2379f7327
9 changed files with 110 additions and 36 deletions
|
|
@ -12,7 +12,7 @@ const TraversaalSearch = require('./structured/TraversaalSearch');
|
|||
const createOpenAIImageTools = require('./structured/OpenAIImageTools');
|
||||
const TavilySearchResults = require('./structured/TavilySearchResults');
|
||||
const createGeminiImageTool = require('./structured/GeminiImageGen');
|
||||
const createLocationTool = require('./structured/GetLocation');
|
||||
const GetLocation = require('./structured/GetLocation');
|
||||
|
||||
module.exports = {
|
||||
...manifest,
|
||||
|
|
@ -28,5 +28,5 @@ module.exports = {
|
|||
TavilySearchResults,
|
||||
createOpenAIImageTools,
|
||||
createGeminiImageTool,
|
||||
createLocationTool,
|
||||
GetLocation,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@
|
|||
"name": "Get Location",
|
||||
"pluginKey": "get_location",
|
||||
"description": "Returns the user's shared location (place, coordinates, timezone) so the assistant can tailor language, units, and regional context.",
|
||||
"icon": "assets/google-search.svg",
|
||||
"icon": "assets/logo.svg",
|
||||
"authConfig": []
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
const { tool } = require('@librechat/agents/langchain/tools');
|
||||
const { Tool } = require('@librechat/agents/langchain/tools');
|
||||
const { formatLocationToolResult } = require('@librechat/api');
|
||||
|
||||
const locationSchema = {
|
||||
|
|
@ -8,22 +8,31 @@ const locationSchema = {
|
|||
};
|
||||
|
||||
/**
|
||||
* Factory for the `get_location` tool, bound to the current request/user.
|
||||
* @param {{ userId?: string, req?: import('express').Request }} params
|
||||
* @returns {Promise<import('@librechat/agents/langchain/tools').DynamicStructuredTool>}
|
||||
* GetLocation - returns the user's shared location (place, coordinates, timezone).
|
||||
* Reads the resolved app config (admin feature flag) and the user's stored
|
||||
* `personalization.location` from the request, and delegates formatting to
|
||||
* `formatLocationToolResult`. Gracefully reports when disabled or not shared.
|
||||
*/
|
||||
module.exports = async function createLocationTool({ req } = {}) {
|
||||
return tool(
|
||||
async () => {
|
||||
const featureEnabled = req?.config?.location?.enabled !== false;
|
||||
const location = req?.user?.personalization?.location;
|
||||
return formatLocationToolResult(location, { featureEnabled });
|
||||
},
|
||||
{
|
||||
name: 'get_location',
|
||||
description:
|
||||
"Returns the user's current location (place, coordinates, timezone) when they have shared it. Use it to tailor language, regional context, units, or weather lookups.",
|
||||
schema: locationSchema,
|
||||
},
|
||||
);
|
||||
};
|
||||
class GetLocation extends Tool {
|
||||
constructor(fields = {}) {
|
||||
super();
|
||||
|
||||
/** @type {boolean} Used to initialize the Tool without request context. */
|
||||
this.override = fields.override ?? false;
|
||||
this.req = fields.req;
|
||||
this.userId = fields.userId;
|
||||
|
||||
this.name = 'get_location';
|
||||
this.description =
|
||||
"Returns the user's current location (place, coordinates, timezone) when they have shared it. Use it to tailor language, regional context, units, or weather lookups.";
|
||||
this.schema = locationSchema;
|
||||
}
|
||||
|
||||
async _call() {
|
||||
const featureEnabled = this.req?.config?.location?.enabled !== false;
|
||||
const location = this.req?.user?.personalization?.location;
|
||||
return formatLocationToolResult(location, { featureEnabled });
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GetLocation;
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
const createLocationTool = require('./GetLocation');
|
||||
const GetLocation = require('./GetLocation');
|
||||
|
||||
const makeReq = ({ location, featureEnabled = true } = {}) => ({
|
||||
config: { location: { enabled: featureEnabled } },
|
||||
user: { id: 'user-1', personalization: location ? { location } : {} },
|
||||
});
|
||||
|
||||
describe('createLocationTool', () => {
|
||||
describe('GetLocation tool', () => {
|
||||
it('returns the user location when enabled', async () => {
|
||||
const tool = await createLocationTool({
|
||||
userId: 'user-1',
|
||||
const tool = new GetLocation({
|
||||
req: makeReq({
|
||||
location: {
|
||||
enabled: true,
|
||||
|
|
@ -24,17 +23,22 @@ describe('createLocationTool', () => {
|
|||
});
|
||||
|
||||
it('returns a not-shared message when the user has not opted in', async () => {
|
||||
const tool = await createLocationTool({ userId: 'user-1', req: makeReq({}) });
|
||||
const tool = new GetLocation({ req: makeReq({}) });
|
||||
const result = await tool.invoke({});
|
||||
expect(result).toMatch(/has not shared/i);
|
||||
});
|
||||
|
||||
it('returns a disabled message when the admin flag is off', async () => {
|
||||
const tool = await createLocationTool({
|
||||
userId: 'user-1',
|
||||
const tool = new GetLocation({
|
||||
req: makeReq({ location: { enabled: true, manual: 'X' }, featureEnabled: false }),
|
||||
});
|
||||
const result = await tool.invoke({});
|
||||
expect(result).toMatch(/disabled/i);
|
||||
});
|
||||
|
||||
it('can be constructed without request context (override) for tool discovery', () => {
|
||||
const tool = new GetLocation({ override: true });
|
||||
expect(tool.name).toBe('get_location');
|
||||
expect(tool.schema).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
const { Tool } = require('@librechat/agents/langchain/tools');
|
||||
const GetLocation = require('../GetLocation');
|
||||
|
||||
describe('get_location tool registration', () => {
|
||||
it('GetLocation is a Tool subclass so loadAndFormatTools discovers it', () => {
|
||||
expect(GetLocation.prototype instanceof Tool).toBe(true);
|
||||
});
|
||||
|
||||
it('can be instantiated with override for discovery without request context', () => {
|
||||
const tool = new GetLocation({ override: true });
|
||||
expect(tool.name).toBe('get_location');
|
||||
expect(tool.schema).toBeDefined();
|
||||
});
|
||||
|
||||
it('has a plain-object schema compatible with loadAndFormatTools (non-Zod)', () => {
|
||||
const tool = new GetLocation({ override: true });
|
||||
expect(tool.schema).toEqual({ type: 'object', properties: {}, required: [] });
|
||||
});
|
||||
});
|
||||
|
|
@ -34,7 +34,7 @@ const {
|
|||
TavilySearchResults,
|
||||
createGeminiImageTool,
|
||||
createOpenAIImageTools,
|
||||
createLocationTool,
|
||||
GetLocation,
|
||||
} = require('../');
|
||||
const {
|
||||
createMCPTool,
|
||||
|
|
@ -182,6 +182,7 @@ const loadTools = async ({
|
|||
google: GoogleSearchAPI,
|
||||
open_weather: OpenWeather,
|
||||
wolfram: StructuredWolfram,
|
||||
get_location: GetLocation,
|
||||
'stable-diffusion': StructuredSD,
|
||||
'azure-ai-search': StructuredACS,
|
||||
traversaal_search: TraversaalSearch,
|
||||
|
|
@ -231,9 +232,6 @@ const loadTools = async ({
|
|||
fileStrategy,
|
||||
});
|
||||
},
|
||||
get_location: async () => {
|
||||
return createLocationTool({ userId: user, req: options.req });
|
||||
},
|
||||
};
|
||||
|
||||
const requestedTools = {};
|
||||
|
|
@ -264,6 +262,7 @@ const loadTools = async ({
|
|||
dalle: imageGenOptions,
|
||||
'stable-diffusion': imageGenOptions,
|
||||
gemini_image_gen: imageGenOptions,
|
||||
get_location: { req: options.req },
|
||||
};
|
||||
|
||||
/** @type {Record<string, string>} */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue