feat(agent): YAML tool specs as the LLM-facing semantic layer
Introduce a first-class semantic layer for tool descriptions, separated
from Python signatures (which stay the source of truth for types and
required-ness).
New
- alfred/agent/tools/spec.py — ToolSpec / ParameterSpec / ReturnsSpec
dataclasses with strict YAML validation (ToolSpecError on malformed
or inconsistent specs). compile_description() builds the rich text
passed to the LLM as Tool.description, with sections for summary,
description, when_to_use, when_not_to_use, next_steps, and returns.
compile_parameter_description() injects the 'why_needed' field next
to each parameter so the LLM sees the *intent* of each argument.
- alfred/agent/tools/spec_loader.py — discovers tools/specs/*.yaml,
enforces filename ↔ spec.name match, rejects duplicates.
- alfred/agent/tools/specs/ — one YAML per tool:
* resolve_season_destination.yaml
* resolve_episode_destination.yaml
* resolve_movie_destination.yaml
* resolve_series_destination.yaml
* move_to_destination.yaml
Refactor
- alfred/agent/registry.py
* _create_tool_from_function now takes an optional ToolSpec.
When provided, the long description + per-parameter descriptions
come from the spec; types and required-ness still come from the
Python signature.
* Cross-validates spec.parameters against the function signature —
crashes on missing or extra entries.
* make_tools() loads all specs at startup and hands the right one
to each tool. Tools without a spec fall back to the old
docstring-only behaviour, so the 14 not-yet-migrated tools keep
working unchanged.
* Adds 'array' and 'object' to the Python→JSON type mapping and
handles Optional[X] / X | None annotations.
- alfred/agent/tools/filesystem.py
* Drops the '_tool' suffix on the 4 resolve_* wrappers (option 1:
alias the use-case imports as _resolve_*). Tool names exposed to
the LLM now match the underlying use case verbatim.
* Wrapper docstrings shrink to a one-liner pointing to the YAML
spec — no more duplicated when_to_use/Args/Returns in Python.
Verified
- make_tools() loads 19 tools (5 with YAML spec, 14 doc-only).
- Compiled descriptions render cleanly with all sections.
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
name: resolve_season_destination
|
||||
|
||||
summary: >
|
||||
Compute destination paths for a season pack (folder move) in the TV library.
|
||||
|
||||
description: |
|
||||
Resolves the target series folder and season subfolder for a complete-season
|
||||
download. Returns the paths only — does not perform any move. If a series
|
||||
folder for this show already exists in the library with a different name
|
||||
(different group/quality/source), returns needs_clarification so the user
|
||||
can decide whether to merge into the existing folder or create a new one.
|
||||
|
||||
when_to_use: |
|
||||
Use after analyze_release has identified the release as a season pack
|
||||
(media_type=tv_show, season set, episode unset). TMDB must already be
|
||||
queried so tmdb_title and tmdb_year are canonical values, not raw tokens
|
||||
from the release name.
|
||||
|
||||
when_not_to_use: |
|
||||
- Single-episode files: use resolve_episode_destination instead.
|
||||
- Multi-season packs (S01-S05 etc.): use resolve_series_destination.
|
||||
- Movies: use resolve_movie_destination.
|
||||
|
||||
next_steps: |
|
||||
- On status=ok: call move_to_destination with source=<download folder> and
|
||||
destination=season_folder.
|
||||
- On status=needs_clarification: present the question and options to the
|
||||
user, then re-call this tool with confirmed_folder set to the user's pick.
|
||||
- On status=error: surface the message to the user; do not move anything.
|
||||
|
||||
parameters:
|
||||
release_name:
|
||||
description: Raw release folder name as it appears on disk.
|
||||
why_needed: |
|
||||
Drives extraction of quality/source/codec/group tokens — these are
|
||||
embedded in the target folder name (Title.Year.Quality.Source.Codec-GROUP)
|
||||
to make releases self-describing on the filesystem.
|
||||
example: Oz.S03.1080p.WEBRip.x265-KONTRAST
|
||||
|
||||
tmdb_title:
|
||||
description: Canonical show title from TMDB.
|
||||
why_needed: |
|
||||
Builds the title prefix of the folder name. Must come from TMDB to
|
||||
avoid typos and variant spellings present in the raw release name.
|
||||
example: Oz
|
||||
|
||||
tmdb_year:
|
||||
description: Show start year from TMDB.
|
||||
why_needed: |
|
||||
Disambiguates shows that share a title across decades (e.g. multiple
|
||||
remakes of "The Office") and locks the folder identity.
|
||||
example: "1997"
|
||||
|
||||
confirmed_folder:
|
||||
description: |
|
||||
Folder name chosen by the user after a previous needs_clarification
|
||||
response.
|
||||
why_needed: |
|
||||
Short-circuits the existing-folder detection and forces the use case
|
||||
to use this exact folder name, even if it doesn't match the computed
|
||||
one.
|
||||
example: Oz.1997.1080p.WEBRip.x265-KONTRAST
|
||||
|
||||
returns:
|
||||
ok:
|
||||
description: Paths resolved unambiguously; ready to move.
|
||||
fields:
|
||||
series_folder: Absolute path to the series root folder.
|
||||
season_folder: Absolute path to the season subfolder (move target).
|
||||
series_folder_name: Just the series folder name, for display.
|
||||
season_folder_name: Just the season folder name, for display.
|
||||
is_new_series_folder: True if the series folder doesn't exist yet.
|
||||
|
||||
needs_clarification:
|
||||
description: A folder already exists with a different name; ask the user.
|
||||
fields:
|
||||
question: Human-readable question for the user.
|
||||
options: List of folder names the user can pick from.
|
||||
|
||||
error:
|
||||
description: Resolution failed (config missing, invalid release name, etc.).
|
||||
fields:
|
||||
error: Short error code (e.g. library_not_set).
|
||||
message: Human-readable explanation.
|
||||
Reference in New Issue
Block a user