Background
Today strava.py is a passive chat: it seeds one activity's JSON into the system prompt and calls conversation.prompt() in a loop. The model only ever sees that single activity — it can't reach for anything else, so it can't compare the run to past efforts or recall earlier coaching. The goal here is a shift in kind, not just a feature: make the chat a small agent — a model that decides, mid-conversation, to call a tool and act on the result. The first tool is a search over the athlete's running log (RUNLOG.txt); the tool-calling loop it establishes is the reusable part.
Existing Behavior
- strava.py — main() builds model.conversation() and calls conversation.prompt(user_input, system=...). No tools, no agentic loop; the model cannot take actions, only answer from seeded context.
- strava_prompts.py — SYSTEM_PROMPT describes the coach role over the activity JSON; says nothing about tools.
- RUNLOG.txt — the athlete's running log, read by nothing today.
- No strava_tools.py, no agent loop, no tests for tool behavior.
Acceptance Criteria
Approach
The core move is conversation.prompt() → conversation.chain(), p tool list — that's what converts passive chat into an agent. Stay on the incumbent llm library: tools are plain Python functions (docstring → schema), so the agent stays a self-contained script with no new dependency and no framework, consistent with the repo convention. Keep the tool itparse_/search_/format_ helpers behind a thin I/O wrapper) so allthe interesting logic is unit-tested without the model. Structure everything so the agent (the loop + tool registry) is the durable abstraction and search_runlog is just its first registered capability.
References
- llm tools / Python API: https://llm.datasette.io/en/stable/too — model.conversation(tools=[...]) + conversation.chain(prompt)runs the automatic tool loop; docstrings become tool schemas.
- Future extension if the agent grows many tools / guardrails / https://developers.openai.com/api/docs/guides/agents (noted in astrava.py comment; deferred to keep the tool surface small).
- Decisions: stay on llm; self-contained strava_tools.py over framework modules; keyword search over semantic.
Risks and Rollback
Low risk — additive, isolated to the strava scripts. The one behavioral change is the loop swap (prompt → chain); a model/plugin that mishandles tool calls could error on a tool round-trip, in which case the chat degrades to a normal answer or an error, not data loss. Agent-design caveat: on the default Chat
Completions plugin the model re-reasons each turn from the visibte does not carry across turns or across a tool hop — so the agent must treat each tool result as self-contained state (format_entries returns full titles + bodies for this reason). Rollback is a clean revert of
strava_tools.py and the strava.py/strava_prompts.py/docs edits;
Screenshots / Recordings
Pending. Capture a transcript showing the agent choosing to call search_runlog (e.g. "how does this compare to my past half marathons?") and uv run pytest output. Note: the test run was interrupted before execution in this session.
Background
Today strava.py is a passive chat: it seeds one activity's JSON into the system prompt and calls conversation.prompt() in a loop. The model only ever sees that single activity — it can't reach for anything else, so it can't compare the run to past efforts or recall earlier coaching. The goal here is a shift in kind, not just a feature: make the chat a small agent — a model that decides, mid-conversation, to call a tool and act on the result. The first tool is a search over the athlete's running log (RUNLOG.txt); the tool-calling loop it establishes is the reusable part.
Existing Behavior
Acceptance Criteria
Approach
The core move is conversation.prompt() → conversation.chain(), p tool list — that's what converts passive chat into an agent. Stay on the incumbent llm library: tools are plain Python functions (docstring → schema), so the agent stays a self-contained script with no new dependency and no framework, consistent with the repo convention. Keep the tool itparse_/search_/format_ helpers behind a thin I/O wrapper) so allthe interesting logic is unit-tested without the model. Structure everything so the agent (the loop + tool registry) is the durable abstraction and search_runlog is just its first registered capability.
References
Risks and Rollback
Low risk — additive, isolated to the strava scripts. The one behavioral change is the loop swap (prompt → chain); a model/plugin that mishandles tool calls could error on a tool round-trip, in which case the chat degrades to a normal answer or an error, not data loss. Agent-design caveat: on the default Chat
Completions plugin the model re-reasons each turn from the visibte does not carry across turns or across a tool hop — so the agent must treat each tool result as self-contained state (format_entries returns full titles + bodies for this reason). Rollback is a clean revert of
strava_tools.py and the strava.py/strava_prompts.py/docs edits;
Screenshots / Recordings
Pending. Capture a transcript showing the agent choosing to call search_runlog (e.g. "how does this compare to my past half marathons?") and uv run pytest output. Note: the test run was interrupted before execution in this session.