AI Exercise Search¶
Overview¶
AI Exercise Search lets therapists find exercises by meaning, not just by name. Instead of matching the letters you type against exercise titles, it sends your query to an AI model that understands target anatomy, movement patterns, therapeutic intent, and tags — so a search for "gentle knee bend after surgery" can surface the right exercises even when none of them contain those exact words.
It runs alongside the existing instant text filter: as you type, the normal name filter keeps working, and a Search with AI button appears so you can ask the AI for semantically related matches on demand. Where the tenant flag hepSmartSearch is on, a second search by meaning runs by itself as you type (see Search as you type) and this button is labelled Extended search.
Written for both human developers and AI assistants working in this codebase.
Where It Appears¶
AI search is wired into the two places therapists look for exercises. Both surfaces share a single hook (useExerciseAiSearch) and a single endpoint, so the behavior is identical.
Exercise Library page¶
File: OktaPT-FE/pages/doctor/exercise-library.tsx
On the All Exercises tab, typing into the search bar reveals an AI search button card at the end of the grid/table. Running a search pins the AI matches to the top of the list and shows the results line ("AI: “…” · 10", one line, with an × to clear it). Enter in the search box runs it too (All tab only). The text search itself is the ranked matcher of the HEP builder's palette since 2026-10-06 (see exercise-library.md).
Template editor¶
File: OktaPT-FE/components/workoutTemplates/ExercisePickerPanel.tsx
Creating or editing a workout template shows the exercise library beside the template (as a sheet on a phone) with the same search, scopes, tag filter, search as you type and Search with AI card as the HEP builder's palette, built on the same hooks.
Exercise Selector (plan / HEP builder)¶
File: OktaPT-FE/components/ExerciseSelector/ExerciseLibraryDialog.tsx
When a therapist clicks Add Exercise while building or editing a plan, the same AI search button appears in the picker whenever there is search text. Matches are pinned to the top with an AI badge so they're easy to spot while assembling a session. In the HEP builder's palette Enter runs this search as well; adding an exercise is always a click or a drag, never a keystroke.
How It Works¶
- The therapist types a query and taps Search with AI (
AiSearchButtonCard). - The frontend calls
POST /v2/exercises/closest-matcheswith the query (and, in the picker, the patient context). - The backend gathers the exercises the therapist can see that have a video or photo, sends a lightweight description of each (name, description, tags) to an AI model, and asks for the best-to-worst matches.
- The model returns the matching exercise IDs; the backend hydrates them into full exercise objects and returns them in ranked order.
- The frontend prepends the matches to the visible list (de-duplicated against the regular filtered results) and shows the AI: “…” · 10 results line.
What gets searched¶
Only active, visible exercises that have a video or photo are candidates — the same visibility rules used elsewhere (public exercises plus the therapist's own; see exercise-scope-service.md). Exercises with no demo media are skipped, since a match is meant to be usable in a plan straight away.
How results are shown¶
| Element | File | Behavior |
|---|---|---|
| Search button card | components/exerciseLibrary/AiSearchButtonCard.tsx |
Appears when there is search text; shows a spinner while searching; disabled on an empty query |
| Results line | components/exerciseLibrary/AiResultsBanner.tsx |
One line: the query (cut short with an ellipsis past 40 characters, shortenAiQuery) and the match count, with a small × that clears; a one-line note when nothing matched or the search failed (the HEP builder's palette shows those two on its search card instead). In the pickers it scrolls away with the cards, and a picker scrolled down to the search card at the end goes back to the top when what the search found is shown there (in the palette, only for new matches); on the library page it sits above the grid |
| AI badge | components/exerciseLibrary/ExerciseGrid.tsx, ExerciseTable.tsx |
AI matches are pinned to the top and tagged with an AI badge in both grid and table views |
The × on the line removes the pinned AI results and returns to the normal filtered list.
Search as you type (hepSmartSearch)¶
A separate, quieter search by meaning, behind the per-tenant flag hepSmartSearch (see feature-flags.md). A moment after typing pauses, the decision model is asked (POST /v2/hep-suggestions/search, through lib/hooks/useSmartExerciseSearch.ts) and what it finds joins the list as ordinary cards, with a brand ring on the web and an "AI match" caption in the app: the first answer on top, what arrives later at the end, so the list shifts once and never again. The only other trace is a quiet spinner in the search box. Where an exercise found this way is chosen (added to a plan in the HEP builder and the app's HEP picker, picked for a personalized video), the pick is recorded; opening one in a library only to look at it is not a pick and records nothing. It works in the HEP builder's palette, on the exercise library page (since 2026-10-06), and in the app's HEP picker and Exercises tab (through the shared hooks/useScopedSmartSearch.ts, which re-filters the answers by the list's segment and tags, which the API knows nothing about) and its personalized-video picker (through hooks/useSmartExerciseSearch.ts directly). Where it is on, this page's on-demand search is labelled Extended search, and the web and the app order their lists the same way: Extended-search matches, the smart search's first answer, the regular results, the smart search's later finds.
Behavior & Limits¶
- Therapists only. The endpoint is doctor-scoped; patient accounts can't call it. When a patient context is supplied in the picker, the backend verifies the therapist has access to that patient first.
- Rate limited to a small number of searches per minute per user to keep AI usage in check.
- Query length is capped; overly long queries are rejected.
- Timeout-safe. If the AI doesn't respond in time the request fails cleanly and the banner shows a retry message — the normal text filter is unaffected.
- Bilingual. The UI strings and the exercise/tag text sent for matching are localized; English and Russian are supported via the
exerciseLibrary.aiSearch.*keys inpublic/locales/{en,ru}/common.json.
There is no feature flag for the on-demand search — it is available to all therapists. Only the search as you type above needs hepSmartSearch.
Related Documentation¶
| Document | Covers |
|---|---|
| exercise-library.md | The full exercise library: browsing, filtering, create/edit, data model |
| patient-workout-management.md | Building and editing plans, where the exercise picker is used |
| exercise-scope-service.md | Visibility rules that determine which exercises are searchable |
Key Files¶
| File | Purpose |
|---|---|
OktaPT-API/src/routes/exercises/exercises.ts |
POST /v2/exercises/closest-matches endpoint (auth, rate limit, validation) |
OktaPT-API/src/services/exerciseService.ts |
Selects video/photo-backed candidates and formats them for matching |
OktaPT-FE/lib/hooks/useExerciseAiSearch.ts |
Shared hook: runs the search, holds results/loading/error state |
OktaPT-FE/components/exerciseLibrary/AiSearchButtonCard.tsx |
"Search with AI" button card |
OktaPT-FE/components/exerciseLibrary/AiResultsBanner.tsx |
Results / empty / error banner |
OktaPT-FE/components/exerciseLibrary/ExerciseGrid.tsx |
Grid view: pinned AI matches + AI badge |
OktaPT-FE/components/exerciseLibrary/ExerciseTable.tsx |
Table view: pinned AI matches + AI badge |
OktaPT-FE/components/ExerciseSelector/ExerciseLibraryDialog.tsx |
AI search in the plan/HEP exercise picker |
OktaPT-FE/pages/doctor/exercise-library.tsx |
Exercise Library page wiring |
OktaPT-FE/lib/utils/exercise.ts |
Builds the display list (merges AI matches, smart matches and filtered results in one order, shared with the HEP builder's palette) |
OktaPT-FE/lib/hooks/useSmartExerciseSearch.ts |
Search as you type by meaning (hepSmartSearch), web |
Okta-Mobile/hooks/useScopedSmartSearch.ts |
The same for the app's exercise lists, re-filtered by segment and tags |