voice-mode 3.34.3__py3-none-any.whl → 4.0.1__py3-none-any.whl
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.
- voice_mode/__version__.py +1 -1
- voice_mode/cli.py +5 -0
- voice_mode/cli_commands/transcribe.py +141 -0
- voice_mode/config.py +139 -37
- voice_mode/frontend/.next/BUILD_ID +1 -0
- voice_mode/frontend/.next/app-build-manifest.json +28 -0
- voice_mode/frontend/.next/app-path-routes-manifest.json +1 -0
- voice_mode/frontend/.next/build-manifest.json +32 -0
- voice_mode/frontend/.next/export-marker.json +1 -0
- voice_mode/frontend/.next/images-manifest.json +1 -0
- voice_mode/frontend/.next/next-minimal-server.js.nft.json +1 -0
- voice_mode/frontend/.next/next-server.js.nft.json +1 -0
- voice_mode/frontend/.next/package.json +1 -0
- voice_mode/frontend/.next/prerender-manifest.json +1 -0
- voice_mode/frontend/.next/react-loadable-manifest.json +1 -0
- voice_mode/frontend/.next/required-server-files.json +1 -0
- voice_mode/frontend/.next/routes-manifest.json +1 -0
- voice_mode/frontend/.next/server/app/_not-found/page.js +1 -0
- voice_mode/frontend/.next/server/app/_not-found/page.js.nft.json +1 -0
- voice_mode/frontend/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
- voice_mode/frontend/.next/server/app/_not-found.html +1 -0
- voice_mode/frontend/.next/server/app/_not-found.meta +6 -0
- voice_mode/frontend/.next/server/app/_not-found.rsc +9 -0
- voice_mode/frontend/.next/server/app/api/connection-details/route.js +12 -0
- voice_mode/frontend/.next/server/app/api/connection-details/route.js.nft.json +1 -0
- voice_mode/frontend/.next/server/app/favicon.ico/route.js +12 -0
- voice_mode/frontend/.next/server/app/favicon.ico/route.js.nft.json +1 -0
- voice_mode/frontend/.next/server/app/favicon.ico.body +0 -0
- voice_mode/frontend/.next/server/app/favicon.ico.meta +1 -0
- voice_mode/frontend/.next/server/app/index.html +1 -0
- voice_mode/frontend/.next/server/app/index.meta +5 -0
- voice_mode/frontend/.next/server/app/index.rsc +7 -0
- voice_mode/frontend/.next/server/app/page.js +11 -0
- voice_mode/frontend/.next/server/app/page.js.nft.json +1 -0
- voice_mode/frontend/.next/server/app/page_client-reference-manifest.js +1 -0
- voice_mode/frontend/.next/server/app-paths-manifest.json +6 -0
- voice_mode/frontend/.next/server/chunks/463.js +1 -0
- voice_mode/frontend/.next/server/chunks/682.js +6 -0
- voice_mode/frontend/.next/server/chunks/948.js +2 -0
- voice_mode/frontend/.next/server/chunks/994.js +2 -0
- voice_mode/frontend/.next/server/chunks/font-manifest.json +1 -0
- voice_mode/frontend/.next/server/font-manifest.json +1 -0
- voice_mode/frontend/.next/server/functions-config-manifest.json +1 -0
- voice_mode/frontend/.next/server/interception-route-rewrite-manifest.js +1 -0
- voice_mode/frontend/.next/server/middleware-build-manifest.js +1 -0
- voice_mode/frontend/.next/server/middleware-manifest.json +6 -0
- voice_mode/frontend/.next/server/middleware-react-loadable-manifest.js +1 -0
- voice_mode/frontend/.next/server/next-font-manifest.js +1 -0
- voice_mode/frontend/.next/server/next-font-manifest.json +1 -0
- voice_mode/frontend/.next/server/pages/404.html +1 -0
- voice_mode/frontend/.next/server/pages/500.html +1 -0
- voice_mode/frontend/.next/server/pages/_app.js +1 -0
- voice_mode/frontend/.next/server/pages/_app.js.nft.json +1 -0
- voice_mode/frontend/.next/server/pages/_document.js +1 -0
- voice_mode/frontend/.next/server/pages/_document.js.nft.json +1 -0
- voice_mode/frontend/.next/server/pages/_error.js +1 -0
- voice_mode/frontend/.next/server/pages/_error.js.nft.json +1 -0
- voice_mode/frontend/.next/server/pages-manifest.json +1 -0
- voice_mode/frontend/.next/server/server-reference-manifest.js +1 -0
- voice_mode/frontend/.next/server/server-reference-manifest.json +1 -0
- voice_mode/frontend/.next/server/webpack-runtime.js +1 -0
- voice_mode/frontend/.next/standalone/.next/BUILD_ID +1 -0
- voice_mode/frontend/.next/standalone/.next/app-build-manifest.json +28 -0
- voice_mode/frontend/.next/standalone/.next/app-path-routes-manifest.json +1 -0
- voice_mode/frontend/.next/standalone/.next/build-manifest.json +32 -0
- voice_mode/frontend/.next/standalone/.next/package.json +1 -0
- voice_mode/frontend/.next/standalone/.next/prerender-manifest.json +1 -0
- voice_mode/frontend/.next/standalone/.next/react-loadable-manifest.json +1 -0
- voice_mode/frontend/.next/standalone/.next/required-server-files.json +1 -0
- voice_mode/frontend/.next/standalone/.next/routes-manifest.json +1 -0
- voice_mode/frontend/.next/standalone/.next/server/app/_not-found/page.js +1 -0
- voice_mode/frontend/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -0
- voice_mode/frontend/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
- voice_mode/frontend/.next/standalone/.next/server/app/_not-found.html +1 -0
- voice_mode/frontend/.next/standalone/.next/server/app/_not-found.meta +6 -0
- voice_mode/frontend/.next/standalone/.next/server/app/_not-found.rsc +9 -0
- voice_mode/frontend/.next/standalone/.next/server/app/api/connection-details/route.js +12 -0
- voice_mode/frontend/.next/standalone/.next/server/app/api/connection-details/route.js.nft.json +1 -0
- voice_mode/frontend/.next/standalone/.next/server/app/favicon.ico/route.js +12 -0
- voice_mode/frontend/.next/standalone/.next/server/app/favicon.ico/route.js.nft.json +1 -0
- voice_mode/frontend/.next/standalone/.next/server/app/favicon.ico.body +0 -0
- voice_mode/frontend/.next/standalone/.next/server/app/favicon.ico.meta +1 -0
- voice_mode/frontend/.next/standalone/.next/server/app/index.html +1 -0
- voice_mode/frontend/.next/standalone/.next/server/app/index.meta +5 -0
- voice_mode/frontend/.next/standalone/.next/server/app/index.rsc +7 -0
- voice_mode/frontend/.next/standalone/.next/server/app/page.js +11 -0
- voice_mode/frontend/.next/standalone/.next/server/app/page.js.nft.json +1 -0
- voice_mode/frontend/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -0
- voice_mode/frontend/.next/standalone/.next/server/app-paths-manifest.json +6 -0
- voice_mode/frontend/.next/standalone/.next/server/chunks/463.js +1 -0
- voice_mode/frontend/.next/standalone/.next/server/chunks/682.js +6 -0
- voice_mode/frontend/.next/standalone/.next/server/chunks/948.js +2 -0
- voice_mode/frontend/.next/standalone/.next/server/chunks/994.js +2 -0
- voice_mode/frontend/.next/standalone/.next/server/font-manifest.json +1 -0
- voice_mode/frontend/.next/standalone/.next/server/middleware-build-manifest.js +1 -0
- voice_mode/frontend/.next/standalone/.next/server/middleware-manifest.json +6 -0
- voice_mode/frontend/.next/standalone/.next/server/middleware-react-loadable-manifest.js +1 -0
- voice_mode/frontend/.next/standalone/.next/server/next-font-manifest.js +1 -0
- voice_mode/frontend/.next/standalone/.next/server/next-font-manifest.json +1 -0
- voice_mode/frontend/.next/standalone/.next/server/pages/404.html +1 -0
- voice_mode/frontend/.next/standalone/.next/server/pages/500.html +1 -0
- voice_mode/frontend/.next/standalone/.next/server/pages/_app.js +1 -0
- voice_mode/frontend/.next/standalone/.next/server/pages/_app.js.nft.json +1 -0
- voice_mode/frontend/.next/standalone/.next/server/pages/_document.js +1 -0
- voice_mode/frontend/.next/standalone/.next/server/pages/_document.js.nft.json +1 -0
- voice_mode/frontend/.next/standalone/.next/server/pages/_error.js +1 -0
- voice_mode/frontend/.next/standalone/.next/server/pages/_error.js.nft.json +1 -0
- voice_mode/frontend/.next/standalone/.next/server/pages-manifest.json +1 -0
- voice_mode/frontend/.next/standalone/.next/server/server-reference-manifest.js +1 -0
- voice_mode/frontend/.next/standalone/.next/server/server-reference-manifest.json +1 -0
- voice_mode/frontend/.next/standalone/.next/server/webpack-runtime.js +1 -0
- voice_mode/frontend/.next/standalone/package.json +40 -0
- voice_mode/frontend/.next/standalone/server.js +38 -0
- voice_mode/frontend/.next/static/c5TIe90lGzrESrqJkkXQa/_buildManifest.js +1 -0
- voice_mode/frontend/.next/static/c5TIe90lGzrESrqJkkXQa/_ssgManifest.js +1 -0
- voice_mode/frontend/.next/static/chunks/117-40bc79a2b97edb21.js +2 -0
- voice_mode/frontend/.next/static/chunks/144d3bae-2d5f122b82426d88.js +1 -0
- voice_mode/frontend/.next/static/chunks/471-bd4b96a33883dfa2.js +3 -0
- voice_mode/frontend/.next/static/chunks/app/_not-found/page-5011050e402ab9c8.js +1 -0
- voice_mode/frontend/.next/static/chunks/app/layout-0074dd8ab91cdbe0.js +1 -0
- voice_mode/frontend/.next/static/chunks/app/page-ae5f3aa9d9ba5993.js +1 -0
- voice_mode/frontend/.next/static/chunks/fd9d1056-af324d327b243cf1.js +1 -0
- voice_mode/frontend/.next/static/chunks/framework-f66176bb897dc684.js +1 -0
- voice_mode/frontend/.next/static/chunks/main-3163eca598b76a9f.js +1 -0
- voice_mode/frontend/.next/static/chunks/main-app-233f6c633f73ae84.js +1 -0
- voice_mode/frontend/.next/static/chunks/pages/_app-72b849fbd24ac258.js +1 -0
- voice_mode/frontend/.next/static/chunks/pages/_error-7ba65e1336b92748.js +1 -0
- voice_mode/frontend/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- voice_mode/frontend/.next/static/chunks/webpack-0ea9b80f19935b70.js +1 -0
- voice_mode/frontend/.next/static/css/a2f49a47752b5010.css +3 -0
- voice_mode/frontend/.next/static/media/01099be941da1820-s.woff2 +0 -0
- voice_mode/frontend/.next/static/media/39883d31a7792467-s.p.woff2 +0 -0
- voice_mode/frontend/.next/static/media/6368404d2e8d66fe-s.woff2 +0 -0
- voice_mode/frontend/.next/trace +43 -0
- voice_mode/frontend/.next/types/app/api/connection-details/route.ts +343 -0
- voice_mode/frontend/.next/types/app/layout.ts +79 -0
- voice_mode/frontend/.next/types/app/page.ts +79 -0
- voice_mode/frontend/.next/types/package.json +1 -0
- voice_mode/frontend/package-lock.json +154 -1
- voice_mode/providers.py +7 -8
- voice_mode/resources/configuration.py +2 -2
- voice_mode/tools/configuration_management.py +106 -5
- voice_mode/tools/converse.py +98 -0
- voice_mode/tools/transcription/__init__.py +14 -0
- voice_mode/tools/transcription/backends.py +287 -0
- voice_mode/tools/transcription/core.py +136 -0
- voice_mode/tools/transcription/formats.py +144 -0
- voice_mode/tools/transcription/types.py +52 -0
- {voice_mode-3.34.3.dist-info → voice_mode-4.0.1.dist-info}/METADATA +5 -2
- voice_mode-4.0.1.dist-info/RECORD +255 -0
- voice_mode/voice_preferences.py +0 -125
- voice_mode-3.34.3.dist-info/RECORD +0 -116
- {voice_mode-3.34.3.dist-info → voice_mode-4.0.1.dist-info}/WHEEL +0 -0
- {voice_mode-3.34.3.dist-info → voice_mode-4.0.1.dist-info}/entry_points.txt +0 -0
@@ -21,6 +21,7 @@
|
|
21
21
|
"@types/node": "^20.17.13",
|
22
22
|
"@types/react": "^18.3.18",
|
23
23
|
"@types/react-dom": "^18.3.5",
|
24
|
+
"autoprefixer": "^10.4.21",
|
24
25
|
"eslint": "^8.57.1",
|
25
26
|
"eslint-config-next": "14.2.29",
|
26
27
|
"eslint-config-prettier": "9.1.0",
|
@@ -1254,6 +1255,44 @@
|
|
1254
1255
|
"node": ">= 0.4"
|
1255
1256
|
}
|
1256
1257
|
},
|
1258
|
+
"node_modules/autoprefixer": {
|
1259
|
+
"version": "10.4.21",
|
1260
|
+
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz",
|
1261
|
+
"integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==",
|
1262
|
+
"dev": true,
|
1263
|
+
"funding": [
|
1264
|
+
{
|
1265
|
+
"type": "opencollective",
|
1266
|
+
"url": "https://opencollective.com/postcss/"
|
1267
|
+
},
|
1268
|
+
{
|
1269
|
+
"type": "tidelift",
|
1270
|
+
"url": "https://tidelift.com/funding/github/npm/autoprefixer"
|
1271
|
+
},
|
1272
|
+
{
|
1273
|
+
"type": "github",
|
1274
|
+
"url": "https://github.com/sponsors/ai"
|
1275
|
+
}
|
1276
|
+
],
|
1277
|
+
"license": "MIT",
|
1278
|
+
"dependencies": {
|
1279
|
+
"browserslist": "^4.24.4",
|
1280
|
+
"caniuse-lite": "^1.0.30001702",
|
1281
|
+
"fraction.js": "^4.3.7",
|
1282
|
+
"normalize-range": "^0.1.2",
|
1283
|
+
"picocolors": "^1.1.1",
|
1284
|
+
"postcss-value-parser": "^4.2.0"
|
1285
|
+
},
|
1286
|
+
"bin": {
|
1287
|
+
"autoprefixer": "bin/autoprefixer"
|
1288
|
+
},
|
1289
|
+
"engines": {
|
1290
|
+
"node": "^10 || ^12 || >=14"
|
1291
|
+
},
|
1292
|
+
"peerDependencies": {
|
1293
|
+
"postcss": "^8.1.0"
|
1294
|
+
}
|
1295
|
+
},
|
1257
1296
|
"node_modules/available-typed-arrays": {
|
1258
1297
|
"version": "1.0.7",
|
1259
1298
|
"dev": true,
|
@@ -1320,6 +1359,39 @@
|
|
1320
1359
|
"node": ">=8"
|
1321
1360
|
}
|
1322
1361
|
},
|
1362
|
+
"node_modules/browserslist": {
|
1363
|
+
"version": "4.25.4",
|
1364
|
+
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.4.tgz",
|
1365
|
+
"integrity": "sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg==",
|
1366
|
+
"dev": true,
|
1367
|
+
"funding": [
|
1368
|
+
{
|
1369
|
+
"type": "opencollective",
|
1370
|
+
"url": "https://opencollective.com/browserslist"
|
1371
|
+
},
|
1372
|
+
{
|
1373
|
+
"type": "tidelift",
|
1374
|
+
"url": "https://tidelift.com/funding/github/npm/browserslist"
|
1375
|
+
},
|
1376
|
+
{
|
1377
|
+
"type": "github",
|
1378
|
+
"url": "https://github.com/sponsors/ai"
|
1379
|
+
}
|
1380
|
+
],
|
1381
|
+
"license": "MIT",
|
1382
|
+
"dependencies": {
|
1383
|
+
"caniuse-lite": "^1.0.30001737",
|
1384
|
+
"electron-to-chromium": "^1.5.211",
|
1385
|
+
"node-releases": "^2.0.19",
|
1386
|
+
"update-browserslist-db": "^1.1.3"
|
1387
|
+
},
|
1388
|
+
"bin": {
|
1389
|
+
"browserslist": "cli.js"
|
1390
|
+
},
|
1391
|
+
"engines": {
|
1392
|
+
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
1393
|
+
}
|
1394
|
+
},
|
1323
1395
|
"node_modules/busboy": {
|
1324
1396
|
"version": "1.6.0",
|
1325
1397
|
"dev": true,
|
@@ -1417,7 +1489,9 @@
|
|
1417
1489
|
}
|
1418
1490
|
},
|
1419
1491
|
"node_modules/caniuse-lite": {
|
1420
|
-
"version": "1.0.
|
1492
|
+
"version": "1.0.30001739",
|
1493
|
+
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001739.tgz",
|
1494
|
+
"integrity": "sha512-y+j60d6ulelrNSwpPyrHdl+9mJnQzHBr08xm48Qno0nSk4h3Qojh+ziv2qE6rXf4k3tadF4o1J/1tAbVm1NtnA==",
|
1421
1495
|
"dev": true,
|
1422
1496
|
"funding": [
|
1423
1497
|
{
|
@@ -1699,6 +1773,13 @@
|
|
1699
1773
|
"dev": true,
|
1700
1774
|
"license": "MIT"
|
1701
1775
|
},
|
1776
|
+
"node_modules/electron-to-chromium": {
|
1777
|
+
"version": "1.5.211",
|
1778
|
+
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.211.tgz",
|
1779
|
+
"integrity": "sha512-IGBvimJkotaLzFnwIVgW9/UD/AOJ2tByUmeOrtqBfACSbAw5b1G0XpvdaieKyc7ULmbwXVx+4e4Be8pOPBrYkw==",
|
1780
|
+
"dev": true,
|
1781
|
+
"license": "ISC"
|
1782
|
+
},
|
1702
1783
|
"node_modules/emoji-regex": {
|
1703
1784
|
"version": "9.2.2",
|
1704
1785
|
"dev": true,
|
@@ -1865,6 +1946,16 @@
|
|
1865
1946
|
"url": "https://github.com/sponsors/ljharb"
|
1866
1947
|
}
|
1867
1948
|
},
|
1949
|
+
"node_modules/escalade": {
|
1950
|
+
"version": "3.2.0",
|
1951
|
+
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
1952
|
+
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
|
1953
|
+
"dev": true,
|
1954
|
+
"license": "MIT",
|
1955
|
+
"engines": {
|
1956
|
+
"node": ">=6"
|
1957
|
+
}
|
1958
|
+
},
|
1868
1959
|
"node_modules/escape-string-regexp": {
|
1869
1960
|
"version": "4.0.0",
|
1870
1961
|
"dev": true,
|
@@ -2462,6 +2553,20 @@
|
|
2462
2553
|
"url": "https://github.com/sponsors/isaacs"
|
2463
2554
|
}
|
2464
2555
|
},
|
2556
|
+
"node_modules/fraction.js": {
|
2557
|
+
"version": "4.3.7",
|
2558
|
+
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
|
2559
|
+
"integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
|
2560
|
+
"dev": true,
|
2561
|
+
"license": "MIT",
|
2562
|
+
"engines": {
|
2563
|
+
"node": "*"
|
2564
|
+
},
|
2565
|
+
"funding": {
|
2566
|
+
"type": "patreon",
|
2567
|
+
"url": "https://github.com/sponsors/rawify"
|
2568
|
+
}
|
2569
|
+
},
|
2465
2570
|
"node_modules/framer-motion": {
|
2466
2571
|
"version": "11.18.2",
|
2467
2572
|
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz",
|
@@ -3693,6 +3798,13 @@
|
|
3693
3798
|
"node": "^10 || ^12 || >=14"
|
3694
3799
|
}
|
3695
3800
|
},
|
3801
|
+
"node_modules/node-releases": {
|
3802
|
+
"version": "2.0.19",
|
3803
|
+
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
|
3804
|
+
"integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
|
3805
|
+
"dev": true,
|
3806
|
+
"license": "MIT"
|
3807
|
+
},
|
3696
3808
|
"node_modules/normalize-path": {
|
3697
3809
|
"version": "3.0.0",
|
3698
3810
|
"dev": true,
|
@@ -3701,6 +3813,16 @@
|
|
3701
3813
|
"node": ">=0.10.0"
|
3702
3814
|
}
|
3703
3815
|
},
|
3816
|
+
"node_modules/normalize-range": {
|
3817
|
+
"version": "0.1.2",
|
3818
|
+
"resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
|
3819
|
+
"integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
|
3820
|
+
"dev": true,
|
3821
|
+
"license": "MIT",
|
3822
|
+
"engines": {
|
3823
|
+
"node": ">=0.10.0"
|
3824
|
+
}
|
3825
|
+
},
|
3704
3826
|
"node_modules/object-assign": {
|
3705
3827
|
"version": "4.1.1",
|
3706
3828
|
"dev": true,
|
@@ -5218,6 +5340,37 @@
|
|
5218
5340
|
"@unrs/resolver-binding-win32-x64-msvc": "1.9.2"
|
5219
5341
|
}
|
5220
5342
|
},
|
5343
|
+
"node_modules/update-browserslist-db": {
|
5344
|
+
"version": "1.1.3",
|
5345
|
+
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
|
5346
|
+
"integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
|
5347
|
+
"dev": true,
|
5348
|
+
"funding": [
|
5349
|
+
{
|
5350
|
+
"type": "opencollective",
|
5351
|
+
"url": "https://opencollective.com/browserslist"
|
5352
|
+
},
|
5353
|
+
{
|
5354
|
+
"type": "tidelift",
|
5355
|
+
"url": "https://tidelift.com/funding/github/npm/browserslist"
|
5356
|
+
},
|
5357
|
+
{
|
5358
|
+
"type": "github",
|
5359
|
+
"url": "https://github.com/sponsors/ai"
|
5360
|
+
}
|
5361
|
+
],
|
5362
|
+
"license": "MIT",
|
5363
|
+
"dependencies": {
|
5364
|
+
"escalade": "^3.2.0",
|
5365
|
+
"picocolors": "^1.1.1"
|
5366
|
+
},
|
5367
|
+
"bin": {
|
5368
|
+
"update-browserslist-db": "cli.js"
|
5369
|
+
},
|
5370
|
+
"peerDependencies": {
|
5371
|
+
"browserslist": ">= 4.21.0"
|
5372
|
+
}
|
5373
|
+
},
|
5221
5374
|
"node_modules/uri-js": {
|
5222
5375
|
"version": "4.4.1",
|
5223
5376
|
"dev": true,
|
voice_mode/providers.py
CHANGED
@@ -9,9 +9,8 @@ import logging
|
|
9
9
|
from typing import Dict, Optional, List, Any, Tuple
|
10
10
|
from openai import AsyncOpenAI
|
11
11
|
|
12
|
-
from .config import TTS_VOICES, TTS_MODELS, TTS_BASE_URLS, OPENAI_API_KEY
|
12
|
+
from .config import TTS_VOICES, TTS_MODELS, TTS_BASE_URLS, OPENAI_API_KEY, get_voice_preferences
|
13
13
|
from .provider_discovery import provider_registry, EndpointInfo
|
14
|
-
from .voice_preferences import get_preferred_voices
|
15
14
|
|
16
15
|
logger = logging.getLogger("voice-mode")
|
17
16
|
|
@@ -68,14 +67,14 @@ async def get_tts_client_and_voice(
|
|
68
67
|
return client, selected_voice, selected_model, endpoint_info
|
69
68
|
|
70
69
|
# Voice-first selection algorithm
|
71
|
-
# Get user preferences
|
72
|
-
|
73
|
-
combined_voice_list =
|
70
|
+
# Get user preferences from configuration
|
71
|
+
voice_preferences = get_voice_preferences()
|
72
|
+
combined_voice_list = voice_preferences
|
74
73
|
|
75
74
|
logger.info(f"TTS Provider Selection (voice-first)")
|
76
|
-
if
|
77
|
-
logger.info(f"
|
78
|
-
logger.info(f"
|
75
|
+
if voice_preferences:
|
76
|
+
logger.info(f" Voice preferences: {voice_preferences}")
|
77
|
+
logger.info(f" Voice list: {combined_voice_list}")
|
79
78
|
logger.info(f" Preferred models: {TTS_MODELS}")
|
80
79
|
logger.info(f" Available endpoints: {TTS_BASE_URLS}")
|
81
80
|
|
@@ -267,7 +267,7 @@ async def environment_variables() -> str:
|
|
267
267
|
("VOICEMODE_AUTO_START_KOKORO", "Auto-start Kokoro service (true/false)"),
|
268
268
|
("VOICEMODE_TTS_BASE_URLS", "Comma-separated list of TTS endpoints"),
|
269
269
|
("VOICEMODE_STT_BASE_URLS", "Comma-separated list of STT endpoints"),
|
270
|
-
("
|
270
|
+
("VOICEMODE_VOICES", "Comma-separated list of preferred voices"),
|
271
271
|
("VOICEMODE_TTS_MODELS", "Comma-separated list of preferred models"),
|
272
272
|
# Audio Settings
|
273
273
|
("VOICEMODE_AUDIO_FORMAT", "Audio format for recording (pcm/mp3/wav/flac/aac/opus)"),
|
@@ -358,7 +358,7 @@ async def environment_template() -> str:
|
|
358
358
|
f"export VOICEMODE_AUTO_START_KOKORO=\"{str(AUTO_START_KOKORO).lower()}\"",
|
359
359
|
f"export VOICEMODE_TTS_BASE_URLS=\"{','.join(TTS_BASE_URLS)}\"",
|
360
360
|
f"export VOICEMODE_STT_BASE_URLS=\"{','.join(STT_BASE_URLS)}\"",
|
361
|
-
f"export
|
361
|
+
f"export VOICEMODE_VOICES=\"{','.join(TTS_VOICES)}\"",
|
362
362
|
f"export VOICEMODE_TTS_MODELS=\"{','.join(TTS_MODELS)}\"",
|
363
363
|
"",
|
364
364
|
"# Audio Settings",
|
@@ -5,7 +5,7 @@ import re
|
|
5
5
|
from pathlib import Path
|
6
6
|
from typing import Dict, Optional, List
|
7
7
|
from voice_mode.server import mcp
|
8
|
-
from voice_mode.config import BASE_DIR
|
8
|
+
from voice_mode.config import BASE_DIR, reload_configuration, find_voicemode_env_files
|
9
9
|
import logging
|
10
10
|
|
11
11
|
logger = logging.getLogger("voice-mode")
|
@@ -109,7 +109,7 @@ async def update_config(key: str, value: str) -> str:
|
|
109
109
|
"""Update a configuration value in the voicemode.env file.
|
110
110
|
|
111
111
|
Args:
|
112
|
-
key: The configuration key to update (e.g., '
|
112
|
+
key: The configuration key to update (e.g., 'VOICEMODE_VOICES')
|
113
113
|
value: The new value for the configuration
|
114
114
|
|
115
115
|
Returns:
|
@@ -175,7 +175,7 @@ async def list_config_keys() -> str:
|
|
175
175
|
("Provider Configuration", [
|
176
176
|
("VOICEMODE_TTS_BASE_URLS", "Comma-separated list of TTS endpoints"),
|
177
177
|
("VOICEMODE_STT_BASE_URLS", "Comma-separated list of STT endpoints"),
|
178
|
-
("
|
178
|
+
("VOICEMODE_VOICES", "Comma-separated list of preferred voices"),
|
179
179
|
("VOICEMODE_TTS_MODELS", "Comma-separated list of preferred models"),
|
180
180
|
("VOICEMODE_PREFER_LOCAL", "Prefer local providers over cloud (true/false)"),
|
181
181
|
("VOICEMODE_ALWAYS_TRY_LOCAL", "Always attempt local providers (true/false)"),
|
@@ -211,6 +211,107 @@ async def list_config_keys() -> str:
|
|
211
211
|
lines.append(f" {description}")
|
212
212
|
lines.append("")
|
213
213
|
|
214
|
-
lines.append("💡 Usage: update_config(key='
|
214
|
+
lines.append("💡 Usage: update_config(key='VOICEMODE_VOICES', value='af_sky,nova')")
|
215
215
|
|
216
|
-
return "\n".join(lines)
|
216
|
+
return "\n".join(lines)
|
217
|
+
|
218
|
+
|
219
|
+
@mcp.tool()
|
220
|
+
async def config_reload() -> str:
|
221
|
+
"""Reload configuration from .voicemode.env files and clear all caches.
|
222
|
+
|
223
|
+
This tool reloads configuration from:
|
224
|
+
1. Global ~/.voicemode/voicemode.env file
|
225
|
+
2. Project-specific .voicemode.env files (searched up directory tree)
|
226
|
+
3. Environment variables (highest priority)
|
227
|
+
|
228
|
+
Returns:
|
229
|
+
Status message showing which files were loaded and any changes
|
230
|
+
"""
|
231
|
+
try:
|
232
|
+
# Get config files before reload
|
233
|
+
old_files = find_voicemode_env_files()
|
234
|
+
|
235
|
+
# Reload configuration
|
236
|
+
reload_configuration()
|
237
|
+
|
238
|
+
# Get config files after reload
|
239
|
+
new_files = find_voicemode_env_files()
|
240
|
+
|
241
|
+
lines = ["✅ Configuration reloaded successfully!", ""]
|
242
|
+
|
243
|
+
if new_files:
|
244
|
+
lines.append("📁 Configuration files loaded (in order):")
|
245
|
+
for i, config_file in enumerate(new_files, 1):
|
246
|
+
lines.append(f" {i}. {config_file}")
|
247
|
+
else:
|
248
|
+
lines.append("📁 No configuration files found - using defaults")
|
249
|
+
|
250
|
+
lines.append("")
|
251
|
+
lines.append("🔄 All caches have been cleared")
|
252
|
+
lines.append("📊 Voice preferences and provider settings updated")
|
253
|
+
|
254
|
+
logger.info(f"Configuration reloaded from {len(new_files)} files")
|
255
|
+
|
256
|
+
return "\n".join(lines)
|
257
|
+
|
258
|
+
except Exception as e:
|
259
|
+
logger.error(f"Failed to reload configuration: {e}")
|
260
|
+
return f"❌ Failed to reload configuration: {str(e)}"
|
261
|
+
|
262
|
+
|
263
|
+
@mcp.tool()
|
264
|
+
async def show_config_files() -> str:
|
265
|
+
"""Show which .voicemode.env files are being used for configuration.
|
266
|
+
|
267
|
+
This shows the current configuration file discovery and loading order:
|
268
|
+
- Global configuration from ~/.voicemode/voicemode.env
|
269
|
+
- Project-specific configuration (searched up directory tree)
|
270
|
+
- Current working directory for context
|
271
|
+
|
272
|
+
Returns:
|
273
|
+
Formatted list of configuration files and their status
|
274
|
+
"""
|
275
|
+
try:
|
276
|
+
config_files = find_voicemode_env_files()
|
277
|
+
|
278
|
+
lines = ["📋 Voice Mode Configuration Files", "=" * 40, ""]
|
279
|
+
lines.append(f"🗂️ Current directory: {Path.cwd()}")
|
280
|
+
lines.append("")
|
281
|
+
|
282
|
+
if config_files:
|
283
|
+
lines.append("📁 Configuration files (loading order):")
|
284
|
+
lines.append("")
|
285
|
+
|
286
|
+
for i, config_file in enumerate(config_files, 1):
|
287
|
+
status = "✅ EXISTS" if config_file.exists() else "❌ MISSING"
|
288
|
+
file_type = ""
|
289
|
+
|
290
|
+
if config_file.name == "voicemode.env" and config_file.parent.name == ".voicemode":
|
291
|
+
if config_file.parent == Path.home() / ".voicemode":
|
292
|
+
file_type = " (Global)"
|
293
|
+
else:
|
294
|
+
file_type = " (Project - in .voicemode dir)"
|
295
|
+
elif config_file.name == ".voicemode.env":
|
296
|
+
if config_file.parent == Path.cwd():
|
297
|
+
file_type = " (Project - current dir)"
|
298
|
+
else:
|
299
|
+
file_type = " (Project - parent dir)"
|
300
|
+
|
301
|
+
lines.append(f" {i}. {config_file}{file_type}")
|
302
|
+
lines.append(f" {status}")
|
303
|
+
lines.append("")
|
304
|
+
else:
|
305
|
+
lines.append("❌ No configuration files found")
|
306
|
+
lines.append("")
|
307
|
+
lines.append("💡 Tip: Create ~/.voicemode/voicemode.env for global configuration")
|
308
|
+
lines.append("💡 Tip: Create .voicemode.env in project directories for project-specific settings")
|
309
|
+
|
310
|
+
lines.append("")
|
311
|
+
lines.append("🔄 Use reload_config() to reload after making changes")
|
312
|
+
|
313
|
+
return "\n".join(lines)
|
314
|
+
|
315
|
+
except Exception as e:
|
316
|
+
logger.error(f"Failed to show config files: {e}")
|
317
|
+
return f"❌ Failed to show config files: {str(e)}"
|
voice_mode/tools/converse.py
CHANGED
@@ -875,6 +875,45 @@ def record_audio(duration: float) -> np.ndarray:
|
|
875
875
|
logger.error(f"Recording failed: {e}")
|
876
876
|
logger.error(f"Audio config when error occurred - Sample rate: {SAMPLE_RATE}, Channels: {CHANNELS}")
|
877
877
|
|
878
|
+
# Check if this is a device error that might be recoverable
|
879
|
+
error_str = str(e).lower()
|
880
|
+
if any(err in error_str for err in ['device unavailable', 'device disconnected',
|
881
|
+
'invalid device', 'unanticipated host error',
|
882
|
+
'portaudio error']):
|
883
|
+
logger.info("Audio device error detected - attempting to reinitialize audio system")
|
884
|
+
|
885
|
+
# Try to reinitialize sounddevice
|
886
|
+
try:
|
887
|
+
# Get current default device info before reinit
|
888
|
+
try:
|
889
|
+
old_device = sd.query_devices(kind='input')
|
890
|
+
old_device_name = old_device.get('name', 'Unknown')
|
891
|
+
except:
|
892
|
+
old_device_name = 'Previous device'
|
893
|
+
|
894
|
+
sd._terminate()
|
895
|
+
sd._initialize()
|
896
|
+
|
897
|
+
# Get new default device info
|
898
|
+
try:
|
899
|
+
new_device = sd.query_devices(kind='input')
|
900
|
+
new_device_name = new_device.get('name', 'Unknown')
|
901
|
+
logger.info(f"Audio system reinitialized - switched from '{old_device_name}' to '{new_device_name}'")
|
902
|
+
except:
|
903
|
+
logger.info("Audio system reinitialized - retrying with new default device")
|
904
|
+
|
905
|
+
# Wait a moment for the system to stabilize
|
906
|
+
import time as time_module
|
907
|
+
time_module.sleep(0.5)
|
908
|
+
|
909
|
+
# Try recording again with the new device (recursive call)
|
910
|
+
logger.info("Retrying recording with new audio device...")
|
911
|
+
return record_audio(duration)
|
912
|
+
|
913
|
+
except Exception as reinit_error:
|
914
|
+
logger.error(f"Failed to reinitialize audio: {reinit_error}")
|
915
|
+
# Fall through to normal error handling
|
916
|
+
|
878
917
|
# Import here to avoid circular imports
|
879
918
|
from voice_mode.utils.audio_diagnostics import get_audio_error_help
|
880
919
|
|
@@ -989,6 +1028,14 @@ def record_audio_with_silence_detection(max_duration: float, disable_silence_det
|
|
989
1028
|
"""Callback for continuous audio stream"""
|
990
1029
|
if status:
|
991
1030
|
logger.warning(f"Audio stream status: {status}")
|
1031
|
+
# Check for device-related errors
|
1032
|
+
status_str = str(status).lower()
|
1033
|
+
if any(err in status_str for err in ['device unavailable', 'device disconnected',
|
1034
|
+
'invalid device', 'unanticipated host error',
|
1035
|
+
'stream is stopped', 'portaudio error']):
|
1036
|
+
# Signal that we should stop recording due to device error
|
1037
|
+
audio_queue.put(None) # Sentinel value to indicate error
|
1038
|
+
return
|
992
1039
|
# Put the audio data in the queue for processing
|
993
1040
|
audio_queue.put(indata.copy())
|
994
1041
|
|
@@ -1007,6 +1054,12 @@ def record_audio_with_silence_detection(max_duration: float, disable_silence_det
|
|
1007
1054
|
# Get audio chunk from queue with timeout
|
1008
1055
|
chunk = audio_queue.get(timeout=0.1)
|
1009
1056
|
|
1057
|
+
# Check for error sentinel
|
1058
|
+
if chunk is None:
|
1059
|
+
logger.error("Audio device error detected - stopping recording")
|
1060
|
+
# Raise an exception to trigger recovery logic
|
1061
|
+
raise sd.PortAudioError("Audio device disconnected or unavailable")
|
1062
|
+
|
1010
1063
|
# Flatten for consistency
|
1011
1064
|
chunk_flat = chunk.flatten()
|
1012
1065
|
chunks.append(chunk_flat)
|
@@ -1109,6 +1162,45 @@ def record_audio_with_silence_detection(max_duration: float, disable_silence_det
|
|
1109
1162
|
# Import here to avoid circular imports
|
1110
1163
|
from voice_mode.utils.audio_diagnostics import get_audio_error_help
|
1111
1164
|
|
1165
|
+
# Check if this is a device error that might be recoverable
|
1166
|
+
error_str = str(e).lower()
|
1167
|
+
if any(err in error_str for err in ['device unavailable', 'device disconnected',
|
1168
|
+
'invalid device', 'unanticipated host error',
|
1169
|
+
'portaudio error']):
|
1170
|
+
logger.info("Audio device error detected - attempting to reinitialize audio system")
|
1171
|
+
|
1172
|
+
# Try to reinitialize sounddevice
|
1173
|
+
try:
|
1174
|
+
# Get current default device info before reinit
|
1175
|
+
try:
|
1176
|
+
old_device = sd.query_devices(kind='input')
|
1177
|
+
old_device_name = old_device.get('name', 'Unknown')
|
1178
|
+
except:
|
1179
|
+
old_device_name = 'Previous device'
|
1180
|
+
|
1181
|
+
sd._terminate()
|
1182
|
+
sd._initialize()
|
1183
|
+
|
1184
|
+
# Get new default device info
|
1185
|
+
try:
|
1186
|
+
new_device = sd.query_devices(kind='input')
|
1187
|
+
new_device_name = new_device.get('name', 'Unknown')
|
1188
|
+
logger.info(f"Audio system reinitialized - switched from '{old_device_name}' to '{new_device_name}'")
|
1189
|
+
except:
|
1190
|
+
logger.info("Audio system reinitialized - retrying with new default device")
|
1191
|
+
|
1192
|
+
# Wait a moment for the system to stabilize
|
1193
|
+
import time as time_module
|
1194
|
+
time_module.sleep(0.5)
|
1195
|
+
|
1196
|
+
# Try recording again with the new device (recursive call in sync context)
|
1197
|
+
logger.info("Retrying recording with new audio device...")
|
1198
|
+
return record_audio_with_silence_detection(max_duration, disable_silence_detection, min_duration, vad_aggressiveness)
|
1199
|
+
|
1200
|
+
except Exception as reinit_error:
|
1201
|
+
logger.error(f"Failed to reinitialize audio: {reinit_error}")
|
1202
|
+
# Fall through to normal error handling
|
1203
|
+
|
1112
1204
|
# Get helpful error message
|
1113
1205
|
help_message = get_audio_error_help(e)
|
1114
1206
|
logger.error(f"\n{help_message}")
|
@@ -1555,6 +1647,12 @@ async def converse(
|
|
1555
1647
|
# Run startup initialization if needed
|
1556
1648
|
await startup_initialization()
|
1557
1649
|
|
1650
|
+
# Refresh audio device cache to pick up any device changes (AirPods, etc.)
|
1651
|
+
# This takes ~1ms and ensures we use the current default device
|
1652
|
+
import sounddevice as sd
|
1653
|
+
sd._terminate()
|
1654
|
+
sd._initialize()
|
1655
|
+
|
1558
1656
|
# Get event logger and start session
|
1559
1657
|
event_logger = get_event_logger()
|
1560
1658
|
session_id = None
|
@@ -0,0 +1,14 @@
|
|
1
|
+
"""Audio transcription with word-level timestamps."""
|
2
|
+
|
3
|
+
from .types import TranscriptionBackend, OutputFormat, TranscriptionResult, WordData, SegmentData
|
4
|
+
from .core import transcribe_audio, transcribe_audio_sync
|
5
|
+
|
6
|
+
__all__ = [
|
7
|
+
'transcribe_audio',
|
8
|
+
'transcribe_audio_sync',
|
9
|
+
'TranscriptionBackend',
|
10
|
+
'OutputFormat',
|
11
|
+
'TranscriptionResult',
|
12
|
+
'WordData',
|
13
|
+
'SegmentData',
|
14
|
+
]
|