Unify the export-llm-input and run-llm summary lines with build-db

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-17 22:38:37 +08:00
co-authored by Claude Opus 5
parent bc44d60d18
commit 3aa5c7a432
4 changed files with 20 additions and 13 deletions
@@ -28,6 +28,7 @@ keys, then that song's own keys, each group in its file order.
import argparse import argparse
import json import json
import sys import sys
import time
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
@@ -36,6 +37,7 @@ from sqlalchemy.orm import Session
from ..database import ds from ..database import ds
from ..models import Song from ..models import Song
from ..utils import format_duration
def parse_args(argv: list[str] | None) -> argparse.Namespace: def parse_args(argv: list[str] | None) -> argparse.Namespace:
@@ -231,6 +233,7 @@ def main(argv: list[str] | None = None) -> int:
``sys.argv``. ``sys.argv``.
:return: The exit status: 0 on success, non-zero on failure. :return: The exit status: 0 on success, non-zero on failure.
""" """
started: float = time.monotonic()
args: argparse.Namespace = parse_args(argv) args: argparse.Namespace = parse_args(argv)
session: Session = ds.get_db() session: Session = ds.get_db()
lines: list[str] lines: list[str]
@@ -252,5 +255,7 @@ def main(argv: list[str] | None = None) -> int:
line: str line: str
for line in lines: for line in lines:
file.write(line + "\n") file.write(line + "\n")
print(f"done: {len(lines)} songs exported", file=sys.stderr) elapsed: str = format_duration(time.monotonic() - started)
print(f"Done. {len(lines)} songs exported."
f" {elapsed} elapsed.", file=sys.stderr)
return 0 return 0
@@ -488,8 +488,9 @@ def main(argv: list[str] | None = None) -> int:
print(json.dumps( print(json.dumps(
build_request(items[0], prompt_text, args.max_tokens), build_request(items[0], prompt_text, args.max_tokens),
ensure_ascii=False, indent=2)) ensure_ascii=False, indent=2))
print(f"dry run: archive created at {archive_dir}", elapsed: str = format_duration(time.monotonic() - started)
file=sys.stderr) print(f"Done. {len(items)} jobs finished."
f" {elapsed} elapsed.", file=sys.stderr)
return 0 return 0
client: anthropic.Anthropic = anthropic.Anthropic( client: anthropic.Anthropic = anthropic.Anthropic(
api_key=get_settings().ANTHROPIC_API_KEY) api_key=get_settings().ANTHROPIC_API_KEY)
@@ -507,7 +508,6 @@ def main(argv: list[str] | None = None) -> int:
file=sys.stderr) file=sys.stderr)
return 1 return 1
elapsed: str = format_duration(time.monotonic() - started) elapsed: str = format_duration(time.monotonic() - started)
print(f"done: {len(items)} items;" print(f"Done. {len(items)} jobs finished."
f" archived to {archive_dir} {elapsed} elapsed.", f" {elapsed} elapsed.", file=sys.stderr)
file=sys.stderr)
return 0 return 0
+2 -2
View File
@@ -141,7 +141,7 @@ class TestExportLlmInput(unittest.TestCase):
self.assertEqual(records, [ self.assertEqual(records, [
{"id": "song-1", "content": "hello lyrics\n"}, {"id": "song-1", "content": "hello lyrics\n"},
{"id": "song-2", "content": "umbrella lyrics\n"}]) {"id": "song-2", "content": "umbrella lyrics\n"}])
self.assertIn("done: 2 songs exported", stderr) self.assertIn("Done. 2 songs exported.", stderr)
def test_preserves_non_ascii_lyrics(self) -> None: def test_preserves_non_ascii_lyrics(self) -> None:
"""Test that non-ASCII lyrics are written verbatim.""" """Test that non-ASCII lyrics are written verbatim."""
@@ -291,7 +291,7 @@ class TestExportLlmInput(unittest.TestCase):
self.__output) self.__output)
self.assertEqual( self.assertEqual(
[x["id"] for x in records], ["song-1", "song-3"]) [x["id"] for x in records], ["song-1", "song-3"])
self.assertIn("done: 2 songs exported", stderr) self.assertIn("Done. 2 songs exported.", stderr)
def test_extras_per_id_unknown_id_fails(self) -> None: def test_extras_per_id_unknown_id_fails(self) -> None:
"""Test that a per-ID extras file naming a song the """Test that a per-ID extras file naming a song the
+7 -5
View File
@@ -349,8 +349,10 @@ class TestMainFlow(RunLLMTestCase):
status: int status: int
stdout: str stdout: str
stderr: str stderr: str
status, stdout, stderr = self.__run_main( with mock.patch(
self.__argv + ["--dry-run"]) "time.monotonic", side_effect=[1000.0, 1125.0]):
status, stdout, stderr = self.__run_main(
self.__argv + ["--dry-run"])
self.assertEqual(status, 0) self.assertEqual(status, 0)
run_dir: Path = self.__archive_dir run_dir: Path = self.__archive_dir
self.assertEqual((run_dir / "prompt.md").read_text( self.assertEqual((run_dir / "prompt.md").read_text(
@@ -366,7 +368,8 @@ class TestMainFlow(RunLLMTestCase):
self.assertEqual(request["custom_id"], "a") self.assertEqual(request["custom_id"], "a")
self.assertEqual(request["params"]["system"], self.assertEqual(request["params"]["system"],
"The task prompt.\n") "The task prompt.\n")
self.assertNotRegex(stderr, r"\d{2}:\d{2} elapsed\.") self.assertTrue(stderr.rstrip("\n").endswith(
"Done. 2 jobs finished. 02:05 elapsed."))
def test_run_produces_output_file(self) -> None: def test_run_produces_output_file(self) -> None:
"""Test that a run submits one batch and writes output.""" """Test that a run submits one batch and writes output."""
@@ -394,8 +397,7 @@ class TestMainFlow(RunLLMTestCase):
self.assertEqual(meta["usage"], self.assertEqual(meta["usage"],
{"input_tokens": 20, "output_tokens": 10}) {"input_tokens": 20, "output_tokens": 10})
self.assertTrue(stderr.rstrip("\n").endswith( self.assertTrue(stderr.rstrip("\n").endswith(
"done: 2 items; archived to" "Done. 2 jobs finished. 02:05 elapsed."))
f" {run_dir} 02:05 elapsed."))
def test_existing_archive_rejected_without_replace(self) -> None: def test_existing_archive_rejected_without_replace(self) -> None:
"""Test that an existing archive without --replace fails.""" """Test that an existing archive without --replace fails."""