grok-assistant 1.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.
- grok_assistant-1.0/LICENSE.md +39 -0
- grok_assistant-1.0/PKG-INFO +58 -0
- grok_assistant-1.0/README.md +39 -0
- grok_assistant-1.0/pyproject.toml +51 -0
- grok_assistant-1.0/setup.cfg +4 -0
- grok_assistant-1.0/setup.py +54 -0
- grok_assistant-1.0/src/grok_assistant/__init__.py +4 -0
- grok_assistant-1.0/src/grok_assistant/__main__.py +110 -0
- grok_assistant-1.0/src/grok_assistant/_bundle/LICENSE.md +39 -0
- grok_assistant-1.0/src/grok_assistant/_bundle/docs/img/grok-mark.png +0 -0
- grok_assistant-1.0/src/grok_assistant/_bundle/docs/img/grok.ico +0 -0
- grok_assistant-1.0/src/grok_assistant/_bundle/listeners/dictation.ps1 +80 -0
- grok_assistant-1.0/src/grok_assistant/_bundle/listeners/install_speech.ps1 +19 -0
- grok_assistant-1.0/src/grok_assistant/_bundle/scripts/build_exe.ps1 +21 -0
- grok_assistant-1.0/src/grok_assistant/_bundle/scripts/speak.ps1 +14 -0
- grok_assistant-1.0/src/grok_assistant/_bundle/scripts/voices.ps1 +6 -0
- grok_assistant-1.0/src/grok_assistant/build_stamp.txt +2 -0
- grok_assistant-1.0/src/grok_assistant/buildinfo.py +69 -0
- grok_assistant-1.0/src/grok_assistant/cloud/__init__.py +1 -0
- grok_assistant-1.0/src/grok_assistant/cloud/account_agents.py +92 -0
- grok_assistant-1.0/src/grok_assistant/cloud/account_usage.py +49 -0
- grok_assistant-1.0/src/grok_assistant/cloud/grok_cli.py +166 -0
- grok_assistant-1.0/src/grok_assistant/cloud/prompts.py +61 -0
- grok_assistant-1.0/src/grok_assistant/cloud/setup_grok.py +167 -0
- grok_assistant-1.0/src/grok_assistant/console.py +26 -0
- grok_assistant-1.0/src/grok_assistant/house/__init__.py +1 -0
- grok_assistant-1.0/src/grok_assistant/house/helptext.py +155 -0
- grok_assistant-1.0/src/grok_assistant/house/marketplace.py +531 -0
- grok_assistant-1.0/src/grok_assistant/house/music.py +166 -0
- grok_assistant-1.0/src/grok_assistant/house/personality.py +430 -0
- grok_assistant-1.0/src/grok_assistant/house/updates.py +165 -0
- grok_assistant-1.0/src/grok_assistant/i18n.py +172 -0
- grok_assistant-1.0/src/grok_assistant/lang/de.json +393 -0
- grok_assistant-1.0/src/grok_assistant/lang/en.json +393 -0
- grok_assistant-1.0/src/grok_assistant/lang/es.json +366 -0
- grok_assistant-1.0/src/grok_assistant/lang/fr.json +393 -0
- grok_assistant-1.0/src/grok_assistant/lines/banter.json +638 -0
- grok_assistant-1.0/src/grok_assistant/lines/hellos-es.txt +188 -0
- grok_assistant-1.0/src/grok_assistant/lines/waits-es.txt +170 -0
- grok_assistant-1.0/src/grok_assistant/listening/__init__.py +1 -0
- grok_assistant-1.0/src/grok_assistant/listening/ear_score.py +87 -0
- grok_assistant-1.0/src/grok_assistant/listening/enroll_audio.py +259 -0
- grok_assistant-1.0/src/grok_assistant/listening/kroko_ear.py +324 -0
- grok_assistant-1.0/src/grok_assistant/listening/listen.py +217 -0
- grok_assistant-1.0/src/grok_assistant/listening/offline_ear.py +243 -0
- grok_assistant-1.0/src/grok_assistant/listening/refine.py +131 -0
- grok_assistant-1.0/src/grok_assistant/listening/voiceprint.py +75 -0
- grok_assistant-1.0/src/grok_assistant/mind/__init__.py +1 -0
- grok_assistant-1.0/src/grok_assistant/mind/local_llm.py +152 -0
- grok_assistant-1.0/src/grok_assistant/notebook/__init__.py +1 -0
- grok_assistant-1.0/src/grok_assistant/notebook/auth.py +48 -0
- grok_assistant-1.0/src/grok_assistant/notebook/settings.py +100 -0
- grok_assistant-1.0/src/grok_assistant/notebook/store.py +669 -0
- grok_assistant-1.0/src/grok_assistant/paths.py +81 -0
- grok_assistant-1.0/src/grok_assistant/quiet.py +15 -0
- grok_assistant-1.0/src/grok_assistant/rules/__init__.py +1 -0
- grok_assistant-1.0/src/grok_assistant/rules/brain.py +72 -0
- grok_assistant-1.0/src/grok_assistant/rules/common.py +100 -0
- grok_assistant-1.0/src/grok_assistant/rules/hub.py +338 -0
- grok_assistant-1.0/src/grok_assistant/rules/match.py +523 -0
- grok_assistant-1.0/src/grok_assistant/rules/orders.py +235 -0
- grok_assistant-1.0/src/grok_assistant/rules/prints.py +173 -0
- grok_assistant-1.0/src/grok_assistant/rules/status.py +159 -0
- grok_assistant-1.0/src/grok_assistant/rules/talk.py +451 -0
- grok_assistant-1.0/src/grok_assistant/rules/trace.py +78 -0
- grok_assistant-1.0/src/grok_assistant/speaking/__init__.py +1 -0
- grok_assistant-1.0/src/grok_assistant/speaking/banter.py +81 -0
- grok_assistant-1.0/src/grok_assistant/speaking/speech.py +222 -0
- grok_assistant-1.0/src/grok_assistant/startup.py +50 -0
- grok_assistant-1.0/src/grok_assistant/textutil.py +55 -0
- grok_assistant-1.0/src/grok_assistant/ui/__init__.py +1 -0
- grok_assistant-1.0/src/grok_assistant/ui/actions.py +624 -0
- grok_assistant-1.0/src/grok_assistant/ui/app.py +36 -0
- grok_assistant-1.0/src/grok_assistant/ui/chrome.py +59 -0
- grok_assistant-1.0/src/grok_assistant/ui/deps.py +39 -0
- grok_assistant-1.0/src/grok_assistant/ui/ears.py +156 -0
- grok_assistant-1.0/src/grok_assistant/ui/flow.py +187 -0
- grok_assistant-1.0/src/grok_assistant/ui/market.py +413 -0
- grok_assistant-1.0/src/grok_assistant/ui/menus.py +649 -0
- grok_assistant-1.0/src/grok_assistant/ui/prints_ui.py +379 -0
- grok_assistant-1.0/src/grok_assistant/ui/round.py +244 -0
- grok_assistant-1.0/src/grok_assistant/ui/theme.py +164 -0
- grok_assistant-1.0/src/grok_assistant/ui/themes/aurora.json +30 -0
- grok_assistant-1.0/src/grok_assistant/ui/themes/cobre.json +30 -0
- grok_assistant-1.0/src/grok_assistant/ui/themes/dia.json +30 -0
- grok_assistant-1.0/src/grok_assistant/ui/themes/lino.json +30 -0
- grok_assistant-1.0/src/grok_assistant/ui/themes/mar.json +30 -0
- grok_assistant-1.0/src/grok_assistant/ui/themes/noche.json +30 -0
- grok_assistant-1.0/src/grok_assistant/ui/themes/oliva.json +30 -0
- grok_assistant-1.0/src/grok_assistant/ui/win_tray.py +649 -0
- grok_assistant-1.0/src/grok_assistant/ui/window.py +538 -0
- grok_assistant-1.0/src/grok_assistant/ui/worker.py +253 -0
- grok_assistant-1.0/src/grok_assistant.egg-info/PKG-INFO +58 -0
- grok_assistant-1.0/src/grok_assistant.egg-info/SOURCES.txt +106 -0
- grok_assistant-1.0/src/grok_assistant.egg-info/dependency_links.txt +1 -0
- grok_assistant-1.0/src/grok_assistant.egg-info/entry_points.txt +2 -0
- grok_assistant-1.0/src/grok_assistant.egg-info/requires.txt +8 -0
- grok_assistant-1.0/src/grok_assistant.egg-info/top_level.txt +1 -0
- grok_assistant-1.0/tests/test_banter.py +86 -0
- grok_assistant-1.0/tests/test_cli.py +117 -0
- grok_assistant-1.0/tests/test_enroll.py +234 -0
- grok_assistant-1.0/tests/test_grok_setup.py +61 -0
- grok_assistant-1.0/tests/test_license.py +38 -0
- grok_assistant-1.0/tests/test_market.py +440 -0
- grok_assistant-1.0/tests/test_personality.py +177 -0
- grok_assistant-1.0/tests/test_rules.py +1109 -0
- grok_assistant-1.0/tests/test_theme.py +234 -0
- grok_assistant-1.0/tests/test_updates.py +119 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Licencia y límites — Grok Assistant
|
|
2
|
+
|
|
3
|
+
Versión candidata 1.0. Aplicación experimental.
|
|
4
|
+
|
|
5
|
+
Autor: Antonio Castellon, Castellon.CH.
|
|
6
|
+
|
|
7
|
+
## Qué es
|
|
8
|
+
|
|
9
|
+
Grok Assistant es un programa experimental para hablar con un asistente en este equipo. El audio se queda aquí. Cuando hace falta, el programa envía texto a Grok. Grok y sus herramientas son un servicio de un tercero, no de este programa.
|
|
10
|
+
|
|
11
|
+
## Responsabilidad
|
|
12
|
+
|
|
13
|
+
Antonio Castellon no se hace responsable de cualquier fallo de seguridad o brecha que pueda darse por el uso de Grok y de sus herramientas. Eso incluye el acceso a la cuenta, lo que Grok busque o escriba, un archivo que cambie cuando esa opción esté permitida, y cualquier daño o pérdida que venga de ese uso.
|
|
14
|
+
|
|
15
|
+
El programa se ofrece tal cual, sin garantía. Quien lo ejecuta lo hace por su cuenta.
|
|
16
|
+
|
|
17
|
+
Si un administrador permite que Grok cambie archivos, esa elección se hace en este equipo. La shell sigue apagada. Aun así, el autor no responde de lo que Grok haga con las herramientas que se hayan dejado activas.
|
|
18
|
+
|
|
19
|
+
## Texto que manda
|
|
20
|
+
|
|
21
|
+
Este archivo está en español. La traducción al inglés es una ayuda para leerlo. Si los dos textos no dicen lo mismo, manda el español.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# License and limits — Grok Assistant
|
|
26
|
+
|
|
27
|
+
Release candidate 1.0. Experimental application.
|
|
28
|
+
|
|
29
|
+
Author: Antonio Castellon, Castellon.CH.
|
|
30
|
+
|
|
31
|
+
Grok Assistant is an experimental program for talking with an assistant on this computer. The audio stays here. When needed, the program sends text to Grok. Grok and its tools are a third-party service, not part of this program.
|
|
32
|
+
|
|
33
|
+
Antonio Castellon is not responsible for any security failure or breach that may come from using Grok and its tools. That includes access to the account, what Grok looks up or writes, a file it changes when that option is allowed, and any damage or loss that comes from that use.
|
|
34
|
+
|
|
35
|
+
The program is offered as it is, without warranty. Anyone who runs it does so on their own account.
|
|
36
|
+
|
|
37
|
+
If an administrator allows Grok to change files, that choice is made on this computer. The shell stays off. The author is still not answerable for what Grok does with the tools that were left on.
|
|
38
|
+
|
|
39
|
+
If the Spanish text and this English text differ, the Spanish text prevails.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: grok_assistant
|
|
3
|
+
Version: 1.0
|
|
4
|
+
Summary: A PC voice assistant that listens at home and only calls Grok on purpose.
|
|
5
|
+
Author: Antonio Castellon
|
|
6
|
+
Project-URL: Homepage, https://github.com/antonio-castellon/Grok_Assistant
|
|
7
|
+
Project-URL: Repository, https://github.com/antonio-castellon/Grok_Assistant
|
|
8
|
+
Requires-Python: >=3.11
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE.md
|
|
11
|
+
Requires-Dist: pystray>=0.19
|
|
12
|
+
Requires-Dist: Pillow>=10
|
|
13
|
+
Requires-Dist: sherpa-onnx==1.13.8
|
|
14
|
+
Requires-Dist: sounddevice==0.5.6
|
|
15
|
+
Requires-Dist: numpy<2.5,>=2.2
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
[Leer en español](README.ES.md) · [Lire en français](README.FR.md) · [Auf Deutsch lesen](README.DE.md)
|
|
21
|
+
|
|
22
|
+

|
|
23
|
+
|
|
24
|
+
# Grok Assistant
|
|
25
|
+
|
|
26
|
+
Release candidate 1.0.
|
|
27
|
+
|
|
28
|
+
This is a voice companion for the moments when reading is difficult and the house is quiet. The laptop version is for an older person who already has a small computer nearby. The Raspberry Pi 4 version, with 4 GB of RAM, is the same assistant as a dedicated device, for a family that would rather not leave a laptop open. I plan to publish that code soon. The microphone can stay ready. The audio stays on the computer. Only text that was meant for the assistant is sent out.
|
|
29
|
+
|
|
30
|
+
I spent years waiting for the Amazon Echo to become a better listener. It never really became more than a speaker with a light ring, so I decided to build my own.
|
|
31
|
+
|
|
32
|
+
In my case, that person is my father. His eyesight is limited, and he spends many hours on his own. I wanted him to have a voice he could simply talk to — one that could answer questions and explain things without making him find a screen or read small text. That is now possible. Grok on this machine is also the foundation for the features and integrations I plan to add next.
|
|
33
|
+
|
|
34
|
+
While the program is running, it can keep listening. Speech is turned into text on this computer. Grok only receives text when the assistant has actually been addressed: after a greeting, for a question, for a command beginning with `comando`, or when someone asks for a song. Ordinary conversation stays in the local session. If a command is unclear or misheard, Grok can help interpret it, but the assistant still asks for a `sí` before doing anything.
|
|
35
|
+
|
|
36
|
+
That is the basic idea. Below are the listening window and the icon menu, with Escucha open. The pictures are the same in every language.
|
|
37
|
+
|
|
38
|
+

|
|
39
|
+
|
|
40
|
+

|
|
41
|
+
|
|
42
|
+
The diagram below shows what happens to a spoken phrase from the moment it is heard.
|
|
43
|
+
|
|
44
|
+

|
|
45
|
+
|
|
46
|
+
In Settings, Grok only searches the internet. An administrator can allow Grok to change files on this machine. The shell stays off, and a relative path lands in the assistant data folder.
|
|
47
|
+
|
|
48
|
+
## Read on
|
|
49
|
+
|
|
50
|
+
- [How to use it](docs/en/guide.md)
|
|
51
|
+
- [Look](docs/en/look.md)
|
|
52
|
+
- [Voice prints and listeners](docs/en/prints.md)
|
|
53
|
+
- [Sessions and agents](docs/en/sessions.md)
|
|
54
|
+
- [Why Grok, and not another chat window](docs/en/why-grok.md)
|
|
55
|
+
- [What is actually running](docs/en/pieces.md)
|
|
56
|
+
- [What you can say](docs/en/saying.md)
|
|
57
|
+
- [Run the executable](docs/en/run.md)
|
|
58
|
+
- [Run it from the source tree](docs/en/source.md)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[Leer en español](README.ES.md) · [Lire en français](README.FR.md) · [Auf Deutsch lesen](README.DE.md)
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
# Grok Assistant
|
|
6
|
+
|
|
7
|
+
Release candidate 1.0.
|
|
8
|
+
|
|
9
|
+
This is a voice companion for the moments when reading is difficult and the house is quiet. The laptop version is for an older person who already has a small computer nearby. The Raspberry Pi 4 version, with 4 GB of RAM, is the same assistant as a dedicated device, for a family that would rather not leave a laptop open. I plan to publish that code soon. The microphone can stay ready. The audio stays on the computer. Only text that was meant for the assistant is sent out.
|
|
10
|
+
|
|
11
|
+
I spent years waiting for the Amazon Echo to become a better listener. It never really became more than a speaker with a light ring, so I decided to build my own.
|
|
12
|
+
|
|
13
|
+
In my case, that person is my father. His eyesight is limited, and he spends many hours on his own. I wanted him to have a voice he could simply talk to — one that could answer questions and explain things without making him find a screen or read small text. That is now possible. Grok on this machine is also the foundation for the features and integrations I plan to add next.
|
|
14
|
+
|
|
15
|
+
While the program is running, it can keep listening. Speech is turned into text on this computer. Grok only receives text when the assistant has actually been addressed: after a greeting, for a question, for a command beginning with `comando`, or when someone asks for a song. Ordinary conversation stays in the local session. If a command is unclear or misheard, Grok can help interpret it, but the assistant still asks for a `sí` before doing anything.
|
|
16
|
+
|
|
17
|
+
That is the basic idea. Below are the listening window and the icon menu, with Escucha open. The pictures are the same in every language.
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+
|
|
21
|
+

|
|
22
|
+
|
|
23
|
+
The diagram below shows what happens to a spoken phrase from the moment it is heard.
|
|
24
|
+
|
|
25
|
+

|
|
26
|
+
|
|
27
|
+
In Settings, Grok only searches the internet. An administrator can allow Grok to change files on this machine. The shell stays off, and a relative path lands in the assistant data folder.
|
|
28
|
+
|
|
29
|
+
## Read on
|
|
30
|
+
|
|
31
|
+
- [How to use it](docs/en/guide.md)
|
|
32
|
+
- [Look](docs/en/look.md)
|
|
33
|
+
- [Voice prints and listeners](docs/en/prints.md)
|
|
34
|
+
- [Sessions and agents](docs/en/sessions.md)
|
|
35
|
+
- [Why Grok, and not another chat window](docs/en/why-grok.md)
|
|
36
|
+
- [What is actually running](docs/en/pieces.md)
|
|
37
|
+
- [What you can say](docs/en/saying.md)
|
|
38
|
+
- [Run the executable](docs/en/run.md)
|
|
39
|
+
- [Run it from the source tree](docs/en/source.md)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "grok_assistant"
|
|
3
|
+
version = "1.0"
|
|
4
|
+
description = "A PC voice assistant that listens at home and only calls Grok on purpose."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
license-files = ["LICENSE.md"]
|
|
8
|
+
authors = [{name = "Antonio Castellon"}]
|
|
9
|
+
dependencies = [
|
|
10
|
+
"pystray>=0.19",
|
|
11
|
+
"Pillow>=10",
|
|
12
|
+
"sherpa-onnx==1.13.8",
|
|
13
|
+
"sounddevice==0.5.6",
|
|
14
|
+
"numpy>=2.2,<2.5",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.optional-dependencies]
|
|
18
|
+
dev = ["pytest>=8"]
|
|
19
|
+
|
|
20
|
+
[project.urls]
|
|
21
|
+
Homepage = "https://github.com/antonio-castellon/Grok_Assistant"
|
|
22
|
+
Repository = "https://github.com/antonio-castellon/Grok_Assistant"
|
|
23
|
+
|
|
24
|
+
[project.scripts]
|
|
25
|
+
grok-assistant = "grok_assistant.__main__:main"
|
|
26
|
+
|
|
27
|
+
[build-system]
|
|
28
|
+
requires = ["setuptools>=68"]
|
|
29
|
+
build-backend = "setuptools.build_meta"
|
|
30
|
+
|
|
31
|
+
[tool.setuptools]
|
|
32
|
+
include-package-data = true
|
|
33
|
+
|
|
34
|
+
[tool.setuptools.packages.find]
|
|
35
|
+
where = ["src"]
|
|
36
|
+
|
|
37
|
+
[tool.setuptools.package-data]
|
|
38
|
+
grok_assistant = [
|
|
39
|
+
"lines/*",
|
|
40
|
+
"lang/*",
|
|
41
|
+
"ui/themes/*",
|
|
42
|
+
"build_stamp.txt",
|
|
43
|
+
"_bundle/scripts/*",
|
|
44
|
+
"_bundle/listeners/*",
|
|
45
|
+
"_bundle/docs/img/*",
|
|
46
|
+
"_bundle/LICENSE.md",
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
[tool.pytest.ini_options]
|
|
50
|
+
testpaths = ["tests"]
|
|
51
|
+
pythonpath = ["src"]
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Stage the files a pip install needs, then let pyproject.toml describe the package."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
import shutil
|
|
6
|
+
|
|
7
|
+
from setuptools import setup
|
|
8
|
+
|
|
9
|
+
ROOT = Path(__file__).resolve().parent
|
|
10
|
+
sys.path.insert(0, str(ROOT / "src"))
|
|
11
|
+
PACKAGE = ROOT / "src" / "grok_assistant"
|
|
12
|
+
BUNDLE = PACKAGE / "_bundle"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def stage() -> None:
|
|
16
|
+
"""Copy repo files into the package. A wheel built from the sdist already has them."""
|
|
17
|
+
from grok_assistant.buildinfo import write_stamp
|
|
18
|
+
|
|
19
|
+
speak = ROOT / "scripts" / "speak.ps1"
|
|
20
|
+
if not speak.is_file():
|
|
21
|
+
if not (BUNDLE / "scripts" / "speak.ps1").is_file():
|
|
22
|
+
raise SystemExit("scripts/speak.ps1 is missing")
|
|
23
|
+
return
|
|
24
|
+
if BUNDLE.exists():
|
|
25
|
+
shutil.rmtree(BUNDLE)
|
|
26
|
+
shutil.copytree(ROOT / "scripts", BUNDLE / "scripts")
|
|
27
|
+
shutil.copytree(ROOT / "listeners", BUNDLE / "listeners")
|
|
28
|
+
images = BUNDLE / "docs" / "img"
|
|
29
|
+
images.mkdir(parents=True)
|
|
30
|
+
for name in ("grok.ico", "grok-mark.png"):
|
|
31
|
+
source = ROOT / "docs" / "img" / name
|
|
32
|
+
if source.is_file():
|
|
33
|
+
shutil.copy2(source, images / name)
|
|
34
|
+
shutil.copy2(ROOT / "LICENSE.md", BUNDLE / "LICENSE.md")
|
|
35
|
+
write_stamp(PACKAGE / "build_stamp.txt")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def packaged_files() -> list[str]:
|
|
39
|
+
names: list[str] = []
|
|
40
|
+
for folder in ("lines", "lang", "ui/themes", "_bundle"):
|
|
41
|
+
base = PACKAGE / folder
|
|
42
|
+
if not base.exists():
|
|
43
|
+
continue
|
|
44
|
+
for path in base.rglob("*"):
|
|
45
|
+
if path.is_file():
|
|
46
|
+
names.append(path.relative_to(PACKAGE).as_posix())
|
|
47
|
+
stamp = PACKAGE / "build_stamp.txt"
|
|
48
|
+
if stamp.is_file():
|
|
49
|
+
names.append("build_stamp.txt")
|
|
50
|
+
return names
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
stage()
|
|
54
|
+
setup(package_data={"grok_assistant": packaged_files()})
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""Grok Assistant, the PC body of the experiment."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import faulthandler
|
|
6
|
+
import sys
|
|
7
|
+
import tempfile
|
|
8
|
+
import traceback
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from grok_assistant.paths import bundle_root, default_data_dir, load_lines
|
|
12
|
+
|
|
13
|
+
_FAULT_LOG = None
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def main(argv: list[str] | None = None) -> None:
|
|
17
|
+
try:
|
|
18
|
+
folder = default_data_dir()
|
|
19
|
+
folder.mkdir(parents=True, exist_ok=True)
|
|
20
|
+
crash = folder / "crash.log"
|
|
21
|
+
# The file has to stay referenced or a later native fault has nowhere to land.
|
|
22
|
+
global _FAULT_LOG
|
|
23
|
+
_FAULT_LOG = crash.open("a", encoding="utf-8")
|
|
24
|
+
faulthandler.enable(_FAULT_LOG, all_threads=True)
|
|
25
|
+
except OSError:
|
|
26
|
+
pass
|
|
27
|
+
try:
|
|
28
|
+
_main(argv)
|
|
29
|
+
except Exception:
|
|
30
|
+
path = _crash_log()
|
|
31
|
+
_tell_crash(path)
|
|
32
|
+
raise SystemExit(1)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _main(argv: list[str] | None) -> None:
|
|
36
|
+
args = list(sys.argv[1:] if argv is None else argv)
|
|
37
|
+
if "--check" in args:
|
|
38
|
+
_write_check()
|
|
39
|
+
return
|
|
40
|
+
if "--console" in args:
|
|
41
|
+
from grok_assistant.console import run
|
|
42
|
+
run()
|
|
43
|
+
return
|
|
44
|
+
from grok_assistant.cloud.setup_grok import ensure_grok
|
|
45
|
+
from grok_assistant.ui.app import run
|
|
46
|
+
|
|
47
|
+
ensure_grok()
|
|
48
|
+
run()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _crash_log() -> Path:
|
|
52
|
+
folder = default_data_dir()
|
|
53
|
+
folder.mkdir(parents=True, exist_ok=True)
|
|
54
|
+
path = folder / "crash.log"
|
|
55
|
+
with path.open("a", encoding="utf-8") as handle:
|
|
56
|
+
handle.write(traceback.format_exc())
|
|
57
|
+
handle.write("\n")
|
|
58
|
+
return path
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _tell_crash(path: Path) -> None:
|
|
62
|
+
try:
|
|
63
|
+
import tkinter as tk
|
|
64
|
+
from tkinter import messagebox
|
|
65
|
+
|
|
66
|
+
root = tk.Tk()
|
|
67
|
+
root.withdraw()
|
|
68
|
+
messagebox.showerror("Grok Assistant", f"Se cerró por un error.\n{path}")
|
|
69
|
+
root.destroy()
|
|
70
|
+
except Exception:
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _importable(name: str) -> bool:
|
|
75
|
+
try:
|
|
76
|
+
__import__(name)
|
|
77
|
+
except Exception:
|
|
78
|
+
return False
|
|
79
|
+
return True
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _kroko_ready() -> bool:
|
|
83
|
+
from grok_assistant.listening.kroko_ear import kroko_dir
|
|
84
|
+
|
|
85
|
+
return kroko_dir() is not None
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _write_check() -> None:
|
|
89
|
+
from grok_assistant import __version__
|
|
90
|
+
from grok_assistant.cloud.grok_cli import GrokCLI
|
|
91
|
+
from grok_assistant.cloud.setup_grok import probe
|
|
92
|
+
|
|
93
|
+
status = probe()
|
|
94
|
+
lines = [
|
|
95
|
+
f"version={__version__}",
|
|
96
|
+
"channel=release-candidate",
|
|
97
|
+
f"hellos={len(load_lines('hellos-es.txt'))}",
|
|
98
|
+
f"scripts={(bundle_root() / 'scripts' / 'speak.ps1').exists()}",
|
|
99
|
+
f"listeners={(bundle_root() / 'listeners' / 'dictation.ps1').exists()}",
|
|
100
|
+
f"grok={GrokCLI.find() or ''}",
|
|
101
|
+
f"state={status.state}",
|
|
102
|
+
f"kroko_model={_kroko_ready()}",
|
|
103
|
+
f"sherpa={_importable('sherpa_onnx')}",
|
|
104
|
+
f"mic={_importable('sounddevice')}",
|
|
105
|
+
]
|
|
106
|
+
Path(tempfile.gettempdir(), "GrokAssistant-check.txt").write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
if __name__ == "__main__":
|
|
110
|
+
main()
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Licencia y límites — Grok Assistant
|
|
2
|
+
|
|
3
|
+
Versión candidata 1.0. Aplicación experimental.
|
|
4
|
+
|
|
5
|
+
Autor: Antonio Castellon, Castellon.CH.
|
|
6
|
+
|
|
7
|
+
## Qué es
|
|
8
|
+
|
|
9
|
+
Grok Assistant es un programa experimental para hablar con un asistente en este equipo. El audio se queda aquí. Cuando hace falta, el programa envía texto a Grok. Grok y sus herramientas son un servicio de un tercero, no de este programa.
|
|
10
|
+
|
|
11
|
+
## Responsabilidad
|
|
12
|
+
|
|
13
|
+
Antonio Castellon no se hace responsable de cualquier fallo de seguridad o brecha que pueda darse por el uso de Grok y de sus herramientas. Eso incluye el acceso a la cuenta, lo que Grok busque o escriba, un archivo que cambie cuando esa opción esté permitida, y cualquier daño o pérdida que venga de ese uso.
|
|
14
|
+
|
|
15
|
+
El programa se ofrece tal cual, sin garantía. Quien lo ejecuta lo hace por su cuenta.
|
|
16
|
+
|
|
17
|
+
Si un administrador permite que Grok cambie archivos, esa elección se hace en este equipo. La shell sigue apagada. Aun así, el autor no responde de lo que Grok haga con las herramientas que se hayan dejado activas.
|
|
18
|
+
|
|
19
|
+
## Texto que manda
|
|
20
|
+
|
|
21
|
+
Este archivo está en español. La traducción al inglés es una ayuda para leerlo. Si los dos textos no dicen lo mismo, manda el español.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# License and limits — Grok Assistant
|
|
26
|
+
|
|
27
|
+
Release candidate 1.0. Experimental application.
|
|
28
|
+
|
|
29
|
+
Author: Antonio Castellon, Castellon.CH.
|
|
30
|
+
|
|
31
|
+
Grok Assistant is an experimental program for talking with an assistant on this computer. The audio stays here. When needed, the program sends text to Grok. Grok and its tools are a third-party service, not part of this program.
|
|
32
|
+
|
|
33
|
+
Antonio Castellon is not responsible for any security failure or breach that may come from using Grok and its tools. That includes access to the account, what Grok looks up or writes, a file it changes when that option is allowed, and any damage or loss that comes from that use.
|
|
34
|
+
|
|
35
|
+
The program is offered as it is, without warranty. Anyone who runs it does so on their own account.
|
|
36
|
+
|
|
37
|
+
If an administrator allows Grok to change files, that choice is made on this computer. The shell stays off. The author is still not answerable for what Grok does with the tools that were left on.
|
|
38
|
+
|
|
39
|
+
If the Spanish text and this English text differ, the Spanish text prevails.
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[string]$Culture = "es-ES",
|
|
3
|
+
[string]$PauseFile = "",
|
|
4
|
+
[string]$ScoreDir = "",
|
|
5
|
+
[switch]$Probe
|
|
6
|
+
)
|
|
7
|
+
$ErrorActionPreference = "Stop"
|
|
8
|
+
Add-Type -AssemblyName System.Speech
|
|
9
|
+
# The string constructor wants a recognizer id such as MS-3082-80-DESK, not a culture name.
|
|
10
|
+
try {
|
|
11
|
+
$cultureInfo = [System.Globalization.CultureInfo]::new($Culture)
|
|
12
|
+
$engine = New-Object System.Speech.Recognition.SpeechRecognitionEngine($cultureInfo)
|
|
13
|
+
} catch {
|
|
14
|
+
$info = [System.Speech.Recognition.SpeechRecognitionEngine]::InstalledRecognizers() |
|
|
15
|
+
Where-Object { $_.Culture.Name -eq $Culture } |
|
|
16
|
+
Select-Object -First 1
|
|
17
|
+
if (-not $info) {
|
|
18
|
+
[Console]::Out.WriteLine("ERR:no-recognizer")
|
|
19
|
+
[Console]::Out.Flush()
|
|
20
|
+
exit 2
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
$engine = New-Object System.Speech.Recognition.SpeechRecognitionEngine($info)
|
|
24
|
+
} catch {
|
|
25
|
+
[Console]::Out.WriteLine("ERR:no-recognizer")
|
|
26
|
+
[Console]::Out.Flush()
|
|
27
|
+
exit 2
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if ($Probe) {
|
|
31
|
+
[Console]::Out.WriteLine("READY")
|
|
32
|
+
[Console]::Out.Flush()
|
|
33
|
+
exit 0
|
|
34
|
+
}
|
|
35
|
+
if ($ScoreDir) {
|
|
36
|
+
try {
|
|
37
|
+
$engine.LoadGrammar((New-Object System.Speech.Recognition.DictationGrammar))
|
|
38
|
+
} catch {
|
|
39
|
+
[Console]::Out.WriteLine("ERR:no-recognizer")
|
|
40
|
+
[Console]::Out.Flush()
|
|
41
|
+
exit 2
|
|
42
|
+
}
|
|
43
|
+
Get-ChildItem -LiteralPath $ScoreDir -Filter *.wav | Sort-Object Name | ForEach-Object {
|
|
44
|
+
$text = ""
|
|
45
|
+
try {
|
|
46
|
+
$engine.SetInputToWaveFile($_.FullName)
|
|
47
|
+
$result = $engine.Recognize()
|
|
48
|
+
if ($result -and $result.Text) { $text = $result.Text }
|
|
49
|
+
} catch {
|
|
50
|
+
$text = ""
|
|
51
|
+
}
|
|
52
|
+
[Console]::Out.WriteLine("LINE:" + $_.Name + "|" + $text)
|
|
53
|
+
[Console]::Out.Flush()
|
|
54
|
+
}
|
|
55
|
+
exit 0
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
$engine.LoadGrammar((New-Object System.Speech.Recognition.DictationGrammar))
|
|
59
|
+
$engine.SetInputToDefaultAudioDevice()
|
|
60
|
+
} catch {
|
|
61
|
+
[Console]::Out.WriteLine("ERR:no-microphone")
|
|
62
|
+
[Console]::Out.Flush()
|
|
63
|
+
exit 3
|
|
64
|
+
}
|
|
65
|
+
$engine.EndSilenceTimeout = [TimeSpan]::FromMilliseconds(700)
|
|
66
|
+
$engine.EndSilenceTimeoutAmbiguous = [TimeSpan]::FromMilliseconds(700)
|
|
67
|
+
$engine.InitialSilenceTimeout = [TimeSpan]::FromSeconds(2)
|
|
68
|
+
[Console]::Out.WriteLine("READY")
|
|
69
|
+
[Console]::Out.Flush()
|
|
70
|
+
while ($true) {
|
|
71
|
+
if ($PauseFile -and (Test-Path -LiteralPath $PauseFile)) {
|
|
72
|
+
Start-Sleep -Milliseconds 200
|
|
73
|
+
continue
|
|
74
|
+
}
|
|
75
|
+
$result = $engine.Recognize()
|
|
76
|
+
if ($result -and $result.Text) {
|
|
77
|
+
[Console]::Out.WriteLine("LINE:" + $result.Text)
|
|
78
|
+
[Console]::Out.Flush()
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[Parameter(Mandatory = $true)]
|
|
3
|
+
[string]$Log
|
|
4
|
+
)
|
|
5
|
+
$ErrorActionPreference = "Stop"
|
|
6
|
+
try {
|
|
7
|
+
$name = "Language.Speech~~~es-ES~0.0.1.0"
|
|
8
|
+
$cap = Get-WindowsCapability -Online | Where-Object { $_.Name -eq $name } | Select-Object -First 1
|
|
9
|
+
if (-not $cap) {
|
|
10
|
+
throw "Windows no ofrece el idioma de voz es-ES en este equipo."
|
|
11
|
+
}
|
|
12
|
+
if ($cap.State -ne "Installed") {
|
|
13
|
+
Add-WindowsCapability -Online -Name $name | Out-Null
|
|
14
|
+
}
|
|
15
|
+
Set-Content -LiteralPath $Log -Value "OK" -Encoding utf8
|
|
16
|
+
} catch {
|
|
17
|
+
Set-Content -LiteralPath $Log -Value ("ERR:" + $_.Exception.Message) -Encoding utf8
|
|
18
|
+
exit 1
|
|
19
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Build the single GrokAssistant.exe. This is for making the program, not for installing it.
|
|
2
|
+
$ErrorActionPreference = "Stop"
|
|
3
|
+
$root = Resolve-Path (Join-Path $PSScriptRoot "..")
|
|
4
|
+
Set-Location $root
|
|
5
|
+
$python = $null
|
|
6
|
+
foreach ($candidate in @("python", "py")) {
|
|
7
|
+
if (Get-Command $candidate -ErrorAction SilentlyContinue) {
|
|
8
|
+
if ($candidate -eq "py") {
|
|
9
|
+
$python = & py -3 -c "import sys; print(sys.executable)"
|
|
10
|
+
} else {
|
|
11
|
+
$python = & python -c "import sys; print(sys.executable)"
|
|
12
|
+
}
|
|
13
|
+
break
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
if (-not $python) { throw "Python 3.11 or newer is required to build the executable." }
|
|
17
|
+
& $python -m pip install --upgrade pip pyinstaller pystray Pillow "sherpa-onnx==1.13.8" "sounddevice==0.5.6" "numpy>=2.2,<2.5"
|
|
18
|
+
& $python -m PyInstaller --noconfirm --clean (Join-Path $root "GrokAssistant.spec")
|
|
19
|
+
$exe = Join-Path $root "dist\GrokAssistant.exe"
|
|
20
|
+
if (-not (Test-Path $exe)) { throw "The executable was not created." }
|
|
21
|
+
Write-Host "Built $exe"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[Parameter(Mandatory = $true)][string]$Path,
|
|
3
|
+
[int]$Volume = 70,
|
|
4
|
+
[string]$Voice = ""
|
|
5
|
+
)
|
|
6
|
+
$ErrorActionPreference = "Stop"
|
|
7
|
+
Add-Type -AssemblyName System.Speech
|
|
8
|
+
$synth = New-Object System.Speech.Synthesis.SpeechSynthesizer
|
|
9
|
+
$synth.Volume = [Math]::Max(0, [Math]::Min(100, $Volume))
|
|
10
|
+
if ($Voice) {
|
|
11
|
+
try { $synth.SelectVoice($Voice) } catch { }
|
|
12
|
+
}
|
|
13
|
+
$text = Get-Content -LiteralPath $Path -Raw -Encoding UTF8
|
|
14
|
+
if ($text) { $synth.Speak($text) }
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""The build baked into this copy. About shows it. The updater compares it."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
from datetime import datetime, timezone
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _git(cwd: Path, *args: str) -> str:
|
|
12
|
+
try:
|
|
13
|
+
from grok_assistant.quiet import no_window
|
|
14
|
+
|
|
15
|
+
out = subprocess.check_output(
|
|
16
|
+
["git", *args],
|
|
17
|
+
cwd=cwd,
|
|
18
|
+
text=True,
|
|
19
|
+
encoding="utf-8",
|
|
20
|
+
errors="replace",
|
|
21
|
+
stderr=subprocess.DEVNULL,
|
|
22
|
+
timeout=5,
|
|
23
|
+
**no_window(),
|
|
24
|
+
)
|
|
25
|
+
except (OSError, subprocess.SubprocessError):
|
|
26
|
+
return ""
|
|
27
|
+
return out.strip()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _read_file(path: Path) -> tuple[str, str]:
|
|
31
|
+
try:
|
|
32
|
+
lines = [line.strip() for line in path.read_text(encoding="utf-8").splitlines() if line.strip()]
|
|
33
|
+
except OSError:
|
|
34
|
+
return "", ""
|
|
35
|
+
ident = lines[0] if lines else ""
|
|
36
|
+
when = lines[1] if len(lines) > 1 else ""
|
|
37
|
+
if not ident or ident == "unknown":
|
|
38
|
+
return "", when
|
|
39
|
+
return ident, when
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def read_stamp() -> tuple[str, str]:
|
|
43
|
+
"""Return the build id and the UTC time it was made. Empty strings when unknown."""
|
|
44
|
+
if getattr(sys, "frozen", False):
|
|
45
|
+
return _read_file(Path(getattr(sys, "_MEIPASS")) / "grok_assistant" / "build_stamp.txt")
|
|
46
|
+
here = Path(__file__).resolve()
|
|
47
|
+
checkout = here.parents[2]
|
|
48
|
+
if (checkout / "scripts" / "speak.ps1").is_file():
|
|
49
|
+
ident = _git(checkout, "rev-parse", "--short=12", "HEAD")
|
|
50
|
+
if ident:
|
|
51
|
+
return ident, _git(checkout, "log", "-1", "--format=%cI")
|
|
52
|
+
return _read_file(here.parent / "build_stamp.txt")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def build_line() -> str:
|
|
56
|
+
ident, _when = read_stamp()
|
|
57
|
+
if not ident:
|
|
58
|
+
return "build —"
|
|
59
|
+
return f"build {ident}"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def write_stamp(path: Path | None = None) -> tuple[str, str]:
|
|
63
|
+
"""Write the source commit and the current UTC time. The executable and the wheel both call this."""
|
|
64
|
+
dest = Path(path) if path else Path(__file__).resolve().parent / "build_stamp.txt"
|
|
65
|
+
checkout = Path(__file__).resolve().parents[2]
|
|
66
|
+
ident = _git(checkout, "rev-parse", "--short=12", "HEAD")
|
|
67
|
+
when = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
68
|
+
dest.write_text(f"{ident}\n{when}\n", encoding="utf-8")
|
|
69
|
+
return ident, when
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""The door to the Grok executable."""
|