Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d310989555 | |||
| ed581ecf9d | |||
| f8aedd4ef5 | |||
| 14c1386496 | |||
| 153b642057 | |||
| dec32102dd | |||
| f365c69265 | |||
| 1724c328b7 | |||
| 6db4a8d341 | |||
| b70e8a8569 | |||
| 2e4828da06 | |||
| 96cfe69753 | |||
| 3e8e246c1c | |||
| 80a5008b93 |
+171
-22
@@ -1,26 +1,175 @@
|
||||
.git/
|
||||
.venv/
|
||||
node_modules/
|
||||
__pycache__/
|
||||
test/
|
||||
tools/
|
||||
etc/test/
|
||||
download/precise-engine/
|
||||
download/kaldi/
|
||||
opt/
|
||||
*
|
||||
!etc/qemu-*
|
||||
|
||||
etc/homeassistant/config/.storage
|
||||
examples/typical/home-assistant/config/.storage
|
||||
examples/typical-intent/home-assistant/config/.storage
|
||||
examples/client-server/home-assistant/config/.storage
|
||||
examples/mqtt-hermes/home-assistant/config/.storage
|
||||
!download/rhasspy-tools*
|
||||
!download/pocketsphinx-python.tar.gz
|
||||
!download/snowboy*
|
||||
!download/kaldi*
|
||||
|
||||
profiles/*/base_dictionary.txt
|
||||
profiles/*/base_language_model.txt
|
||||
profiles/*/acoustic_model/
|
||||
profiles/*/g2p.fst
|
||||
!requirements.txt
|
||||
!dist/
|
||||
!etc/wav
|
||||
|
||||
profiles/en-kaldi/
|
||||
profiles/en-zamia/
|
||||
!docker/run.sh
|
||||
!docker/rhasspy
|
||||
|
||||
profiles/*/download/
|
||||
!profiles/defaults.json
|
||||
|
||||
!profiles/zh/profile.json
|
||||
!profiles/zh/custom_words.txt
|
||||
!profiles/zh/espeak_phonemes.txt
|
||||
!profiles/zh/phoneme_examples.txt
|
||||
!profiles/zh/frequent_words.txt
|
||||
!profiles/zh/sentences.ini
|
||||
!profiles/zh/stop_words.txt
|
||||
!profiles/zh/slots
|
||||
!profiles/zh/slot_programs
|
||||
|
||||
!profiles/hi/profile.json
|
||||
!profiles/hi/custom_words.txt
|
||||
!profiles/hi/espeak_phonemes.txt
|
||||
!profiles/hi/phoneme_examples.txt
|
||||
!profiles/hi/frequent_words.txt
|
||||
!profiles/hi/sentences.ini
|
||||
!profiles/hi/stop_words.txt
|
||||
!profiles/hi/slots
|
||||
!profiles/hi/slot_programs
|
||||
|
||||
!profiles/el/profile.json
|
||||
!profiles/el/custom_words.txt
|
||||
!profiles/el/espeak_phonemes.txt
|
||||
!profiles/el/phoneme_examples.txt
|
||||
!profiles/el/frequent_words.txt
|
||||
!profiles/el/sentences.ini
|
||||
!profiles/el/stop_words.txt
|
||||
!profiles/el/slots
|
||||
!profiles/el/slot_programs
|
||||
|
||||
!profiles/es/profile.json
|
||||
!profiles/es/custom_words.txt
|
||||
!profiles/es/espeak_phonemes.txt
|
||||
!profiles/es/phoneme_examples.txt
|
||||
!profiles/es/frequent_words.txt
|
||||
!profiles/es/sentences.ini
|
||||
!profiles/es/stop_words.txt
|
||||
!profiles/es/slots
|
||||
!profiles/es/slot_programs
|
||||
|
||||
!profiles/it/profile.json
|
||||
!profiles/it/custom_words.txt
|
||||
!profiles/it/espeak_phonemes.txt
|
||||
!profiles/it/phoneme_examples.txt
|
||||
!profiles/it/frequent_words.txt
|
||||
!profiles/it/sentences.ini
|
||||
!profiles/it/stop_words.txt
|
||||
!profiles/it/slots
|
||||
!profiles/it/slot_programs
|
||||
|
||||
!profiles/ru/profile.json
|
||||
!profiles/ru/custom_words.txt
|
||||
!profiles/ru/espeak_phonemes.txt
|
||||
!profiles/ru/phoneme_examples.txt
|
||||
!profiles/ru/frequent_words.txt
|
||||
!profiles/ru/sentences.ini
|
||||
!profiles/ru/stop_words.txt
|
||||
!profiles/ru/slots
|
||||
!profiles/ru/slot_programs
|
||||
|
||||
!profiles/pt/profile.json
|
||||
!profiles/pt/custom_words.txt
|
||||
!profiles/pt/espeak_phonemes.txt
|
||||
!profiles/pt/phoneme_examples.txt
|
||||
!profiles/pt/frequent_words.txt
|
||||
!profiles/pt/sentences.ini
|
||||
!profiles/pt/stop_words.txt
|
||||
!profiles/pt/slots
|
||||
!profiles/pt/slot_programs
|
||||
|
||||
!profiles/sv/profile.json
|
||||
!profiles/sv/custom_words.txt
|
||||
!profiles/sv/espeak_phonemes.txt
|
||||
!profiles/sv/phoneme_examples.txt
|
||||
!profiles/sv/frequent_words.txt
|
||||
!profiles/sv/sentences.ini
|
||||
!profiles/sv/stop_words.txt
|
||||
!profiles/sv/slots
|
||||
!profiles/sv/slot_programs
|
||||
|
||||
!profiles/vi/profile.json
|
||||
!profiles/vi/custom_words.txt
|
||||
!profiles/vi/espeak_phonemes.txt
|
||||
!profiles/vi/phoneme_examples.txt
|
||||
!profiles/vi/frequent_words.txt
|
||||
!profiles/vi/sentences.ini
|
||||
!profiles/vi/stop_words.txt
|
||||
!profiles/vi/slots
|
||||
!profiles/vi/slot_programs
|
||||
|
||||
!profiles/ca/profile.json
|
||||
!profiles/ca/custom_words.txt
|
||||
!profiles/ca/espeak_phonemes.txt
|
||||
!profiles/ca/phoneme_examples.txt
|
||||
!profiles/ca/frequent_words.txt
|
||||
!profiles/ca/sentences.ini
|
||||
!profiles/ca/stop_words.txt
|
||||
!profiles/ca/slots
|
||||
!profiles/ca/slot_programs
|
||||
|
||||
!profiles/nl/profile.json
|
||||
!profiles/nl/custom_words.txt
|
||||
!profiles/nl/espeak_phonemes.txt
|
||||
!profiles/nl/phoneme_examples.txt
|
||||
!profiles/nl/frequent_words.txt
|
||||
!profiles/nl/sentences.ini
|
||||
!profiles/nl/stop_words.txt
|
||||
!profiles/nl/slots
|
||||
!profiles/nl/slot_programs
|
||||
!profiles/nl/kaldi/custom_words.txt
|
||||
!profiles/nl/kaldi/espeak_phonemes.txt
|
||||
!profiles/nl/kaldi/phoneme_examples.txt
|
||||
|
||||
!profiles/de/profile.json
|
||||
!profiles/de/custom_words.txt
|
||||
!profiles/de/espeak_phonemes.txt
|
||||
!profiles/de/phoneme_examples.txt
|
||||
!profiles/de/frequent_words.txt
|
||||
!profiles/de/sentences.ini
|
||||
!profiles/de/stop_words.txt
|
||||
!profiles/de/slots
|
||||
!profiles/de/slot_programs
|
||||
!profiles/de/kaldi/custom_words.txt
|
||||
!profiles/de/kaldi/espeak_phonemes.txt
|
||||
!profiles/de/kaldi/phoneme_examples.txt
|
||||
|
||||
!profiles/fr/profile.json
|
||||
!profiles/fr/custom_words.txt
|
||||
!profiles/fr/espeak_phonemes.txt
|
||||
!profiles/fr/phoneme_examples.txt
|
||||
!profiles/fr/frequent_words.txt
|
||||
!profiles/fr/sentences.ini
|
||||
!profiles/fr/stop_words.txt
|
||||
!profiles/fr/slots
|
||||
!profiles/fr/slot_programs
|
||||
!profiles/fr/kaldi/custom_words.txt
|
||||
!profiles/fr/kaldi/espeak_phonemes.txt
|
||||
!profiles/fr/kaldi/phoneme_examples.txt
|
||||
|
||||
!profiles/en/profile.json
|
||||
!profiles/en/custom_words.txt
|
||||
!profiles/en/espeak_phonemes.txt
|
||||
!profiles/en/phoneme_examples.txt
|
||||
!profiles/en/frequent_words.txt
|
||||
!profiles/en/sentences.ini
|
||||
!profiles/en/stop_words.txt
|
||||
!profiles/en/slots
|
||||
!profiles/en/slot_programs
|
||||
!profiles/en/kaldi/custom_words.txt
|
||||
!profiles/en/kaldi/espeak_phonemes.txt
|
||||
!profiles/en/kaldi/phoneme_examples.txt
|
||||
|
||||
!rhasspy/profile_schema.json
|
||||
!rhasspy/*.py
|
||||
!rhasspy/train/*.py
|
||||
!rhasspy/train/jsgf2fst/*.py
|
||||
!*.py
|
||||
!VERSION
|
||||
@@ -5,7 +5,9 @@ SHELL := bash
|
||||
# Docker
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
docker: web-dist docker-amd64 docker-armhf docker-aarch64 docker-push manifest
|
||||
docker: web-dist docker-amd64 docker-armhf docker-aarch64
|
||||
|
||||
docker-deploy: docker-push manifest
|
||||
|
||||
docker-amd64:
|
||||
docker build . -f docker/templates/dockerfiles/Dockerfile.prebuilt.alsa.all \
|
||||
|
||||
+10
-14
@@ -18,22 +18,18 @@ def main():
|
||||
profile = json.load(profile_file)
|
||||
locale_name = profile["locale"] + ".UTF-8"
|
||||
locale.setlocale(locale.LC_ALL, locale_name)
|
||||
|
||||
slots_dir = profile_dir / "slots" / "rhasspy"
|
||||
slots_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Day names
|
||||
with open(slots_dir / "days", "w") as days_file:
|
||||
for day_num in range(7):
|
||||
print(calendar.day_name[day_num], file=days_file)
|
||||
|
||||
# Month names
|
||||
with open(slots_dir / "months", "w") as month_file:
|
||||
for month_num in range(1, 13):
|
||||
print(calendar.month_name[month_num], file=month_file)
|
||||
|
||||
print(locale_name)
|
||||
|
||||
slots_dir = profile_dir / "slots" / "rhasspy"
|
||||
slots_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Day names
|
||||
(slots_dir / "days").write_text('\n'.join(calendar.day_name))
|
||||
|
||||
# Month names
|
||||
(slots_dir / "months").write_text('\n'.join(filter(None, calendar.month_name)))
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
+14
-5
@@ -329,31 +329,40 @@ case "${CPU_ARCH}" in
|
||||
esac
|
||||
|
||||
requirements_file="${temp_dir}/requirements.txt"
|
||||
temp_requirements_file="${temp_dir}/temp_requirements.txt"
|
||||
cp "${this_dir}/requirements.txt" "${requirements_file}"
|
||||
|
||||
# Exclude requirements
|
||||
if [[ -n "${no_flair}" ]]; then
|
||||
echo "Excluding flair from virtual environment"
|
||||
sed -i '/^flair/d' "${requirements_file}"
|
||||
sed '/^flair/d' "${requirements_file}" > "${temp_requirements_file}" &&
|
||||
mv "${temp_requirements_file}" "${requirements_file}"
|
||||
|
||||
fi
|
||||
|
||||
if [[ -n "${no_precise}" ]]; then
|
||||
echo "Excluding Mycroft Precise from virtual environment"
|
||||
sed -i '/^precise-runner/d' "${requirements_file}"
|
||||
sed '/^precise-runner/d' "${requirements_file}" > "${temp_requirements_file}" &&
|
||||
mv "${temp_requirements_file}" "${requirements_file}"
|
||||
|
||||
fi
|
||||
|
||||
if [[ -n "${no_adapt}" ]]; then
|
||||
echo "Excluding Mycroft Adapt from virtual environment"
|
||||
sed -i '/^adapt-parser/d' "${requirements_file}"
|
||||
sed '/^adapt-parser/d' "${requirements_file}" > "${temp_requirements_file}" &&
|
||||
mv "${temp_requirements_file}" "${requirements_file}"
|
||||
|
||||
fi
|
||||
|
||||
if [[ -n "${no_google}" ]]; then
|
||||
echo "Excluding Google Text to Speech from virtual environment"
|
||||
sed -i '/^google-cloud-texttospeech/d' "${requirements_file}"
|
||||
sed '/^google-cloud-texttospeech/d' "${requirements_file}" > "${temp_requirements_file}" &&
|
||||
mv "${temp_requirements_file}" "${requirements_file}"
|
||||
fi
|
||||
|
||||
# Install everything except openfst first
|
||||
sed -i '/^openfst/d' "${requirements_file}"
|
||||
sed '/^openfst/d' "${requirements_file}" > "${temp_requirements_file}" &&
|
||||
mv "${temp_requirements_file}" "${requirements_file}"
|
||||
|
||||
"${python}" -m pip install -r "${requirements_file}"
|
||||
|
||||
|
||||
@@ -1,132 +1 @@
|
||||
COPY profiles/zh/profile.json \
|
||||
profiles/zh/custom_words.txt \
|
||||
profiles/zh/espeak_phonemes.txt \
|
||||
profiles/zh/phoneme_examples.txt \
|
||||
profiles/zh/frequent_words.txt \
|
||||
profiles/zh/sentences.ini \
|
||||
profiles/zh/stop_words.txt ${RHASSPY_APP}/profiles/zh/
|
||||
|
||||
COPY profiles/hi/ \
|
||||
profiles/hi/profile.json \
|
||||
profiles/hi/custom_words.txt \
|
||||
profiles/hi/espeak_phonemes.txt \
|
||||
profiles/hi/phoneme_examples.txt \
|
||||
profiles/hi/frequent_words.txt \
|
||||
profiles/hi/sentences.ini \
|
||||
profiles/hi/stop_words.txt ${RHASSPY_APP}/profiles/hi/
|
||||
|
||||
COPY profiles/el/profile.json \
|
||||
profiles/el/custom_words.txt \
|
||||
profiles/el/espeak_phonemes.txt \
|
||||
profiles/el/phoneme_examples.txt \
|
||||
profiles/el/frequent_words.txt \
|
||||
profiles/el/sentences.ini \
|
||||
profiles/el/stop_words.txt ${RHASSPY_APP}/profiles/el/
|
||||
|
||||
COPY profiles/de/profile.json \
|
||||
profiles/de/custom_words.txt \
|
||||
profiles/de/espeak_phonemes.txt \
|
||||
profiles/de/phoneme_examples.txt \
|
||||
profiles/de/frequent_words.txt \
|
||||
profiles/de/sentences.ini \
|
||||
profiles/de/stop_words.txt ${RHASSPY_APP}/profiles/de/
|
||||
|
||||
COPY profiles/de/kaldi/custom_words.txt \
|
||||
profiles/de/kaldi/espeak_phonemes.txt \
|
||||
profiles/de/kaldi/phoneme_examples.txt \
|
||||
${RHASSPY_APP}/profiles/de/kaldi/
|
||||
|
||||
COPY profiles/it/profile.json \
|
||||
profiles/it/custom_words.txt \
|
||||
profiles/it/espeak_phonemes.txt \
|
||||
profiles/it/phoneme_examples.txt \
|
||||
profiles/it/frequent_words.txt \
|
||||
profiles/it/sentences.ini \
|
||||
profiles/it/stop_words.txt ${RHASSPY_APP}/profiles/it/
|
||||
|
||||
COPY profiles/es/profile.json \
|
||||
profiles/es/custom_words.txt \
|
||||
profiles/es/espeak_phonemes.txt \
|
||||
profiles/es/phoneme_examples.txt \
|
||||
profiles/es/frequent_words.txt \
|
||||
profiles/es/sentences.ini \
|
||||
profiles/es/stop_words.txt ${RHASSPY_APP}/profiles/es/
|
||||
|
||||
COPY profiles/fr/profile.json \
|
||||
profiles/fr/custom_words.txt \
|
||||
profiles/fr/espeak_phonemes.txt \
|
||||
profiles/fr/phoneme_examples.txt \
|
||||
profiles/fr/frequent_words.txt \
|
||||
profiles/fr/sentences.ini \
|
||||
profiles/fr/stop_words.txt ${RHASSPY_APP}/profiles/fr/
|
||||
|
||||
COPY profiles/fr/kaldi/custom_words.txt \
|
||||
profiles/fr/kaldi/espeak_phonemes.txt \
|
||||
profiles/fr/kaldi/phoneme_examples.txt \
|
||||
${RHASSPY_APP}/profiles/fr/kaldi/
|
||||
|
||||
COPY profiles/ru/profile.json \
|
||||
profiles/ru/custom_words.txt \
|
||||
profiles/ru/espeak_phonemes.txt \
|
||||
profiles/ru/phoneme_examples.txt \
|
||||
profiles/ru/frequent_words.txt \
|
||||
profiles/ru/sentences.ini \
|
||||
profiles/ru/stop_words.txt ${RHASSPY_APP}/profiles/ru/
|
||||
|
||||
COPY profiles/nl/profile.json \
|
||||
profiles/nl/custom_words.txt \
|
||||
profiles/nl/espeak_phonemes.txt \
|
||||
profiles/nl/phoneme_examples.txt \
|
||||
profiles/nl/frequent_words.txt \
|
||||
profiles/nl/sentences.ini \
|
||||
profiles/nl/stop_words.txt ${RHASSPY_APP}/profiles/nl/
|
||||
|
||||
COPY profiles/nl/kaldi/custom_words.txt \
|
||||
profiles/nl/kaldi/espeak_phonemes.txt \
|
||||
profiles/nl/kaldi/phoneme_examples.txt \
|
||||
${RHASSPY_APP}/profiles/nl/kaldi/
|
||||
|
||||
COPY profiles/vi/profile.json \
|
||||
profiles/vi/custom_words.txt \
|
||||
profiles/vi/espeak_phonemes.txt \
|
||||
profiles/vi/phoneme_examples.txt \
|
||||
profiles/vi/frequent_words.txt \
|
||||
profiles/vi/sentences.ini \
|
||||
profiles/vi/stop_words.txt ${RHASSPY_APP}/profiles/vi/
|
||||
|
||||
COPY profiles/pt/profile.json \
|
||||
profiles/pt/custom_words.txt \
|
||||
profiles/pt/espeak_phonemes.txt \
|
||||
profiles/pt/phoneme_examples.txt \
|
||||
profiles/pt/frequent_words.txt \
|
||||
profiles/pt/sentences.ini \
|
||||
profiles/pt/stop_words.txt ${RHASSPY_APP}/profiles/pt/
|
||||
|
||||
COPY profiles/sv/profile.json \
|
||||
profiles/sv/custom_words.txt \
|
||||
profiles/sv/espeak_phonemes.txt \
|
||||
profiles/sv/phoneme_examples.txt \
|
||||
profiles/sv/frequent_words.txt \
|
||||
profiles/sv/sentences.ini \
|
||||
profiles/sv/stop_words.txt ${RHASSPY_APP}/profiles/sv/
|
||||
|
||||
COPY profiles/ca/profile.json \
|
||||
profiles/ca/custom_words.txt \
|
||||
profiles/ca/espeak_phonemes.txt \
|
||||
profiles/ca/phoneme_examples.txt \
|
||||
profiles/ca/frequent_words.txt \
|
||||
profiles/ca/sentences.ini \
|
||||
profiles/ca/stop_words.txt ${RHASSPY_APP}/profiles/ca/
|
||||
|
||||
COPY profiles/en/profile.json \
|
||||
profiles/en/custom_words.txt \
|
||||
profiles/en/espeak_phonemes.txt \
|
||||
profiles/en/phoneme_examples.txt \
|
||||
profiles/en/frequent_words.txt \
|
||||
profiles/en/sentences.ini \
|
||||
profiles/en/stop_words.txt ${RHASSPY_APP}/profiles/en/
|
||||
|
||||
COPY profiles/en/kaldi/custom_words.txt \
|
||||
profiles/en/kaldi/espeak_phonemes.txt \
|
||||
profiles/en/kaldi/phoneme_examples.txt \
|
||||
${RHASSPY_APP}/profiles/en/kaldi/
|
||||
COPY profiles/ ${RHASSPY_APP}/profiles/
|
||||
|
||||
@@ -72,138 +72,7 @@ RUN chmod +x /run.sh
|
||||
|
||||
|
||||
|
||||
COPY profiles/zh/profile.json \
|
||||
profiles/zh/custom_words.txt \
|
||||
profiles/zh/espeak_phonemes.txt \
|
||||
profiles/zh/phoneme_examples.txt \
|
||||
profiles/zh/frequent_words.txt \
|
||||
profiles/zh/sentences.ini \
|
||||
profiles/zh/stop_words.txt ${RHASSPY_APP}/profiles/zh/
|
||||
|
||||
COPY profiles/hi/ \
|
||||
profiles/hi/profile.json \
|
||||
profiles/hi/custom_words.txt \
|
||||
profiles/hi/espeak_phonemes.txt \
|
||||
profiles/hi/phoneme_examples.txt \
|
||||
profiles/hi/frequent_words.txt \
|
||||
profiles/hi/sentences.ini \
|
||||
profiles/hi/stop_words.txt ${RHASSPY_APP}/profiles/hi/
|
||||
|
||||
COPY profiles/el/profile.json \
|
||||
profiles/el/custom_words.txt \
|
||||
profiles/el/espeak_phonemes.txt \
|
||||
profiles/el/phoneme_examples.txt \
|
||||
profiles/el/frequent_words.txt \
|
||||
profiles/el/sentences.ini \
|
||||
profiles/el/stop_words.txt ${RHASSPY_APP}/profiles/el/
|
||||
|
||||
COPY profiles/de/profile.json \
|
||||
profiles/de/custom_words.txt \
|
||||
profiles/de/espeak_phonemes.txt \
|
||||
profiles/de/phoneme_examples.txt \
|
||||
profiles/de/frequent_words.txt \
|
||||
profiles/de/sentences.ini \
|
||||
profiles/de/stop_words.txt ${RHASSPY_APP}/profiles/de/
|
||||
|
||||
COPY profiles/de/kaldi/custom_words.txt \
|
||||
profiles/de/kaldi/espeak_phonemes.txt \
|
||||
profiles/de/kaldi/phoneme_examples.txt \
|
||||
${RHASSPY_APP}/profiles/de/kaldi/
|
||||
|
||||
COPY profiles/it/profile.json \
|
||||
profiles/it/custom_words.txt \
|
||||
profiles/it/espeak_phonemes.txt \
|
||||
profiles/it/phoneme_examples.txt \
|
||||
profiles/it/frequent_words.txt \
|
||||
profiles/it/sentences.ini \
|
||||
profiles/it/stop_words.txt ${RHASSPY_APP}/profiles/it/
|
||||
|
||||
COPY profiles/es/profile.json \
|
||||
profiles/es/custom_words.txt \
|
||||
profiles/es/espeak_phonemes.txt \
|
||||
profiles/es/phoneme_examples.txt \
|
||||
profiles/es/frequent_words.txt \
|
||||
profiles/es/sentences.ini \
|
||||
profiles/es/stop_words.txt ${RHASSPY_APP}/profiles/es/
|
||||
|
||||
COPY profiles/fr/profile.json \
|
||||
profiles/fr/custom_words.txt \
|
||||
profiles/fr/espeak_phonemes.txt \
|
||||
profiles/fr/phoneme_examples.txt \
|
||||
profiles/fr/frequent_words.txt \
|
||||
profiles/fr/sentences.ini \
|
||||
profiles/fr/stop_words.txt ${RHASSPY_APP}/profiles/fr/
|
||||
|
||||
COPY profiles/fr/kaldi/custom_words.txt \
|
||||
profiles/fr/kaldi/espeak_phonemes.txt \
|
||||
profiles/fr/kaldi/phoneme_examples.txt \
|
||||
${RHASSPY_APP}/profiles/fr/kaldi/
|
||||
|
||||
COPY profiles/ru/profile.json \
|
||||
profiles/ru/custom_words.txt \
|
||||
profiles/ru/espeak_phonemes.txt \
|
||||
profiles/ru/phoneme_examples.txt \
|
||||
profiles/ru/frequent_words.txt \
|
||||
profiles/ru/sentences.ini \
|
||||
profiles/ru/stop_words.txt ${RHASSPY_APP}/profiles/ru/
|
||||
|
||||
COPY profiles/nl/profile.json \
|
||||
profiles/nl/custom_words.txt \
|
||||
profiles/nl/espeak_phonemes.txt \
|
||||
profiles/nl/phoneme_examples.txt \
|
||||
profiles/nl/frequent_words.txt \
|
||||
profiles/nl/sentences.ini \
|
||||
profiles/nl/stop_words.txt ${RHASSPY_APP}/profiles/nl/
|
||||
|
||||
COPY profiles/nl/kaldi/custom_words.txt \
|
||||
profiles/nl/kaldi/espeak_phonemes.txt \
|
||||
profiles/nl/kaldi/phoneme_examples.txt \
|
||||
${RHASSPY_APP}/profiles/nl/kaldi/
|
||||
|
||||
COPY profiles/vi/profile.json \
|
||||
profiles/vi/custom_words.txt \
|
||||
profiles/vi/espeak_phonemes.txt \
|
||||
profiles/vi/phoneme_examples.txt \
|
||||
profiles/vi/frequent_words.txt \
|
||||
profiles/vi/sentences.ini \
|
||||
profiles/vi/stop_words.txt ${RHASSPY_APP}/profiles/vi/
|
||||
|
||||
COPY profiles/pt/profile.json \
|
||||
profiles/pt/custom_words.txt \
|
||||
profiles/pt/espeak_phonemes.txt \
|
||||
profiles/pt/phoneme_examples.txt \
|
||||
profiles/pt/frequent_words.txt \
|
||||
profiles/pt/sentences.ini \
|
||||
profiles/pt/stop_words.txt ${RHASSPY_APP}/profiles/pt/
|
||||
|
||||
COPY profiles/sv/profile.json \
|
||||
profiles/sv/custom_words.txt \
|
||||
profiles/sv/espeak_phonemes.txt \
|
||||
profiles/sv/phoneme_examples.txt \
|
||||
profiles/sv/frequent_words.txt \
|
||||
profiles/sv/sentences.ini \
|
||||
profiles/sv/stop_words.txt ${RHASSPY_APP}/profiles/sv/
|
||||
|
||||
COPY profiles/ca/profile.json \
|
||||
profiles/ca/custom_words.txt \
|
||||
profiles/ca/espeak_phonemes.txt \
|
||||
profiles/ca/phoneme_examples.txt \
|
||||
profiles/ca/frequent_words.txt \
|
||||
profiles/ca/sentences.ini \
|
||||
profiles/ca/stop_words.txt ${RHASSPY_APP}/profiles/ca/
|
||||
|
||||
COPY profiles/en/profile.json \
|
||||
profiles/en/custom_words.txt \
|
||||
profiles/en/espeak_phonemes.txt \
|
||||
profiles/en/phoneme_examples.txt \
|
||||
profiles/en/frequent_words.txt \
|
||||
profiles/en/sentences.ini \
|
||||
profiles/en/stop_words.txt ${RHASSPY_APP}/profiles/en/
|
||||
|
||||
COPY profiles/en/kaldi/custom_words.txt \
|
||||
profiles/en/kaldi/espeak_phonemes.txt \
|
||||
profiles/en/kaldi/phoneme_examples.txt \
|
||||
${RHASSPY_APP}/profiles/en/kaldi/
|
||||
COPY profiles/ ${RHASSPY_APP}/profiles/
|
||||
|
||||
COPY profiles/defaults.json ${RHASSPY_APP}/profiles/
|
||||
COPY docker/rhasspy ${RHASSPY_APP}/bin/
|
||||
|
||||
+4
-1
@@ -4,6 +4,9 @@ Rhasspy is designed to be run on different kinds of hardware, such as:
|
||||
|
||||
* Raspberry Pi 2-3 B/B+ (`armhf`/`aarch64`)
|
||||
* Desktop/laptop/server (`amd64`)
|
||||
* Raspberry Pi Zero (`armv6l`)
|
||||
* You must use a [virtual environment](installation.md#virtual-environment)
|
||||
* The [Kaldi speech recognizer](speech-to-text.md#kaldi) is **not** supported
|
||||
|
||||
The table below summarizes architecture compatibility with Rhasspy's components:
|
||||
|
||||
@@ -30,7 +33,7 @@ The table below summarizes architecture compatibility with Rhasspy's components:
|
||||
|
||||
To run Rhasspy on a Raspberry Pi, you'll need at least a 4 GB SD card and a good power supply. I highly recommend the [CanaKit Starter Kit](https://www.amazon.com/CanaKit-Raspberry-Starter-Premium-Black/dp/B07BCC8PK7), which includes a 32 GB SD card, a 2.5 A power supply, and a case.
|
||||
|
||||
Some components of Rhasspy will not work on the Raspberry Pi 3 B+ model (`aarch64`). As of the time of this writing, these are:
|
||||
Some components of Rhasspy will not work on the Raspberry Pi 3 B+ model with a 64-bit operating system (`aarch64`). As of the time of this writing, these are:
|
||||
|
||||
* [snowboy](wake-word.md#snowboy) (wake word)
|
||||
* [Mycroft Precise](wake-word.md#mycroft-precise) (wake word)
|
||||
|
||||
+15
-2
@@ -54,7 +54,13 @@ To update your Rhasspy Docker image, just run:
|
||||
```bash
|
||||
docker pull synesthesiam/rhasspy-server:latest
|
||||
```
|
||||
on your Rhasspy server and restart the Docker container.
|
||||
on your Rhasspy server and restart the Docker container. This may require running something like:
|
||||
|
||||
```bash
|
||||
docker rm <container-name>
|
||||
```
|
||||
|
||||
before doing a `docker run...`
|
||||
|
||||
## Hass.io
|
||||
|
||||
@@ -108,7 +114,14 @@ To update your Rhasspy virtual environment to the latest version, run:
|
||||
git pull origin master
|
||||
```
|
||||
|
||||
in your `rhasspy` directory. You should also re-build the web interface:
|
||||
in your `rhasspy` directory, and then update your Python dependencies:
|
||||
|
||||
```bash
|
||||
source .venv/bin/activate
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
|
||||
You should also re-build the web interface:
|
||||
|
||||
1. Install [yarn](https://yarnpkg.com) on your system
|
||||
2. Run `yarn build` in the `rhasspy` directory
|
||||
|
||||
@@ -262,6 +262,13 @@ You can pass **arguments** to your program using the syntax `$name,arg1,arg2,...
|
||||
|
||||
Like regular slots lists, slot programs can also be put in sub-directories under `slot_programs`. A program in `slot_programs/foo/bar` should be referenced in `sentences.ini` as `$foo/bar`.
|
||||
|
||||
#### Built-in Slots
|
||||
|
||||
Rhasspy includes a few built-in slots for each language:
|
||||
|
||||
* `$rhasspy/days` - day names of the week
|
||||
* `$rhasspy/months` - month names of the year
|
||||
|
||||
### Converters
|
||||
|
||||
By default, all named entity values in a recognized intent's JSON are strings. If you need a different data type, such as an integer or float, or want to do some kind of complex *conversion*, use a converter:
|
||||
|
||||
@@ -17,9 +17,7 @@ def main():
|
||||
step = int(rest_args[0])
|
||||
|
||||
if upper < lower:
|
||||
temp_lower = lower
|
||||
lower = upper
|
||||
upper = temp_lower
|
||||
lower, upper = upper, lower
|
||||
|
||||
for n in range(lower, upper + 1, step):
|
||||
print(n)
|
||||
|
||||
@@ -17,9 +17,7 @@ def main():
|
||||
step = int(rest_args[0])
|
||||
|
||||
if upper < lower:
|
||||
temp_lower = lower
|
||||
lower = upper
|
||||
upper = temp_lower
|
||||
lower, upper = upper, lower
|
||||
|
||||
for n in range(lower, upper + 1, step):
|
||||
print(n)
|
||||
|
||||
@@ -17,9 +17,7 @@ def main():
|
||||
step = int(rest_args[0])
|
||||
|
||||
if upper < lower:
|
||||
temp_lower = lower
|
||||
lower = upper
|
||||
upper = temp_lower
|
||||
lower, upper = upper, lower
|
||||
|
||||
for n in range(lower, upper + 1, step):
|
||||
print(n)
|
||||
|
||||
@@ -17,9 +17,7 @@ def main():
|
||||
step = int(rest_args[0])
|
||||
|
||||
if upper < lower:
|
||||
temp_lower = lower
|
||||
lower = upper
|
||||
upper = temp_lower
|
||||
lower, upper = upper, lower
|
||||
|
||||
for n in range(lower, upper + 1, step):
|
||||
print(n)
|
||||
|
||||
@@ -17,9 +17,7 @@ def main():
|
||||
step = int(rest_args[0])
|
||||
|
||||
if upper < lower:
|
||||
temp_lower = lower
|
||||
lower = upper
|
||||
upper = temp_lower
|
||||
lower, upper = upper, lower
|
||||
|
||||
for n in range(lower, upper + 1, step):
|
||||
print(n)
|
||||
|
||||
@@ -17,9 +17,7 @@ def main():
|
||||
step = int(rest_args[0])
|
||||
|
||||
if upper < lower:
|
||||
temp_lower = lower
|
||||
lower = upper
|
||||
upper = temp_lower
|
||||
lower, upper = upper, lower
|
||||
|
||||
for n in range(lower, upper + 1, step):
|
||||
print(n)
|
||||
|
||||
@@ -17,9 +17,7 @@ def main():
|
||||
step = int(rest_args[0])
|
||||
|
||||
if upper < lower:
|
||||
temp_lower = lower
|
||||
lower = upper
|
||||
upper = temp_lower
|
||||
lower, upper = upper, lower
|
||||
|
||||
for n in range(lower, upper + 1, step):
|
||||
print(n)
|
||||
|
||||
@@ -17,9 +17,7 @@ def main():
|
||||
step = int(rest_args[0])
|
||||
|
||||
if upper < lower:
|
||||
temp_lower = lower
|
||||
lower = upper
|
||||
upper = temp_lower
|
||||
lower, upper = upper, lower
|
||||
|
||||
for n in range(lower, upper + 1, step):
|
||||
print(n)
|
||||
|
||||
@@ -17,9 +17,7 @@ def main():
|
||||
step = int(rest_args[0])
|
||||
|
||||
if upper < lower:
|
||||
temp_lower = lower
|
||||
lower = upper
|
||||
upper = temp_lower
|
||||
lower, upper = upper, lower
|
||||
|
||||
for n in range(lower, upper + 1, step):
|
||||
print(n)
|
||||
|
||||
@@ -17,9 +17,7 @@ def main():
|
||||
step = int(rest_args[0])
|
||||
|
||||
if upper < lower:
|
||||
temp_lower = lower
|
||||
lower = upper
|
||||
upper = temp_lower
|
||||
lower, upper = upper, lower
|
||||
|
||||
for n in range(lower, upper + 1, step):
|
||||
print(n)
|
||||
|
||||
@@ -17,9 +17,7 @@ def main():
|
||||
step = int(rest_args[0])
|
||||
|
||||
if upper < lower:
|
||||
temp_lower = lower
|
||||
lower = upper
|
||||
upper = temp_lower
|
||||
lower, upper = upper, lower
|
||||
|
||||
for n in range(lower, upper + 1, step):
|
||||
print(n)
|
||||
|
||||
@@ -17,9 +17,7 @@ def main():
|
||||
step = int(rest_args[0])
|
||||
|
||||
if upper < lower:
|
||||
temp_lower = lower
|
||||
lower = upper
|
||||
upper = temp_lower
|
||||
lower, upper = upper, lower
|
||||
|
||||
for n in range(lower, upper + 1, step):
|
||||
print(n)
|
||||
|
||||
@@ -17,9 +17,7 @@ def main():
|
||||
step = int(rest_args[0])
|
||||
|
||||
if upper < lower:
|
||||
temp_lower = lower
|
||||
lower = upper
|
||||
upper = temp_lower
|
||||
lower, upper = upper, lower
|
||||
|
||||
for n in range(lower, upper + 1, step):
|
||||
print(n)
|
||||
|
||||
@@ -17,9 +17,7 @@ def main():
|
||||
step = int(rest_args[0])
|
||||
|
||||
if upper < lower:
|
||||
temp_lower = lower
|
||||
lower = upper
|
||||
upper = temp_lower
|
||||
lower, upper = upper, lower
|
||||
|
||||
for n in range(lower, upper + 1, step):
|
||||
print(n)
|
||||
|
||||
+10
-4
@@ -116,9 +116,6 @@ class RhasspyActor:
|
||||
|
||||
def stop(self, block=True):
|
||||
"""Stop this actor and its children."""
|
||||
for child_actor in self._actors:
|
||||
child_actor.stop(block=block)
|
||||
|
||||
self.send(self, ActorExitRequest())
|
||||
if block:
|
||||
self._thread.join()
|
||||
@@ -127,6 +124,15 @@ class RhasspyActor:
|
||||
"""Main loop for this actor."""
|
||||
while self._running:
|
||||
message_dict = self._queue.get()
|
||||
message = message_dict.get("message")
|
||||
if isinstance(message, ActorExitRequest):
|
||||
for child in self._actors:
|
||||
self.send(child, ActorExitRequest())
|
||||
|
||||
self._running = False
|
||||
self.transition("stopped")
|
||||
self.send(self._parent, ChildActorExited(self))
|
||||
|
||||
self.on_receive(message_dict)
|
||||
|
||||
@property
|
||||
@@ -296,7 +302,7 @@ class InboxActor(RhasspyActor):
|
||||
return self
|
||||
|
||||
def __exit__(self, *args):
|
||||
self.stop(block=False)
|
||||
self.stop(block=True)
|
||||
|
||||
|
||||
class ActorSystem:
|
||||
|
||||
+7
-5
@@ -7,8 +7,8 @@ from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Type
|
||||
|
||||
import pydash
|
||||
import pywrapfst as fst
|
||||
import requests
|
||||
import rhasspynlu
|
||||
|
||||
from rhasspy.actor import (
|
||||
ActorExitRequest,
|
||||
@@ -555,12 +555,14 @@ class DialogueManager(RhasspyActor):
|
||||
|
||||
self.transition("training_intent")
|
||||
|
||||
intent_fst_path = self.profile.read_path(
|
||||
self.profile.get("intent.fsticuffs.intent_fst", "intent.fst")
|
||||
intent_graph_path = self.profile.read_path(
|
||||
self.profile.get("intent.fsticuffs.intent_graph", "intent.json")
|
||||
)
|
||||
|
||||
intent_fst = fst.Fst.read(str(intent_fst_path))
|
||||
self.send(self.intent_trainer, TrainIntent(intent_fst))
|
||||
with open(intent_graph_path, "r") as graph_file:
|
||||
json_graph = json.load(graph_file)
|
||||
intent_graph = rhasspynlu.json_to_graph(json_graph)
|
||||
self.send(self.intent_trainer, TrainIntent(intent_graph))
|
||||
except Exception as e:
|
||||
self.transition("ready")
|
||||
self.send(self.training_receiver, ProfileTrainingFailed(str(e)))
|
||||
|
||||
+2
-2
@@ -246,8 +246,8 @@ class IntentForwarded:
|
||||
class TrainIntent:
|
||||
"""Request to train intent recognizer."""
|
||||
|
||||
def __init__(self, intent_fst, receiver: Optional[RhasspyActor] = None) -> None:
|
||||
self.intent_fst = intent_fst
|
||||
def __init__(self, intent_graph, receiver: Optional[RhasspyActor] = None) -> None:
|
||||
self.intent_graph = intent_graph
|
||||
self.receiver = receiver
|
||||
|
||||
|
||||
|
||||
+2
-56
@@ -1,13 +1,11 @@
|
||||
"""Support for intent recognition."""
|
||||
import concurrent.futures
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type
|
||||
from urllib.parse import urljoin
|
||||
|
||||
@@ -18,7 +16,7 @@ from rhasspynlu import json_to_graph, recognize
|
||||
|
||||
from rhasspy.actor import RhasspyActor
|
||||
from rhasspy.events import IntentRecognized, RecognizeIntent, SpeakSentence
|
||||
from rhasspy.utils import empty_intent, hass_request_kwargs
|
||||
from rhasspy.utils import empty_intent, hass_request_kwargs, load_converters
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
@@ -137,32 +135,6 @@ class RemoteRecognizer(RhasspyActor):
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
class CliConverter:
|
||||
"""Command-line converter for intent recognition"""
|
||||
|
||||
def __init__(self, name: str, command_path: Path):
|
||||
self.name = name
|
||||
self.command_path = command_path
|
||||
|
||||
def __call__(self, *args, converter_args=None):
|
||||
"""Runs external program to convert JSON values"""
|
||||
converter_args = converter_args or []
|
||||
proc = subprocess.Popen(
|
||||
[str(self.command_path)] + converter_args,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
universal_newlines=True,
|
||||
)
|
||||
|
||||
with io.StringIO() as input_file:
|
||||
for arg in args:
|
||||
json.dump(arg, input_file)
|
||||
|
||||
stdout, _ = proc.communicate(input=input_file.getvalue())
|
||||
|
||||
return [json.loads(line) for line in stdout.splitlines() if line.strip()]
|
||||
|
||||
|
||||
class FsticuffsRecognizer(RhasspyActor):
|
||||
"""Recognize intents using OpenFST."""
|
||||
|
||||
@@ -188,33 +160,7 @@ class FsticuffsRecognizer(RhasspyActor):
|
||||
self.fuzzy = self.profile.get("intent.fsticuffs.fuzzy", True)
|
||||
|
||||
# Load user-defined converters
|
||||
converters_dir = Path(
|
||||
self.profile.read_path(
|
||||
self.profile.get("intent.fsticuffs.converters_dir", "converters")
|
||||
)
|
||||
)
|
||||
if converters_dir.is_dir():
|
||||
self._logger.debug("Loading converters from %s", converters_dir)
|
||||
for converter_path in converters_dir.glob("**/*"):
|
||||
if not converter_path.is_file():
|
||||
continue
|
||||
|
||||
# Retain directory structure in name
|
||||
converter_name = str(
|
||||
converter_path.relative_to(converters_dir).with_suffix("")
|
||||
)
|
||||
|
||||
# Run converter as external program.
|
||||
# Input arguments are encoded as JSON on individual lines.
|
||||
# Output values should be encoded as JSON on individual lines.
|
||||
converter = CliConverter(converter_name, converter_path)
|
||||
|
||||
# Key off name without file extension
|
||||
self.converters[converter_name] = converter
|
||||
|
||||
self._logger.debug(
|
||||
"Loaded converter %s from %s", converter_name, converter_path
|
||||
)
|
||||
self.converters = load_converters(self.profile)
|
||||
|
||||
self.transition("loaded")
|
||||
|
||||
|
||||
+38
-12
@@ -9,14 +9,12 @@ import tempfile
|
||||
import time
|
||||
from collections import Counter, defaultdict
|
||||
from io import StringIO
|
||||
from typing import Any, Dict, List, Set, Type
|
||||
from typing import Any, Callable, Dict, List, Set, Type
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from rhasspy.actor import RhasspyActor
|
||||
from rhasspy.events import (IntentTrainingComplete, IntentTrainingFailed,
|
||||
TrainIntent)
|
||||
from rhasspy.utils import (lcm, make_sentences_by_intent,
|
||||
sample_sentences_by_intent)
|
||||
from rhasspy.events import IntentTrainingComplete, IntentTrainingFailed, TrainIntent
|
||||
from rhasspy.utils import lcm, make_sentences_by_intent, load_converters
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
@@ -112,23 +110,30 @@ class FsticuffsIntentTrainer(DummyIntentTrainer):
|
||||
class FuzzyWuzzyIntentTrainer(RhasspyActor):
|
||||
"""Save examples to JSON for fuzzy string matching later."""
|
||||
|
||||
def __init__(self):
|
||||
RhasspyActor.__init__(self)
|
||||
self.converters: Dict[str, Callable[..., Any]] = {}
|
||||
|
||||
def in_started(self, message: Any, sender: RhasspyActor) -> None:
|
||||
"""Handle messages in started state."""
|
||||
if isinstance(message, TrainIntent):
|
||||
try:
|
||||
self.train(message.intent_fst)
|
||||
self.train(message.intent_graph)
|
||||
self.send(message.receiver or sender, IntentTrainingComplete())
|
||||
except Exception as e:
|
||||
self._logger.exception("train")
|
||||
self.send(message.receiver or sender, IntentTrainingFailed(repr(e)))
|
||||
|
||||
def train(self, intent_fst) -> None:
|
||||
def train(self, intent_graph) -> None:
|
||||
"""Save examples to JSON file."""
|
||||
examples_path = self.profile.write_path(
|
||||
self.profile.get("intent.fuzzywuzzy.examples_json")
|
||||
)
|
||||
|
||||
sentences_by_intent: Dict[str, Any] = make_sentences_by_intent(intent_fst)
|
||||
converters = load_converters(self.profile)
|
||||
sentences_by_intent = make_sentences_by_intent(
|
||||
intent_graph, extra_converters=converters
|
||||
)
|
||||
with open(examples_path, "w") as examples_file:
|
||||
json.dump(sentences_by_intent, examples_file, indent=4)
|
||||
|
||||
@@ -144,6 +149,10 @@ class FuzzyWuzzyIntentTrainer(RhasspyActor):
|
||||
class RasaIntentTrainer(RhasspyActor):
|
||||
"""Uses Rasa NLU HTTP API to train a recognizer."""
|
||||
|
||||
def __init__(self):
|
||||
RhasspyActor.__init__(self)
|
||||
self.converters: Dict[str, Callable[..., Any]] = {}
|
||||
|
||||
def in_started(self, message: Any, sender: RhasspyActor) -> None:
|
||||
"""Handle messages in started state."""
|
||||
if isinstance(message, TrainIntent):
|
||||
@@ -256,7 +265,9 @@ class RasaIntentTrainer(RhasspyActor):
|
||||
response.raise_for_status()
|
||||
except Exception:
|
||||
# Rasa gives quite helpful error messages, so extract them from the response.
|
||||
raise Exception(f'{response.reason}: {json.loads(response.content)["message"]}')
|
||||
raise Exception(
|
||||
f'{response.reason}: {json.loads(response.content)["message"]}'
|
||||
)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -268,6 +279,14 @@ class RasaIntentTrainer(RhasspyActor):
|
||||
class AdaptIntentTrainer(RhasspyActor):
|
||||
"""Configure a Mycroft Adapt engine."""
|
||||
|
||||
def __init__(self):
|
||||
RhasspyActor.__init__(self)
|
||||
self.converters: Dict[str, Callable[..., Any]] = {}
|
||||
|
||||
def to_started(self, from_state: str) -> None:
|
||||
# Load user-defined converters
|
||||
self.converters = load_converters(self.profile)
|
||||
|
||||
def in_started(self, message: Any, sender: RhasspyActor) -> None:
|
||||
"""Handle messages in started state."""
|
||||
if isinstance(message, TrainIntent):
|
||||
@@ -287,9 +306,7 @@ class AdaptIntentTrainer(RhasspyActor):
|
||||
stop_words_path = self.profile.read_path("stop_words.txt")
|
||||
if os.path.exists(stop_words_path):
|
||||
with open(stop_words_path, "r") as stop_words_file:
|
||||
stop_words = {
|
||||
line.strip() for line in stop_words_file if line.strip()
|
||||
}
|
||||
stop_words = {line.strip() for line in stop_words_file if line.strip()}
|
||||
|
||||
# { intent: [ { 'text': ..., 'entities': { ... } }, ... ] }
|
||||
sentences_by_intent: Dict[str, Any] = make_sentences_by_intent(intent_fst)
|
||||
@@ -410,6 +427,11 @@ class FlairIntentTrainer(RhasspyActor):
|
||||
def __init__(self):
|
||||
RhasspyActor.__init__(self)
|
||||
self.embeddings = []
|
||||
self.converters: Dict[str, Callable[..., Any]] = {}
|
||||
|
||||
def to_started(self, from_state: str) -> None:
|
||||
# Load user-defined converters
|
||||
self.converters = load_converters(self.profile)
|
||||
|
||||
def in_started(self, message: Any, sender: RhasspyActor) -> None:
|
||||
"""Handle messages in started state."""
|
||||
@@ -671,6 +693,7 @@ class CommandIntentTrainer(RhasspyActor):
|
||||
def __init__(self):
|
||||
RhasspyActor.__init__(self)
|
||||
self.command: List[str] = []
|
||||
self.converters: Dict[str, Callable[..., Any]] = {}
|
||||
|
||||
def to_started(self, from_state: str) -> None:
|
||||
"""Transition to started state."""
|
||||
@@ -682,6 +705,9 @@ class CommandIntentTrainer(RhasspyActor):
|
||||
for a in self.profile.get("training.intent.command.arguments", [])
|
||||
]
|
||||
|
||||
# Load user-defined converters
|
||||
self.converters = load_converters(self.profile)
|
||||
|
||||
self.command = [program] + arguments
|
||||
|
||||
def in_started(self, message: Any, sender: RhasspyActor) -> None:
|
||||
|
||||
@@ -165,9 +165,7 @@ def train_profile(profile_dir: Path, profile: Profile) -> Tuple[int, List[str]]:
|
||||
# Check for arguments.
|
||||
# Slot name retains argument(s).
|
||||
if "," in slot_name:
|
||||
parts = slot_name.split(",")
|
||||
slot_name = parts[0]
|
||||
slot_args = parts[1:]
|
||||
slot_name, *slot_args = slot_name.split(",")
|
||||
else:
|
||||
slot_args = None
|
||||
|
||||
@@ -228,7 +226,7 @@ def train_profile(profile_dir: Path, profile: Profile) -> Tuple[int, List[str]]:
|
||||
upper_bound = int(match.group(2))
|
||||
step = 1
|
||||
|
||||
if len(match.groups()) > 2:
|
||||
if len(match.groups()) > 3:
|
||||
# Exclude ,
|
||||
step = int(match.group(3)[1:])
|
||||
|
||||
@@ -270,7 +268,7 @@ def train_profile(profile_dir: Path, profile: Profile) -> Tuple[int, List[str]]:
|
||||
word.converters = ["int"]
|
||||
return word
|
||||
|
||||
# Hard case, split into mutliple Words
|
||||
# Hard case, split into multiple Words
|
||||
return jsgf.Sequence(
|
||||
text=number_text,
|
||||
type=jsgf.SequenceType.GROUP,
|
||||
|
||||
@@ -91,7 +91,7 @@ def make_dict(
|
||||
if (i < 1) or no_number:
|
||||
print(word, pronounce, file=dictionary_file)
|
||||
else:
|
||||
print(f"{word, i + 1}({pronounce})", file=dictionary_file)
|
||||
print(f"{word}({i + 1})", pronounce, file=dictionary_file)
|
||||
|
||||
words_in_dict.add(word)
|
||||
|
||||
|
||||
+96
-44
@@ -1,23 +1,24 @@
|
||||
"""Rhasspy utility functions."""
|
||||
import collections
|
||||
import concurrent.futures
|
||||
import gzip
|
||||
import io
|
||||
import itertools
|
||||
import json
|
||||
import logging
|
||||
import math
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import subprocess
|
||||
import threading
|
||||
import wave
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
from typing import (Any, Callable, Dict, Iterable, List, Mapping, Optional,
|
||||
Set, Tuple)
|
||||
from typing import Any, Callable, Dict, Iterable, List, Mapping, Optional, Set, Tuple
|
||||
|
||||
import pywrapfst as fst
|
||||
import networkx as nx
|
||||
import rhasspynlu
|
||||
|
||||
from num2words import num2words
|
||||
|
||||
WHITESPACE_PATTERN = re.compile(r"\s+")
|
||||
@@ -329,55 +330,45 @@ def grouper(iterable, n, fillvalue=None):
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
def make_sentences_by_intent(intent_fst: fst.Fst) -> Dict[str, Any]:
|
||||
"""Get all sentences from an FST."""
|
||||
from rhasspy.train.jsgf2fst import fstprintall, symbols2intent
|
||||
def make_sentences_by_intent(
|
||||
intent_graph: nx.DiGraph, num_samples: Optional[int] = None, extra_converters=None
|
||||
) -> Dict[str, List[Dict[str, Any]]]:
|
||||
"""Get all sentences from a graph."""
|
||||
|
||||
# { intent: [ { 'text': ..., 'entities': { ... } }, ... ] }
|
||||
sentences_by_intent: Dict[str, Any] = defaultdict(list)
|
||||
|
||||
for symbols in fstprintall(intent_fst, exclude_meta=False):
|
||||
intent = symbols2intent(symbols)
|
||||
intent_name = intent["intent"]["name"]
|
||||
sentences_by_intent[intent_name].append(intent)
|
||||
start_node = None
|
||||
end_node = None
|
||||
for node, node_data in intent_graph.nodes(data=True):
|
||||
if node_data.get("start", False):
|
||||
start_node = node
|
||||
elif node_data.get("final", False):
|
||||
end_node = node
|
||||
|
||||
return sentences_by_intent
|
||||
if start_node and end_node:
|
||||
break
|
||||
|
||||
assert (start_node is not None) and (
|
||||
end_node is not None
|
||||
), "Missing start/end node(s)"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
def sample_sentences_by_intent(
|
||||
intent_fst_paths: Dict[str, str], num_samples: int
|
||||
) -> Dict[str, Any]:
|
||||
"""Generate random intents"""
|
||||
from rhasspy.train.jsgf2fst import fstprintall, symbols2intent
|
||||
|
||||
def sample_sentences(intent_name: str, intent_fst_path: str):
|
||||
rand_fst = fst.Fst.read_from_string(
|
||||
subprocess.check_output(
|
||||
["fstrandgen", f"--npath={num_samples}", intent_fst_path]
|
||||
)
|
||||
if num_samples is not None:
|
||||
# Randomly sample
|
||||
paths = random.sample(
|
||||
list(nx.all_simple_paths(intent_graph, start_node, end_node)), num_samples
|
||||
)
|
||||
else:
|
||||
# Use generator
|
||||
paths = nx.all_simple_paths(intent_graph, start_node, end_node)
|
||||
|
||||
sentences: List[Dict[str, Any]] = []
|
||||
for symbols in fstprintall(rand_fst, exclude_meta=False):
|
||||
intent = symbols2intent(symbols)
|
||||
sentences.append(intent)
|
||||
|
||||
return sentences
|
||||
|
||||
# Generate samples in parallel
|
||||
future_to_intent = {}
|
||||
with concurrent.futures.ThreadPoolExecutor() as executor:
|
||||
for intent_name, intent_fst_path in intent_fst_paths.items():
|
||||
future = executor.submit(sample_sentences, intent_name, intent_fst_path)
|
||||
future_to_intent[future] = intent_name
|
||||
|
||||
# { intent: [ { 'text': ..., 'entities': { ... } }, ... ] }
|
||||
sentences_by_intent: Dict[str, Any] = {}
|
||||
for future, intent_name in future_to_intent.items():
|
||||
sentences_by_intent[intent_name] = future.result()
|
||||
# TODO: Add converters
|
||||
for path in paths:
|
||||
_, recognition = rhasspynlu.fsticuffs.path_to_recognition(
|
||||
path, intent_graph, extra_converters=extra_converters
|
||||
)
|
||||
assert recognition, "Path failed"
|
||||
sentences_by_intent[recognition.intent.name].append(recognition.asdict())
|
||||
|
||||
return sentences_by_intent
|
||||
|
||||
@@ -507,3 +498,64 @@ def get_all_intents(ini_paths: List[Path]) -> Dict[str, Any]:
|
||||
_LOGGER.exception("Failed to parse %s", ini_paths)
|
||||
|
||||
return {}
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
class CliConverter:
|
||||
"""Command-line converter for intent recognition"""
|
||||
|
||||
def __init__(self, name: str, command_path: Path):
|
||||
self.name = name
|
||||
self.command_path = command_path
|
||||
|
||||
def __call__(self, *args, converter_args=None):
|
||||
"""Runs external program to convert JSON values"""
|
||||
converter_args = converter_args or []
|
||||
proc = subprocess.Popen(
|
||||
[str(self.command_path)] + converter_args,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
universal_newlines=True,
|
||||
)
|
||||
|
||||
with io.StringIO() as input_file:
|
||||
for arg in args:
|
||||
json.dump(arg, input_file)
|
||||
|
||||
stdout, _ = proc.communicate(input=input_file.getvalue())
|
||||
|
||||
return [json.loads(line) for line in stdout.splitlines() if line.strip()]
|
||||
|
||||
|
||||
def load_converters(profile) -> Dict[str, Any]:
|
||||
# Load user-defined converters
|
||||
converters = {}
|
||||
|
||||
converters_dir = Path(
|
||||
profile.read_path(profile.get("intent.fsticuffs.converters_dir", "converters"))
|
||||
)
|
||||
|
||||
if converters_dir.is_dir():
|
||||
_LOGGER.debug("Loading converters from %s", converters_dir)
|
||||
for converter_path in converters_dir.glob("**/*"):
|
||||
if not converter_path.is_file():
|
||||
continue
|
||||
|
||||
# Retain directory structure in name
|
||||
converter_name = str(
|
||||
converter_path.relative_to(converters_dir).with_suffix("")
|
||||
)
|
||||
|
||||
# Run converter as external program.
|
||||
# Input arguments are encoded as JSON on individual lines.
|
||||
# Output values should be encoded as JSON on individual lines.
|
||||
converter = CliConverter(converter_name, converter_path)
|
||||
|
||||
# Key off name without file extension
|
||||
converters[converter_name] = converter
|
||||
|
||||
_LOGGER.debug("Loaded converter %s from %s", converter_name, converter_path)
|
||||
|
||||
return converters
|
||||
|
||||
Reference in New Issue
Block a user