log_workout
UNIT INPUTS: value: pass the user's number unconverted; tool converts once before storage. alternate_unit: set the field's matching input_* companion; canonical_unit: omit companion. precedence: overrides instructions to convert manually.
COMPLETED WORKOUTS, ANY ACTIVITY:
With actual exercises/sets, provide them as usual. Session totals and activity metrics can accompany real sets in the SAME log_workout call.
With only an activity description (walking, yoga, swimming, a kettlebell circuit, strength, HIIT, cycling, etc.), give focus_type and reported session details; OMIT exercises or pass []. A session does not require fake exercises or sets. Never invent exercises, reps, weights, splits, heart rates or calories.
Use notes for reported context, including uncertain durations (e.g. "15-20 minutes, light intensity"). Leave unknown numeric fields unset rather than converting a range into false precision. Mark estimated=true ONLY when numeric values really are estimates; it is not implied by having zero sets.
Walking: focus_type "Walking"; cycling/biking: "Cycling"; rowing/RowErg: "Rowing". Other activity names remain as stated. log_run remains the richer dedicated route for completed runs with known distance+duration, running splits or planned runs. Never send a walk, ride or row to log_run.
distance_mi/distance_km/distance_meters are alternatives; send the unit the user provided. The server converts, never convert it yourself. duration_sec is total workout/moving seconds, not a set or a repetition. Use reported HR, power, cadence, elevation, actual laps/segments only when known.
Log a complete workout session: exercises, sets, reps, weights, and session metadata. Use when the user describes finishing a workout, lists exercises performed, or asks to log training. workout_to_do: in-app chat uses propose_workout, including save/start requests; discover it if needed. External clients: see SAVED WORKOUT MODE.
EXERCISE NAMES:
Reuse known canonical names; otherwise call list_exercises and match each exercise to the closest canonical name. No reasonable match → use the name as stated. Don't ask before logging, match silently and log.
"Chest press" (machine) and "bench press" (barbell) are DISTINCT — pass the user's term through so the resolver's aliases pin the right one.
name is ONLY the exercise name, never reps/weights/sets — those go in the sets array.
LITERAL NAME: literal_name: true keeps the user's exact wording instead of the closest library match, skips the resolver, and gets no NSI score (no benchmark to compare an unmatched name against). Use for "call it exactly X", "not the standard one", "literally X", or a rejected match.
The result says when a name was matched to something other than what the user said. Relay it in your own words rather than repeating the line verbatim. If a name matches nothing closely enough, the result names near-miss library exercises; ask the user which they meant rather than accept the unscored custom log silently.
EQUIPMENT (load basis): dumbbell_pair is one dumbbell in EACH hand, weight_lb PER HAND (2x for NSI); dumbbell_single is one implement total. Laterality (single-leg/arm) does NOT decide this alone. Set it when the user describes the load (each hand, machine, band); a wrong or missing tag silently halves or doubles NSI. Values: barbell, dumbbell_pair, dumbbell_single, machine, kettlebell, bodyweight, band, cable, trx, other.
SETS:
"3 sets of 15 reps" → 3 set objects with reps: 15. "15/12/10" → 3 sets with reps 15, 12, 10.
Pure isometric holds (planks, dead hangs, wall sits) have no reps: "30 second plank" = { hold_length_sec: 30 }.
Tempo/pause work combines reps + weight_lb + hold_length_sec (seconds per rep) on the same set, never in notes.
Loaded carries (farmers carry, sled push, weighted plank) are one set per trip: hold_length_sec + weight_lb, omit reps unless a trip count is given. weight_lb is PER HAND for a two-implement carry, TOTAL for one implement. Distance has no column and is never a duration — put it in notes.
INFER — do not ask:
date: today, or from context
focus_type: from the exercises (bench/shoulders/triceps=Push, rows/pulldowns/curls=Pull, squats/deadlifts/lunges=Legs, mixed upper=Upper, everything=Full Body)
is_bodyweight: true for pull-ups, push-ups, dips, bodyweight squats; missing load alone does not mean bodyweight
superset_group: same integer for exercises done back-to-back or as a superset
slot_type: 'warmup' for prep at the start, 'finisher' for burnout/cardio at the end, 'working' (default) otherwise
RPE (Rate of Perceived Exertion) — 1-10 scale, half steps allowed (7.5, 8.5):
Session-level RPE: overall session difficulty. Infer from user comments like "brutal session" (8-9) or "easy day" (3-4). Optional.
Per-set RPE: how hard each individual set felt. Include only if the user explicitly mentions per-set effort or failure. Optional.
Scale: 1=minimal effort, 5=moderate, 7=hard, 8=few reps left, 9=one rep left, 10=maximal/failure.
optional_metadata: infer location/focus_type when supported, else omit; heart_points: reported values only. Missing external load: follow LIVE LOADS.
RETURNS the new session's ID (as "[ID NN]"). Pass it to update_workout / delete_workout / get_workout for follow-ups in this conversation.
LIVE LOADS: for user-driven completed-workout logging, obvious bodyweight movements may omit load and are inferred as bodyweight. If a performed set normally uses external resistance, include that set's load. If the user did not provide it, ask one batched clarification before calling this tool. Only set load_unknown=true on a missing set when the user explicitly says they do not know, do not remember, or want to save without that load. Preserve partial known loads positionally; never copy one set's load to another or treat a missing external load as bodyweight.
SAVED WORKOUT MODE: in-app chat uses propose_workout for new workout plans, whether requested to save or start; discover it if needed. External clients without cards: set save_as_saved_workout=true and pass the full prescription through this same tool. In saved mode the workout is NOT logged as completed history, and LIVE LOADS does not apply: prescribed weight may be omitted. To replace an existing Saved Workout, also pass saved_workout_id; the supplied prescription fully replaces its prior prescribed sets/reps/weights. Use saved_workout_title when the reusable name should differ from focus_type.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| rpe | No | Session RPE, 1-10, half steps allowed: 5 moderate, 7 hard, 9 one rep left, 10 failure. Infer from comments about overall difficulty, or omit. | |
| date | Yes | YYYY-MM-DD. Default to today. | |
| notes | No | Session notes: how it went, PRs hit, how they felt. | |
| avg_hr | No | Reported average heart rate (bpm). | |
| max_hr | No | Reported maximum heart rate (bpm). | |
| splits | No | Actually reported laps/splits, same structure as the in-app run logger; never invent. | |
| calories | No | Reported session calories; never invent. | |
| location | No | Gym, Home, Outdoor. Infer from context, else omit. | |
| segments | No | Actually performed intervals/blocks, same structure as the in-app run logger; never invent. | |
| estimated | No | True only when a saved numeric value is an AI approximation; describe its source/range in notes. | |
| exercises | No | Only the actual exercises/sets provided by the user. For a session-level log, omit or use []. | |
| intensity | No | Qualitative effort as reported (does not manufacture an RPE). | |
| focus_type | No | Specific completed activity (Walking, Cycling, Yoga, Kettlebell Circuit, Full Body, etc.). Infer from user wording; omit only if notes describe the activity. | |
| start_time | No | Local HH:MM when explicitly supplied; otherwise omit. | |
| avg_power_w | No | Reported average power in watts (e.g. cycling). | |
| distance_km | No | Session distance in kilometers. Server converts to miles. | |
| distance_mi | No | Session distance in miles, when supplied in miles. In mi, or km with input_distance_unit set. See UNIT INPUTS. | |
| elapsed_sec | No | Elapsed seconds including pauses; omit if unknown. | |
| max_power_w | No | Reported peak power in watts. | |
| duration_sec | No | Total workout or moving time in seconds. Convert user-stated minutes to seconds; not a set duration. | |
| avg_cadence_rpm | No | Reported cycling/rowing cadence, revs/min. | |
| avg_cadence_spm | No | Reported running/walking cadence, steps/min. | |
| distance_meters | No | Session distance in meters. Server converts to miles. | |
| elevation_gain_m | No | Reported elevation gain in meters. | |
| saved_workout_id | No | Existing Saved Workout ID to replace in saved mode. Omit to create a new Saved Workout. | |
| heart_points_peak | No | Peak-intensity heart points, if mentioned. | |
| avg_pace_sec_per_mi | No | Reported average seconds per mile. Never invent a pace. In mi, or km with input_distance_unit set. See UNIT INPUTS. | |
| input_distance_unit | No | Set to km when the user gave km for the _mi fields in this object. Omit when they are already mi. | |
| saved_workout_title | No | Optional reusable workout name in saved mode. Defaults to focus_type. | |
| heart_points_moderate | No | Moderate-intensity heart points, Google Fit or equivalent, if mentioned. | |
| save_as_saved_workout | No | True when this payload is a reusable Saved Workout prescription, not a completed workout. Defaults to false. |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes | Human-readable result text returned by the tool. |