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.
- {stackvox-0.12.0/stackvox.egg-info → stackvox-0.13.0}/PKG-INFO +4 -2
- {stackvox-0.12.0 → stackvox-0.13.0}/README.md +3 -1
- {stackvox-0.12.0 → stackvox-0.13.0}/pyproject.toml +1 -1
- {stackvox-0.12.0 → stackvox-0.13.0}/stackvox/cli.py +8 -1
- {stackvox-0.12.0 → stackvox-0.13.0}/stackvox/text.py +38 -3
- {stackvox-0.12.0 → stackvox-0.13.0/stackvox.egg-info}/PKG-INFO +4 -2
- {stackvox-0.12.0 → stackvox-0.13.0}/tests/test_cli.py +1 -0
- {stackvox-0.12.0 → stackvox-0.13.0}/tests/test_text.py +41 -0
- {stackvox-0.12.0 → stackvox-0.13.0}/LICENSE +0 -0
- {stackvox-0.12.0 → stackvox-0.13.0}/NOTICE +0 -0
- {stackvox-0.12.0 → stackvox-0.13.0}/setup.cfg +0 -0
- {stackvox-0.12.0 → stackvox-0.13.0}/stackvox/__init__.py +0 -0
- {stackvox-0.12.0 → stackvox-0.13.0}/stackvox/__main__.py +0 -0
- {stackvox-0.12.0 → stackvox-0.13.0}/stackvox/config.py +0 -0
- {stackvox-0.12.0 → stackvox-0.13.0}/stackvox/daemon.py +0 -0
- {stackvox-0.12.0 → stackvox-0.13.0}/stackvox/data/stackvox-say +0 -0
- {stackvox-0.12.0 → stackvox-0.13.0}/stackvox/engine.py +0 -0
- {stackvox-0.12.0 → stackvox-0.13.0}/stackvox/paths.py +0 -0
- {stackvox-0.12.0 → stackvox-0.13.0}/stackvox/py.typed +0 -0
- {stackvox-0.12.0 → stackvox-0.13.0}/stackvox/updates.py +0 -0
- {stackvox-0.12.0 → stackvox-0.13.0}/stackvox.egg-info/SOURCES.txt +0 -0
- {stackvox-0.12.0 → stackvox-0.13.0}/stackvox.egg-info/dependency_links.txt +0 -0
- {stackvox-0.12.0 → stackvox-0.13.0}/stackvox.egg-info/entry_points.txt +0 -0
- {stackvox-0.12.0 → stackvox-0.13.0}/stackvox.egg-info/requires.txt +0 -0
- {stackvox-0.12.0 → stackvox-0.13.0}/stackvox.egg-info/top_level.txt +0 -0
- {stackvox-0.12.0 → stackvox-0.13.0}/tests/test_config.py +0 -0
- {stackvox-0.12.0 → stackvox-0.13.0}/tests/test_daemon.py +0 -0
- {stackvox-0.12.0 → stackvox-0.13.0}/tests/test_daemon_protocol.py +0 -0
- {stackvox-0.12.0 → stackvox-0.13.0}/tests/test_engine.py +0 -0
- {stackvox-0.12.0 → stackvox-0.13.0}/tests/test_paths.py +0 -0
- {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.
|
|
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:
|
|
@@ -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
|
|
705
|
-
#
|
|
706
|
-
|
|
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.
|
|
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:
|
|
@@ -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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|