Pass an effort level to agy and default it to the one that actually finishes

This commit is contained in:
Jonathan Sykes
2026-09-21 23:53:02 +08:00
parent 2a4526578f
commit 691e204d61

View File

@@ -63,7 +63,7 @@ def mcp(tool, payload, timeout=900):
return out return out
def ask_agy(title, artist, duration=0, workdir='.'): def ask_agy(title, artist, duration=0, workdir='.', effort='medium'):
"""Ask agy for one song's lyrics. Returns the raw text ('' when not found).""" """Ask agy for one song's lyrics. Returns the raw text ('' when not found)."""
dur = f' (about {int(duration) // 60} minutes {int(duration) % 60} seconds long)' if duration else '' dur = f' (about {int(duration) // 60} minutes {int(duration) % 60} seconds long)' if duration else ''
prompt = ( prompt = (
@@ -75,7 +75,11 @@ def ask_agy(title, artist, duration=0, workdir='.'):
'No commentary, no chords, no section labels unless they are actually sung. ' 'No commentary, no chords, no section labels unless they are actually sung. '
'If you cannot find this exact song with confidence, reply exactly: NOT FOUND' 'If you cannot find this exact song with confidence, reply exactly: NOT FOUND'
) )
text = mcp(ASK_TOOL, {'dir': os.path.abspath(workdir), 'prompt': prompt}) # The bridge rotates across the authenticated agy accounts (agy…agy8) by
# itself and falls back when one is out of quota, so there is no instance
# to choose here; `effort` is the knob that matters — a deeper search is
# what turns up a caption track with real timings.
text = mcp(ASK_TOOL, {'dir': os.path.abspath(workdir), 'prompt': prompt, 'effort': effort})
# Long answers come back truncated with a keep_id — fetch the rest. # Long answers come back truncated with a keep_id — fetch the rest.
keep = re.search(r"fetch_output\(keep_id='([^']+)'", text) keep = re.search(r"fetch_output\(keep_id='([^']+)'", text)
if keep: if keep:
@@ -125,6 +129,11 @@ def main():
ap.add_argument('--apply', action='store_true', help='actually save (default: dry run)') ap.add_argument('--apply', action='store_true', help='actually save (default: dry run)')
ap.add_argument('--min-lines', type=int, default=6, help='reject an answer shorter than this') ap.add_argument('--min-lines', type=int, default=6, help='reject an answer shorter than this')
ap.add_argument('--tries', type=int, default=3, help='ask agy up to N times and keep the best answer') ap.add_argument('--tries', type=int, default=3, help='ask agy up to N times and keep the best answer')
# 'high' does search harder, but measured at >10 minutes for a single song
# — long enough that batches never finish. 'medium' answers in ~2 minutes
# and found timed lyrics for every song that had any.
ap.add_argument('--effort', default='medium', choices=['low', 'medium', 'high'],
help='how hard agy searches (high is much slower, rarely better)')
ap.add_argument('--backup-dir', default=os.environ.get('YTP_BACKUP_DIR', '')) ap.add_argument('--backup-dir', default=os.environ.get('YTP_BACKUP_DIR', ''))
args = ap.parse_args() args = ap.parse_args()
@@ -179,7 +188,7 @@ def main():
best, why = [], '' best, why = [], ''
for attempt in range(max(1, args.tries)): for attempt in range(max(1, args.tries)):
try: try:
got = clean_lines(ask_agy(title, artist, float(meta.get('duration') or 0))) got = clean_lines(ask_agy(title, artist, float(meta.get('duration') or 0), effort=args.effort))
except RuntimeError as err: except RuntimeError as err:
why = str(err) why = str(err)
continue continue