openvoiceui 1.0.0
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.
- package/.env.example +104 -0
- package/Dockerfile +30 -0
- package/LICENSE +21 -0
- package/README.md +638 -0
- package/SETUP.md +360 -0
- package/app.py +232 -0
- package/auto-approve-devices.js +111 -0
- package/cli/index.js +372 -0
- package/config/__init__.py +4 -0
- package/config/default.yaml +43 -0
- package/config/flags.yaml +67 -0
- package/config/loader.py +203 -0
- package/config/providers.yaml +71 -0
- package/config/speech_normalization.yaml +182 -0
- package/config/theme.json +4 -0
- package/data/greetings.json +25 -0
- package/default-pages/ai-image-creator.html +915 -0
- package/default-pages/bulk-image-uploader.html +492 -0
- package/default-pages/desktop.html +2865 -0
- package/default-pages/file-explorer.html +854 -0
- package/default-pages/interactive-map.html +655 -0
- package/default-pages/style-guide.html +1005 -0
- package/default-pages/website-setup.html +1623 -0
- package/deploy/openclaw/Dockerfile +46 -0
- package/deploy/openvoiceui.service +30 -0
- package/deploy/setup-nginx.sh +50 -0
- package/deploy/setup-sudo.sh +306 -0
- package/deploy/skill-runner/Dockerfile +19 -0
- package/deploy/skill-runner/requirements.txt +14 -0
- package/deploy/skill-runner/server.py +269 -0
- package/deploy/supertonic/Dockerfile +22 -0
- package/deploy/supertonic/server.py +79 -0
- package/docker-compose.pinokio.yml +11 -0
- package/docker-compose.yml +59 -0
- package/greetings.json +25 -0
- package/index.html +65 -0
- package/inject-device-identity.js +142 -0
- package/package.json +82 -0
- package/profiles/default.json +114 -0
- package/profiles/manager.py +354 -0
- package/profiles/schema.json +337 -0
- package/prompts/voice-system-prompt.md +149 -0
- package/providers/__init__.py +39 -0
- package/providers/base.py +63 -0
- package/providers/llm/__init__.py +12 -0
- package/providers/llm/base.py +71 -0
- package/providers/llm/clawdbot_provider.py +112 -0
- package/providers/llm/zai_provider.py +115 -0
- package/providers/registry.py +320 -0
- package/providers/stt/__init__.py +12 -0
- package/providers/stt/base.py +58 -0
- package/providers/stt/webspeech_provider.py +49 -0
- package/providers/stt/whisper_provider.py +100 -0
- package/providers/tts/__init__.py +20 -0
- package/providers/tts/base.py +91 -0
- package/providers/tts/groq_provider.py +74 -0
- package/providers/tts/supertonic_provider.py +72 -0
- package/requirements.txt +38 -0
- package/routes/__init__.py +10 -0
- package/routes/admin.py +515 -0
- package/routes/canvas.py +1315 -0
- package/routes/chat.py +51 -0
- package/routes/conversation.py +2158 -0
- package/routes/elevenlabs_hybrid.py +306 -0
- package/routes/greetings.py +98 -0
- package/routes/icons.py +279 -0
- package/routes/image_gen.py +364 -0
- package/routes/instructions.py +190 -0
- package/routes/music.py +838 -0
- package/routes/onboarding.py +43 -0
- package/routes/pi.py +62 -0
- package/routes/profiles.py +215 -0
- package/routes/report_issue.py +68 -0
- package/routes/static_files.py +533 -0
- package/routes/suno.py +664 -0
- package/routes/theme.py +81 -0
- package/routes/transcripts.py +199 -0
- package/routes/vision.py +348 -0
- package/routes/workspace.py +288 -0
- package/server.py +1510 -0
- package/services/__init__.py +1 -0
- package/services/auth.py +143 -0
- package/services/canvas_versioning.py +239 -0
- package/services/db_pool.py +107 -0
- package/services/gateway.py +16 -0
- package/services/gateway_manager.py +333 -0
- package/services/gateways/__init__.py +12 -0
- package/services/gateways/base.py +110 -0
- package/services/gateways/compat.py +264 -0
- package/services/gateways/openclaw.py +1134 -0
- package/services/health.py +100 -0
- package/services/memory_client.py +455 -0
- package/services/paths.py +26 -0
- package/services/speech_normalizer.py +285 -0
- package/services/tts.py +270 -0
- package/setup-config.js +262 -0
- package/sounds/air_horn.mp3 +0 -0
- package/sounds/bruh.mp3 +0 -0
- package/sounds/crowd_cheer.mp3 +0 -0
- package/sounds/gunshot.mp3 +0 -0
- package/sounds/impact.mp3 +0 -0
- package/sounds/lets_go.mp3 +0 -0
- package/sounds/record_stop.mp3 +0 -0
- package/sounds/rewind.mp3 +0 -0
- package/sounds/sad_trombone.mp3 +0 -0
- package/sounds/scratch_long.mp3 +0 -0
- package/sounds/yeah.mp3 +0 -0
- package/src/adapters/ClawdBotAdapter.js +264 -0
- package/src/adapters/_template.js +133 -0
- package/src/adapters/elevenlabs-classic.js +841 -0
- package/src/adapters/elevenlabs-hybrid.js +812 -0
- package/src/adapters/hume-evi.js +676 -0
- package/src/admin.html +1339 -0
- package/src/app.js +8802 -0
- package/src/core/Config.js +173 -0
- package/src/core/EmotionEngine.js +307 -0
- package/src/core/EventBridge.js +180 -0
- package/src/core/EventBus.js +117 -0
- package/src/core/VoiceSession.js +607 -0
- package/src/face/BaseFace.js +259 -0
- package/src/face/EyeFace.js +208 -0
- package/src/face/HaloSmokeFace.js +509 -0
- package/src/face/manifest.json +27 -0
- package/src/face/previews/eyes.svg +16 -0
- package/src/face/previews/orb.svg +29 -0
- package/src/features/MusicPlayer.js +620 -0
- package/src/features/Soundboard.js +128 -0
- package/src/providers/DeepgramSTT.js +472 -0
- package/src/providers/DeepgramStreamingSTT.js +766 -0
- package/src/providers/GroqSTT.js +559 -0
- package/src/providers/TTSPlayer.js +323 -0
- package/src/providers/WebSpeechSTT.js +479 -0
- package/src/providers/tts/BaseTTSProvider.js +81 -0
- package/src/providers/tts/HumeProvider.js +77 -0
- package/src/providers/tts/SupertonicProvider.js +174 -0
- package/src/providers/tts/index.js +140 -0
- package/src/shell/adapter-registry.js +154 -0
- package/src/shell/caller-bridge.js +35 -0
- package/src/shell/camera-bridge.js +28 -0
- package/src/shell/canvas-bridge.js +32 -0
- package/src/shell/commercial-bridge.js +44 -0
- package/src/shell/face-bridge.js +44 -0
- package/src/shell/music-bridge.js +60 -0
- package/src/shell/orchestrator.js +233 -0
- package/src/shell/profile-discovery.js +303 -0
- package/src/shell/sounds-bridge.js +28 -0
- package/src/shell/transcript-bridge.js +61 -0
- package/src/shell/waveform-bridge.js +33 -0
- package/src/styles/base.css +2862 -0
- package/src/styles/face.css +417 -0
- package/src/styles/pi-overrides.css +89 -0
- package/src/styles/theme-dark.css +67 -0
- package/src/test-tts.html +175 -0
- package/src/ui/AppShell.js +544 -0
- package/src/ui/ProfileSwitcher.js +228 -0
- package/src/ui/SessionControl.js +240 -0
- package/src/ui/face/FacePicker.js +195 -0
- package/src/ui/face/FaceRenderer.js +309 -0
- package/src/ui/settings/PlaylistEditor.js +366 -0
- package/src/ui/settings/SettingsPanel.css +684 -0
- package/src/ui/settings/SettingsPanel.js +419 -0
- package/src/ui/settings/TTSVoicePreview.js +210 -0
- package/src/ui/themes/ThemeManager.js +213 -0
- package/src/ui/visualizers/BaseVisualizer.js +29 -0
- package/src/ui/visualizers/PartyFXVisualizer.css +291 -0
- package/src/ui/visualizers/PartyFXVisualizer.js +637 -0
- package/static/emulators/jsdos/js-dos.css +1 -0
- package/static/emulators/jsdos/js-dos.js +22 -0
- package/static/favicon.svg +55 -0
- package/static/icons/apple-touch-icon.png +0 -0
- package/static/icons/favicon-32.png +0 -0
- package/static/icons/icon-192.png +0 -0
- package/static/icons/icon-512.png +0 -0
- package/static/install.html +449 -0
- package/static/manifest.json +26 -0
- package/static/sw.js +21 -0
- package/tts_providers/__init__.py +136 -0
- package/tts_providers/base_provider.py +319 -0
- package/tts_providers/groq_provider.py +155 -0
- package/tts_providers/hume_provider.py +226 -0
- package/tts_providers/providers_config.json +119 -0
- package/tts_providers/qwen3_provider.py +371 -0
- package/tts_providers/resemble_provider.py +315 -0
- package/tts_providers/supertonic_provider.py +557 -0
- package/tts_providers/supertonic_tts.py +399 -0
package/config/loader.py
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Config loader: reads config/default.yaml with environment variable overrides.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
from config.loader import config
|
|
6
|
+
|
|
7
|
+
config.get('server.port') # -> 5001
|
|
8
|
+
config.get('gateway.url') # -> 'ws://127.0.0.1:18791'
|
|
9
|
+
config['tts.provider'] # dict-style access also works
|
|
10
|
+
config.flag('use_blueprints') # -> True/False (from flags.yaml)
|
|
11
|
+
|
|
12
|
+
Environment variable override rules:
|
|
13
|
+
- Direct named overrides (highest priority):
|
|
14
|
+
PORT -> server.port
|
|
15
|
+
CLAWDBOT_GATEWAY_URL -> gateway.url
|
|
16
|
+
CLAWDBOT_AUTH_TOKEN -> gateway.auth_token
|
|
17
|
+
GATEWAY_SESSION_KEY -> gateway.session_key
|
|
18
|
+
GEMINI_MODEL -> models.gemini
|
|
19
|
+
TTS_PROVIDER -> tts.provider
|
|
20
|
+
USE_GROQ_TTS -> tts.use_groq (true/false string)
|
|
21
|
+
MAX_HISTORY_MESSAGES -> conversation.max_history_messages
|
|
22
|
+
LOG_LEVEL -> logging.level
|
|
23
|
+
ENABLE_FTS -> features.enable_fts
|
|
24
|
+
ENABLE_BRIEFING -> features.enable_briefing
|
|
25
|
+
ENABLE_HISTORY_RELOAD-> features.enable_history_reload
|
|
26
|
+
- Generic double-underscore override:
|
|
27
|
+
SERVER__PORT=5002 -> server.port = 5002
|
|
28
|
+
|
|
29
|
+
Feature flag environment variable override:
|
|
30
|
+
- FEATURE_<FLAG_NAME_UPPER>=true/false
|
|
31
|
+
FEATURE_USE_BLUEPRINTS=true -> flags.use_blueprints = True
|
|
32
|
+
FEATURE_PERSISTENT_WEBSOCKET=true -> flags.use_persistent_websocket = True
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
import os
|
|
36
|
+
from pathlib import Path
|
|
37
|
+
from typing import Any, Optional
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
import yaml
|
|
41
|
+
_YAML_AVAILABLE = True
|
|
42
|
+
except ImportError:
|
|
43
|
+
_YAML_AVAILABLE = False
|
|
44
|
+
|
|
45
|
+
# Path to the default config file (same directory as this module)
|
|
46
|
+
_DEFAULT_YAML = Path(__file__).parent / "default.yaml"
|
|
47
|
+
|
|
48
|
+
# Path to the feature flags file
|
|
49
|
+
_FLAGS_YAML = Path(__file__).parent / "flags.yaml"
|
|
50
|
+
|
|
51
|
+
# Named env var → dotted config key mappings
|
|
52
|
+
_ENV_MAP = {
|
|
53
|
+
"PORT": ("server.port", int),
|
|
54
|
+
"CLAWDBOT_GATEWAY_URL": ("gateway.url", str),
|
|
55
|
+
"CLAWDBOT_AUTH_TOKEN": ("gateway.auth_token", str),
|
|
56
|
+
"GATEWAY_SESSION_KEY": ("gateway.session_key", str),
|
|
57
|
+
"GEMINI_MODEL": ("models.gemini", str),
|
|
58
|
+
"TTS_PROVIDER": ("tts.provider", str),
|
|
59
|
+
"USE_GROQ_TTS": ("tts.use_groq", "bool"),
|
|
60
|
+
"MAX_HISTORY_MESSAGES": ("conversation.max_history_messages", int),
|
|
61
|
+
"LOG_LEVEL": ("logging.level", str),
|
|
62
|
+
"ENABLE_FTS": ("features.enable_fts", "bool"),
|
|
63
|
+
"ENABLE_BRIEFING": ("features.enable_briefing", "bool"),
|
|
64
|
+
"ENABLE_HISTORY_RELOAD": ("features.enable_history_reload", "bool"),
|
|
65
|
+
"OPENCLAW_WORKSPACE": ("paths.openclaw_workspace", str),
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _cast(value: str, cast_type) -> Any:
|
|
70
|
+
"""Cast a string env var value to the target type."""
|
|
71
|
+
if cast_type == "bool":
|
|
72
|
+
return value.lower() in ("true", "1", "yes")
|
|
73
|
+
if cast_type == int:
|
|
74
|
+
try:
|
|
75
|
+
return int(value)
|
|
76
|
+
except ValueError:
|
|
77
|
+
raise ValueError(f"Cannot cast {value!r} to int")
|
|
78
|
+
if cast_type == float:
|
|
79
|
+
return float(value)
|
|
80
|
+
return value # str passthrough
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _deep_set(data: dict, dotted_key: str, value: Any) -> None:
|
|
84
|
+
"""Set a value in a nested dict using a dotted key path."""
|
|
85
|
+
parts = dotted_key.split(".")
|
|
86
|
+
node = data
|
|
87
|
+
for part in parts[:-1]:
|
|
88
|
+
node = node.setdefault(part, {})
|
|
89
|
+
node[parts[-1]] = value
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _deep_get(data: dict, dotted_key: str, default: Any = None) -> Any:
|
|
93
|
+
"""Get a value from a nested dict using a dotted key path."""
|
|
94
|
+
parts = dotted_key.split(".")
|
|
95
|
+
node = data
|
|
96
|
+
for part in parts:
|
|
97
|
+
if not isinstance(node, dict) or part not in node:
|
|
98
|
+
return default
|
|
99
|
+
node = node[part]
|
|
100
|
+
return node
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _load_yaml(path: Path) -> dict:
|
|
104
|
+
"""Load a YAML file, returning empty dict if missing or yaml unavailable."""
|
|
105
|
+
if not _YAML_AVAILABLE:
|
|
106
|
+
print("⚠ Warning: PyYAML not installed. Run: pip install pyyaml")
|
|
107
|
+
return {}
|
|
108
|
+
if not path.exists():
|
|
109
|
+
print(f"⚠ Warning: Config file not found: {path}")
|
|
110
|
+
return {}
|
|
111
|
+
with open(path) as f:
|
|
112
|
+
return yaml.safe_load(f) or {}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _apply_env_overrides(data: dict) -> None:
|
|
116
|
+
"""Apply named env var overrides to the config dict (in-place)."""
|
|
117
|
+
# Named mappings (highest precedence)
|
|
118
|
+
for env_key, (config_key, cast_type) in _ENV_MAP.items():
|
|
119
|
+
value = os.environ.get(env_key)
|
|
120
|
+
if value is not None:
|
|
121
|
+
try:
|
|
122
|
+
_deep_set(data, config_key, _cast(value, cast_type))
|
|
123
|
+
except (ValueError, TypeError) as e:
|
|
124
|
+
import logging
|
|
125
|
+
logging.getLogger(__name__).warning(
|
|
126
|
+
f"Ignoring env var {env_key}={value!r}: {e}"
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# Generic double-underscore overrides: SERVER__PORT=5002 → server.port
|
|
130
|
+
for env_key, value in os.environ.items():
|
|
131
|
+
if "__" in env_key:
|
|
132
|
+
parts = env_key.lower().split("__", 1)
|
|
133
|
+
if len(parts) == 2:
|
|
134
|
+
dotted = f"{parts[0]}.{parts[1]}"
|
|
135
|
+
# Only override if the key already exists in the loaded config
|
|
136
|
+
if _deep_get(data, dotted) is not None:
|
|
137
|
+
_deep_set(data, dotted, value)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _load_flags(path: Path) -> dict:
|
|
141
|
+
"""Load feature flags from flags.yaml with env var overrides.
|
|
142
|
+
|
|
143
|
+
Each flag can be overridden via FEATURE_<FLAG_NAME_UPPER>=true/false.
|
|
144
|
+
Example: FEATURE_USE_BLUEPRINTS=true overrides flags.use_blueprints.
|
|
145
|
+
"""
|
|
146
|
+
raw = _load_yaml(path)
|
|
147
|
+
flags: dict = raw.get("flags", {})
|
|
148
|
+
|
|
149
|
+
# Apply env overrides: FEATURE_USE_BLUEPRINTS=true -> use_blueprints=True
|
|
150
|
+
for key in list(flags.keys()):
|
|
151
|
+
env_key = f"FEATURE_{key.upper()}"
|
|
152
|
+
env_val = os.environ.get(env_key)
|
|
153
|
+
if env_val is not None:
|
|
154
|
+
flags[key] = env_val.lower() in ("true", "1", "yes")
|
|
155
|
+
|
|
156
|
+
return flags
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class Config:
|
|
160
|
+
"""Immutable config accessor loaded from YAML + env overrides."""
|
|
161
|
+
|
|
162
|
+
def __init__(self, yaml_path: Path = _DEFAULT_YAML, flags_path: Path = _FLAGS_YAML):
|
|
163
|
+
self._data = _load_yaml(yaml_path)
|
|
164
|
+
_apply_env_overrides(self._data)
|
|
165
|
+
self._flags = _load_flags(flags_path)
|
|
166
|
+
|
|
167
|
+
def get(self, key: str, default: Any = None) -> Any:
|
|
168
|
+
"""Get a config value by dotted key. Returns default if not found."""
|
|
169
|
+
return _deep_get(self._data, key, default)
|
|
170
|
+
|
|
171
|
+
def __getitem__(self, key: str) -> Any:
|
|
172
|
+
value = _deep_get(self._data, key)
|
|
173
|
+
if value is None:
|
|
174
|
+
raise KeyError(f"Config key not found: {key}")
|
|
175
|
+
return value
|
|
176
|
+
|
|
177
|
+
def __contains__(self, key: str) -> bool:
|
|
178
|
+
return _deep_get(self._data, key) is not None
|
|
179
|
+
|
|
180
|
+
def as_dict(self) -> dict:
|
|
181
|
+
"""Return a copy of the full config dict."""
|
|
182
|
+
import copy
|
|
183
|
+
return copy.deepcopy(self._data)
|
|
184
|
+
|
|
185
|
+
def flag(self, name: str, default: bool = False) -> bool:
|
|
186
|
+
"""Check if a feature flag is enabled.
|
|
187
|
+
|
|
188
|
+
Flags are loaded from config/flags.yaml with env var overrides.
|
|
189
|
+
Example: config.flag('use_blueprints') -> True/False
|
|
190
|
+
Env override: FEATURE_USE_BLUEPRINTS=true
|
|
191
|
+
"""
|
|
192
|
+
return self._flags.get(name, default)
|
|
193
|
+
|
|
194
|
+
def reload(self) -> None:
|
|
195
|
+
"""Reload config from YAML file and re-apply env overrides."""
|
|
196
|
+
self._data = _load_yaml(_DEFAULT_YAML)
|
|
197
|
+
_apply_env_overrides(self._data)
|
|
198
|
+
self._flags = _load_flags(_FLAGS_YAML)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
# Module-level singleton — import this everywhere:
|
|
202
|
+
# from config.loader import config
|
|
203
|
+
config = Config()
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Provider configuration — P5-T2: Provider registry + auto-discovery
|
|
2
|
+
# ADR-001: YAML format with env interpolation
|
|
3
|
+
# ADR-003: Abstract base class + registry pattern
|
|
4
|
+
#
|
|
5
|
+
# ${ENV_VAR} placeholders are resolved at runtime from environment variables.
|
|
6
|
+
# 'modules' lists Python module paths imported during autodiscover() so that
|
|
7
|
+
# each module's register() calls fire automatically.
|
|
8
|
+
|
|
9
|
+
llm:
|
|
10
|
+
default_provider: zai
|
|
11
|
+
fallback_provider: clawdbot
|
|
12
|
+
modules:
|
|
13
|
+
- providers.llm.zai_provider
|
|
14
|
+
- providers.llm.clawdbot_provider
|
|
15
|
+
|
|
16
|
+
providers:
|
|
17
|
+
zai:
|
|
18
|
+
name: "Z.AI GLM"
|
|
19
|
+
api_key: ${ZAI_API_KEY}
|
|
20
|
+
default_model: glm-4-7-flash
|
|
21
|
+
priority: 10
|
|
22
|
+
models:
|
|
23
|
+
- id: glm-4-7-flash
|
|
24
|
+
name: "GLM-4.7 Flash"
|
|
25
|
+
- id: glm-4-plus
|
|
26
|
+
name: "GLM-4 Plus"
|
|
27
|
+
|
|
28
|
+
clawdbot:
|
|
29
|
+
name: "Clawdbot Gateway"
|
|
30
|
+
gateway_url: ${CLAWDBOT_GATEWAY_URL}
|
|
31
|
+
auth_token: ${CLAWDBOT_AUTH_TOKEN}
|
|
32
|
+
default_agent: main
|
|
33
|
+
priority: 20
|
|
34
|
+
|
|
35
|
+
tts:
|
|
36
|
+
default_provider: supertonic
|
|
37
|
+
fallback_provider: groq
|
|
38
|
+
modules:
|
|
39
|
+
- providers.tts.supertonic_provider
|
|
40
|
+
- providers.tts.groq_provider
|
|
41
|
+
|
|
42
|
+
providers:
|
|
43
|
+
supertonic:
|
|
44
|
+
name: "Supertonic ONNX"
|
|
45
|
+
onnx_dir: "" # Set SUPERTONIC_ONNX_DIR env var or configure path here
|
|
46
|
+
default_voice: M1
|
|
47
|
+
priority: 10
|
|
48
|
+
|
|
49
|
+
groq:
|
|
50
|
+
name: "Groq Orpheus"
|
|
51
|
+
api_key: ${GROQ_API_KEY}
|
|
52
|
+
model: orpheus-english
|
|
53
|
+
voice: alloy
|
|
54
|
+
priority: 20
|
|
55
|
+
|
|
56
|
+
stt:
|
|
57
|
+
default_provider: webspeech
|
|
58
|
+
modules:
|
|
59
|
+
- providers.stt.webspeech_provider
|
|
60
|
+
- providers.stt.whisper_provider
|
|
61
|
+
|
|
62
|
+
providers:
|
|
63
|
+
webspeech:
|
|
64
|
+
name: "Web Speech API"
|
|
65
|
+
priority: 10
|
|
66
|
+
|
|
67
|
+
whisper:
|
|
68
|
+
name: "Whisper Local"
|
|
69
|
+
model: base
|
|
70
|
+
device: cpu
|
|
71
|
+
priority: 20
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# Speech Normalization Configuration
|
|
2
|
+
# Controls how text is cleaned and transformed before being sent to TTS providers.
|
|
3
|
+
#
|
|
4
|
+
# Rules are applied in order within each section.
|
|
5
|
+
# Per-profile overrides can extend or replace these global rules.
|
|
6
|
+
#
|
|
7
|
+
# ADR-001: YAML format with comments
|
|
8
|
+
# Referenced by: P5-T5 (Canvas Section 23 — Config-Driven Agent Packages)
|
|
9
|
+
|
|
10
|
+
# ── Global defaults ────────────────────────────────────────────────────────────
|
|
11
|
+
global:
|
|
12
|
+
# Strip markdown formatting that sounds awkward when spoken aloud
|
|
13
|
+
strip_markdown: true
|
|
14
|
+
|
|
15
|
+
# Remove URLs entirely (TTS reads them character-by-character otherwise)
|
|
16
|
+
strip_urls: true
|
|
17
|
+
|
|
18
|
+
# Remove emoji characters
|
|
19
|
+
strip_emoji: true
|
|
20
|
+
|
|
21
|
+
# Collapse multiple whitespace/newlines into a single space
|
|
22
|
+
collapse_whitespace: true
|
|
23
|
+
|
|
24
|
+
# Trim leading/trailing whitespace
|
|
25
|
+
trim: true
|
|
26
|
+
|
|
27
|
+
# Maximum text length sent to TTS (hard cap, prevents ONNX overflow)
|
|
28
|
+
max_length: 800
|
|
29
|
+
|
|
30
|
+
# ── Abbreviation expansion ─────────────────────────────────────────────────────
|
|
31
|
+
# Applied after markdown/URL stripping.
|
|
32
|
+
# Keys are exact strings (case-sensitive); values are their spoken form.
|
|
33
|
+
# Wrap with word boundaries in the normalizer so "API" doesn't match inside words.
|
|
34
|
+
abbreviations:
|
|
35
|
+
# Technical
|
|
36
|
+
"API": "A P I"
|
|
37
|
+
"APIs": "A P Is"
|
|
38
|
+
"CPU": "C P U"
|
|
39
|
+
"GPU": "G P U"
|
|
40
|
+
"RAM": "RAM"
|
|
41
|
+
"SSD": "S S D"
|
|
42
|
+
"VPS": "V P S"
|
|
43
|
+
"URL": "U R L"
|
|
44
|
+
"URLs": "U R Ls"
|
|
45
|
+
"HTML": "H T M L"
|
|
46
|
+
"CSS": "C S S"
|
|
47
|
+
"JS": "J S"
|
|
48
|
+
"JSON": "J SON"
|
|
49
|
+
"YAML": "YAML"
|
|
50
|
+
"CLI": "C L I"
|
|
51
|
+
"SSH": "S S H"
|
|
52
|
+
"TTS": "T T S"
|
|
53
|
+
"STT": "S T T"
|
|
54
|
+
"LLM": "L L M"
|
|
55
|
+
"LLMs": "L L Ms"
|
|
56
|
+
"AI": "A I"
|
|
57
|
+
"ML": "M L"
|
|
58
|
+
"UI": "U I"
|
|
59
|
+
"UX": "U X"
|
|
60
|
+
"DB": "database"
|
|
61
|
+
"SQL": "sequel"
|
|
62
|
+
"NoSQL": "no sequel"
|
|
63
|
+
"HTTP": "H T T P"
|
|
64
|
+
"HTTPS": "H T T P S"
|
|
65
|
+
"OS": "O S"
|
|
66
|
+
"FTS": "full-text search"
|
|
67
|
+
"ORM": "O R M"
|
|
68
|
+
"SDK": "S D K"
|
|
69
|
+
"CI": "C I"
|
|
70
|
+
"CD": "C D"
|
|
71
|
+
"PR": "pull request"
|
|
72
|
+
|
|
73
|
+
# Common text abbreviations
|
|
74
|
+
"etc.": "et cetera"
|
|
75
|
+
"e.g.": "for example"
|
|
76
|
+
"i.e.": "that is"
|
|
77
|
+
"vs.": "versus"
|
|
78
|
+
"vs": "versus"
|
|
79
|
+
"approx.": "approximately"
|
|
80
|
+
"mins": "minutes"
|
|
81
|
+
"hrs": "hours"
|
|
82
|
+
"sec": "second"
|
|
83
|
+
"secs": "seconds"
|
|
84
|
+
|
|
85
|
+
# ── Markdown patterns ─────────────────────────────────────────────────────────
|
|
86
|
+
# Regex patterns to strip or replace when strip_markdown is true.
|
|
87
|
+
# Each entry: {pattern: regex, replacement: string}
|
|
88
|
+
# Patterns are applied in order.
|
|
89
|
+
markdown_patterns:
|
|
90
|
+
# Code blocks (``` ... ```)
|
|
91
|
+
- pattern: "```[\\s\\S]*?```"
|
|
92
|
+
replacement: ""
|
|
93
|
+
|
|
94
|
+
# Inline code (`...`)
|
|
95
|
+
- pattern: "`[^`]+`"
|
|
96
|
+
replacement: ""
|
|
97
|
+
|
|
98
|
+
# Headers (## Header → Header)
|
|
99
|
+
- pattern: "^#{1,6}\\s+"
|
|
100
|
+
replacement: ""
|
|
101
|
+
flags: "multiline"
|
|
102
|
+
|
|
103
|
+
# Bold (**text** or __text__)
|
|
104
|
+
- pattern: "\\*\\*(.+?)\\*\\*"
|
|
105
|
+
replacement: "\\1"
|
|
106
|
+
|
|
107
|
+
- pattern: "__(.+?)__"
|
|
108
|
+
replacement: "\\1"
|
|
109
|
+
|
|
110
|
+
# Italic (*text* or _text_)
|
|
111
|
+
- pattern: "\\*(.+?)\\*"
|
|
112
|
+
replacement: "\\1"
|
|
113
|
+
|
|
114
|
+
- pattern: "_(.+?)_"
|
|
115
|
+
replacement: "\\1"
|
|
116
|
+
|
|
117
|
+
# Strikethrough (~~text~~)
|
|
118
|
+
- pattern: "~~(.+?)~~"
|
|
119
|
+
replacement: "\\1"
|
|
120
|
+
|
|
121
|
+
# Horizontal rules
|
|
122
|
+
- pattern: "^[-*_]{3,}$"
|
|
123
|
+
replacement: ""
|
|
124
|
+
flags: "multiline"
|
|
125
|
+
|
|
126
|
+
# Markdown links [text](url) → text
|
|
127
|
+
- pattern: "\\[([^\\]]+)\\]\\([^)]+\\)"
|
|
128
|
+
replacement: "\\1"
|
|
129
|
+
|
|
130
|
+
# Bare markdown images 
|
|
131
|
+
- pattern: "!\\[[^\\]]*\\]\\([^)]+\\)"
|
|
132
|
+
replacement: ""
|
|
133
|
+
|
|
134
|
+
# Blockquotes (> text)
|
|
135
|
+
- pattern: "^>\\s*"
|
|
136
|
+
replacement: ""
|
|
137
|
+
flags: "multiline"
|
|
138
|
+
|
|
139
|
+
# Bullet list markers (-, *, +)
|
|
140
|
+
- pattern: "^[\\-\\*\\+]\\s+"
|
|
141
|
+
replacement: ""
|
|
142
|
+
flags: "multiline"
|
|
143
|
+
|
|
144
|
+
# Numbered list markers (1. 2. etc.)
|
|
145
|
+
- pattern: "^\\d+\\.\\s+"
|
|
146
|
+
replacement: ""
|
|
147
|
+
flags: "multiline"
|
|
148
|
+
|
|
149
|
+
# ── URL pattern ───────────────────────────────────────────────────────────────
|
|
150
|
+
# Applied when strip_urls is true.
|
|
151
|
+
url_pattern: "https?://[^\\s]+"
|
|
152
|
+
|
|
153
|
+
# ── Emoji pattern ─────────────────────────────────────────────────────────────
|
|
154
|
+
# Unicode ranges for common emoji (applied when strip_emoji is true).
|
|
155
|
+
# Uses a simplified set; the normalizer will use a broader regex.
|
|
156
|
+
emoji_pattern: "[\\U0001F300-\\U0001FFFF\\u2600-\\u27BF]+"
|
|
157
|
+
|
|
158
|
+
# ── Profile overrides ─────────────────────────────────────────────────────────
|
|
159
|
+
# Per-profile settings that override the global defaults.
|
|
160
|
+
# Profile IDs must match profiles/*.json "id" fields.
|
|
161
|
+
profiles:
|
|
162
|
+
default:
|
|
163
|
+
strip_markdown: true
|
|
164
|
+
strip_urls: true
|
|
165
|
+
strip_emoji: true
|
|
166
|
+
max_length: 600
|
|
167
|
+
abbreviations: {}
|
|
168
|
+
|
|
169
|
+
developer:
|
|
170
|
+
strip_markdown: true
|
|
171
|
+
strip_urls: true
|
|
172
|
+
strip_emoji: true
|
|
173
|
+
max_length: 800
|
|
174
|
+
# Developer profile: spell out more technical terms
|
|
175
|
+
abbreviations:
|
|
176
|
+
"fn": "function"
|
|
177
|
+
"var": "variable"
|
|
178
|
+
"const": "constant"
|
|
179
|
+
"req": "request"
|
|
180
|
+
"res": "response"
|
|
181
|
+
"err": "error"
|
|
182
|
+
"cb": "callback"
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"updated_at": "",
|
|
4
|
+
"updated_by": "system",
|
|
5
|
+
"notes": "Default greeting library. Edit or extend via /api/greetings/add. Categories: generic (all users) and contextual (face-recognized users).",
|
|
6
|
+
"next_greeting": null,
|
|
7
|
+
"last_context": null,
|
|
8
|
+
"greetings": {
|
|
9
|
+
"generic": {
|
|
10
|
+
"standard": [
|
|
11
|
+
"Hello! How can I help you today?",
|
|
12
|
+
"Hey there! What can I do for you?",
|
|
13
|
+
"Hi! What's on your mind?",
|
|
14
|
+
"Good to see you. What do you need?",
|
|
15
|
+
"Hello! Ready when you are.",
|
|
16
|
+
"Hey! What can I help you with?",
|
|
17
|
+
"Hi there! Ask me anything.",
|
|
18
|
+
"What can I do for you today?",
|
|
19
|
+
"Hey, what's up? How can I help?",
|
|
20
|
+
"Hello! What would you like to talk about?"
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
"contextual": []
|
|
24
|
+
}
|
|
25
|
+
}
|