@xopcai/xopc 0.0.143 → 0.0.145
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/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/Combination-DolwnuKi.js +41 -0
- package/dist/gateway/static/root/assets/{activity-center-store-jITiX2vJ.js → activity-center-store-3l9ahUqb.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-DiME9u_P.js → agent-avatar-dicebear-BEUxdhZx.js} +1 -1
- package/dist/gateway/static/root/assets/agents-1bpT0xRO.js +2 -0
- package/dist/gateway/static/root/assets/app-management-settings-panel-CRHSZJWi.js +1 -0
- package/dist/gateway/static/root/assets/{appearance-settings-DUfdNQqk.js → appearance-settings-BunADYJb.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-Cc4JgVUQ.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-B-Qk6cGr.js → archive-plugin-B6vTkH2A.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-Di6EEbQX.js → attachment-load-BjZXzaNv.js} +1 -1
- package/dist/gateway/static/root/assets/{automation-suggestion-card-DGxvRxtO.js → automation-suggestion-card-ZDA-Stg1.js} +1 -1
- package/dist/gateway/static/root/assets/automations-page-mb0fdVpq.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-ZaOZDH2I.js → binary-plugins-DYZfLvPz.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-BTFhjWfg.js → block-editor-C3MUlYC9.js} +46 -46
- package/dist/gateway/static/root/assets/browser-settings-page-Bb0xhaMN.js +8 -0
- package/dist/gateway/static/root/assets/capability-presets-api-BV76yN9a.js +1 -0
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-CUcGKZqC.js +2 -0
- package/dist/gateway/static/root/assets/channel-recipient-api-fTlTnWB4.js +1 -0
- package/dist/gateway/static/root/assets/channels-settings-BlSDMGdy.js +1 -0
- package/dist/gateway/static/root/assets/channels-status-swr-CJauF1CW.js +1 -0
- package/dist/gateway/static/root/assets/{code-editor--tMO38Tr.js → code-editor-D4_k-6QN.js} +1 -1
- package/dist/gateway/static/root/assets/connector-logo-BVFNT2zW.js +1 -0
- package/dist/gateway/static/root/assets/connectors-page-CMgAFGvb.js +2 -0
- package/dist/gateway/static/root/assets/copy-to-clipboard-HOJ79TWv.js +1 -0
- package/dist/gateway/static/root/assets/{desktop-pet---yzVxVR.js → desktop-pet-_jUmoXd3.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-Dc3gqBJE.js → desktop-pet-settings-DwGOy4AQ.js} +1 -1
- package/dist/gateway/static/root/assets/{directory-picker-path-field-DCmOGLtK.js → directory-picker-path-field-akvF0F-u.js} +1 -1
- package/dist/gateway/static/root/assets/dist-C99RGfAH.js +1 -0
- package/dist/gateway/static/root/assets/dreaming-settings-BLetQiYY.js +3 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-ByaKTTq4.js → extension-debug-page-Cof2ncxE.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-BmRzHAZr.js → extension-page-CwWktRNz.js} +1 -1
- package/dist/gateway/static/root/assets/extension-settings-page-CuggWOIl.js +1 -0
- package/dist/gateway/static/root/assets/gateway-config-api-B7sxIbza.js +1 -0
- package/dist/gateway/static/root/assets/gateway-settings-DIv0eKMf.js +1 -0
- package/dist/gateway/static/root/assets/gateway-startup-retry-DtoLihQE.js +1 -0
- package/dist/gateway/static/root/assets/goal-create-dialog-C4Kw0mfS.js +1 -0
- package/dist/gateway/static/root/assets/goal-detail-page-BMmzYpZL.js +4 -0
- package/dist/gateway/static/root/assets/goals-config-api-B0jm5JaA.js +1 -0
- package/dist/gateway/static/root/assets/goals-page-CGeT77PC.js +2 -0
- package/dist/gateway/static/root/assets/goals-settings-N5_iL7es.js +1 -0
- package/dist/gateway/static/root/assets/heartbeat-settings-XzH3lbRR.js +1 -0
- package/dist/gateway/static/root/assets/{html-workspace-editor-COqmtESs.js → html-workspace-editor-C3DKAkMc.js} +1 -1
- package/dist/gateway/static/root/assets/index-CSLF84-Q.css +1 -0
- package/dist/gateway/static/root/assets/index-I-ZCenVw.js +259 -0
- package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-x_v0nrIi.js +1 -0
- package/dist/gateway/static/root/assets/locale-store-CiM-D9jA.js +1 -0
- package/dist/gateway/static/root/assets/logs-page-BSh1t8cD.js +7 -0
- package/dist/gateway/static/root/assets/markdown-editor-BvUebh3C.js +2 -0
- package/dist/gateway/static/root/assets/{markdown-split-Dw-yE2-y.js → markdown-split-5rJx5flj.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-D9NNNUgE.js → media-plugins-DYXccwAd.js} +1 -1
- package/dist/gateway/static/root/assets/messages-Cb1VMeqP.js +5 -0
- package/dist/gateway/static/root/assets/models-hub-panel-BNpTenAI.js +2 -0
- package/dist/gateway/static/root/assets/{notes-page-CRgbJGgk.js → notes-page-BgLyndCO.js} +1 -1
- package/dist/gateway/static/root/assets/notes-workbench-teklkhcE.js +4 -0
- package/dist/gateway/static/root/assets/{page-tabs-D4hkWVt_.js → page-tabs-6Al9_sGz.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-CIckv0uy.js → preview-open-alternatives-Cm3G72Hv.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-CCB_dKoh.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-VOAFAHYj.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-USYu64Ru.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-B0b42kB2.js → save-bar-controls-BYtfzu_F.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-BtjEkczK.js → schedule-field-CvIRc6yK.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-Bf0bk3i6.js → schema-form-CQuKx0n7.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-oGC8O5h_.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-D4rP66R1.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-BTKTdTNX.js → settings-collapsible-section-CF3HHM-t.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-DL4P0sAj.js → settings-form-section-CwWjoRN6.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-loading-skeleton-BV6KFNj4.js → settings-loading-skeleton-aUFmJa13.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-B_4DsX5W.js → settings-page-B2UUU9TF.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-layout-CyGlrInB.js → settings-page-layout-DqpbTWqI.js} +1 -1
- package/dist/gateway/static/root/assets/setup-status-panel-Ck9keROI.js +1 -0
- package/dist/gateway/static/root/assets/share-preview-page-BOhlPsS1.js +2 -0
- package/dist/gateway/static/root/assets/shares-settings-BYLsCuqj.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-BNHUyVML.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-CpXbQH4n.js +2 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-D85fBBuI.js → system-settings-panel-CWxSvuwD.js} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-Bt6vAkSB.js → theme-store-C9Dxcppm.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-CwEuYrHX.js → typed-models-lib-qROAvqTw.js} +1 -1
- package/dist/gateway/static/root/assets/use-channel-catalog-DE9cpn4G.js +1 -0
- package/dist/gateway/static/root/assets/user-context-page-DbNEK4Yp.js +1 -0
- package/dist/gateway/static/root/assets/user-profile-fields-editor-CWxc0ghP.js +1 -0
- package/dist/gateway/static/root/assets/{user-profile-settings-panel-CWM0fEpb.js → user-profile-settings-panel-DW-Up6vt.js} +1 -1
- package/dist/gateway/static/root/assets/{utils--BO6xT3u.js → utils-D53uRXVr.js} +1 -1
- package/dist/gateway/static/root/assets/{vendor-codemirror-C1WKjCk5.js → vendor-codemirror--gTq3YAI.js} +17 -17
- package/dist/gateway/static/root/assets/voice-api-key-field-Cs7MOrg0.js +1 -0
- package/dist/gateway/static/root/assets/work-item-detail-page-D1ziG1Pp.js +1 -0
- package/dist/gateway/static/root/assets/workflow-run-setup-panel-D_x9ELJp.js +3 -0
- package/dist/gateway/static/root/assets/workflows-page-DHGV_n6i.js +11 -0
- package/dist/gateway/static/root/index.html +10 -10
- package/dist/package.js +1 -1
- package/dist/src/agent/embedded/session-runner.js +3 -3
- package/dist/src/agent/embedded/session-runner.js.map +1 -1
- package/dist/src/agent/embedded/xopc-auth-storage.d.ts +3 -1
- package/dist/src/agent/embedded/xopc-auth-storage.js +11 -1
- package/dist/src/agent/embedded/xopc-auth-storage.js.map +1 -1
- package/dist/src/agent/memory/dreaming/config.js +1 -1
- package/dist/src/agent-runtime/index.js +1 -1
- package/dist/src/agent-runtime/runtime-profile.js +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/config/agent-profile.js +1 -1
- package/dist/src/config/index.js +2 -2
- package/dist/src/config/schema.d.ts +60 -5
- package/dist/src/config/schema.js +30 -9
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/connectors/composio-catalog.js +11 -2
- package/dist/src/connectors/composio-catalog.js.map +1 -1
- package/dist/src/connectors/connector-memory-sync.js +135 -26
- package/dist/src/connectors/connector-memory-sync.js.map +1 -1
- package/dist/src/gateway/agents-admin.js +1 -1
- package/dist/src/gateway/heartbeat/service.js +1 -1
- package/dist/src/gateway/hono/app.js +1 -1
- package/dist/src/gateway/hono/app.js.map +1 -1
- package/dist/src/gateway/hono/lib/config-payload.d.ts +11 -0
- package/dist/src/gateway/hono/lib/config-payload.js +1 -0
- package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js +6 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js.map +1 -1
- package/dist/src/gateway/hono/routes/memory.js +11 -1
- package/dist/src/gateway/hono/routes/memory.js.map +1 -1
- package/dist/src/gateway/hono/routes/status.js +3 -0
- package/dist/src/gateway/hono/routes/status.js.map +1 -1
- package/dist/src/gateway/hono/routes/voice.d.ts +5 -5
- package/dist/src/gateway/hono/routes/voice.js +288 -62
- package/dist/src/gateway/hono/routes/voice.js.map +1 -1
- package/dist/src/gateway/hono/routes/you.js +5 -1
- package/dist/src/gateway/hono/routes/you.js.map +1 -1
- package/dist/src/gateway/service.d.ts +8 -0
- package/dist/src/gateway/service.js +41 -11
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/heartbeat/index.js +1 -1
- package/dist/src/knowledge/connected-knowledge-pipeline.d.ts +21 -0
- package/dist/src/knowledge/connected-knowledge-pipeline.js +267 -0
- package/dist/src/knowledge/connected-knowledge-pipeline.js.map +1 -0
- package/dist/src/knowledge/coordinator.d.ts +10 -0
- package/dist/src/knowledge/coordinator.js +52 -0
- package/dist/src/knowledge/coordinator.js.map +1 -0
- package/dist/src/knowledge/index.d.ts +2 -0
- package/dist/src/knowledge/index.js +3 -1
- package/dist/src/knowledge/ingestion-service.d.ts +7 -1
- package/dist/src/knowledge/ingestion-service.js +28 -0
- package/dist/src/knowledge/ingestion-service.js.map +1 -1
- package/dist/src/knowledge/types.d.ts +18 -0
- package/dist/src/media-understanding/audio-transcription-runner.js +5 -3
- package/dist/src/media-understanding/audio-transcription-runner.js.map +1 -1
- package/dist/src/media-understanding/runner.js +8 -3
- package/dist/src/media-understanding/runner.js.map +1 -1
- package/dist/src/media-understanding/types.d.ts +7 -0
- package/dist/src/storage/sqlite/index.d.ts +1 -1
- package/dist/src/storage/sqlite/index.js +2 -2
- package/dist/src/storage/sqlite/knowledge-repository.d.ts +26 -1
- package/dist/src/storage/sqlite/knowledge-repository.js +120 -6
- package/dist/src/storage/sqlite/knowledge-repository.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/037_connected_knowledge_pipeline.sql +47 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/user-context/projection.d.ts +13 -4
- package/dist/src/user-context/projection.js +19 -7
- package/dist/src/user-context/projection.js.map +1 -1
- package/dist/src/voice/language-profile.d.ts +20 -0
- package/dist/src/voice/language-profile.js +156 -0
- package/dist/src/voice/language-profile.js.map +1 -0
- package/dist/src/voice/local/model-files.d.ts +14 -0
- package/dist/src/voice/local/model-files.js +94 -0
- package/dist/src/voice/local/model-files.js.map +1 -0
- package/dist/src/voice/local/model-manager.d.ts +18 -0
- package/dist/src/voice/local/model-manager.js +135 -0
- package/dist/src/voice/local/model-manager.js.map +1 -0
- package/dist/src/voice/local/models.d.ts +28 -0
- package/dist/src/voice/local/models.js +101 -0
- package/dist/src/voice/local/models.js.map +1 -0
- package/dist/src/voice/local/runtime-client.d.ts +26 -0
- package/dist/src/voice/local/runtime-client.js +166 -0
- package/dist/src/voice/local/runtime-client.js.map +1 -0
- package/dist/src/voice/local/runtime-dispatcher.d.ts +9 -0
- package/dist/src/voice/local/runtime-dispatcher.js +22 -0
- package/dist/src/voice/local/runtime-dispatcher.js.map +1 -0
- package/dist/src/voice/local/runtime-worker.d.ts +1 -0
- package/dist/src/voice/local/runtime-worker.js +249 -0
- package/dist/src/voice/local/runtime-worker.js.map +1 -0
- package/dist/src/voice/local/wav.d.ts +6 -0
- package/dist/src/voice/local/wav.js +69 -0
- package/dist/src/voice/local/wav.js.map +1 -0
- package/dist/src/voice/metadata/builtin.js +46 -0
- package/dist/src/voice/metadata/builtin.js.map +1 -1
- package/dist/src/voice/stt/factory.js +8 -2
- package/dist/src/voice/stt/factory.js.map +1 -1
- package/dist/src/voice/stt/providers/alibaba-transcription.js +14 -8
- package/dist/src/voice/stt/providers/alibaba-transcription.js.map +1 -1
- package/dist/src/voice/stt/providers/index.d.ts +1 -0
- package/dist/src/voice/stt/providers/index.js +2 -1
- package/dist/src/voice/stt/providers/local-transcription.d.ts +2 -0
- package/dist/src/voice/stt/providers/local-transcription.js +51 -0
- package/dist/src/voice/stt/providers/local-transcription.js.map +1 -0
- package/dist/src/voice/stt/providers/openai-transcription.js +5 -3
- package/dist/src/voice/stt/providers/openai-transcription.js.map +1 -1
- package/dist/src/voice/stt/transcribe-core.js +5 -4
- package/dist/src/voice/stt/transcribe-core.js.map +1 -1
- package/dist/src/voice/stt/types.d.ts +4 -0
- package/dist/src/voice/stt/types.js +5 -4
- package/dist/src/voice/stt/types.js.map +1 -1
- package/dist/src/voice/tts/providers/edge-speech.js +2 -2
- package/dist/src/voice/tts/providers/edge-speech.js.map +1 -1
- package/dist/src/voice/tts/types.js +3 -3
- package/dist/src/voice/tts/types.js.map +1 -1
- package/package.json +4 -2
- package/dist/gateway/static/root/assets/Combination-HAlzriaz.js +0 -41
- package/dist/gateway/static/root/assets/agents-DzBXhZXO.js +0 -2
- package/dist/gateway/static/root/assets/app-management-settings-panel-Cuo7SbUP.js +0 -1
- package/dist/gateway/static/root/assets/apps-page-CKuKK5AU.js +0 -1
- package/dist/gateway/static/root/assets/automations-page-D-FF_WKV.js +0 -4
- package/dist/gateway/static/root/assets/browser-settings-page-BvKLdEQt.js +0 -8
- package/dist/gateway/static/root/assets/capability-presets-api-BU-pVZiZ.js +0 -1
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-DgyeEwzh.js +0 -2
- package/dist/gateway/static/root/assets/channel-recipient-api-DuulnhIj.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-BbVsdD_I.js +0 -1
- package/dist/gateway/static/root/assets/channels-status-swr-B5lVrUsN.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-B8QqVy3I.js +0 -2
- package/dist/gateway/static/root/assets/dist-Mys7eFJ5.js +0 -1
- package/dist/gateway/static/root/assets/dreaming-settings-BgT5KZVe.js +0 -3
- package/dist/gateway/static/root/assets/extension-settings-page-JUtwIhbF.js +0 -1
- package/dist/gateway/static/root/assets/fetch-8QNN25Sh.js +0 -1
- package/dist/gateway/static/root/assets/gateway-config-api-CxicF50_.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-Bz2I_EO5.js +0 -1
- package/dist/gateway/static/root/assets/gateway-startup-retry-5YBnS9Fj.js +0 -1
- package/dist/gateway/static/root/assets/goal-create-dialog-VcoyuZGN.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-Cilkap2G.js +0 -4
- package/dist/gateway/static/root/assets/goals-config-api-CwcHRdC0.js +0 -1
- package/dist/gateway/static/root/assets/goals-page-C6eoni4z.js +0 -2
- package/dist/gateway/static/root/assets/goals-settings-o4i-qAOS.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-DQQkETX8.js +0 -1
- package/dist/gateway/static/root/assets/index-BKRqeRHm.js +0 -257
- package/dist/gateway/static/root/assets/index-CphxClbs.css +0 -1
- package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-LT6qB-ua.js +0 -1
- package/dist/gateway/static/root/assets/locale-store-Ba72bFmP.js +0 -1
- package/dist/gateway/static/root/assets/logs-page-Dli2e3fS.js +0 -7
- package/dist/gateway/static/root/assets/markdown-editor-jD1LPiP-.js +0 -2
- package/dist/gateway/static/root/assets/messages-CzKxM6I4.js +0 -5
- package/dist/gateway/static/root/assets/models-hub-panel-DVn2Q1Ik.js +0 -2
- package/dist/gateway/static/root/assets/notes-workbench-BxZRHLWx.js +0 -2
- package/dist/gateway/static/root/assets/project-detail-page-Doz53xLn.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-DoMtcRLg.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-BeHVEgrm.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-Bk4bQsBe.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-DdFyaRMm.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-DJ1_MLgX.js +0 -1
- package/dist/gateway/static/root/assets/share-preview-page-BQBV7YrV.js +0 -2
- package/dist/gateway/static/root/assets/shares-settings-0VuFLBeU.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-DO87Ckeo.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-D3VdtxAn.js +0 -2
- package/dist/gateway/static/root/assets/use-channel-catalog-COXIfXcl.js +0 -1
- package/dist/gateway/static/root/assets/user-context-page-m9JVQ73F.js +0 -1
- package/dist/gateway/static/root/assets/user-profile-fields-editor-DtQiuY7E.js +0 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-LoT3wgZ9.js +0 -1
- package/dist/gateway/static/root/assets/work-item-detail-page-DSTMz_mO.js +0 -1
- package/dist/gateway/static/root/assets/workflow-run-setup-panel-B35lJzvm.js +0 -3
- package/dist/gateway/static/root/assets/workflows-page-BfSm9oR8.js +0 -11
|
@@ -19,7 +19,7 @@ const log = createLogger('Sqlite:Migrations');
|
|
|
19
19
|
export const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;
|
|
20
20
|
|
|
21
21
|
/** Latest schema version this release supports (increment when adding migrations). */
|
|
22
|
-
export const XOPC_DB_SCHEMA_VERSION =
|
|
22
|
+
export const XOPC_DB_SCHEMA_VERSION = 37;
|
|
23
23
|
|
|
24
24
|
const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
25
25
|
|
|
@@ -31,7 +31,7 @@ function ensureXopcDatabaseSchema(db) {
|
|
|
31
31
|
bootstrapFreshDatabase(db);
|
|
32
32
|
currentVersion = 11;
|
|
33
33
|
}
|
|
34
|
-
applyPendingMigrations(db, { targetVersion:
|
|
34
|
+
applyPendingMigrations(db, { targetVersion: 37 });
|
|
35
35
|
}
|
|
36
36
|
//#endregion
|
|
37
37
|
export { SCHEMA_META_SCHEMA_VERSION_KEY, XOPC_DB_BASELINE_SCHEMA_VERSION, XOPC_DB_SCHEMA_VERSION, ensureSchemaMetaTable, ensureXopcDatabaseSchema, readSchemaVersion, readSchemaVersionForTest, setSchemaVersion };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { MemoryKind, MemoryRecord } from '../agent/memory/types.js';
|
|
2
2
|
import type { ConnectorDefinition, ConnectorInstance } from '../connectors/types.js';
|
|
3
|
+
import type { KnowledgeSourceItem, KnowledgeSyncRun } from '../knowledge/types.js';
|
|
3
4
|
export type UserContextFacet = 'basics' | 'collaboration' | 'boundaries' | 'priorities' | 'people' | 'current';
|
|
4
5
|
export type UserContextOrigin = 'told_by_you' | 'observed' | 'inferred' | 'connected_source';
|
|
5
6
|
export declare function isUserContextRecord(record: MemoryRecord): boolean;
|
|
@@ -29,10 +30,10 @@ export declare function projectUserContextRecord(record: MemoryRecord): {
|
|
|
29
30
|
};
|
|
30
31
|
export declare function isPersonalContextConnector(definition: ConnectorDefinition): boolean;
|
|
31
32
|
export declare function recordsDerivedFromPersonalContextSource(records: MemoryRecord[], agentId: string, connectorId: string): MemoryRecord[];
|
|
32
|
-
export declare function projectPersonalContextSources(definitions: ConnectorDefinition[], instances: ConnectorInstance[], records?: MemoryRecord[]
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
export declare function projectPersonalContextSources(definitions: ConnectorDefinition[], instances: ConnectorInstance[], records?: MemoryRecord[], knowledge?: {
|
|
34
|
+
sourceItems?: KnowledgeSourceItem[];
|
|
35
|
+
syncRuns?: KnowledgeSyncRun[];
|
|
36
|
+
}): {
|
|
36
37
|
category: import("../connectors/types.js").ConnectorCategory;
|
|
37
38
|
capabilities: ("context" | "tools" | "events" | "memory_source")[];
|
|
38
39
|
access: {
|
|
@@ -51,4 +52,12 @@ export declare function projectPersonalContextSources(definitions: ConnectorDefi
|
|
|
51
52
|
lastHealthStatus: import("../connectors/types.js").ConnectorHealthStatus;
|
|
52
53
|
lastActivityAt: string;
|
|
53
54
|
derivedUnderstandingCount: number;
|
|
55
|
+
knowledgeItemCount: number;
|
|
56
|
+
lastSyncAt: string;
|
|
57
|
+
lastSyncStatus: import("../knowledge/types.js").KnowledgeSyncRunStatus;
|
|
58
|
+
lastSyncError: string;
|
|
59
|
+
branding?: import("../connectors/types.js").ConnectorBranding;
|
|
60
|
+
id: string;
|
|
61
|
+
displayName: string;
|
|
62
|
+
description: string;
|
|
54
63
|
}[];
|
|
@@ -70,7 +70,7 @@ function isPersonalContextConnector(definition) {
|
|
|
70
70
|
function recordsDerivedFromPersonalContextSource(records, agentId, connectorId) {
|
|
71
71
|
return records.filter((record) => record.scope.agentId === agentId && record.source.provider === connectorId && isUserContextRecord(record));
|
|
72
72
|
}
|
|
73
|
-
function projectPersonalContextSources(definitions, instances, records = []) {
|
|
73
|
+
function projectPersonalContextSources(definitions, instances, records = [], knowledge = {}) {
|
|
74
74
|
const instanceByConnector = /* @__PURE__ */ new Map();
|
|
75
75
|
for (const instance of instances) {
|
|
76
76
|
const current = instanceByConnector.get(instance.connectorId) ?? [];
|
|
@@ -80,6 +80,9 @@ function projectPersonalContextSources(definitions, instances, records = []) {
|
|
|
80
80
|
return definitions.filter(isPersonalContextConnector).map((definition) => {
|
|
81
81
|
const connected = instanceByConnector.get(definition.id) ?? [];
|
|
82
82
|
const relatedRecords = records.filter((record) => record.source.provider === definition.id);
|
|
83
|
+
const sourceItems = (knowledge.sourceItems ?? []).filter((item) => item.metadata.connectorId === definition.id);
|
|
84
|
+
const sourceInstanceIds = new Set(sourceItems.map((item) => item.sourceInstanceId));
|
|
85
|
+
const latestSync = (knowledge.syncRuns ?? []).filter((run) => sourceInstanceIds.has(run.sourceInstanceId) || run.sourceInstanceId.startsWith(`composio:${definition.id}:`) || run.sourceInstanceId.startsWith(`${definition.id}:`)).sort((left, right) => Date.parse(right.startedAt) - Date.parse(left.startedAt))[0];
|
|
83
86
|
const latestHealth = connected.filter((instance) => instance.usage.lastHealthCheckAt && instance.usage.lastHealthStatus).sort((left, right) => Date.parse(right.usage.lastHealthCheckAt) - Date.parse(left.usage.lastHealthCheckAt))[0]?.usage;
|
|
84
87
|
const permissionDetails = definition.permissions?.data ?? [];
|
|
85
88
|
const canWrite = permissionDetails.some((permission) => /(?:^|[.:/_-])(write|create|update|delete|manage|send|admin)(?:$|[.:/_-])/i.test(permission));
|
|
@@ -87,6 +90,7 @@ function projectPersonalContextSources(definitions, instances, records = []) {
|
|
|
87
90
|
id: definition.id,
|
|
88
91
|
displayName: definition.displayName,
|
|
89
92
|
description: definition.description,
|
|
93
|
+
...definition.branding ? { branding: definition.branding } : {},
|
|
90
94
|
category: definition.category,
|
|
91
95
|
capabilities: definition.capabilities.filter((capability) => capability === "context" || capability === "memory_source" || capability === "tools" || capability === "events"),
|
|
92
96
|
access: {
|
|
@@ -103,12 +107,20 @@ function projectPersonalContextSources(definitions, instances, records = []) {
|
|
|
103
107
|
lastConnectedAt: connected.map((instance) => instance.lastConnectedAt).filter((value) => Boolean(value)).sort().at(-1),
|
|
104
108
|
lastHealthCheckAt: latestHealth?.lastHealthCheckAt,
|
|
105
109
|
lastHealthStatus: latestHealth?.lastHealthStatus,
|
|
106
|
-
lastActivityAt:
|
|
107
|
-
instance
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
lastActivityAt: [
|
|
111
|
+
...connected.flatMap((instance) => [
|
|
112
|
+
instance.lastConnectedAt,
|
|
113
|
+
instance.usage.lastHealthCheckAt,
|
|
114
|
+
...instance.audit.map((entry) => entry.at)
|
|
115
|
+
]),
|
|
116
|
+
latestSync?.finishedAt,
|
|
117
|
+
latestSync?.startedAt
|
|
118
|
+
].filter((value) => Boolean(value)).sort().at(-1),
|
|
119
|
+
derivedUnderstandingCount: relatedRecords.length,
|
|
120
|
+
knowledgeItemCount: sourceItems.filter((item) => !item.deletedAt).length,
|
|
121
|
+
lastSyncAt: latestSync?.finishedAt ?? latestSync?.startedAt,
|
|
122
|
+
lastSyncStatus: latestSync?.status,
|
|
123
|
+
lastSyncError: latestSync?.error
|
|
112
124
|
};
|
|
113
125
|
}).sort((left, right) => {
|
|
114
126
|
if (left.installed !== right.installed) return left.installed ? -1 : 1;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"projection.js","names":[],"sources":["../../../src/user-context/projection.ts"],"sourcesContent":["import type { MemoryKind, MemoryRecord } from '../agent/memory/types.js';\nimport { effectiveMemoryStatus, resolveMemoryStability } from '../agent/memory/lifecycle.js';\nimport { classifyMemoryContextOrigin } from '../agent/memory/source-origin.js';\nimport type { ConnectorDefinition, ConnectorInstance } from '../connectors/types.js';\n\nexport type UserContextFacet =\n | 'basics'\n | 'collaboration'\n | 'boundaries'\n | 'priorities'\n | 'people'\n | 'current';\n\nexport type UserContextOrigin = 'told_by_you' | 'observed' | 'inferred' | 'connected_source';\n\nconst ALWAYS_PERSONAL_KINDS = new Set<MemoryKind>([\n 'user_profile',\n 'preference',\n 'boundary',\n 'relationship',\n 'commitment',\n 'routine',\n 'personal_logistics',\n 'current_state',\n 'tool_preference',\n 'long_term_goal',\n]);\n\nconst CONTEXTUAL_USER_KINDS = new Set<MemoryKind>([\n 'project_context',\n 'open_question',\n 'milestone',\n 'derived_insight',\n 'task_lesson',\n]);\n\nexport function isUserContextRecord(record: MemoryRecord): boolean {\n if (ALWAYS_PERSONAL_KINDS.has(record.kind)) return true;\n return CONTEXTUAL_USER_KINDS.has(record.kind) && record.tags?.includes('user-understanding') === true;\n}\n\nexport function isUserContextMemoryKind(value: unknown): value is MemoryKind {\n return typeof value === 'string'\n && (ALWAYS_PERSONAL_KINDS.has(value as MemoryKind) || CONTEXTUAL_USER_KINDS.has(value as MemoryKind));\n}\n\nexport function facetForMemoryKind(kind: MemoryKind): UserContextFacet {\n if (kind === 'user_profile' || kind === 'personal_logistics') return 'basics';\n if (kind === 'preference' || kind === 'routine' || kind === 'tool_preference' || kind === 'task_lesson') {\n return 'collaboration';\n }\n if (kind === 'boundary') return 'boundaries';\n if (kind === 'relationship') return 'people';\n if (kind === 'current_state' || kind === 'open_question') return 'current';\n return 'priorities';\n}\n\nexport function originForMemoryRecord(record: MemoryRecord): UserContextOrigin {\n const origin = classifyMemoryContextOrigin(record);\n return origin === 'told_by_user' ? 'told_by_you' : origin;\n}\n\nexport function projectUserContextRecord(record: MemoryRecord) {\n const lifecycle = resolveMemoryStability(record);\n const latestEvidenceAt = record.evidence\n ?.map((evidence) => evidence.observedAt)\n .filter((value): value is string => Boolean(value))\n .sort()\n .at(-1);\n return {\n id: record.id,\n statement: record.content,\n facet: facetForMemoryKind(record.kind),\n kind: record.kind,\n status: effectiveMemoryStatus(record),\n origin: originForMemoryRecord(record),\n sourceName: record.source.provider ?? 'local',\n updatedAt: record.updatedAt,\n sensitivity: record.sensitivity ?? 'normal',\n explicitness: record.explicitness,\n durability: record.durability,\n canReference: record.disclosurePolicy !== 'silent',\n stability: lifecycle.band,\n stabilityScore: lifecycle.score,\n reviewAt: lifecycle.reviewAt,\n reviewDue: lifecycle.reviewDue,\n evidenceCount: record.evidence?.length ?? 0,\n sourcePath: record.source.path,\n latestEvidenceAt,\n };\n}\n\nexport function isPersonalContextConnector(definition: ConnectorDefinition): boolean {\n return definition.capabilities.includes('context') || definition.capabilities.includes('memory_source');\n}\n\nexport function recordsDerivedFromPersonalContextSource(\n records: MemoryRecord[],\n agentId: string,\n connectorId: string,\n): MemoryRecord[] {\n return records.filter((record) => (\n record.scope.agentId === agentId\n && record.source.provider === connectorId\n && isUserContextRecord(record)\n ));\n}\n\nexport function projectPersonalContextSources(\n definitions: ConnectorDefinition[],\n instances: ConnectorInstance[],\n records: MemoryRecord[] = [],\n) {\n const instanceByConnector = new Map<string, ConnectorInstance[]>();\n for (const instance of instances) {\n const current = instanceByConnector.get(instance.connectorId) ?? [];\n current.push(instance);\n instanceByConnector.set(instance.connectorId, current);\n }\n return definitions\n .filter(isPersonalContextConnector)\n .map((definition) => {\n const connected = instanceByConnector.get(definition.id) ?? [];\n const relatedRecords = records.filter((record) => record.source.provider === definition.id);\n const latestHealth = connected\n .filter((instance) => instance.usage.lastHealthCheckAt && instance.usage.lastHealthStatus)\n .sort((left, right) => (\n Date.parse(right.usage.lastHealthCheckAt!) - Date.parse(left.usage.lastHealthCheckAt!)\n ))[0]?.usage;\n const permissionDetails = definition.permissions?.data ?? [];\n const canWrite = permissionDetails.some((permission) => (\n /(?:^|[.:/_-])(write|create|update|delete|manage|send|admin)(?:$|[.:/_-])/i.test(permission)\n ));\n return {\n id: definition.id,\n displayName: definition.displayName,\n description: definition.description,\n category: definition.category,\n capabilities: definition.capabilities.filter((capability) => (\n capability === 'context' || capability === 'memory_source' || capability === 'tools' || capability === 'events'\n )),\n access: {\n context: definition.capabilities.includes('context'),\n memory: definition.capabilities.includes('memory_source'),\n read: definition.capabilities.includes('context')\n || definition.capabilities.includes('resources')\n || definition.capabilities.includes('tools'),\n write: canWrite,\n },\n permissionDetails,\n installed: connected.length > 0,\n enabled: connected.some((instance) => instance.enabled),\n status: connected[0]?.status ?? 'not_installed',\n instanceId: connected[0]?.instanceId,\n lastConnectedAt: connected\n .map((instance) => instance.lastConnectedAt)\n .filter((value): value is string => Boolean(value))\n .sort()\n .at(-1),\n lastHealthCheckAt: latestHealth?.lastHealthCheckAt,\n lastHealthStatus: latestHealth?.lastHealthStatus,\n lastActivityAt: connected\n .flatMap((instance) => [\n instance.lastConnectedAt,\n instance.usage.lastHealthCheckAt,\n ...instance.audit.map((entry) => entry.at),\n ])\n .filter((value): value is string => Boolean(value))\n .sort()\n .at(-1),\n derivedUnderstandingCount: relatedRecords.length,\n };\n })\n .sort((left, right) => {\n if (left.installed !== right.installed) return left.installed ? -1 : 1;\n return left.displayName.localeCompare(right.displayName);\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"projection.js","names":[],"sources":["../../../src/user-context/projection.ts"],"sourcesContent":["import type { MemoryKind, MemoryRecord } from '../agent/memory/types.js';\nimport { effectiveMemoryStatus, resolveMemoryStability } from '../agent/memory/lifecycle.js';\nimport { classifyMemoryContextOrigin } from '../agent/memory/source-origin.js';\nimport type { ConnectorDefinition, ConnectorInstance } from '../connectors/types.js';\nimport type { KnowledgeSourceItem, KnowledgeSyncRun } from '../knowledge/types.js';\n\nexport type UserContextFacet =\n | 'basics'\n | 'collaboration'\n | 'boundaries'\n | 'priorities'\n | 'people'\n | 'current';\n\nexport type UserContextOrigin = 'told_by_you' | 'observed' | 'inferred' | 'connected_source';\n\nconst ALWAYS_PERSONAL_KINDS = new Set<MemoryKind>([\n 'user_profile',\n 'preference',\n 'boundary',\n 'relationship',\n 'commitment',\n 'routine',\n 'personal_logistics',\n 'current_state',\n 'tool_preference',\n 'long_term_goal',\n]);\n\nconst CONTEXTUAL_USER_KINDS = new Set<MemoryKind>([\n 'project_context',\n 'open_question',\n 'milestone',\n 'derived_insight',\n 'task_lesson',\n]);\n\nexport function isUserContextRecord(record: MemoryRecord): boolean {\n if (ALWAYS_PERSONAL_KINDS.has(record.kind)) return true;\n return CONTEXTUAL_USER_KINDS.has(record.kind) && record.tags?.includes('user-understanding') === true;\n}\n\nexport function isUserContextMemoryKind(value: unknown): value is MemoryKind {\n return typeof value === 'string'\n && (ALWAYS_PERSONAL_KINDS.has(value as MemoryKind) || CONTEXTUAL_USER_KINDS.has(value as MemoryKind));\n}\n\nexport function facetForMemoryKind(kind: MemoryKind): UserContextFacet {\n if (kind === 'user_profile' || kind === 'personal_logistics') return 'basics';\n if (kind === 'preference' || kind === 'routine' || kind === 'tool_preference' || kind === 'task_lesson') {\n return 'collaboration';\n }\n if (kind === 'boundary') return 'boundaries';\n if (kind === 'relationship') return 'people';\n if (kind === 'current_state' || kind === 'open_question') return 'current';\n return 'priorities';\n}\n\nexport function originForMemoryRecord(record: MemoryRecord): UserContextOrigin {\n const origin = classifyMemoryContextOrigin(record);\n return origin === 'told_by_user' ? 'told_by_you' : origin;\n}\n\nexport function projectUserContextRecord(record: MemoryRecord) {\n const lifecycle = resolveMemoryStability(record);\n const latestEvidenceAt = record.evidence\n ?.map((evidence) => evidence.observedAt)\n .filter((value): value is string => Boolean(value))\n .sort()\n .at(-1);\n return {\n id: record.id,\n statement: record.content,\n facet: facetForMemoryKind(record.kind),\n kind: record.kind,\n status: effectiveMemoryStatus(record),\n origin: originForMemoryRecord(record),\n sourceName: record.source.provider ?? 'local',\n updatedAt: record.updatedAt,\n sensitivity: record.sensitivity ?? 'normal',\n explicitness: record.explicitness,\n durability: record.durability,\n canReference: record.disclosurePolicy !== 'silent',\n stability: lifecycle.band,\n stabilityScore: lifecycle.score,\n reviewAt: lifecycle.reviewAt,\n reviewDue: lifecycle.reviewDue,\n evidenceCount: record.evidence?.length ?? 0,\n sourcePath: record.source.path,\n latestEvidenceAt,\n };\n}\n\nexport function isPersonalContextConnector(definition: ConnectorDefinition): boolean {\n return definition.capabilities.includes('context') || definition.capabilities.includes('memory_source');\n}\n\nexport function recordsDerivedFromPersonalContextSource(\n records: MemoryRecord[],\n agentId: string,\n connectorId: string,\n): MemoryRecord[] {\n return records.filter((record) => (\n record.scope.agentId === agentId\n && record.source.provider === connectorId\n && isUserContextRecord(record)\n ));\n}\n\nexport function projectPersonalContextSources(\n definitions: ConnectorDefinition[],\n instances: ConnectorInstance[],\n records: MemoryRecord[] = [],\n knowledge: { sourceItems?: KnowledgeSourceItem[]; syncRuns?: KnowledgeSyncRun[] } = {},\n) {\n const instanceByConnector = new Map<string, ConnectorInstance[]>();\n for (const instance of instances) {\n const current = instanceByConnector.get(instance.connectorId) ?? [];\n current.push(instance);\n instanceByConnector.set(instance.connectorId, current);\n }\n return definitions\n .filter(isPersonalContextConnector)\n .map((definition) => {\n const connected = instanceByConnector.get(definition.id) ?? [];\n const relatedRecords = records.filter((record) => record.source.provider === definition.id);\n const sourceItems = (knowledge.sourceItems ?? []).filter((item) => item.metadata.connectorId === definition.id);\n const sourceInstanceIds = new Set(sourceItems.map((item) => item.sourceInstanceId));\n const latestSync = (knowledge.syncRuns ?? [])\n .filter((run) => (\n sourceInstanceIds.has(run.sourceInstanceId)\n || run.sourceInstanceId.startsWith(`composio:${definition.id}:`)\n || run.sourceInstanceId.startsWith(`${definition.id}:`)\n ))\n .sort((left, right) => Date.parse(right.startedAt) - Date.parse(left.startedAt))[0];\n const latestHealth = connected\n .filter((instance) => instance.usage.lastHealthCheckAt && instance.usage.lastHealthStatus)\n .sort((left, right) => (\n Date.parse(right.usage.lastHealthCheckAt!) - Date.parse(left.usage.lastHealthCheckAt!)\n ))[0]?.usage;\n const permissionDetails = definition.permissions?.data ?? [];\n const canWrite = permissionDetails.some((permission) => (\n /(?:^|[.:/_-])(write|create|update|delete|manage|send|admin)(?:$|[.:/_-])/i.test(permission)\n ));\n return {\n id: definition.id,\n displayName: definition.displayName,\n description: definition.description,\n ...(definition.branding ? { branding: definition.branding } : {}),\n category: definition.category,\n capabilities: definition.capabilities.filter((capability) => (\n capability === 'context' || capability === 'memory_source' || capability === 'tools' || capability === 'events'\n )),\n access: {\n context: definition.capabilities.includes('context'),\n memory: definition.capabilities.includes('memory_source'),\n read: definition.capabilities.includes('context')\n || definition.capabilities.includes('resources')\n || definition.capabilities.includes('tools'),\n write: canWrite,\n },\n permissionDetails,\n installed: connected.length > 0,\n enabled: connected.some((instance) => instance.enabled),\n status: connected[0]?.status ?? 'not_installed',\n instanceId: connected[0]?.instanceId,\n lastConnectedAt: connected\n .map((instance) => instance.lastConnectedAt)\n .filter((value): value is string => Boolean(value))\n .sort()\n .at(-1),\n lastHealthCheckAt: latestHealth?.lastHealthCheckAt,\n lastHealthStatus: latestHealth?.lastHealthStatus,\n lastActivityAt: [\n ...connected.flatMap((instance) => [\n instance.lastConnectedAt,\n instance.usage.lastHealthCheckAt,\n ...instance.audit.map((entry) => entry.at),\n ]),\n latestSync?.finishedAt,\n latestSync?.startedAt,\n ]\n .filter((value): value is string => Boolean(value))\n .sort()\n .at(-1),\n derivedUnderstandingCount: relatedRecords.length,\n knowledgeItemCount: sourceItems.filter((item) => !item.deletedAt).length,\n lastSyncAt: latestSync?.finishedAt ?? latestSync?.startedAt,\n lastSyncStatus: latestSync?.status,\n lastSyncError: latestSync?.error,\n };\n })\n .sort((left, right) => {\n if (left.installed !== right.installed) return left.installed ? -1 : 1;\n return left.displayName.localeCompare(right.displayName);\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Config } from '../config/schema.js';
|
|
2
|
+
export type ProductLanguage = 'en' | 'zh';
|
|
3
|
+
export type VoiceLanguageMode = 'auto' | 'manual';
|
|
4
|
+
export interface VoiceLanguageProfile {
|
|
5
|
+
language: ProductLanguage;
|
|
6
|
+
sttLanguage: 'auto' | 'en';
|
|
7
|
+
edgeLanguage: 'en-US' | 'zh-CN';
|
|
8
|
+
edgeVoice: 'en-US-MichelleNeural' | 'zh-CN-XiaoxiaoNeural';
|
|
9
|
+
}
|
|
10
|
+
export declare function resolveVoiceLanguageProfile(language: ProductLanguage): VoiceLanguageProfile;
|
|
11
|
+
export declare function inferProductLanguageFromEnvironment(): ProductLanguage;
|
|
12
|
+
/**
|
|
13
|
+
* Install safe product defaults while preserving every explicit on/off choice.
|
|
14
|
+
* Older configs with a persisted language or Edge voice are treated as manual.
|
|
15
|
+
*/
|
|
16
|
+
export declare function initializeVoiceDefaults(config: Config, language?: ProductLanguage): boolean;
|
|
17
|
+
/** Apply product language only to providers whose language mapping is known. */
|
|
18
|
+
export declare function applyAutomaticVoiceLanguage(config: Config, language: ProductLanguage): boolean;
|
|
19
|
+
/** Start the model download without delaying gateway readiness. */
|
|
20
|
+
export declare function prepareConfiguredLocalVoiceModel(config: Config): void;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { createLogger } from "../utils/logger/index.js";
|
|
2
|
+
import { init_logger } from "../utils/logger.js";
|
|
3
|
+
import { DEFAULT_LOCAL_VOICE_MODEL_ID, isLocalVoiceModelInstalled } from "./local/models.js";
|
|
4
|
+
import { startLocalVoiceModelInstall } from "./local/model-manager.js";
|
|
5
|
+
//#region src/voice/language-profile.ts
|
|
6
|
+
init_logger();
|
|
7
|
+
const log = createLogger("Voice:Language");
|
|
8
|
+
function resolveVoiceLanguageProfile(language) {
|
|
9
|
+
return language === "zh" ? {
|
|
10
|
+
language,
|
|
11
|
+
sttLanguage: "auto",
|
|
12
|
+
edgeLanguage: "zh-CN",
|
|
13
|
+
edgeVoice: "zh-CN-XiaoxiaoNeural"
|
|
14
|
+
} : {
|
|
15
|
+
language,
|
|
16
|
+
sttLanguage: "en",
|
|
17
|
+
edgeLanguage: "en-US",
|
|
18
|
+
edgeVoice: "en-US-MichelleNeural"
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function inferProductLanguageFromEnvironment() {
|
|
22
|
+
return [
|
|
23
|
+
process.env.LC_ALL,
|
|
24
|
+
process.env.LC_MESSAGES,
|
|
25
|
+
process.env.LANG,
|
|
26
|
+
Intl.DateTimeFormat().resolvedOptions().locale
|
|
27
|
+
].find((value) => typeof value === "string" && value.trim())?.trim().toLowerCase().startsWith("zh") ? "zh" : "en";
|
|
28
|
+
}
|
|
29
|
+
function hasLegacyManualLanguageSelection(config) {
|
|
30
|
+
const local = config.tools?.media?.audio?.providers?.["xopc-local"];
|
|
31
|
+
const edge = config.messages?.tts?.providers?.edge;
|
|
32
|
+
return typeof local?.language === "string" && local.language.trim().length > 0 || typeof edge?.voice === "string" && edge.voice.trim().length > 0 || typeof edge?.lang === "string" && edge.lang.trim().length > 0;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Install safe product defaults while preserving every explicit on/off choice.
|
|
36
|
+
* Older configs with a persisted language or Edge voice are treated as manual.
|
|
37
|
+
*/
|
|
38
|
+
function initializeVoiceDefaults(config, language = inferProductLanguageFromEnvironment()) {
|
|
39
|
+
const before = JSON.stringify({
|
|
40
|
+
voice: config.voice,
|
|
41
|
+
tools: config.tools?.media?.audio,
|
|
42
|
+
tts: config.messages?.tts
|
|
43
|
+
});
|
|
44
|
+
const languageMode = config.voice?.languageMode ?? (hasLegacyManualLanguageSelection(config) ? "manual" : "auto");
|
|
45
|
+
config.voice = {
|
|
46
|
+
...config.voice,
|
|
47
|
+
languageMode,
|
|
48
|
+
language: config.voice?.language ?? language
|
|
49
|
+
};
|
|
50
|
+
config.tools = config.tools ?? {};
|
|
51
|
+
config.tools.media = config.tools.media ?? {};
|
|
52
|
+
const audio = config.tools.media.audio;
|
|
53
|
+
config.tools.media.audio = {
|
|
54
|
+
...audio,
|
|
55
|
+
enabled: audio?.enabled ?? true,
|
|
56
|
+
provider: audio?.provider ?? "xopc-local",
|
|
57
|
+
fallback: audio?.fallback ?? {
|
|
58
|
+
enabled: false,
|
|
59
|
+
order: ["xopc-local"]
|
|
60
|
+
},
|
|
61
|
+
providers: {
|
|
62
|
+
...audio?.providers ?? {},
|
|
63
|
+
"xopc-local": {
|
|
64
|
+
model: DEFAULT_LOCAL_VOICE_MODEL_ID,
|
|
65
|
+
...audio?.providers?.["xopc-local"] ?? {}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
config.messages = config.messages ?? {};
|
|
70
|
+
const tts = config.messages.tts;
|
|
71
|
+
config.messages.tts = {
|
|
72
|
+
...tts,
|
|
73
|
+
enabled: tts?.enabled ?? true,
|
|
74
|
+
provider: tts?.provider ?? "edge",
|
|
75
|
+
trigger: tts?.trigger ?? "inbound",
|
|
76
|
+
providers: {
|
|
77
|
+
...tts?.providers ?? {},
|
|
78
|
+
edge: {
|
|
79
|
+
enabled: true,
|
|
80
|
+
...tts?.providers?.edge ?? {}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
if (languageMode === "auto") applyAutomaticVoiceLanguage(config, language);
|
|
85
|
+
return before !== JSON.stringify({
|
|
86
|
+
voice: config.voice,
|
|
87
|
+
tools: config.tools?.media?.audio,
|
|
88
|
+
tts: config.messages?.tts
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/** Apply product language only to providers whose language mapping is known. */
|
|
92
|
+
function applyAutomaticVoiceLanguage(config, language) {
|
|
93
|
+
if ((config.voice?.languageMode ?? "auto") !== "auto") return false;
|
|
94
|
+
const before = JSON.stringify({
|
|
95
|
+
voice: config.voice,
|
|
96
|
+
tools: config.tools?.media?.audio,
|
|
97
|
+
tts: config.messages?.tts
|
|
98
|
+
});
|
|
99
|
+
const profile = resolveVoiceLanguageProfile(language);
|
|
100
|
+
config.voice = {
|
|
101
|
+
...config.voice,
|
|
102
|
+
languageMode: "auto",
|
|
103
|
+
language
|
|
104
|
+
};
|
|
105
|
+
const audio = config.tools?.media?.audio;
|
|
106
|
+
if (audio?.provider === "xopc-local") audio.providers = {
|
|
107
|
+
...audio.providers ?? {},
|
|
108
|
+
"xopc-local": {
|
|
109
|
+
...audio.providers?.["xopc-local"] ?? {},
|
|
110
|
+
model: DEFAULT_LOCAL_VOICE_MODEL_ID,
|
|
111
|
+
language: profile.sttLanguage
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
const tts = config.messages?.tts;
|
|
115
|
+
if (tts?.provider === "edge") tts.providers = {
|
|
116
|
+
...tts.providers ?? {},
|
|
117
|
+
edge: {
|
|
118
|
+
enabled: true,
|
|
119
|
+
...tts.providers?.edge ?? {},
|
|
120
|
+
voice: profile.edgeVoice,
|
|
121
|
+
lang: profile.edgeLanguage
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
return before !== JSON.stringify({
|
|
125
|
+
voice: config.voice,
|
|
126
|
+
tools: config.tools?.media?.audio,
|
|
127
|
+
tts: config.messages?.tts
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/** Start the model download without delaying gateway readiness. */
|
|
131
|
+
function prepareConfiguredLocalVoiceModel(config) {
|
|
132
|
+
const audio = config.tools?.media?.audio;
|
|
133
|
+
if (audio?.enabled === false || (audio?.provider ?? "xopc-local") !== "xopc-local") return;
|
|
134
|
+
const configuredModel = audio?.providers?.["xopc-local"]?.model;
|
|
135
|
+
const modelId = typeof configuredModel === "string" && configuredModel.trim() ? configuredModel.trim() : DEFAULT_LOCAL_VOICE_MODEL_ID;
|
|
136
|
+
if (isLocalVoiceModelInstalled(modelId)) return;
|
|
137
|
+
try {
|
|
138
|
+
startLocalVoiceModelInstall(modelId);
|
|
139
|
+
log.info({
|
|
140
|
+
modelId,
|
|
141
|
+
phase: "startup_prepare"
|
|
142
|
+
}, "Local voice model preparation started");
|
|
143
|
+
} catch (err) {
|
|
144
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
145
|
+
log.warn({
|
|
146
|
+
err,
|
|
147
|
+
modelId,
|
|
148
|
+
errorMessage,
|
|
149
|
+
phase: "startup_prepare"
|
|
150
|
+
}, `Local voice model preparation failed: ${errorMessage}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
//#endregion
|
|
154
|
+
export { applyAutomaticVoiceLanguage, inferProductLanguageFromEnvironment, initializeVoiceDefaults, prepareConfiguredLocalVoiceModel, resolveVoiceLanguageProfile };
|
|
155
|
+
|
|
156
|
+
//# sourceMappingURL=language-profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"language-profile.js","names":[],"sources":["../../../src/voice/language-profile.ts"],"sourcesContent":["import type { Config } from '../config/schema.js';\nimport { createLogger } from '../utils/logger.js';\nimport { startLocalVoiceModelInstall } from './local/model-manager.js';\nimport {\n DEFAULT_LOCAL_VOICE_MODEL_ID,\n isLocalVoiceModelInstalled,\n} from './local/models.js';\n\nconst log = createLogger('Voice:Language');\n\nexport type ProductLanguage = 'en' | 'zh';\nexport type VoiceLanguageMode = 'auto' | 'manual';\n\nexport interface VoiceLanguageProfile {\n language: ProductLanguage;\n sttLanguage: 'auto' | 'en';\n edgeLanguage: 'en-US' | 'zh-CN';\n edgeVoice: 'en-US-MichelleNeural' | 'zh-CN-XiaoxiaoNeural';\n}\n\nexport function resolveVoiceLanguageProfile(language: ProductLanguage): VoiceLanguageProfile {\n return language === 'zh'\n ? {\n language,\n sttLanguage: 'auto',\n edgeLanguage: 'zh-CN',\n edgeVoice: 'zh-CN-XiaoxiaoNeural',\n }\n : {\n language,\n sttLanguage: 'en',\n edgeLanguage: 'en-US',\n edgeVoice: 'en-US-MichelleNeural',\n };\n}\n\nexport function inferProductLanguageFromEnvironment(): ProductLanguage {\n const locale = [\n process.env.LC_ALL,\n process.env.LC_MESSAGES,\n process.env.LANG,\n Intl.DateTimeFormat().resolvedOptions().locale,\n ].find((value) => typeof value === 'string' && value.trim());\n return locale?.trim().toLowerCase().startsWith('zh') ? 'zh' : 'en';\n}\n\nfunction hasLegacyManualLanguageSelection(config: Config): boolean {\n const local = config.tools?.media?.audio?.providers?.['xopc-local'];\n const edge = config.messages?.tts?.providers?.edge;\n return (\n (typeof local?.language === 'string' && local.language.trim().length > 0) ||\n (typeof edge?.voice === 'string' && edge.voice.trim().length > 0) ||\n (typeof edge?.lang === 'string' && edge.lang.trim().length > 0)\n );\n}\n\n/**\n * Install safe product defaults while preserving every explicit on/off choice.\n * Older configs with a persisted language or Edge voice are treated as manual.\n */\nexport function initializeVoiceDefaults(\n config: Config,\n language: ProductLanguage = inferProductLanguageFromEnvironment(),\n): boolean {\n const before = JSON.stringify({ voice: config.voice, tools: config.tools?.media?.audio, tts: config.messages?.tts });\n const existingMode = config.voice?.languageMode;\n const languageMode: VoiceLanguageMode =\n existingMode ?? (hasLegacyManualLanguageSelection(config) ? 'manual' : 'auto');\n\n config.voice = {\n ...config.voice,\n languageMode,\n language: config.voice?.language ?? language,\n };\n\n config.tools = config.tools ?? {};\n config.tools.media = config.tools.media ?? {};\n const audio = config.tools.media.audio;\n config.tools.media.audio = {\n ...audio,\n enabled: audio?.enabled ?? true,\n provider: audio?.provider ?? 'xopc-local',\n fallback: audio?.fallback ?? { enabled: false, order: ['xopc-local'] },\n providers: {\n ...(audio?.providers ?? {}),\n 'xopc-local': {\n model: DEFAULT_LOCAL_VOICE_MODEL_ID,\n ...(audio?.providers?.['xopc-local'] ?? {}),\n },\n },\n };\n\n config.messages = config.messages ?? {};\n const tts = config.messages.tts;\n config.messages.tts = {\n ...tts,\n enabled: tts?.enabled ?? true,\n provider: tts?.provider ?? 'edge',\n trigger: tts?.trigger ?? 'inbound',\n providers: {\n ...(tts?.providers ?? {}),\n edge: {\n enabled: true,\n ...(tts?.providers?.edge ?? {}),\n },\n },\n };\n\n if (languageMode === 'auto') {\n applyAutomaticVoiceLanguage(config, language);\n }\n return before !== JSON.stringify({ voice: config.voice, tools: config.tools?.media?.audio, tts: config.messages?.tts });\n}\n\n/** Apply product language only to providers whose language mapping is known. */\nexport function applyAutomaticVoiceLanguage(config: Config, language: ProductLanguage): boolean {\n if ((config.voice?.languageMode ?? 'auto') !== 'auto') return false;\n const before = JSON.stringify({ voice: config.voice, tools: config.tools?.media?.audio, tts: config.messages?.tts });\n const profile = resolveVoiceLanguageProfile(language);\n\n config.voice = { ...config.voice, languageMode: 'auto', language };\n const audio = config.tools?.media?.audio;\n if (audio?.provider === 'xopc-local') {\n audio.providers = {\n ...(audio.providers ?? {}),\n 'xopc-local': {\n ...(audio.providers?.['xopc-local'] ?? {}),\n model: DEFAULT_LOCAL_VOICE_MODEL_ID,\n language: profile.sttLanguage,\n },\n };\n }\n const tts = config.messages?.tts;\n if (tts?.provider === 'edge') {\n tts.providers = {\n ...(tts.providers ?? {}),\n edge: {\n enabled: true,\n ...(tts.providers?.edge ?? {}),\n voice: profile.edgeVoice,\n lang: profile.edgeLanguage,\n },\n };\n }\n return before !== JSON.stringify({ voice: config.voice, tools: config.tools?.media?.audio, tts: config.messages?.tts });\n}\n\n/** Start the model download without delaying gateway readiness. */\nexport function prepareConfiguredLocalVoiceModel(config: Config): void {\n const audio = config.tools?.media?.audio;\n if (audio?.enabled === false || (audio?.provider ?? 'xopc-local') !== 'xopc-local') return;\n const configuredModel = audio?.providers?.['xopc-local']?.model;\n const modelId = typeof configuredModel === 'string' && configuredModel.trim()\n ? configuredModel.trim()\n : DEFAULT_LOCAL_VOICE_MODEL_ID;\n if (isLocalVoiceModelInstalled(modelId)) return;\n try {\n startLocalVoiceModelInstall(modelId);\n log.info({ modelId, phase: 'startup_prepare' }, 'Local voice model preparation started');\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.warn({ err, modelId, errorMessage, phase: 'startup_prepare' }, `Local voice model preparation failed: ${errorMessage}`);\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { LocalVoiceModelDefinition } from './models.js';
|
|
2
|
+
export interface LocalVoiceFileProgress {
|
|
3
|
+
status: 'download';
|
|
4
|
+
file: string;
|
|
5
|
+
loaded: number;
|
|
6
|
+
total: number;
|
|
7
|
+
progress: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function downloadLocalVoiceModelFiles(options: {
|
|
10
|
+
model: LocalVoiceModelDefinition;
|
|
11
|
+
destinationDir: string;
|
|
12
|
+
remoteHosts: readonly string[];
|
|
13
|
+
onProgress?: (progress: LocalVoiceFileProgress) => void;
|
|
14
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { dirname, join } from "node:path";
|
|
2
|
+
import { createReadStream, createWriteStream } from "node:fs";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { mkdir, rm, stat } from "node:fs/promises";
|
|
5
|
+
import { Readable } from "node:stream";
|
|
6
|
+
import { pipeline } from "node:stream/promises";
|
|
7
|
+
//#region src/voice/local/model-files.ts
|
|
8
|
+
function resolveModelFileUrl(remoteHost, model, file) {
|
|
9
|
+
const base = remoteHost.endsWith("/") ? remoteHost : `${remoteHost}/`;
|
|
10
|
+
const path = file.path.split("/").map(encodeURIComponent).join("/");
|
|
11
|
+
return new URL(`${model.repository}/resolve/${model.revision}/${path}`, base).toString();
|
|
12
|
+
}
|
|
13
|
+
async function sha256File(path) {
|
|
14
|
+
const hash = createHash("sha256");
|
|
15
|
+
for await (const chunk of createReadStream(path)) hash.update(chunk);
|
|
16
|
+
return hash.digest("hex");
|
|
17
|
+
}
|
|
18
|
+
async function downloadFile(url, destination, expectedBytes, onBytes) {
|
|
19
|
+
const existing = await stat(destination).catch(() => null);
|
|
20
|
+
let offset = existing?.isFile() ? existing.size : 0;
|
|
21
|
+
if (offset > expectedBytes) {
|
|
22
|
+
await rm(destination, { force: true });
|
|
23
|
+
offset = 0;
|
|
24
|
+
}
|
|
25
|
+
if (offset === expectedBytes) {
|
|
26
|
+
onBytes(offset);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const response = await fetch(url, {
|
|
30
|
+
redirect: "follow",
|
|
31
|
+
headers: offset > 0 ? { Range: `bytes=${offset}-` } : void 0
|
|
32
|
+
});
|
|
33
|
+
if (!response.ok || !response.body) throw new Error(`Model download failed with HTTP ${response.status}`);
|
|
34
|
+
const isResume = offset > 0 && response.status === 206;
|
|
35
|
+
if (offset > 0 && !isResume) offset = 0;
|
|
36
|
+
const declaredBytes = Number(response.headers.get("content-length"));
|
|
37
|
+
const expectedResponseBytes = expectedBytes - offset;
|
|
38
|
+
if (Number.isFinite(declaredBytes) && declaredBytes > 0 && declaredBytes !== expectedResponseBytes) throw new Error(`Model download size mismatch: expected ${expectedResponseBytes}, received ${declaredBytes}`);
|
|
39
|
+
await mkdir(dirname(destination), { recursive: true });
|
|
40
|
+
let loaded = offset;
|
|
41
|
+
let lastReportedBytes = offset;
|
|
42
|
+
let lastReportedAt = 0;
|
|
43
|
+
const source = Readable.fromWeb(response.body);
|
|
44
|
+
source.on("data", (chunk) => {
|
|
45
|
+
loaded += chunk.length;
|
|
46
|
+
const now = Date.now();
|
|
47
|
+
if (loaded === expectedBytes || loaded - lastReportedBytes >= 256 * 1024 || now - lastReportedAt >= 250) {
|
|
48
|
+
lastReportedBytes = loaded;
|
|
49
|
+
lastReportedAt = now;
|
|
50
|
+
onBytes(loaded);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
await pipeline(source, createWriteStream(destination, {
|
|
54
|
+
flags: isResume ? "a" : "w",
|
|
55
|
+
mode: 384
|
|
56
|
+
}));
|
|
57
|
+
if (loaded !== expectedBytes) throw new Error(`Model download was incomplete: expected ${expectedBytes}, received ${loaded}`);
|
|
58
|
+
}
|
|
59
|
+
async function verifyModelFile(path, file) {
|
|
60
|
+
if ((await stat(path)).size !== file.bytes) throw new Error(`Model file size mismatch for ${file.path}`);
|
|
61
|
+
if (await sha256File(path) !== file.sha256) throw new Error(`Model file checksum mismatch for ${file.path}`);
|
|
62
|
+
}
|
|
63
|
+
async function downloadLocalVoiceModelFiles(options) {
|
|
64
|
+
const files = options.model.files;
|
|
65
|
+
if (!files?.length) throw new Error(`No downloadable files declared for ${options.model.id}`);
|
|
66
|
+
const total = files.reduce((sum, file) => sum + file.bytes, 0);
|
|
67
|
+
let completedBytes = 0;
|
|
68
|
+
for (const file of files) {
|
|
69
|
+
const destination = join(options.destinationDir, file.path);
|
|
70
|
+
let lastError;
|
|
71
|
+
for (const host of options.remoteHosts) try {
|
|
72
|
+
await downloadFile(resolveModelFileUrl(host, options.model, file), destination, file.bytes, (fileBytes) => options.onProgress?.({
|
|
73
|
+
status: "download",
|
|
74
|
+
file: file.path,
|
|
75
|
+
loaded: completedBytes + fileBytes,
|
|
76
|
+
total,
|
|
77
|
+
progress: (completedBytes + fileBytes) / total
|
|
78
|
+
}));
|
|
79
|
+
await verifyModelFile(destination, file);
|
|
80
|
+
lastError = void 0;
|
|
81
|
+
break;
|
|
82
|
+
} catch (error) {
|
|
83
|
+
lastError = error;
|
|
84
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
85
|
+
if (message.includes("checksum mismatch") || message.includes("size mismatch")) await rm(destination, { force: true });
|
|
86
|
+
}
|
|
87
|
+
if (lastError) throw lastError;
|
|
88
|
+
completedBytes += file.bytes;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
92
|
+
export { downloadLocalVoiceModelFiles };
|
|
93
|
+
|
|
94
|
+
//# sourceMappingURL=model-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-files.js","names":[],"sources":["../../../../src/voice/local/model-files.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { createReadStream, createWriteStream } from 'node:fs';\nimport { mkdir, rm, stat } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { Readable } from 'node:stream';\nimport { pipeline } from 'node:stream/promises';\n\nimport type { LocalVoiceModelDefinition, LocalVoiceModelFile } from './models.js';\n\nexport interface LocalVoiceFileProgress {\n status: 'download';\n file: string;\n loaded: number;\n total: number;\n progress: number;\n}\n\nfunction resolveModelFileUrl(\n remoteHost: string,\n model: LocalVoiceModelDefinition,\n file: LocalVoiceModelFile,\n): string {\n const base = remoteHost.endsWith('/') ? remoteHost : `${remoteHost}/`;\n const path = file.path.split('/').map(encodeURIComponent).join('/');\n return new URL(`${model.repository}/resolve/${model.revision}/${path}`, base).toString();\n}\n\nasync function sha256File(path: string): Promise<string> {\n const hash = createHash('sha256');\n for await (const chunk of createReadStream(path)) hash.update(chunk as Buffer);\n return hash.digest('hex');\n}\n\nasync function downloadFile(\n url: string,\n destination: string,\n expectedBytes: number,\n onBytes: (bytes: number) => void,\n): Promise<void> {\n const existing = await stat(destination).catch(() => null);\n let offset = existing?.isFile() ? existing.size : 0;\n if (offset > expectedBytes) {\n await rm(destination, { force: true });\n offset = 0;\n }\n if (offset === expectedBytes) {\n onBytes(offset);\n return;\n }\n const response = await fetch(url, {\n redirect: 'follow',\n headers: offset > 0 ? { Range: `bytes=${offset}-` } : undefined,\n });\n if (!response.ok || !response.body) {\n throw new Error(`Model download failed with HTTP ${response.status}`);\n }\n const isResume = offset > 0 && response.status === 206;\n if (offset > 0 && !isResume) offset = 0;\n const declaredBytes = Number(response.headers.get('content-length'));\n const expectedResponseBytes = expectedBytes - offset;\n if (Number.isFinite(declaredBytes) && declaredBytes > 0 && declaredBytes !== expectedResponseBytes) {\n throw new Error(`Model download size mismatch: expected ${expectedResponseBytes}, received ${declaredBytes}`);\n }\n\n await mkdir(dirname(destination), { recursive: true });\n let loaded = offset;\n let lastReportedBytes = offset;\n let lastReportedAt = 0;\n const source = Readable.fromWeb(response.body as never);\n source.on('data', (chunk: Buffer) => {\n loaded += chunk.length;\n const now = Date.now();\n if (loaded === expectedBytes || loaded - lastReportedBytes >= 256 * 1024 || now - lastReportedAt >= 250) {\n lastReportedBytes = loaded;\n lastReportedAt = now;\n onBytes(loaded);\n }\n });\n await pipeline(source, createWriteStream(destination, { flags: isResume ? 'a' : 'w', mode: 0o600 }));\n if (loaded !== expectedBytes) {\n throw new Error(`Model download was incomplete: expected ${expectedBytes}, received ${loaded}`);\n }\n}\n\nasync function verifyModelFile(path: string, file: LocalVoiceModelFile): Promise<void> {\n const info = await stat(path);\n if (info.size !== file.bytes) {\n throw new Error(`Model file size mismatch for ${file.path}`);\n }\n const actual = await sha256File(path);\n if (actual !== file.sha256) {\n throw new Error(`Model file checksum mismatch for ${file.path}`);\n }\n}\n\nexport async function downloadLocalVoiceModelFiles(options: {\n model: LocalVoiceModelDefinition;\n destinationDir: string;\n remoteHosts: readonly string[];\n onProgress?: (progress: LocalVoiceFileProgress) => void;\n}): Promise<void> {\n const files = options.model.files;\n if (!files?.length) throw new Error(`No downloadable files declared for ${options.model.id}`);\n const total = files.reduce((sum, file) => sum + file.bytes, 0);\n let completedBytes = 0;\n\n for (const file of files) {\n const destination = join(options.destinationDir, file.path);\n let lastError: unknown;\n for (const host of options.remoteHosts) {\n try {\n await downloadFile(\n resolveModelFileUrl(host, options.model, file),\n destination,\n file.bytes,\n (fileBytes) => options.onProgress?.({\n status: 'download',\n file: file.path,\n loaded: completedBytes + fileBytes,\n total,\n progress: (completedBytes + fileBytes) / total,\n }),\n );\n await verifyModelFile(destination, file);\n lastError = undefined;\n break;\n } catch (error) {\n lastError = error;\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes('checksum mismatch') || message.includes('size mismatch')) {\n await rm(destination, { force: true });\n }\n }\n }\n if (lastError) {\n throw lastError;\n }\n completedBytes += file.bytes;\n }\n}\n"],"mappings":";;;;;;;AAiBA,SAAS,oBACP,YACA,OACA,MACQ;CACR,MAAM,OAAO,WAAW,SAAS,IAAI,GAAG,aAAa,GAAG,WAAW;CACnE,MAAM,OAAO,KAAK,KAAK,MAAM,IAAI,CAAC,IAAI,mBAAmB,CAAC,KAAK,IAAI;AACnE,QAAO,IAAI,IAAI,GAAG,MAAM,WAAW,WAAW,MAAM,SAAS,GAAG,QAAQ,KAAK,CAAC,UAAU;;AAG1F,eAAe,WAAW,MAA+B;CACvD,MAAM,OAAO,WAAW,SAAS;AACjC,YAAW,MAAM,SAAS,iBAAiB,KAAK,CAAE,MAAK,OAAO,MAAgB;AAC9E,QAAO,KAAK,OAAO,MAAM;;AAG3B,eAAe,aACb,KACA,aACA,eACA,SACe;CACf,MAAM,WAAW,MAAM,KAAK,YAAY,CAAC,YAAY,KAAK;CAC1D,IAAI,SAAS,UAAU,QAAQ,GAAG,SAAS,OAAO;AAClD,KAAI,SAAS,eAAe;AAC1B,QAAM,GAAG,aAAa,EAAE,OAAO,MAAM,CAAC;AACtC,WAAS;;AAEX,KAAI,WAAW,eAAe;AAC5B,UAAQ,OAAO;AACf;;CAEF,MAAM,WAAW,MAAM,MAAM,KAAK;EAChC,UAAU;EACV,SAAS,SAAS,IAAI,EAAE,OAAO,SAAS,OAAO,IAAI,GAAG,KAAA;EACvD,CAAC;AACF,KAAI,CAAC,SAAS,MAAM,CAAC,SAAS,KAC5B,OAAM,IAAI,MAAM,mCAAmC,SAAS,SAAS;CAEvE,MAAM,WAAW,SAAS,KAAK,SAAS,WAAW;AACnD,KAAI,SAAS,KAAK,CAAC,SAAU,UAAS;CACtC,MAAM,gBAAgB,OAAO,SAAS,QAAQ,IAAI,iBAAiB,CAAC;CACpE,MAAM,wBAAwB,gBAAgB;AAC9C,KAAI,OAAO,SAAS,cAAc,IAAI,gBAAgB,KAAK,kBAAkB,sBAC3E,OAAM,IAAI,MAAM,0CAA0C,sBAAsB,aAAa,gBAAgB;AAG/G,OAAM,MAAM,QAAQ,YAAY,EAAE,EAAE,WAAW,MAAM,CAAC;CACtD,IAAI,SAAS;CACb,IAAI,oBAAoB;CACxB,IAAI,iBAAiB;CACrB,MAAM,SAAS,SAAS,QAAQ,SAAS,KAAc;AACvD,QAAO,GAAG,SAAS,UAAkB;AACnC,YAAU,MAAM;EAChB,MAAM,MAAM,KAAK,KAAK;AACtB,MAAI,WAAW,iBAAiB,SAAS,qBAAqB,MAAM,QAAQ,MAAM,kBAAkB,KAAK;AACvG,uBAAoB;AACpB,oBAAiB;AACjB,WAAQ,OAAO;;GAEjB;AACF,OAAM,SAAS,QAAQ,kBAAkB,aAAa;EAAE,OAAO,WAAW,MAAM;EAAK,MAAM;EAAO,CAAC,CAAC;AACpG,KAAI,WAAW,cACb,OAAM,IAAI,MAAM,2CAA2C,cAAc,aAAa,SAAS;;AAInG,eAAe,gBAAgB,MAAc,MAA0C;AAErF,MAAI,MADe,KAAK,KAAK,EACpB,SAAS,KAAK,MACrB,OAAM,IAAI,MAAM,gCAAgC,KAAK,OAAO;AAG9D,KAAI,MADiB,WAAW,KAAK,KACtB,KAAK,OAClB,OAAM,IAAI,MAAM,oCAAoC,KAAK,OAAO;;AAIpE,eAAsB,6BAA6B,SAKjC;CAChB,MAAM,QAAQ,QAAQ,MAAM;AAC5B,KAAI,CAAC,OAAO,OAAQ,OAAM,IAAI,MAAM,sCAAsC,QAAQ,MAAM,KAAK;CAC7F,MAAM,QAAQ,MAAM,QAAQ,KAAK,SAAS,MAAM,KAAK,OAAO,EAAE;CAC9D,IAAI,iBAAiB;AAErB,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,cAAc,KAAK,QAAQ,gBAAgB,KAAK,KAAK;EAC3D,IAAI;AACJ,OAAK,MAAM,QAAQ,QAAQ,YACzB,KAAI;AACF,SAAM,aACJ,oBAAoB,MAAM,QAAQ,OAAO,KAAK,EAC9C,aACA,KAAK,QACJ,cAAc,QAAQ,aAAa;IAClC,QAAQ;IACR,MAAM,KAAK;IACX,QAAQ,iBAAiB;IACzB;IACA,WAAW,iBAAiB,aAAa;IAC1C,CAAC,CACH;AACD,SAAM,gBAAgB,aAAa,KAAK;AACxC,eAAY,KAAA;AACZ;WACO,OAAO;AACd,eAAY;GACZ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AACtE,OAAI,QAAQ,SAAS,oBAAoB,IAAI,QAAQ,SAAS,gBAAgB,CAC5E,OAAM,GAAG,aAAa,EAAE,OAAO,MAAM,CAAC;;AAI5C,MAAI,UACF,OAAM;AAER,oBAAkB,KAAK"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type LocalVoiceModelState = 'not_installed' | 'downloading' | 'ready' | 'error';
|
|
2
|
+
export interface LocalVoiceModelStatus {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
approximateBytes: number;
|
|
7
|
+
engine: string;
|
|
8
|
+
languages: readonly string[];
|
|
9
|
+
recommended?: boolean;
|
|
10
|
+
state: LocalVoiceModelState;
|
|
11
|
+
progress?: number;
|
|
12
|
+
downloadedBytes?: number;
|
|
13
|
+
totalBytes?: number;
|
|
14
|
+
error?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function listLocalVoiceModelStatuses(): Promise<LocalVoiceModelStatus[]>;
|
|
17
|
+
export declare function startLocalVoiceModelInstall(modelId: string): LocalVoiceModelStatus;
|
|
18
|
+
export declare function removeLocalVoiceModel(modelId: string): Promise<void>;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { createLogger } from "../../utils/logger/index.js";
|
|
2
|
+
import { init_logger } from "../../utils/logger.js";
|
|
3
|
+
import { LOCAL_VOICE_MODELS, getLocalVoiceModel, isLocalVoiceModelInstalled, resolveLocalVoiceModelDir, resolveLocalVoiceModelMarkerPath } from "./models.js";
|
|
4
|
+
import { getLocalVoiceRuntimeClient } from "./runtime-client.js";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
7
|
+
//#region src/voice/local/model-manager.ts
|
|
8
|
+
init_logger();
|
|
9
|
+
const log = createLogger("LocalVoice:Models");
|
|
10
|
+
const jobs = /* @__PURE__ */ new Map();
|
|
11
|
+
function progressFraction(progress) {
|
|
12
|
+
if (typeof progress.progress === "number" && Number.isFinite(progress.progress)) return progress.progress > 1 ? progress.progress / 100 : progress.progress;
|
|
13
|
+
if (typeof progress.loaded === "number" && typeof progress.total === "number" && progress.total > 0) return progress.loaded / progress.total;
|
|
14
|
+
}
|
|
15
|
+
async function listLocalVoiceModelStatuses() {
|
|
16
|
+
return Promise.all(LOCAL_VOICE_MODELS.map(async (model) => {
|
|
17
|
+
const active = jobs.get(model.id);
|
|
18
|
+
if (active) return { ...active };
|
|
19
|
+
if (!isLocalVoiceModelInstalled(model.id)) return {
|
|
20
|
+
id: model.id,
|
|
21
|
+
name: model.name,
|
|
22
|
+
description: model.description,
|
|
23
|
+
approximateBytes: model.approximateBytes,
|
|
24
|
+
engine: model.engine,
|
|
25
|
+
languages: model.languages,
|
|
26
|
+
recommended: model.recommended,
|
|
27
|
+
state: "not_installed"
|
|
28
|
+
};
|
|
29
|
+
let installedAt;
|
|
30
|
+
try {
|
|
31
|
+
installedAt = JSON.parse(await readFile(resolveLocalVoiceModelMarkerPath(model.id), "utf8")).installedAt;
|
|
32
|
+
} catch {}
|
|
33
|
+
return {
|
|
34
|
+
id: model.id,
|
|
35
|
+
name: model.name,
|
|
36
|
+
description: installedAt ? `${model.description} Installed ${installedAt}.` : model.description,
|
|
37
|
+
approximateBytes: model.approximateBytes,
|
|
38
|
+
engine: model.engine,
|
|
39
|
+
languages: model.languages,
|
|
40
|
+
recommended: model.recommended,
|
|
41
|
+
state: "ready"
|
|
42
|
+
};
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
function startLocalVoiceModelInstall(modelId) {
|
|
46
|
+
const model = getLocalVoiceModel(modelId);
|
|
47
|
+
const existing = jobs.get(model.id);
|
|
48
|
+
if (existing?.state === "downloading") return { ...existing };
|
|
49
|
+
const status = {
|
|
50
|
+
id: model.id,
|
|
51
|
+
name: model.name,
|
|
52
|
+
description: model.description,
|
|
53
|
+
approximateBytes: model.approximateBytes,
|
|
54
|
+
engine: model.engine,
|
|
55
|
+
languages: model.languages,
|
|
56
|
+
recommended: model.recommended,
|
|
57
|
+
state: "downloading",
|
|
58
|
+
progress: 0
|
|
59
|
+
};
|
|
60
|
+
jobs.set(model.id, status);
|
|
61
|
+
(async () => {
|
|
62
|
+
const modelDir = resolveLocalVoiceModelDir(model.id);
|
|
63
|
+
const stagingDir = `${modelDir}.download`;
|
|
64
|
+
try {
|
|
65
|
+
if (model.engine === "transformers.js") await rm(stagingDir, {
|
|
66
|
+
recursive: true,
|
|
67
|
+
force: true
|
|
68
|
+
});
|
|
69
|
+
await mkdir(stagingDir, { recursive: true });
|
|
70
|
+
await getLocalVoiceRuntimeClient().request("model.install", {
|
|
71
|
+
modelId: model.id,
|
|
72
|
+
cacheDir: stagingDir
|
|
73
|
+
}, { onProgress: (progress) => {
|
|
74
|
+
const current = jobs.get(model.id);
|
|
75
|
+
if (!current) return;
|
|
76
|
+
current.progress = progressFraction(progress) ?? current.progress;
|
|
77
|
+
current.downloadedBytes = progress.loaded;
|
|
78
|
+
current.totalBytes = progress.total;
|
|
79
|
+
} });
|
|
80
|
+
await writeFile(join(stagingDir, "installed.json"), `${JSON.stringify({
|
|
81
|
+
modelId: model.id,
|
|
82
|
+
repository: model.repository,
|
|
83
|
+
revision: model.revision,
|
|
84
|
+
engine: model.engine,
|
|
85
|
+
...model.dtype ? { dtype: model.dtype } : {},
|
|
86
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
87
|
+
}, null, 2)}\n`, { mode: 384 });
|
|
88
|
+
await rm(modelDir, {
|
|
89
|
+
recursive: true,
|
|
90
|
+
force: true
|
|
91
|
+
});
|
|
92
|
+
await rename(stagingDir, modelDir);
|
|
93
|
+
jobs.delete(model.id);
|
|
94
|
+
log.info({ modelId: model.id }, "Local voice model installed");
|
|
95
|
+
} catch (error) {
|
|
96
|
+
if (model.engine === "transformers.js") await rm(stagingDir, {
|
|
97
|
+
recursive: true,
|
|
98
|
+
force: true
|
|
99
|
+
}).catch(() => {});
|
|
100
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
101
|
+
jobs.set(model.id, {
|
|
102
|
+
...status,
|
|
103
|
+
state: "error",
|
|
104
|
+
error: message
|
|
105
|
+
});
|
|
106
|
+
log.error({
|
|
107
|
+
err: error,
|
|
108
|
+
modelId: model.id
|
|
109
|
+
}, `Local voice model installation failed: ${message}`);
|
|
110
|
+
}
|
|
111
|
+
})();
|
|
112
|
+
return { ...status };
|
|
113
|
+
}
|
|
114
|
+
async function removeLocalVoiceModel(modelId) {
|
|
115
|
+
const model = getLocalVoiceModel(modelId);
|
|
116
|
+
if (jobs.get(model.id)?.state === "downloading") throw new Error("Cannot remove a model while it is downloading");
|
|
117
|
+
try {
|
|
118
|
+
await getLocalVoiceRuntimeClient().request("model.unload", { modelId: model.id }, { timeoutMs: 1e4 });
|
|
119
|
+
} catch (error) {
|
|
120
|
+
log.warn({
|
|
121
|
+
err: error,
|
|
122
|
+
modelId: model.id
|
|
123
|
+
}, "Local voice runtime unload failed; removing model files");
|
|
124
|
+
}
|
|
125
|
+
await rm(resolveLocalVoiceModelDir(model.id), {
|
|
126
|
+
recursive: true,
|
|
127
|
+
force: true
|
|
128
|
+
});
|
|
129
|
+
jobs.delete(model.id);
|
|
130
|
+
log.info({ modelId: model.id }, "Local voice model removed");
|
|
131
|
+
}
|
|
132
|
+
//#endregion
|
|
133
|
+
export { listLocalVoiceModelStatuses, removeLocalVoiceModel, startLocalVoiceModelInstall };
|
|
134
|
+
|
|
135
|
+
//# sourceMappingURL=model-manager.js.map
|