stackvox 0.12.0__tar.gz → 0.13.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. {stackvox-0.12.0/stackvox.egg-info → stackvox-0.13.0}/PKG-INFO +4 -2
  2. {stackvox-0.12.0 → stackvox-0.13.0}/README.md +3 -1
  3. {stackvox-0.12.0 → stackvox-0.13.0}/pyproject.toml +1 -1
  4. {stackvox-0.12.0 → stackvox-0.13.0}/stackvox/cli.py +8 -1
  5. {stackvox-0.12.0 → stackvox-0.13.0}/stackvox/text.py +38 -3
  6. {stackvox-0.12.0 → stackvox-0.13.0/stackvox.egg-info}/PKG-INFO +4 -2
  7. {stackvox-0.12.0 → stackvox-0.13.0}/tests/test_cli.py +1 -0
  8. {stackvox-0.12.0 → stackvox-0.13.0}/tests/test_text.py +41 -0
  9. {stackvox-0.12.0 → stackvox-0.13.0}/LICENSE +0 -0
  10. {stackvox-0.12.0 → stackvox-0.13.0}/NOTICE +0 -0
  11. {stackvox-0.12.0 → stackvox-0.13.0}/setup.cfg +0 -0
  12. {stackvox-0.12.0 → stackvox-0.13.0}/stackvox/__init__.py +0 -0
  13. {stackvox-0.12.0 → stackvox-0.13.0}/stackvox/__main__.py +0 -0
  14. {stackvox-0.12.0 → stackvox-0.13.0}/stackvox/config.py +0 -0
  15. {stackvox-0.12.0 → stackvox-0.13.0}/stackvox/daemon.py +0 -0
  16. {stackvox-0.12.0 → stackvox-0.13.0}/stackvox/data/stackvox-say +0 -0
  17. {stackvox-0.12.0 → stackvox-0.13.0}/stackvox/engine.py +0 -0
  18. {stackvox-0.12.0 → stackvox-0.13.0}/stackvox/paths.py +0 -0
  19. {stackvox-0.12.0 → stackvox-0.13.0}/stackvox/py.typed +0 -0
  20. {stackvox-0.12.0 → stackvox-0.13.0}/stackvox/updates.py +0 -0
  21. {stackvox-0.12.0 → stackvox-0.13.0}/stackvox.egg-info/SOURCES.txt +0 -0
  22. {stackvox-0.12.0 → stackvox-0.13.0}/stackvox.egg-info/dependency_links.txt +0 -0
  23. {stackvox-0.12.0 → stackvox-0.13.0}/stackvox.egg-info/entry_points.txt +0 -0
  24. {stackvox-0.12.0 → stackvox-0.13.0}/stackvox.egg-info/requires.txt +0 -0
  25. {stackvox-0.12.0 → stackvox-0.13.0}/stackvox.egg-info/top_level.txt +0 -0
  26. {stackvox-0.12.0 → stackvox-0.13.0}/tests/test_config.py +0 -0
  27. {stackvox-0.12.0 → stackvox-0.13.0}/tests/test_daemon.py +0 -0
  28. {stackvox-0.12.0 → stackvox-0.13.0}/tests/test_daemon_protocol.py +0 -0
  29. {stackvox-0.12.0 → stackvox-0.13.0}/tests/test_engine.py +0 -0
  30. {stackvox-0.12.0 → stackvox-0.13.0}/tests/test_paths.py +0 -0
  31. {stackvox-0.12.0 → stackvox-0.13.0}/tests/test_updates.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stackvox
3
- Version: 0.12.0
3
+ Version: 0.13.0
4
4
  Summary: Offline TTS library using Kokoro-82M
5
5
  Author-email: "StackOne Technologies Ltd." <support@stackone.com>
6
6
  License-Expression: Apache-2.0
@@ -103,10 +103,12 @@ stackvox speak --normalize --file post.md # normalize, then synthesize
103
103
  stackvox say --normalize "**Shipped** 1,198.9 MPG" # normalize, then send to daemon
104
104
  ```
105
105
 
106
- `--normalize` is off by default on `speak`/`say`, so notification phrases synthesize exactly as before. The tuning flags mirror the library defaults (all on except emoji-strip): `--no-markdown`, `--no-dev-terms`, `--no-expand-units`, `--no-expand-numbers`, `--no-pauses`, `--tables {drop,csv}`, `--code-blocks {drop,placeholder}`, `--strip-emoji`, `--no-terminal-stops`, `--locale`, and a `--pronunciations FILE` (a JSON `{"written": "spoken"}` map, applied whole-word and case-insensitively).
106
+ `--normalize` is off by default on `speak`/`say`, so notification phrases synthesize exactly as before. The tuning flags mirror the library defaults (all on except emoji-strip): `--no-markdown`, `--no-dev-terms`, `--no-abbreviations`, `--no-filenames`, `--no-expand-units`, `--no-expand-numbers`, `--no-pauses`, `--tables {drop,csv}`, `--code-blocks {drop,placeholder}`, `--strip-emoji`, `--no-terminal-stops`, `--locale`, and a `--pronunciations FILE` (a JSON `{"written": "spoken"}` map, applied whole-word and case-insensitively).
107
107
 
108
108
  **Dev-output handling** (on by default; `--no-dev-terms` to disable) covers two things espeak reads badly: acronyms it says as words (`CLI` → "C L I", `AWS`, `URI`, `IAM`, …) and file-line references, which it voices literally as "dot py colon". A `file.ext:line` ref is re-spoken lead-with-the-line: `engine.py:42` → "line 42 of engine py", `cli.py:100-118` → "lines 100 to 118 of cli py", `foo.ts:666:10` → "line 666, column 10 of foo ts". Times, ratios, and versions (`12:30`, `3:1`, `1.2.3`) are left alone.
109
109
 
110
+ **Chat shorthand** (on by default; `--no-abbreviations` to disable) is expanded to the words it stands for, so a response that reads "lmk" is spoken as "let me know": `lmk`, `iirc`, `afaik`, `idk`, `tbh`, `imo`, `btw`, `fyi`, `asap`, `wrt`, `tl;dr`/`tldr`. Whole-word and case-insensitive. Shorthand that doubles as a real word or is already voiced correctly (`rn`, `eta`, `aka`) is deliberately left alone; a `--pronunciations` entry overrides any of these.
111
+
110
112
  Fenced code blocks are dropped by default. `--code-blocks placeholder` instead speaks a short stand-in so a skipped block doesn't sound disjointed (ChatGPT-style) — set the wording with `--code-placeholder` (e.g. `--code-blocks placeholder --code-placeholder "you can see the code in the chat history"`); consecutive blocks collapse to one.
111
113
 
112
114
  Bash completion:
@@ -69,10 +69,12 @@ stackvox speak --normalize --file post.md # normalize, then synthesize
69
69
  stackvox say --normalize "**Shipped** 1,198.9 MPG" # normalize, then send to daemon
70
70
  ```
71
71
 
72
- `--normalize` is off by default on `speak`/`say`, so notification phrases synthesize exactly as before. The tuning flags mirror the library defaults (all on except emoji-strip): `--no-markdown`, `--no-dev-terms`, `--no-expand-units`, `--no-expand-numbers`, `--no-pauses`, `--tables {drop,csv}`, `--code-blocks {drop,placeholder}`, `--strip-emoji`, `--no-terminal-stops`, `--locale`, and a `--pronunciations FILE` (a JSON `{"written": "spoken"}` map, applied whole-word and case-insensitively).
72
+ `--normalize` is off by default on `speak`/`say`, so notification phrases synthesize exactly as before. The tuning flags mirror the library defaults (all on except emoji-strip): `--no-markdown`, `--no-dev-terms`, `--no-abbreviations`, `--no-filenames`, `--no-expand-units`, `--no-expand-numbers`, `--no-pauses`, `--tables {drop,csv}`, `--code-blocks {drop,placeholder}`, `--strip-emoji`, `--no-terminal-stops`, `--locale`, and a `--pronunciations FILE` (a JSON `{"written": "spoken"}` map, applied whole-word and case-insensitively).
73
73
 
74
74
  **Dev-output handling** (on by default; `--no-dev-terms` to disable) covers two things espeak reads badly: acronyms it says as words (`CLI` → "C L I", `AWS`, `URI`, `IAM`, …) and file-line references, which it voices literally as "dot py colon". A `file.ext:line` ref is re-spoken lead-with-the-line: `engine.py:42` → "line 42 of engine py", `cli.py:100-118` → "lines 100 to 118 of cli py", `foo.ts:666:10` → "line 666, column 10 of foo ts". Times, ratios, and versions (`12:30`, `3:1`, `1.2.3`) are left alone.
75
75
 
76
+ **Chat shorthand** (on by default; `--no-abbreviations` to disable) is expanded to the words it stands for, so a response that reads "lmk" is spoken as "let me know": `lmk`, `iirc`, `afaik`, `idk`, `tbh`, `imo`, `btw`, `fyi`, `asap`, `wrt`, `tl;dr`/`tldr`. Whole-word and case-insensitive. Shorthand that doubles as a real word or is already voiced correctly (`rn`, `eta`, `aka`) is deliberately left alone; a `--pronunciations` entry overrides any of these.
77
+
76
78
  Fenced code blocks are dropped by default. `--code-blocks placeholder` instead speaks a short stand-in so a skipped block doesn't sound disjointed (ChatGPT-style) — set the wording with `--code-placeholder` (e.g. `--code-blocks placeholder --code-placeholder "you can see the code in the chat history"`); consecutive blocks collapse to one.
77
79
 
78
80
  Bash completion:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "stackvox"
7
- version = "0.12.0"
7
+ version = "0.13.0"
8
8
  description = "Offline TTS library using Kokoro-82M"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -87,7 +87,7 @@ _stackvox_completion() {
87
87
  ;;
88
88
  esac
89
89
 
90
- local norm_flags="--no-markdown --no-dev-terms --no-filenames --pronunciations --no-expand-units --no-expand-numbers --no-pauses --tables --code-blocks --code-placeholder --strip-emoji --no-terminal-stops --locale"
90
+ local norm_flags="--no-markdown --no-dev-terms --no-abbreviations --no-filenames --pronunciations --no-expand-units --no-expand-numbers --no-pauses --tables --code-blocks --code-placeholder --strip-emoji --no-terminal-stops --locale"
91
91
 
92
92
  case "$subcommand" in
93
93
  speak)
@@ -220,6 +220,12 @@ def _add_normalize_args(parser: argparse.ArgumentParser, *, with_switch: bool) -
220
220
  action="store_false",
221
221
  help="Do not spell out dev acronyms espeak mispronounces (CLI, CI, IDE, AWS, URI, IAM, ...)",
222
222
  )
223
+ parser.add_argument(
224
+ "--no-abbreviations",
225
+ dest="abbreviations",
226
+ action="store_false",
227
+ help="Leave chat shorthand as written (no 'lmk' -> 'let me know', 'iirc', 'btw', ...)",
228
+ )
223
229
  parser.add_argument(
224
230
  "--no-filenames",
225
231
  dest="filenames",
@@ -300,6 +306,7 @@ def _normalize_kwargs(args: argparse.Namespace) -> dict:
300
306
  "markdown": args.markdown,
301
307
  "pronunciations": _load_pronunciations(args.pronunciations),
302
308
  "dev_terms": args.dev_terms,
309
+ "abbreviations": args.abbreviations,
303
310
  "filenames": args.filenames,
304
311
  "expand_units": args.expand_units,
305
312
  "expand_numbers": args.expand_numbers,
@@ -484,6 +484,36 @@ _DEV_PRONUNCIATIONS: dict[str, str] = {
484
484
  }
485
485
 
486
486
 
487
+ # --------------------------------------------------------------------------- #
488
+ # Abbreviations #
489
+ # --------------------------------------------------------------------------- #
490
+ # Chat shorthand, expanded to the words it stands for. Distinct from
491
+ # _DEV_PRONUNCIATIONS above, which fixes how a term *sounds*; this changes
492
+ # *which words are said*, so it gets its own flag (`abbreviations`). That is the
493
+ # lesson `filenames` taught when it was coupled to `dev_terms`.
494
+ #
495
+ # Applied whole-word and case-insensitive, so "LMK", "Lmk" and "lmk" all match.
496
+ # Deliberately excluded ("rn", "eta", "aka" and friends): real words, Greek
497
+ # letters, or acronyms espeak already voices correctly, so expanding them costs
498
+ # more in false positives than it buys. "fyi" keeps its letters because that is how it is
499
+ # said aloud, not "for your information".
500
+ _ABBREVIATIONS: dict[str, str] = {
501
+ "lmk": "let me know",
502
+ "iirc": "if I recall correctly",
503
+ "afaik": "as far as I know",
504
+ "idk": "I don't know",
505
+ "tbh": "to be honest",
506
+ "imo": "in my opinion",
507
+ "btw": "by the way",
508
+ "fyi": "F Y I",
509
+ "asap": "as soon as possible",
510
+ "wrt": "with respect to",
511
+ # Both spellings of the same abbreviation; the bare form is at least as common.
512
+ "tl;dr": "too long, didn't read",
513
+ "tldr": "too long, didn't read",
514
+ }
515
+
516
+
487
517
  # --------------------------------------------------------------------------- #
488
518
  # Pauses #
489
519
  # --------------------------------------------------------------------------- #
@@ -687,6 +717,7 @@ def normalize_for_speech(
687
717
  markdown: bool = True,
688
718
  pronunciations: dict[str, str] | None = None,
689
719
  dev_terms: bool = True,
720
+ abbreviations: bool = True,
690
721
  filenames: bool = True,
691
722
  expand_units: bool = True,
692
723
  expand_numbers: bool = True,
@@ -701,9 +732,13 @@ def normalize_for_speech(
701
732
  """Normalize ``text`` into speakable prose. Returns paragraphs joined by
702
733
  newlines. See ``docs/speech-normalization.md`` for the full contract."""
703
734
  expand_units_flag, expand_numbers_flag = expand_units, expand_numbers
704
- # Built-in dev-term fixes first; caller-supplied pronunciations override them
705
- # (keyed case-insensitively, so a caller's "CLI" beats the default "cli").
706
- effective_pronunciations: dict[str, str] = dict(_DEV_PRONUNCIATIONS) if dev_terms else {}
735
+ # Built-in fixes first; caller-supplied pronunciations override them (keyed
736
+ # case-insensitively, so a caller's "CLI" beats the default "cli"). The two
737
+ # built-in dicts share no keys, and neither one's spoken form contains a key
738
+ # of the other, so the sequential passes can't feed each other.
739
+ effective_pronunciations: dict[str, str] = dict(_ABBREVIATIONS) if abbreviations else {}
740
+ if dev_terms:
741
+ effective_pronunciations.update(_DEV_PRONUNCIATIONS)
707
742
  for written, spoken in (pronunciations or {}).items():
708
743
  effective_pronunciations[written.lower()] = spoken
709
744
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stackvox
3
- Version: 0.12.0
3
+ Version: 0.13.0
4
4
  Summary: Offline TTS library using Kokoro-82M
5
5
  Author-email: "StackOne Technologies Ltd." <support@stackone.com>
6
6
  License-Expression: Apache-2.0
@@ -103,10 +103,12 @@ stackvox speak --normalize --file post.md # normalize, then synthesize
103
103
  stackvox say --normalize "**Shipped** 1,198.9 MPG" # normalize, then send to daemon
104
104
  ```
105
105
 
106
- `--normalize` is off by default on `speak`/`say`, so notification phrases synthesize exactly as before. The tuning flags mirror the library defaults (all on except emoji-strip): `--no-markdown`, `--no-dev-terms`, `--no-expand-units`, `--no-expand-numbers`, `--no-pauses`, `--tables {drop,csv}`, `--code-blocks {drop,placeholder}`, `--strip-emoji`, `--no-terminal-stops`, `--locale`, and a `--pronunciations FILE` (a JSON `{"written": "spoken"}` map, applied whole-word and case-insensitively).
106
+ `--normalize` is off by default on `speak`/`say`, so notification phrases synthesize exactly as before. The tuning flags mirror the library defaults (all on except emoji-strip): `--no-markdown`, `--no-dev-terms`, `--no-abbreviations`, `--no-filenames`, `--no-expand-units`, `--no-expand-numbers`, `--no-pauses`, `--tables {drop,csv}`, `--code-blocks {drop,placeholder}`, `--strip-emoji`, `--no-terminal-stops`, `--locale`, and a `--pronunciations FILE` (a JSON `{"written": "spoken"}` map, applied whole-word and case-insensitively).
107
107
 
108
108
  **Dev-output handling** (on by default; `--no-dev-terms` to disable) covers two things espeak reads badly: acronyms it says as words (`CLI` → "C L I", `AWS`, `URI`, `IAM`, …) and file-line references, which it voices literally as "dot py colon". A `file.ext:line` ref is re-spoken lead-with-the-line: `engine.py:42` → "line 42 of engine py", `cli.py:100-118` → "lines 100 to 118 of cli py", `foo.ts:666:10` → "line 666, column 10 of foo ts". Times, ratios, and versions (`12:30`, `3:1`, `1.2.3`) are left alone.
109
109
 
110
+ **Chat shorthand** (on by default; `--no-abbreviations` to disable) is expanded to the words it stands for, so a response that reads "lmk" is spoken as "let me know": `lmk`, `iirc`, `afaik`, `idk`, `tbh`, `imo`, `btw`, `fyi`, `asap`, `wrt`, `tl;dr`/`tldr`. Whole-word and case-insensitive. Shorthand that doubles as a real word or is already voiced correctly (`rn`, `eta`, `aka`) is deliberately left alone; a `--pronunciations` entry overrides any of these.
111
+
110
112
  Fenced code blocks are dropped by default. `--code-blocks placeholder` instead speaks a short stand-in so a skipped block doesn't sound disjointed (ChatGPT-style) — set the wording with `--code-placeholder` (e.g. `--code-blocks placeholder --code-placeholder "you can see the code in the chat history"`); consecutive blocks collapse to one.
111
113
 
112
114
  Bash completion:
@@ -129,6 +129,7 @@ def _norm_ns(text=None, file=None, **overrides):
129
129
  markdown=True,
130
130
  pronunciations=None,
131
131
  dev_terms=True,
132
+ abbreviations=True,
132
133
  filenames=True,
133
134
  expand_units=True,
134
135
  expand_numbers=True,
@@ -487,3 +487,44 @@ def test_caller_pronunciations_override_dev_terms():
487
487
  out = normalize_for_speech("Use the CLI.", markdown=False, pronunciations={"CLI": "command line"})
488
488
  assert "command line" in out
489
489
  assert "C L I" not in out
490
+
491
+
492
+ def test_chat_abbreviations_are_expanded():
493
+ assert normalize_for_speech("lmk what you think", markdown=False) == "let me know what you think."
494
+ assert "if I recall correctly" in normalize_for_speech("iirc it shipped", markdown=False)
495
+ assert "by the way" in normalize_for_speech("btw the build is green", markdown=False)
496
+ assert "as soon as possible" in normalize_for_speech("ship it asap", markdown=False)
497
+ assert "with respect to" in normalize_for_speech("wrt the plan", markdown=False)
498
+
499
+
500
+ def test_chat_abbreviations_are_case_insensitive():
501
+ assert "let me know" in normalize_for_speech("LMK if that works", markdown=False)
502
+ assert "to be honest" in normalize_for_speech("Tbh it is fine", markdown=False)
503
+
504
+
505
+ def test_tldr_expands_in_both_spellings():
506
+ assert "too long, didn't read" in normalize_for_speech("tl;dr it works", markdown=False)
507
+ assert "too long, didn't read" in normalize_for_speech("tldr it works", markdown=False)
508
+
509
+
510
+ def test_chat_abbreviations_are_whole_word_only():
511
+ # must not rewrite the middle of a longer token
512
+ assert "let me know" not in normalize_for_speech("lmkfoo", markdown=False)
513
+ assert "in my opinion" not in normalize_for_speech("imos", markdown=False)
514
+
515
+
516
+ def test_chat_abbreviations_can_be_disabled():
517
+ assert normalize_for_speech("lmk soon", markdown=False, abbreviations=False) == "lmk soon."
518
+
519
+
520
+ def test_caller_pronunciations_override_abbreviations():
521
+ out = normalize_for_speech("lmk soon", markdown=False, pronunciations={"LMK": "tell me"})
522
+ assert "tell me" in out
523
+ assert "let me know" not in out
524
+
525
+
526
+ def test_abbreviations_independent_of_dev_terms():
527
+ # the `filenames` lesson: an unrelated switch must not silently disable this
528
+ out = normalize_for_speech("lmk about the CLI", markdown=False, dev_terms=False)
529
+ assert "let me know" in out
530
+ assert "C L I" not in out
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes