We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Do we need _capitalize_defaults now that Talon lexicon includes capitalization?


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `code/vocabulary.py`
Content:
```
1 import logging
2 from typing import Dict, Sequence
3 
4 from talon import Context, Module, actions
5 from .user_settings import get_list_from_csv
6 
7 mod = Module()
8 ctx = Context()
9 
10 mod.list("vocabulary", desc="additional vocabulary words")
11 
12 
13 # Default words that will need to be capitalized (particularly under w2l).
14 # NB. These defaults and those later in this file are ONLY used when
15 # auto-creating the corresponding settings/*.csv files. Those csv files
16 # determine the contents of user.vocabulary and dictate.word_map. Once they
17 # exist, the contents of the lists/dictionaries below are irrelevant.
18 _capitalize_defaults = [
19     "I",
20     "I'm",
21     "I've",
22     "I'll",
23     "I'd",
24     "Monday",
25     "Mondays",
26     "Tuesday",
27     "Tuesdays",
28     "Wednesday",
29     "Wednesdays",
30     "Thursday",
31     "Thursdays",
32     "Friday",
33     "Fridays",
34     "Saturday",
35     "Saturdays",
36     "Sunday",
37     "Sundays",
38     "January",
39     "February",
40     # March omitted because it's a regular word too
41     "April",
42     # May omitted because it's a regular word too
43     "June",
44     "July",
45     "August",
46     "September",
47     "October",
48     "November",
49     "December",
50 ]
51 
52 # Default words that need to be remapped.
53 _word_map_defaults = {
54     # E.g:
55     # "cash": "cache",
56     # This is the opposite ordering to words_to_replace.csv (the latter has the target word first)
57 }
58 _word_map_defaults.update({word.lower(): word for word in _capitalize_defaults})
59 
60 
61 # phrases_to_replace is a spoken form -> written form map, used by
62 # `user.replace_phrases` to rewrite words and phrases Talon recognized.
63 # This does not change the priority with which Talon recognizes
64 # particular phrases over others.
65 phrases_to_replace = get_list_from_csv(
66     "words_to_replace.csv",
67     headers=("Replacement", "Original"),
68     default=_word_map_defaults
69 )
70 
71 # "dictate.word_map" is used by `actions.dictate.replace_words`;
72 # a built-in Talon action similar to `replace_phrases`, but supporting
73 # only single-word replacements. Multi-word phrases are ignored.
74 ctx.settings["dictate.word_map"] = phrases_to_replace
75 
76 
77 # Default words that should be added to Talon's vocabulary.
78 # Don't edit this. Edit 'additional_vocabulary.csv' instead
79 _simple_vocab_default = ["nmap", "admin", "Cisco", "Citrix", "VPN", "DNS", "Minecraft"]
80 
81 # Defaults for different pronounciations of words that need to be added to
82 # Talon's vocabulary.
83 _default_vocabulary = {
84     "N map": "nmap",
85     "under documented": "under-documented",
86 }
87 _default_vocabulary.update({word: word for word in _simple_vocab_default})
88 
89 # "user.vocabulary" is used to explicitly add words/phrases that Talon doesn't
90 # recognize. Words in user.vocabulary (or other lists and captures) are
91 # "command-like" and their recognition is prioritized over ordinary words.
92 ctx.lists["user.vocabulary"] = get_list_from_csv(
93     "additional_words.csv",
94     headers=("Word(s)", "Spoken Form (If Different)"),
95     default=_default_vocabulary,
96 )
97 
98 # for quick verification of the reload
99 # print(str(ctx.settings["dictate.word_map"]))
100 # print(str(ctx.lists["user.vocabulary"]))
101 
102 class PhraseReplacer:
103     """Utility for replacing phrases by other phrases inside text or word lists.
104 
105     Replacing longer phrases has priority.
106 
107     Args:
108       - phrase_dict: dictionary mapping recognized/spoken forms to written forms
109     """
110 
111     def __init__(self, phrase_dict: Dict[str, str]):
112         # Index phrases by first word, then number of subsequent words n_next
113         phrase_index = dict()
114         for spoken_form, written_form in phrase_dict.items():
115             words = spoken_form.split()
116             if not words:
117                 logging.warning("Found empty spoken form for written form"
118                                 f"{written_form}, ignored")
119                 continue
120             first_word, n_next = words[0], len(words) - 1
121             phrase_index.setdefault(first_word, {}) \
122                         .setdefault(n_next, {})[tuple(words[1:])] = written_form
123 
124         # Sort n_next index so longer phrases have priority
125         self.phrase_index = {
126             first_word: list(sorted(same_first_word.items(), key=lambda x: -x[0]))
127             for first_word, same_first_word in phrase_index.items()
128         }
129 
130     def replace(self, input_words: Sequence[str]) -> Sequence[str]:
131         input_words = tuple(input_words) # tuple to ensure hashability of slices
132         output_words = []
133         first_word_i = 0
134         while first_word_i < len(input_words):
135             first_word = input_words[first_word_i]
136             next_word_i = first_word_i + 1
137             # Could this word be the first of a phrase we should replace?
138             for n_next, phrases_n_next in self.phrase_index.get(first_word, []):
139                 # Yes. Perhaps a phrase with n_next subsequent words?
140                 continuation = input_words[next_word_i : next_word_i + n_next]
141                 if continuation in phrases_n_next:
142                     # Found a match!
143                     output_words.append(phrases_n_next[continuation])
144                     first_word_i += 1 + n_next
145                     break
146             else:
147                 # No match, just add the word to the result
148                 output_words.append(first_word)
149                 first_word_i += 1
150         return output_words
151 
152     # Wrapper used for testing.
153     def replace_string(self, text: str) -> str:
154         return ' '.join(self.replace(text.split()))
155 
156 # Unit tests for PhraseReplacer
157 rep = PhraseReplacer({
158     'this': 'foo',
159     'that': 'bar',
160     'this is': 'stopping early',
161     'this is a test': 'it worked!',
162 })
163 assert rep.replace_string('gnork') == 'gnork'
164 assert rep.replace_string('this') == 'foo'
165 assert rep.replace_string('this that this') == 'foo bar foo'
166 assert rep.replace_string('this is a test') == 'it worked!'
167 assert rep.replace_string('well this is a test really') == 'well it worked! really'
168 assert rep.replace_string('try this is too') == 'try stopping early too'
169 assert rep.replace_string('this is a tricky one') == 'stopping early a tricky one'
170 
171 phrase_replacer = PhraseReplacer(phrases_to_replace)
172 
173 @mod.action_class
174 class Actions:
175     def replace_phrases(words: Sequence[str]) -> Sequence[str]:
176         """Replace phrases according to words_to_replace.csv"""
177         try:
178             return phrase_replacer.replace(words)
179         except:
180             # fall back to dictate.replace_words for error-robustness
181             logging.error("phrase replacer failed!")
182             return actions.dictate.replace_words(words)
183
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/code/vocabulary.py b/code/vocabulary.py
--- a/code/vocabulary.py
+++ b/code/vocabulary.py
@@ -10,31 +10,15 @@
 mod.list("vocabulary", desc="additional vocabulary words")
 
 
-# Default words that will need to be capitalized (particularly under w2l).
-# NB. These defaults and those later in this file are ONLY used when
+# Default words that will need to be capitalized.
+# DON'T EDIT THIS. Edit settings/words_to_replace.csv instead.
+# These defaults and those later in this file are ONLY used when
 # auto-creating the corresponding settings/*.csv files. Those csv files
 # determine the contents of user.vocabulary and dictate.word_map. Once they
 # exist, the contents of the lists/dictionaries below are irrelevant.
 _capitalize_defaults = [
-    "I",
-    "I'm",
-    "I've",
-    "I'll",
-    "I'd",
-    "Monday",
-    "Mondays",
-    "Tuesday",
-    "Tuesdays",
-    "Wednesday",
-    "Wednesdays",
-    "Thursday",
-    "Thursdays",
-    "Friday",
-    "Fridays",
-    "Saturday",
-    "Saturdays",
-    "Sunday",
-    "Sundays",
+    # NB. the lexicon now capitalizes January/February by default, but not the
+    # others below. Not sure why.
     "January",
     "February",
     # March omitted because it's a regular word too
@@ -42,7 +26,7 @@
     # May omitted because it's a regular word too
     "June",
     "July",
-    "August",
+    "August", # technically also an adjective but the month is far more common
     "September",
     "October",
     "November",

{"golden_diff": "diff --git a/code/vocabulary.py b/code/vocabulary.py\n--- a/code/vocabulary.py\n+++ b/code/vocabulary.py\n@@ -10,31 +10,15 @@\n mod.list(\"vocabulary\", desc=\"additional vocabulary words\")\n \n \n-# Default words that will need to be capitalized (particularly under w2l).\n-# NB. These defaults and those later in this file are ONLY used when\n+# Default words that will need to be capitalized.\n+# DON'T EDIT THIS. Edit settings/words_to_replace.csv instead.\n+# These defaults and those later in this file are ONLY used when\n # auto-creating the corresponding settings/*.csv files. Those csv files\n # determine the contents of user.vocabulary and dictate.word_map. Once they\n # exist, the contents of the lists/dictionaries below are irrelevant.\n _capitalize_defaults = [\n-    \"I\",\n-    \"I'm\",\n-    \"I've\",\n-    \"I'll\",\n-    \"I'd\",\n-    \"Monday\",\n-    \"Mondays\",\n-    \"Tuesday\",\n-    \"Tuesdays\",\n-    \"Wednesday\",\n-    \"Wednesdays\",\n-    \"Thursday\",\n-    \"Thursdays\",\n-    \"Friday\",\n-    \"Fridays\",\n-    \"Saturday\",\n-    \"Saturdays\",\n-    \"Sunday\",\n-    \"Sundays\",\n+    # NB. the lexicon now capitalizes January/February by default, but not the\n+    # others below. Not sure why.\n     \"January\",\n     \"February\",\n     # March omitted because it's a regular word too\n@@ -42,7 +26,7 @@\n     # May omitted because it's a regular word too\n     \"June\",\n     \"July\",\n-    \"August\",\n+    \"August\", # technically also an adjective but the month is far more common\n     \"September\",\n     \"October\",\n     \"November\",\n", "issue": "Do we need _capitalize_defaults now that Talon lexicon includes capitalization?\n\n", "before_files": [{"content": "import logging\nfrom typing import Dict, Sequence\n\nfrom talon import Context, Module, actions\nfrom .user_settings import get_list_from_csv\n\nmod = Module()\nctx = Context()\n\nmod.list(\"vocabulary\", desc=\"additional vocabulary words\")\n\n\n# Default words that will need to be capitalized (particularly under w2l).\n# NB. These defaults and those later in this file are ONLY used when\n# auto-creating the corresponding settings/*.csv files. Those csv files\n# determine the contents of user.vocabulary and dictate.word_map. Once they\n# exist, the contents of the lists/dictionaries below are irrelevant.\n_capitalize_defaults = [\n    \"I\",\n    \"I'm\",\n    \"I've\",\n    \"I'll\",\n    \"I'd\",\n    \"Monday\",\n    \"Mondays\",\n    \"Tuesday\",\n    \"Tuesdays\",\n    \"Wednesday\",\n    \"Wednesdays\",\n    \"Thursday\",\n    \"Thursdays\",\n    \"Friday\",\n    \"Fridays\",\n    \"Saturday\",\n    \"Saturdays\",\n    \"Sunday\",\n    \"Sundays\",\n    \"January\",\n    \"February\",\n    # March omitted because it's a regular word too\n    \"April\",\n    # May omitted because it's a regular word too\n    \"June\",\n    \"July\",\n    \"August\",\n    \"September\",\n    \"October\",\n    \"November\",\n    \"December\",\n]\n\n# Default words that need to be remapped.\n_word_map_defaults = {\n    # E.g:\n    # \"cash\": \"cache\",\n    # This is the opposite ordering to words_to_replace.csv (the latter has the target word first)\n}\n_word_map_defaults.update({word.lower(): word for word in _capitalize_defaults})\n\n\n# phrases_to_replace is a spoken form -> written form map, used by\n# `user.replace_phrases` to rewrite words and phrases Talon recognized.\n# This does not change the priority with which Talon recognizes\n# particular phrases over others.\nphrases_to_replace = get_list_from_csv(\n    \"words_to_replace.csv\",\n    headers=(\"Replacement\", \"Original\"),\n    default=_word_map_defaults\n)\n\n# \"dictate.word_map\" is used by `actions.dictate.replace_words`;\n# a built-in Talon action similar to `replace_phrases`, but supporting\n# only single-word replacements. Multi-word phrases are ignored.\nctx.settings[\"dictate.word_map\"] = phrases_to_replace\n\n\n# Default words that should be added to Talon's vocabulary.\n# Don't edit this. Edit 'additional_vocabulary.csv' instead\n_simple_vocab_default = [\"nmap\", \"admin\", \"Cisco\", \"Citrix\", \"VPN\", \"DNS\", \"Minecraft\"]\n\n# Defaults for different pronounciations of words that need to be added to\n# Talon's vocabulary.\n_default_vocabulary = {\n    \"N map\": \"nmap\",\n    \"under documented\": \"under-documented\",\n}\n_default_vocabulary.update({word: word for word in _simple_vocab_default})\n\n# \"user.vocabulary\" is used to explicitly add words/phrases that Talon doesn't\n# recognize. Words in user.vocabulary (or other lists and captures) are\n# \"command-like\" and their recognition is prioritized over ordinary words.\nctx.lists[\"user.vocabulary\"] = get_list_from_csv(\n    \"additional_words.csv\",\n    headers=(\"Word(s)\", \"Spoken Form (If Different)\"),\n    default=_default_vocabulary,\n)\n\n# for quick verification of the reload\n# print(str(ctx.settings[\"dictate.word_map\"]))\n# print(str(ctx.lists[\"user.vocabulary\"]))\n\nclass PhraseReplacer:\n    \"\"\"Utility for replacing phrases by other phrases inside text or word lists.\n\n    Replacing longer phrases has priority.\n\n    Args:\n      - phrase_dict: dictionary mapping recognized/spoken forms to written forms\n    \"\"\"\n\n    def __init__(self, phrase_dict: Dict[str, str]):\n        # Index phrases by first word, then number of subsequent words n_next\n        phrase_index = dict()\n        for spoken_form, written_form in phrase_dict.items():\n            words = spoken_form.split()\n            if not words:\n                logging.warning(\"Found empty spoken form for written form\"\n                                f\"{written_form}, ignored\")\n                continue\n            first_word, n_next = words[0], len(words) - 1\n            phrase_index.setdefault(first_word, {}) \\\n                        .setdefault(n_next, {})[tuple(words[1:])] = written_form\n\n        # Sort n_next index so longer phrases have priority\n        self.phrase_index = {\n            first_word: list(sorted(same_first_word.items(), key=lambda x: -x[0]))\n            for first_word, same_first_word in phrase_index.items()\n        }\n\n    def replace(self, input_words: Sequence[str]) -> Sequence[str]:\n        input_words = tuple(input_words) # tuple to ensure hashability of slices\n        output_words = []\n        first_word_i = 0\n        while first_word_i < len(input_words):\n            first_word = input_words[first_word_i]\n            next_word_i = first_word_i + 1\n            # Could this word be the first of a phrase we should replace?\n            for n_next, phrases_n_next in self.phrase_index.get(first_word, []):\n                # Yes. Perhaps a phrase with n_next subsequent words?\n                continuation = input_words[next_word_i : next_word_i + n_next]\n                if continuation in phrases_n_next:\n                    # Found a match!\n                    output_words.append(phrases_n_next[continuation])\n                    first_word_i += 1 + n_next\n                    break\n            else:\n                # No match, just add the word to the result\n                output_words.append(first_word)\n                first_word_i += 1\n        return output_words\n\n    # Wrapper used for testing.\n    def replace_string(self, text: str) -> str:\n        return ' '.join(self.replace(text.split()))\n\n# Unit tests for PhraseReplacer\nrep = PhraseReplacer({\n    'this': 'foo',\n    'that': 'bar',\n    'this is': 'stopping early',\n    'this is a test': 'it worked!',\n})\nassert rep.replace_string('gnork') == 'gnork'\nassert rep.replace_string('this') == 'foo'\nassert rep.replace_string('this that this') == 'foo bar foo'\nassert rep.replace_string('this is a test') == 'it worked!'\nassert rep.replace_string('well this is a test really') == 'well it worked! really'\nassert rep.replace_string('try this is too') == 'try stopping early too'\nassert rep.replace_string('this is a tricky one') == 'stopping early a tricky one'\n\nphrase_replacer = PhraseReplacer(phrases_to_replace)\n\n@mod.action_class\nclass Actions:\n    def replace_phrases(words: Sequence[str]) -> Sequence[str]:\n        \"\"\"Replace phrases according to words_to_replace.csv\"\"\"\n        try:\n            return phrase_replacer.replace(words)\n        except:\n            # fall back to dictate.replace_words for error-robustness\n            logging.error(\"phrase replacer failed!\")\n            return actions.dictate.replace_words(words)\n", "path": "code/vocabulary.py"}], "after_files": [{"content": "import logging\nfrom typing import Dict, Sequence\n\nfrom talon import Context, Module, actions\nfrom .user_settings import get_list_from_csv\n\nmod = Module()\nctx = Context()\n\nmod.list(\"vocabulary\", desc=\"additional vocabulary words\")\n\n\n# Default words that will need to be capitalized.\n# DON'T EDIT THIS. Edit settings/words_to_replace.csv instead.\n# These defaults and those later in this file are ONLY used when\n# auto-creating the corresponding settings/*.csv files. Those csv files\n# determine the contents of user.vocabulary and dictate.word_map. Once they\n# exist, the contents of the lists/dictionaries below are irrelevant.\n_capitalize_defaults = [\n    # NB. the lexicon now capitalizes January/February by default, but not the\n    # others below. Not sure why.\n    \"January\",\n    \"February\",\n    # March omitted because it's a regular word too\n    \"April\",\n    # May omitted because it's a regular word too\n    \"June\",\n    \"July\",\n    \"August\", # technically also an adjective but the month is far more common\n    \"September\",\n    \"October\",\n    \"November\",\n    \"December\",\n]\n\n# Default words that need to be remapped.\n_word_map_defaults = {\n    # E.g:\n    # \"cash\": \"cache\",\n    # This is the opposite ordering to words_to_replace.csv (the latter has the target word first)\n}\n_word_map_defaults.update({word.lower(): word for word in _capitalize_defaults})\n\n\n# phrases_to_replace is a spoken form -> written form map, used by\n# `user.replace_phrases` to rewrite words and phrases Talon recognized.\n# This does not change the priority with which Talon recognizes\n# particular phrases over others.\nphrases_to_replace = get_list_from_csv(\n    \"words_to_replace.csv\",\n    headers=(\"Replacement\", \"Original\"),\n    default=_word_map_defaults\n)\n\n# \"dictate.word_map\" is used by `actions.dictate.replace_words`;\n# a built-in Talon action similar to `replace_phrases`, but supporting\n# only single-word replacements. Multi-word phrases are ignored.\nctx.settings[\"dictate.word_map\"] = phrases_to_replace\n\n\n# Default words that should be added to Talon's vocabulary.\n# Don't edit this. Edit 'additional_vocabulary.csv' instead\n_simple_vocab_default = [\"nmap\", \"admin\", \"Cisco\", \"Citrix\", \"VPN\", \"DNS\", \"Minecraft\"]\n\n# Defaults for different pronounciations of words that need to be added to\n# Talon's vocabulary.\n_default_vocabulary = {\n    \"N map\": \"nmap\",\n    \"under documented\": \"under-documented\",\n}\n_default_vocabulary.update({word: word for word in _simple_vocab_default})\n\n# \"user.vocabulary\" is used to explicitly add words/phrases that Talon doesn't\n# recognize. Words in user.vocabulary (or other lists and captures) are\n# \"command-like\" and their recognition is prioritized over ordinary words.\nctx.lists[\"user.vocabulary\"] = get_list_from_csv(\n    \"additional_words.csv\",\n    headers=(\"Word(s)\", \"Spoken Form (If Different)\"),\n    default=_default_vocabulary,\n)\n\n# for quick verification of the reload\n# print(str(ctx.settings[\"dictate.word_map\"]))\n# print(str(ctx.lists[\"user.vocabulary\"]))\n\nclass PhraseReplacer:\n    \"\"\"Utility for replacing phrases by other phrases inside text or word lists.\n\n    Replacing longer phrases has priority.\n\n    Args:\n      - phrase_dict: dictionary mapping recognized/spoken forms to written forms\n    \"\"\"\n\n    def __init__(self, phrase_dict: Dict[str, str]):\n        # Index phrases by first word, then number of subsequent words n_next\n        phrase_index = dict()\n        for spoken_form, written_form in phrase_dict.items():\n            words = spoken_form.split()\n            if not words:\n                logging.warning(\"Found empty spoken form for written form\"\n                                f\"{written_form}, ignored\")\n                continue\n            first_word, n_next = words[0], len(words) - 1\n            phrase_index.setdefault(first_word, {}) \\\n                        .setdefault(n_next, {})[tuple(words[1:])] = written_form\n\n        # Sort n_next index so longer phrases have priority\n        self.phrase_index = {\n            first_word: list(sorted(same_first_word.items(), key=lambda x: -x[0]))\n            for first_word, same_first_word in phrase_index.items()\n        }\n\n    def replace(self, input_words: Sequence[str]) -> Sequence[str]:\n        input_words = tuple(input_words) # tuple to ensure hashability of slices\n        output_words = []\n        first_word_i = 0\n        while first_word_i < len(input_words):\n            first_word = input_words[first_word_i]\n            next_word_i = first_word_i + 1\n            # Could this word be the first of a phrase we should replace?\n            for n_next, phrases_n_next in self.phrase_index.get(first_word, []):\n                # Yes. Perhaps a phrase with n_next subsequent words?\n                continuation = input_words[next_word_i : next_word_i + n_next]\n                if continuation in phrases_n_next:\n                    # Found a match!\n                    output_words.append(phrases_n_next[continuation])\n                    first_word_i += 1 + n_next\n                    break\n            else:\n                # No match, just add the word to the result\n                output_words.append(first_word)\n                first_word_i += 1\n        return output_words\n\n    # Wrapper used for testing.\n    def replace_string(self, text: str) -> str:\n        return ' '.join(self.replace(text.split()))\n\n# Unit tests for PhraseReplacer\nrep = PhraseReplacer({\n    'this': 'foo',\n    'that': 'bar',\n    'this is': 'stopping early',\n    'this is a test': 'it worked!',\n})\nassert rep.replace_string('gnork') == 'gnork'\nassert rep.replace_string('this') == 'foo'\nassert rep.replace_string('this that this') == 'foo bar foo'\nassert rep.replace_string('this is a test') == 'it worked!'\nassert rep.replace_string('well this is a test really') == 'well it worked! really'\nassert rep.replace_string('try this is too') == 'try stopping early too'\nassert rep.replace_string('this is a tricky one') == 'stopping early a tricky one'\n\nphrase_replacer = PhraseReplacer(phrases_to_replace)\n\n@mod.action_class\nclass Actions:\n    def replace_phrases(words: Sequence[str]) -> Sequence[str]:\n        \"\"\"Replace phrases according to words_to_replace.csv\"\"\"\n        try:\n            return phrase_replacer.replace(words)\n        except:\n            # fall back to dictate.replace_words for error-robustness\n            logging.error(\"phrase replacer failed!\")\n            return actions.dictate.replace_words(words)\n", "path": "code/vocabulary.py"}]}