mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
feat(plugins): let a plugin answer place searches from its own index
TREK searches its own place index and OpenStreetMap. Neither carries a rating, so "the best rated hotel around here" has never been answerable, and an operator with a better index for their region had no way to reach the search box at all. The searchProvider hook is that way in: a plugin is handed the query, a limit, the language and the coordinate the search is biased toward, and answers with places in the same shape the core search returns, plus a rating. It sits beside the core search rather than inside it. The plugin runtime already imports PlacesModule, which imports MapsModule, so asking the providers from the maps service would close a module cycle — the same reason the other fourteen contribution controllers stand next to what they contribute to. The client asks both at once and merges, at the single point every caller already goes through, so all nine search surfaces get plugin hits without knowing the hook exists. A plugin index may never hold up the list: the client stops waiting at two and a half seconds and the host gives a provider two, which is also why the budget is the shortest of any hook here. With no such plugin installed the route answers without reaching the runtime at all. Every hit is normalized host-side and shared by the REST route and the MCP tool, so a cap that holds for the browser holds for the assistant: coordinates are range-checked, strings capped, the rating clamped to 0..5, a website must be http or https, and ids are namespaced so they can never collide with an OSM one. search_places_via_plugins is the MCP half, because what the user can see the assistant has to be able to see; search_place now points at it.
This commit is contained in:
@@ -225,6 +225,7 @@ skipped, never fatal):
|
||||
| `mapMarkerProvider` | ≤200 markers per provider |
|
||||
| `warningProvider` | ≤20 warnings per provider, each message ≤300 chars |
|
||||
| `placeDetailProvider` | ≤12 items per provider |
|
||||
| `searchProvider` | ≤20 places per provider, 2 s to answer |
|
||||
| `photoProvider` | ≤60 photos per page |
|
||||
| `calendarSource` | ≤500 events per source per request |
|
||||
| `tableContributor` | ≤20 columns / ≤10 actions per entity |
|
||||
|
||||
@@ -212,6 +212,7 @@ export const PERMISSION_FAMILIES: PermissionFamily[] = [
|
||||
{ value: 'hook:photo-provider', hint: 'Supply place photos to TREK' },
|
||||
{ value: 'hook:calendar-source', hint: 'Supply calendar events to TREK' },
|
||||
{ value: 'hook:place-detail-provider', hint: 'Contribute extra details (reviews, ratings, links) to a place' },
|
||||
{ value: 'hook:search-provider', hint: 'Answer place searches from your own index, beside the core results' },
|
||||
{ value: 'hook:trip-warning-provider', hint: 'Raise validation warnings on a trip (shown in the planner)' },
|
||||
{ value: 'hook:table-contributor', hint: 'Add columns to TREK\'s tables' },
|
||||
{ value: 'hook:map-marker-provider', hint: 'Add your own markers to the map' },
|
||||
|
||||
@@ -6,6 +6,7 @@ export const HOOK_PERMISSION: Readonly<Record<string, string>> = {
|
||||
photoProvider: 'hook:photo-provider',
|
||||
calendarSource: 'hook:calendar-source',
|
||||
placeDetailProvider: 'hook:place-detail-provider',
|
||||
searchProvider: 'hook:search-provider',
|
||||
warningProvider: 'hook:trip-warning-provider',
|
||||
tableContributor: 'hook:table-contributor',
|
||||
mapMarkerProvider: 'hook:map-marker-provider',
|
||||
@@ -65,6 +66,7 @@ export const KNOWN_PERMISSIONS: string[] = [
|
||||
'hook:photo-provider',
|
||||
'hook:calendar-source',
|
||||
'hook:place-detail-provider',
|
||||
'hook:search-provider',
|
||||
'hook:trip-warning-provider',
|
||||
'hook:table-contributor',
|
||||
'hook:map-marker-provider',
|
||||
|
||||
@@ -476,6 +476,45 @@ export interface PlaceDetailProvider {
|
||||
* The host caps results at 12 items per provider. */
|
||||
getDetails(placeId: number, ctx: PluginContext): Promise<PlaceDetailItem[]>;
|
||||
}
|
||||
/**
|
||||
* A place a search provider found, in the shape the host turns into a search row.
|
||||
*
|
||||
* `rating` is the one field open data cannot answer, and the reason this hook exists:
|
||||
* OpenStreetMap carries no ratings at all, so "the best one around here" needs an
|
||||
* index that has them. Zero to five, the scale every such index uses.
|
||||
*/
|
||||
export interface SearchResultPlace {
|
||||
/** Stable id in your own index. The host namespaces it as `plugin:<yourId>:<id>`. */
|
||||
id?: string;
|
||||
name: string;
|
||||
lat: number;
|
||||
lng: number;
|
||||
address?: string;
|
||||
rating?: number;
|
||||
website?: string;
|
||||
phone?: string;
|
||||
category?: string;
|
||||
description?: string;
|
||||
}
|
||||
/** What the host asks a search provider to look for. */
|
||||
export interface SearchRequest {
|
||||
query: string;
|
||||
/** The most rows worth returning. The host caps it at 20 whatever it is asked for. */
|
||||
limit: number;
|
||||
/** The caller's language tag, for indexes that carry localized names. */
|
||||
lang?: string;
|
||||
/** Where the person is looking, when there is somewhere to bias toward. */
|
||||
near?: { lat: number; lng: number };
|
||||
}
|
||||
export interface SearchProvider {
|
||||
/** Places for a query, drawn into TREK's own search list beside the core results.
|
||||
* Needs `hook:search-provider`. The host caps results at 20 places per provider and
|
||||
* gives you 2 seconds to answer, because somebody is waiting on the list.
|
||||
*
|
||||
* Called for an explicit search, not for every keystroke: an external index has rate
|
||||
* limits, and a request per typed letter would spend them on words nobody finished. */
|
||||
search(request: SearchRequest, ctx: PluginContext): Promise<SearchResultPlace[]>;
|
||||
}
|
||||
/** A validation/warning a plugin raises on a trip; TREK surfaces it in the planner. */
|
||||
export interface TripWarning { level: 'info' | 'warning' | 'error'; message: string; dayId?: number; placeId?: number; }
|
||||
export interface WarningProvider {
|
||||
@@ -828,6 +867,7 @@ export interface PluginDefinition {
|
||||
photoProvider?: PhotoProvider;
|
||||
calendarSource?: CalendarSource;
|
||||
placeDetailProvider?: PlaceDetailProvider;
|
||||
searchProvider?: SearchProvider;
|
||||
warningProvider?: WarningProvider;
|
||||
tableContributor?: TableContributor;
|
||||
mapMarkerProvider?: MapMarkerProvider;
|
||||
|
||||
Reference in New Issue
Block a user