@pie-players/pie-assessment-toolkit 0.2.7 → 0.2.10
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/README.md +200 -67
- package/dist/attempt/TestSession.d.ts +23 -21
- package/dist/attempt/TestSession.d.ts.map +1 -1
- package/dist/attempt/TestSession.js +17 -15
- package/dist/attempt/TestSession.js.map +1 -1
- package/dist/attempt/adapters/activity-to-test-attempt-session.d.ts +50 -0
- package/dist/attempt/adapters/activity-to-test-attempt-session.d.ts.map +1 -0
- package/dist/attempt/adapters/activity-to-test-attempt-session.js +135 -0
- package/dist/attempt/adapters/activity-to-test-attempt-session.js.map +1 -0
- package/dist/components/ItemToolBar.custom-element.js +953 -0
- package/dist/components/PieAssessmentToolkit.custom-element.js +757 -0
- package/dist/components/SectionToolBar.custom-element.js +162 -0
- package/dist/components/item-toolbar-element.d.ts +3 -0
- package/dist/components/item-toolbar-element.d.ts.map +1 -0
- package/dist/components/item-toolbar-element.js +2 -0
- package/dist/components/item-toolbar-element.js.map +1 -0
- package/dist/components/pie-assessment-toolkit-element.d.ts +3 -0
- package/dist/components/pie-assessment-toolkit-element.d.ts.map +1 -0
- package/dist/components/pie-assessment-toolkit-element.js +2 -0
- package/dist/components/pie-assessment-toolkit-element.js.map +1 -0
- package/dist/components/section-toolbar-element.d.ts +3 -0
- package/dist/components/section-toolbar-element.d.ts.map +1 -0
- package/dist/components/section-toolbar-element.js +2 -0
- package/dist/components/section-toolbar-element.js.map +1 -0
- package/dist/context/assessment-toolkit-context.d.ts +52 -0
- package/dist/context/assessment-toolkit-context.d.ts.map +1 -0
- package/dist/context/assessment-toolkit-context.js +6 -0
- package/dist/context/assessment-toolkit-context.js.map +1 -0
- package/dist/context/runtime-context-consumer.d.ts +14 -0
- package/dist/context/runtime-context-consumer.d.ts.map +1 -0
- package/dist/context/runtime-context-consumer.js +52 -0
- package/dist/context/runtime-context-consumer.js.map +1 -0
- package/dist/index.d.ts +19 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -11
- package/dist/index.js.map +1 -1
- package/dist/runtime/RuntimeRegistry.d.ts +13 -0
- package/dist/runtime/RuntimeRegistry.d.ts.map +1 -0
- package/dist/runtime/RuntimeRegistry.js +50 -0
- package/dist/runtime/RuntimeRegistry.js.map +1 -0
- package/dist/runtime/SectionRuntimeEngine.d.ts +45 -0
- package/dist/runtime/SectionRuntimeEngine.d.ts.map +1 -0
- package/dist/runtime/SectionRuntimeEngine.js +81 -0
- package/dist/runtime/SectionRuntimeEngine.js.map +1 -0
- package/dist/runtime/registration-events.d.ts +19 -0
- package/dist/runtime/registration-events.d.ts.map +1 -0
- package/dist/runtime/registration-events.js +4 -0
- package/dist/runtime/registration-events.js.map +1 -0
- package/dist/runtime/runtime-event-guards.d.ts +3 -0
- package/dist/runtime/runtime-event-guards.d.ts.map +1 -0
- package/dist/runtime/runtime-event-guards.js +9 -0
- package/dist/runtime/runtime-event-guards.js.map +1 -0
- package/dist/runtime/tool-host-contract.d.ts +30 -0
- package/dist/runtime/tool-host-contract.d.ts.map +1 -0
- package/dist/runtime/tool-host-contract.js +48 -0
- package/dist/runtime/tool-host-contract.js.map +1 -0
- package/dist/services/HighlightCoordinator.d.ts +9 -0
- package/dist/services/HighlightCoordinator.d.ts.map +1 -1
- package/dist/services/HighlightCoordinator.js +196 -55
- package/dist/services/HighlightCoordinator.js.map +1 -1
- package/dist/services/TTSService.d.ts +21 -1
- package/dist/services/TTSService.d.ts.map +1 -1
- package/dist/services/TTSService.js +356 -117
- package/dist/services/TTSService.js.map +1 -1
- package/dist/services/ThemeProvider.d.ts +1 -0
- package/dist/services/ThemeProvider.d.ts.map +1 -1
- package/dist/services/ThemeProvider.js +82 -46
- package/dist/services/ThemeProvider.js.map +1 -1
- package/dist/services/ToolConfigResolver.d.ts +1 -1
- package/dist/services/ToolConfigResolver.js +1 -1
- package/dist/services/ToolCoordinator.d.ts +1 -1
- package/dist/services/ToolCoordinator.d.ts.map +1 -1
- package/dist/services/ToolRegistry.d.ts +76 -67
- package/dist/services/ToolRegistry.d.ts.map +1 -1
- package/dist/services/ToolRegistry.js +73 -22
- package/dist/services/ToolRegistry.js.map +1 -1
- package/dist/services/ToolkitCoordinator.d.ts +126 -8
- package/dist/services/ToolkitCoordinator.d.ts.map +1 -1
- package/dist/services/ToolkitCoordinator.js +607 -72
- package/dist/services/ToolkitCoordinator.js.map +1 -1
- package/dist/services/createDefaultToolRegistry.d.ts +11 -12
- package/dist/services/createDefaultToolRegistry.d.ts.map +1 -1
- package/dist/services/createDefaultToolRegistry.js +9 -56
- package/dist/services/createDefaultToolRegistry.js.map +1 -1
- package/dist/services/defaultPersonalNeedsProfile.d.ts +4 -0
- package/dist/services/defaultPersonalNeedsProfile.d.ts.map +1 -0
- package/dist/services/defaultPersonalNeedsProfile.js +25 -0
- package/dist/services/defaultPersonalNeedsProfile.js.map +1 -0
- package/dist/services/interfaces.d.ts +62 -2
- package/dist/services/interfaces.d.ts.map +1 -1
- package/dist/services/interfaces.js.map +1 -1
- package/dist/services/pnp-standard-features.d.ts +3 -4
- package/dist/services/pnp-standard-features.d.ts.map +1 -1
- package/dist/services/pnp-standard-features.js +0 -3
- package/dist/services/pnp-standard-features.js.map +1 -1
- package/dist/services/section-controller-types.d.ts +37 -0
- package/dist/services/section-controller-types.d.ts.map +1 -0
- package/dist/services/section-controller-types.js +2 -0
- package/dist/services/section-controller-types.js.map +1 -0
- package/dist/services/tool-config-defaults.d.ts +14 -0
- package/dist/services/tool-config-defaults.d.ts.map +1 -0
- package/dist/services/tool-config-defaults.js +23 -0
- package/dist/services/tool-config-defaults.js.map +1 -0
- package/dist/services/tool-context.d.ts +4 -4
- package/dist/services/tool-context.d.ts.map +1 -1
- package/dist/services/tool-context.js +156 -20
- package/dist/services/tool-context.js.map +1 -1
- package/dist/services/tool-instance-id.d.ts +15 -0
- package/dist/services/tool-instance-id.d.ts.map +1 -0
- package/dist/services/tool-instance-id.js +55 -0
- package/dist/services/tool-instance-id.js.map +1 -0
- package/dist/services/tool-providers/IToolProvider.d.ts +2 -3
- package/dist/services/tool-providers/IToolProvider.d.ts.map +1 -1
- package/dist/services/tool-providers/IToolProvider.js +1 -2
- package/dist/services/tool-providers/IToolProvider.js.map +1 -1
- package/dist/services/tool-providers/MathJsToolProvider.d.ts +25 -0
- package/dist/services/tool-providers/MathJsToolProvider.d.ts.map +1 -0
- package/dist/services/tool-providers/MathJsToolProvider.js +53 -0
- package/dist/services/tool-providers/MathJsToolProvider.js.map +1 -0
- package/dist/services/tool-providers/index.d.ts +2 -0
- package/dist/services/tool-providers/index.d.ts.map +1 -1
- package/dist/services/tool-providers/index.js +1 -0
- package/dist/services/tool-providers/index.js.map +1 -1
- package/dist/services/toolbar-items.d.ts +26 -0
- package/dist/services/toolbar-items.d.ts.map +1 -0
- package/dist/services/toolbar-items.js +10 -0
- package/dist/services/toolbar-items.js.map +1 -0
- package/dist/services/tools-config-normalizer.d.ts +26 -0
- package/dist/services/tools-config-normalizer.d.ts.map +1 -0
- package/dist/services/tools-config-normalizer.js +62 -0
- package/dist/services/tools-config-normalizer.js.map +1 -0
- package/dist/services/tts/browser-provider.d.ts.map +1 -1
- package/dist/services/tts/browser-provider.js +201 -34
- package/dist/services/tts/browser-provider.js.map +1 -1
- package/dist/services/tts/text-processing.d.ts +17 -0
- package/dist/services/tts/text-processing.d.ts.map +1 -0
- package/dist/services/tts/text-processing.js +144 -0
- package/dist/services/tts/text-processing.js.map +1 -0
- package/dist/tools/calculators/ti-provider.d.ts.map +1 -1
- package/dist/tools/calculators/ti-provider.js +127 -4
- package/dist/tools/calculators/ti-provider.js.map +1 -1
- package/dist/tools/client.d.ts +2 -1
- package/dist/tools/client.d.ts.map +1 -1
- package/dist/tools/client.js +2 -1
- package/dist/tools/client.js.map +1 -1
- package/dist/tools/default-tool-module-loaders.d.ts +9 -0
- package/dist/tools/default-tool-module-loaders.d.ts.map +1 -0
- package/dist/tools/default-tool-module-loaders.js +11 -0
- package/dist/tools/default-tool-module-loaders.js.map +1 -0
- package/dist/tools/index.d.ts +2 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +2 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/library-loader.d.ts +1 -1
- package/dist/tools/library-loader.d.ts.map +1 -1
- package/dist/tools/library-loader.js +7 -2
- package/dist/tools/library-loader.js.map +1 -1
- package/dist/tools/registrations/accessibility-tools.d.ts +8 -11
- package/dist/tools/registrations/accessibility-tools.d.ts.map +1 -1
- package/dist/tools/registrations/accessibility-tools.js +108 -120
- package/dist/tools/registrations/accessibility-tools.js.map +1 -1
- package/dist/tools/registrations/calculator.d.ts +2 -2
- package/dist/tools/registrations/calculator.d.ts.map +1 -1
- package/dist/tools/registrations/calculator.js +42 -55
- package/dist/tools/registrations/calculator.js.map +1 -1
- package/dist/tools/registrations/interaction-tools.d.ts.map +1 -1
- package/dist/tools/registrations/interaction-tools.js +77 -52
- package/dist/tools/registrations/interaction-tools.js.map +1 -1
- package/dist/tools/registrations/measurement-tools.d.ts.map +1 -1
- package/dist/tools/registrations/measurement-tools.js +63 -38
- package/dist/tools/registrations/measurement-tools.js.map +1 -1
- package/dist/tools/registrations/subject-specific-tools.d.ts.map +1 -1
- package/dist/tools/registrations/subject-specific-tools.js +63 -38
- package/dist/tools/registrations/subject-specific-tools.js.map +1 -1
- package/dist/tools/registrations/tts.d.ts +1 -1
- package/dist/tools/registrations/tts.d.ts.map +1 -1
- package/dist/tools/registrations/tts.js +171 -41
- package/dist/tools/registrations/tts.js.map +1 -1
- package/dist/tools/response-discovery.d.ts +12 -14
- package/dist/tools/response-discovery.d.ts.map +1 -1
- package/dist/tools/response-discovery.js +23 -80
- package/dist/tools/response-discovery.js.map +1 -1
- package/dist/tools/tool-tag-map.d.ts +3 -3
- package/dist/tools/tool-tag-map.d.ts.map +1 -1
- package/dist/tools/tool-tag-map.js +4 -4
- package/dist/tools/tool-tag-map.js.map +1 -1
- package/dist/tools/types.d.ts +3 -1
- package/dist/tools/types.d.ts.map +1 -1
- package/package.json +28 -27
- package/src/README.md +16 -32
- package/src/components/ItemToolBar.svelte +701 -0
- package/src/components/PieAssessmentToolkit.svelte +610 -0
- package/src/components/SectionToolBar.svelte +58 -0
- package/src/components/ToolButton.svelte +11 -11
- package/src/components/ToolButtonGroup.svelte +27 -11
- package/src/tools/README.md +6 -5
- package/src/tools/calculators/README.md +4 -4
- package/dist/player/AssessmentPlayer.d.ts +0 -397
- package/dist/player/AssessmentPlayer.d.ts.map +0 -1
- package/dist/player/AssessmentPlayer.js +0 -974
- package/dist/player/AssessmentPlayer.js.map +0 -1
- package/dist/player/index.d.ts +0 -17
- package/dist/player/index.d.ts.map +0 -1
- package/dist/player/index.js +0 -15
- package/dist/player/index.js.map +0 -1
- package/dist/player/navigation-types.d.ts +0 -63
- package/dist/player/navigation-types.d.ts.map +0 -1
- package/dist/player/navigation-types.js +0 -7
- package/dist/player/navigation-types.js.map +0 -1
- package/dist/player/qti-navigation.d.ts +0 -29
- package/dist/player/qti-navigation.d.ts.map +0 -1
- package/dist/player/qti-navigation.js +0 -160
- package/dist/player/qti-navigation.js.map +0 -1
- package/dist/reference-layout/index.d.ts +0 -19
- package/dist/reference-layout/index.d.ts.map +0 -1
- package/dist/reference-layout/index.js +0 -20
- package/dist/reference-layout/index.js.map +0 -1
- package/dist/services/AnnotationToolbarConfig.d.ts +0 -134
- package/dist/services/AnnotationToolbarConfig.d.ts.map +0 -1
- package/dist/services/AnnotationToolbarConfig.js +0 -100
- package/dist/services/AnnotationToolbarConfig.js.map +0 -1
- package/dist/services/AssessmentAuthoringService.d.ts +0 -60
- package/dist/services/AssessmentAuthoringService.d.ts.map +0 -1
- package/dist/services/AssessmentAuthoringService.js +0 -183
- package/dist/services/AssessmentAuthoringService.js.map +0 -1
- package/dist/services/PNPMapper.d.ts +0 -81
- package/dist/services/PNPMapper.d.ts.map +0 -1
- package/dist/services/PNPMapper.js +0 -108
- package/dist/services/PNPMapper.js.map +0 -1
- package/dist/services/tts/provider-interface.d.ts +0 -131
- package/dist/services/tts/provider-interface.d.ts.map +0 -1
- package/dist/services/tts/provider-interface.js +0 -10
- package/dist/services/tts/provider-interface.js.map +0 -1
- package/src/components/PNPProfileTester.example.svelte +0 -188
- package/src/components/PNPProfileTester.svelte +0 -397
- package/src/components/PNPProvenanceViewer.svelte +0 -260
- package/src/components/QuestionToolBar.svelte +0 -483
- package/src/player/AssessmentLayout.svelte +0 -68
- package/src/player/README.md +0 -154
- package/src/reference-layout/README.md +0 -135
- package/src/reference-layout/ReferenceLayout.svelte +0 -201
- package/src/reference-layout/components/AssessmentContent.svelte +0 -259
- package/src/reference-layout/components/AssessmentFooter.svelte +0 -89
- package/src/reference-layout/components/AssessmentHeader.svelte +0 -250
- package/src/reference-layout/components/AssessmentNavigation.svelte +0 -134
- package/src/reference-layout/components/AssessmentToolsBar.svelte +0 -176
- package/src/reference-layout/components/ItemPanel.svelte +0 -200
- package/src/reference-layout/components/NotesPanel.svelte +0 -179
- package/src/reference-layout/components/PassagePanel.svelte +0 -76
|
@@ -13,13 +13,14 @@
|
|
|
13
13
|
*
|
|
14
14
|
* Part of PIE Assessment Toolkit.
|
|
15
15
|
*/
|
|
16
|
+
import { normalizeToolsConfig, resolveToolsForLevel, } from "./tools-config-normalizer.js";
|
|
16
17
|
import { AccessibilityCatalogResolver } from "./AccessibilityCatalogResolver.js";
|
|
17
18
|
import { ElementToolStateStore } from "./ElementToolStateStore.js";
|
|
18
19
|
import { HighlightCoordinator } from "./HighlightCoordinator.js";
|
|
19
20
|
import { ToolCoordinator } from "./ToolCoordinator.js";
|
|
20
21
|
import { TTSService } from "./TTSService.js";
|
|
21
22
|
import { BrowserTTSProvider } from "./tts/browser-provider.js";
|
|
22
|
-
import { ToolProviderRegistry, DesmosToolProvider, TIToolProvider, TTSToolProvider, } from "./tool-providers/index.js";
|
|
23
|
+
import { ToolProviderRegistry, DesmosToolProvider, MathJsToolProvider, TIToolProvider, TTSToolProvider, } from "./tool-providers/index.js";
|
|
23
24
|
/**
|
|
24
25
|
* ToolkitCoordinator - Orchestrates all assessment toolkit services
|
|
25
26
|
*
|
|
@@ -54,106 +55,535 @@ export class ToolkitCoordinator {
|
|
|
54
55
|
elementToolStateStore;
|
|
55
56
|
catalogResolver;
|
|
56
57
|
toolProviderRegistry;
|
|
58
|
+
hooks;
|
|
59
|
+
lazyInit;
|
|
57
60
|
/** Track TTS initialization state */
|
|
58
61
|
ttsInitialized = false;
|
|
62
|
+
ttsInitPromise;
|
|
63
|
+
stateLoaded = false;
|
|
64
|
+
stateLoadPromise;
|
|
65
|
+
coordinatorReadyPromise;
|
|
66
|
+
coordinatorReadyNotified = false;
|
|
67
|
+
providerInitPromises = new Map();
|
|
68
|
+
sectionControllers = new Map();
|
|
69
|
+
sectionControllerInitPromises = new Map();
|
|
70
|
+
sectionPersistenceStrategies = new Map();
|
|
71
|
+
sectionControllerLifecycleListeners = new Set();
|
|
59
72
|
/** Callback for floating tools changes */
|
|
60
73
|
floatingToolsChangeCallback = null;
|
|
74
|
+
static resolveConfig(config) {
|
|
75
|
+
const normalized = normalizeToolsConfig(config.tools);
|
|
76
|
+
const defaultProviders = {
|
|
77
|
+
tts: {
|
|
78
|
+
enabled: true,
|
|
79
|
+
backend: "browser",
|
|
80
|
+
},
|
|
81
|
+
calculator: {
|
|
82
|
+
enabled: true,
|
|
83
|
+
provider: "desmos",
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
return {
|
|
87
|
+
...config,
|
|
88
|
+
tools: {
|
|
89
|
+
...normalized,
|
|
90
|
+
providers: {
|
|
91
|
+
...defaultProviders,
|
|
92
|
+
...normalized.providers,
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
accessibility: {
|
|
96
|
+
language: "en-US",
|
|
97
|
+
...(config.accessibility ?? {}),
|
|
98
|
+
catalogs: config.accessibility?.catalogs ?? [],
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
61
102
|
constructor(config) {
|
|
62
103
|
if (!config.assessmentId) {
|
|
63
104
|
throw new Error("ToolkitCoordinator requires assessmentId in config");
|
|
64
105
|
}
|
|
65
|
-
|
|
66
|
-
this.
|
|
106
|
+
const resolvedConfig = ToolkitCoordinator.resolveConfig(config);
|
|
107
|
+
this.assessmentId = resolvedConfig.assessmentId;
|
|
108
|
+
this.config = resolvedConfig;
|
|
109
|
+
this.hooks = resolvedConfig.hooks ?? {};
|
|
110
|
+
this.lazyInit = config.lazyInit === true;
|
|
67
111
|
// Initialize all services
|
|
68
112
|
this.toolCoordinator = new ToolCoordinator();
|
|
69
113
|
this.highlightCoordinator = new HighlightCoordinator();
|
|
70
114
|
this.elementToolStateStore = new ElementToolStateStore();
|
|
71
|
-
this.catalogResolver = new AccessibilityCatalogResolver(
|
|
115
|
+
this.catalogResolver = new AccessibilityCatalogResolver(resolvedConfig.accessibility?.catalogs || [], resolvedConfig.accessibility?.language || "en-US");
|
|
72
116
|
// Initialize tool provider registry
|
|
73
117
|
this.toolProviderRegistry = new ToolProviderRegistry();
|
|
74
118
|
this._registerToolProviders();
|
|
75
119
|
// Initialize TTS service based on config
|
|
76
120
|
this.ttsService = new TTSService();
|
|
77
|
-
|
|
78
|
-
if (
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
121
|
+
this.setupStatePersistenceHooks();
|
|
122
|
+
if (!this.lazyInit) {
|
|
123
|
+
void this.waitUntilReady().catch((err) => {
|
|
124
|
+
console.error("[ToolkitCoordinator] Failed eager initialization:", err);
|
|
125
|
+
this.handleError(err, { phase: "coordinator-ready" });
|
|
82
126
|
});
|
|
83
127
|
}
|
|
84
128
|
}
|
|
129
|
+
async emitTelemetry(eventName, payload) {
|
|
130
|
+
try {
|
|
131
|
+
await this.hooks.onTelemetry?.(eventName, payload);
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
console.warn("[ToolkitCoordinator] telemetry hook failed:", err);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
emitSectionControllerLifecycle(event) {
|
|
138
|
+
for (const listener of this.sectionControllerLifecycleListeners) {
|
|
139
|
+
try {
|
|
140
|
+
listener(event);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
console.warn("[ToolkitCoordinator] section controller lifecycle listener failed:", error);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
handleError(error, context) {
|
|
148
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
149
|
+
try {
|
|
150
|
+
this.hooks.onError?.(normalized, context);
|
|
151
|
+
}
|
|
152
|
+
catch (hookError) {
|
|
153
|
+
console.warn("[ToolkitCoordinator] onError hook failed:", hookError);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
getSectionControllerMapKey(key) {
|
|
157
|
+
return `${key.assessmentId}::${key.sectionId}::${key.attemptId || ""}`;
|
|
158
|
+
}
|
|
159
|
+
createDefaultSectionPersistence() {
|
|
160
|
+
const storage = (() => {
|
|
161
|
+
try {
|
|
162
|
+
if (typeof window === "undefined")
|
|
163
|
+
return null;
|
|
164
|
+
return window.localStorage;
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
})();
|
|
170
|
+
const getStorageKey = (context) => {
|
|
171
|
+
const { assessmentId, sectionId, attemptId } = context.key;
|
|
172
|
+
return `pie:section-controller:v1:${assessmentId}:${sectionId}:${attemptId || "default"}`;
|
|
173
|
+
};
|
|
174
|
+
return {
|
|
175
|
+
async load(context) {
|
|
176
|
+
if (!storage)
|
|
177
|
+
return null;
|
|
178
|
+
const value = storage.getItem(getStorageKey(context));
|
|
179
|
+
if (!value)
|
|
180
|
+
return null;
|
|
181
|
+
try {
|
|
182
|
+
return JSON.parse(value);
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
async save(context, snapshot) {
|
|
189
|
+
if (!storage)
|
|
190
|
+
return;
|
|
191
|
+
storage.setItem(getStorageKey(context), JSON.stringify(snapshot));
|
|
192
|
+
},
|
|
193
|
+
async clear(context) {
|
|
194
|
+
if (!storage)
|
|
195
|
+
return;
|
|
196
|
+
storage.removeItem(getStorageKey(context));
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
async resolveSectionPersistence(context) {
|
|
201
|
+
const cacheKey = this.getSectionControllerMapKey(context.key);
|
|
202
|
+
const existing = this.sectionPersistenceStrategies.get(cacheKey);
|
|
203
|
+
if (existing)
|
|
204
|
+
return existing;
|
|
205
|
+
const defaults = {
|
|
206
|
+
createDefaultPersistence: () => this.createDefaultSectionPersistence(),
|
|
207
|
+
};
|
|
208
|
+
const strategy = (await this.hooks.createSectionPersistence?.(context, defaults)) ??
|
|
209
|
+
(await defaults.createDefaultPersistence());
|
|
210
|
+
this.sectionPersistenceStrategies.set(cacheKey, strategy);
|
|
211
|
+
return strategy;
|
|
212
|
+
}
|
|
213
|
+
setupStatePersistenceHooks() {
|
|
214
|
+
this.elementToolStateStore.setOnStateChange((state) => {
|
|
215
|
+
if (!this.hooks.saveToolState)
|
|
216
|
+
return;
|
|
217
|
+
void Promise.resolve(this.hooks.saveToolState(state)).catch((err) => {
|
|
218
|
+
this.handleError(err, { phase: "state-save" });
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
async ensureStateLoaded() {
|
|
223
|
+
if (this.stateLoaded)
|
|
224
|
+
return;
|
|
225
|
+
if (this.stateLoadPromise)
|
|
226
|
+
return this.stateLoadPromise;
|
|
227
|
+
this.stateLoadPromise = (async () => {
|
|
228
|
+
const loader = this.hooks.loadToolState;
|
|
229
|
+
if (!loader) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
const state = await loader();
|
|
234
|
+
if (state && typeof state === "object") {
|
|
235
|
+
this.elementToolStateStore.loadState(state);
|
|
236
|
+
}
|
|
237
|
+
this.stateLoaded = true;
|
|
238
|
+
await this.emitTelemetry("tool-state-loaded", {
|
|
239
|
+
hasState: Boolean(state),
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
catch (err) {
|
|
243
|
+
this.handleError(err, { phase: "state-load" });
|
|
244
|
+
}
|
|
245
|
+
})().finally(() => {
|
|
246
|
+
this.stateLoadPromise = undefined;
|
|
247
|
+
});
|
|
248
|
+
return this.stateLoadPromise;
|
|
249
|
+
}
|
|
85
250
|
/**
|
|
86
251
|
* Register tool providers in the registry
|
|
87
252
|
*/
|
|
88
253
|
_registerToolProviders() {
|
|
89
254
|
// Register TTS provider
|
|
90
|
-
const ttsConfig = this.config.tools?.tts;
|
|
255
|
+
const ttsConfig = this.config.tools?.providers?.tts;
|
|
91
256
|
if (ttsConfig?.enabled !== false) {
|
|
92
257
|
const backend = ttsConfig?.backend || "browser";
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
258
|
+
const serverProvider = ttsConfig?.serverProvider ||
|
|
259
|
+
ttsConfig?.provider ||
|
|
260
|
+
(backend === "polly" || backend === "google" ? backend : undefined);
|
|
261
|
+
void this.registerProvider("tts", {
|
|
262
|
+
provider: new TTSToolProvider(backend),
|
|
263
|
+
config: {
|
|
264
|
+
backend,
|
|
265
|
+
apiEndpoint: ttsConfig?.apiEndpoint,
|
|
266
|
+
serverProvider,
|
|
267
|
+
providerOptions: backend === "polly"
|
|
268
|
+
? {
|
|
269
|
+
...(ttsConfig?.engine ? { engine: ttsConfig.engine } : {}),
|
|
270
|
+
...(typeof ttsConfig?.sampleRate === "number"
|
|
271
|
+
? { sampleRate: ttsConfig.sampleRate }
|
|
272
|
+
: {}),
|
|
273
|
+
...(ttsConfig?.format ? { format: ttsConfig.format } : {}),
|
|
274
|
+
speechMarkTypes: ttsConfig?.speechMarksMode === "word+sentence"
|
|
275
|
+
? ["word", "sentence"]
|
|
276
|
+
: ["word"],
|
|
277
|
+
}
|
|
278
|
+
: undefined,
|
|
279
|
+
voice: ttsConfig?.defaultVoice,
|
|
280
|
+
rate: ttsConfig?.rate,
|
|
281
|
+
pitch: ttsConfig?.pitch,
|
|
282
|
+
},
|
|
283
|
+
lazy: true,
|
|
284
|
+
authFetcher: ttsConfig?.authFetcher,
|
|
285
|
+
});
|
|
110
286
|
}
|
|
111
287
|
// Register calculator providers
|
|
112
|
-
const
|
|
113
|
-
const calculatorConfig = floatingTools?.calculator;
|
|
288
|
+
const calculatorConfig = this.config.tools?.providers?.calculator;
|
|
114
289
|
if (calculatorConfig?.enabled !== false) {
|
|
115
290
|
const provider = calculatorConfig?.provider || "desmos";
|
|
116
291
|
if (provider === "desmos") {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
catch (error) {
|
|
126
|
-
console.warn("[ToolkitCoordinator] Failed to register Desmos calculator provider:", error);
|
|
127
|
-
}
|
|
292
|
+
void this.registerProvider("calculator-desmos", {
|
|
293
|
+
provider: new DesmosToolProvider(),
|
|
294
|
+
config: {},
|
|
295
|
+
lazy: true,
|
|
296
|
+
authFetcher: calculatorConfig?.authFetcher,
|
|
297
|
+
});
|
|
128
298
|
}
|
|
129
299
|
else if (provider === "ti") {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
300
|
+
void this.registerProvider("calculator-ti", {
|
|
301
|
+
provider: new TIToolProvider(),
|
|
302
|
+
config: {},
|
|
303
|
+
lazy: true,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
else if (provider === "mathjs") {
|
|
307
|
+
void this.registerProvider("calculator-mathjs", {
|
|
308
|
+
provider: new MathJsToolProvider(),
|
|
309
|
+
config: {},
|
|
310
|
+
lazy: true,
|
|
311
|
+
});
|
|
140
312
|
}
|
|
141
313
|
}
|
|
142
314
|
}
|
|
315
|
+
async registerProvider(providerId, config) {
|
|
316
|
+
try {
|
|
317
|
+
this.toolProviderRegistry.register(providerId, config);
|
|
318
|
+
const meta = {
|
|
319
|
+
providerId,
|
|
320
|
+
providerName: config.provider.providerName,
|
|
321
|
+
};
|
|
322
|
+
await this.hooks.onProviderRegistered?.(providerId, meta);
|
|
323
|
+
await this.emitTelemetry("provider-registered", {
|
|
324
|
+
providerId,
|
|
325
|
+
providerName: config.provider.providerName,
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
catch (err) {
|
|
329
|
+
console.warn(`[ToolkitCoordinator] Failed to register provider "${providerId}":`, err);
|
|
330
|
+
this.handleError(err, { phase: "provider-register", providerId });
|
|
331
|
+
this.hooks.onProviderError?.(providerId, err, {
|
|
332
|
+
phase: "provider-register",
|
|
333
|
+
providerId,
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
async ensureProviderReady(providerId) {
|
|
338
|
+
const existing = this.providerInitPromises.get(providerId);
|
|
339
|
+
if (existing)
|
|
340
|
+
return existing;
|
|
341
|
+
const promise = (async () => {
|
|
342
|
+
const provider = await this.toolProviderRegistry.getProvider(providerId, false);
|
|
343
|
+
const meta = {
|
|
344
|
+
providerId,
|
|
345
|
+
providerName: provider.providerName,
|
|
346
|
+
};
|
|
347
|
+
try {
|
|
348
|
+
await this.hooks.onProviderInitStart?.(providerId, meta);
|
|
349
|
+
await this.toolProviderRegistry.initialize(providerId);
|
|
350
|
+
await this.hooks.onProviderReady?.(providerId, meta);
|
|
351
|
+
await this.emitTelemetry("provider-ready", { providerId });
|
|
352
|
+
return provider;
|
|
353
|
+
}
|
|
354
|
+
catch (err) {
|
|
355
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
356
|
+
this.hooks.onProviderError?.(providerId, error, {
|
|
357
|
+
phase: "provider-init",
|
|
358
|
+
providerId,
|
|
359
|
+
});
|
|
360
|
+
this.handleError(error, { phase: "provider-init", providerId });
|
|
361
|
+
throw error;
|
|
362
|
+
}
|
|
363
|
+
})().finally(() => {
|
|
364
|
+
this.providerInitPromises.delete(providerId);
|
|
365
|
+
});
|
|
366
|
+
this.providerInitPromises.set(providerId, promise);
|
|
367
|
+
return promise;
|
|
368
|
+
}
|
|
369
|
+
setHooks(hooks) {
|
|
370
|
+
Object.assign(this.hooks, hooks);
|
|
371
|
+
this.setupStatePersistenceHooks();
|
|
372
|
+
if (hooks.onCoordinatorReady && this.isReady()) {
|
|
373
|
+
void Promise.resolve(hooks.onCoordinatorReady(this)).catch((err) => {
|
|
374
|
+
this.handleError(err, { phase: "coordinator-ready" });
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
getSectionController(args) {
|
|
379
|
+
const key = {
|
|
380
|
+
assessmentId: this.assessmentId,
|
|
381
|
+
sectionId: args.sectionId,
|
|
382
|
+
attemptId: args.attemptId,
|
|
383
|
+
};
|
|
384
|
+
return this.sectionControllers.get(this.getSectionControllerMapKey(key));
|
|
385
|
+
}
|
|
386
|
+
onSectionControllerLifecycle(listener) {
|
|
387
|
+
this.sectionControllerLifecycleListeners.add(listener);
|
|
388
|
+
return () => {
|
|
389
|
+
this.sectionControllerLifecycleListeners.delete(listener);
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
async getOrCreateSectionController(args) {
|
|
393
|
+
const key = {
|
|
394
|
+
assessmentId: this.assessmentId,
|
|
395
|
+
sectionId: args.sectionId,
|
|
396
|
+
attemptId: args.attemptId,
|
|
397
|
+
};
|
|
398
|
+
const mapKey = this.getSectionControllerMapKey(key);
|
|
399
|
+
const existingController = this.sectionControllers.get(mapKey);
|
|
400
|
+
if (existingController) {
|
|
401
|
+
if (args.updateExisting !== false) {
|
|
402
|
+
await existingController.updateInput?.(args.input);
|
|
403
|
+
}
|
|
404
|
+
return existingController;
|
|
405
|
+
}
|
|
406
|
+
const existingPromise = this.sectionControllerInitPromises.get(mapKey);
|
|
407
|
+
if (existingPromise)
|
|
408
|
+
return existingPromise;
|
|
409
|
+
const initPromise = (async () => {
|
|
410
|
+
const context = {
|
|
411
|
+
key,
|
|
412
|
+
coordinator: this,
|
|
413
|
+
input: args.input,
|
|
414
|
+
};
|
|
415
|
+
const persistence = await this.resolveSectionPersistence(context);
|
|
416
|
+
const defaults = {
|
|
417
|
+
createDefaultController: args.createDefaultController,
|
|
418
|
+
};
|
|
419
|
+
const controller = (await this.hooks.createSectionController?.(context, defaults)) ??
|
|
420
|
+
(await defaults.createDefaultController());
|
|
421
|
+
await controller.setPersistenceStrategy?.(persistence);
|
|
422
|
+
await controller.setPersistenceContext?.(context);
|
|
423
|
+
await controller.initialize?.(args.input);
|
|
424
|
+
await controller.hydrate?.();
|
|
425
|
+
this.sectionControllers.set(mapKey, controller);
|
|
426
|
+
this.emitSectionControllerLifecycle({
|
|
427
|
+
type: "ready",
|
|
428
|
+
key,
|
|
429
|
+
controller,
|
|
430
|
+
});
|
|
431
|
+
await this.hooks.onSectionControllerReady?.(context, controller);
|
|
432
|
+
await this.emitTelemetry("section-controller-ready", {
|
|
433
|
+
assessmentId: key.assessmentId,
|
|
434
|
+
sectionId: key.sectionId,
|
|
435
|
+
attemptId: key.attemptId,
|
|
436
|
+
});
|
|
437
|
+
return controller;
|
|
438
|
+
})().catch((err) => {
|
|
439
|
+
this.handleError(err, {
|
|
440
|
+
phase: "section-controller-init",
|
|
441
|
+
details: {
|
|
442
|
+
sectionId: args.sectionId,
|
|
443
|
+
attemptId: args.attemptId,
|
|
444
|
+
},
|
|
445
|
+
});
|
|
446
|
+
throw err;
|
|
447
|
+
}).finally(() => {
|
|
448
|
+
this.sectionControllerInitPromises.delete(mapKey);
|
|
449
|
+
});
|
|
450
|
+
this.sectionControllerInitPromises.set(mapKey, initPromise);
|
|
451
|
+
return initPromise;
|
|
452
|
+
}
|
|
453
|
+
async disposeSectionController(args) {
|
|
454
|
+
const key = {
|
|
455
|
+
assessmentId: this.assessmentId,
|
|
456
|
+
sectionId: args.sectionId,
|
|
457
|
+
attemptId: args.attemptId,
|
|
458
|
+
};
|
|
459
|
+
const mapKey = this.getSectionControllerMapKey(key);
|
|
460
|
+
const controller = this.sectionControllers.get(mapKey);
|
|
461
|
+
if (!controller)
|
|
462
|
+
return;
|
|
463
|
+
const context = {
|
|
464
|
+
key,
|
|
465
|
+
coordinator: this,
|
|
466
|
+
};
|
|
467
|
+
try {
|
|
468
|
+
if (args.persistBeforeDispose !== false) {
|
|
469
|
+
await controller.persist?.();
|
|
470
|
+
}
|
|
471
|
+
await controller.dispose?.();
|
|
472
|
+
await this.hooks.onSectionControllerDispose?.(context, controller);
|
|
473
|
+
await this.emitTelemetry("section-controller-disposed", {
|
|
474
|
+
assessmentId: key.assessmentId,
|
|
475
|
+
sectionId: key.sectionId,
|
|
476
|
+
attemptId: key.attemptId,
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
catch (err) {
|
|
480
|
+
this.handleError(err, {
|
|
481
|
+
phase: "section-controller-dispose",
|
|
482
|
+
details: {
|
|
483
|
+
sectionId: args.sectionId,
|
|
484
|
+
attemptId: args.attemptId,
|
|
485
|
+
},
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
finally {
|
|
489
|
+
this.sectionControllers.delete(mapKey);
|
|
490
|
+
this.emitSectionControllerLifecycle({
|
|
491
|
+
type: "disposed",
|
|
492
|
+
key,
|
|
493
|
+
});
|
|
494
|
+
if (args.clearPersistence) {
|
|
495
|
+
const strategy = this.sectionPersistenceStrategies.get(mapKey);
|
|
496
|
+
await strategy?.clear?.(context);
|
|
497
|
+
}
|
|
498
|
+
this.sectionPersistenceStrategies.delete(mapKey);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
143
501
|
/**
|
|
144
502
|
* Initialize TTS service with provider
|
|
145
503
|
*/
|
|
504
|
+
async ensureTTSReady(config) {
|
|
505
|
+
if (this.ttsInitialized)
|
|
506
|
+
return;
|
|
507
|
+
if (this.ttsInitPromise)
|
|
508
|
+
return this.ttsInitPromise;
|
|
509
|
+
this.ttsInitPromise = this._initializeTTS(config).finally(() => {
|
|
510
|
+
this.ttsInitPromise = undefined;
|
|
511
|
+
});
|
|
512
|
+
return this.ttsInitPromise;
|
|
513
|
+
}
|
|
146
514
|
async _initializeTTS(config) {
|
|
147
515
|
if (this.ttsInitialized)
|
|
148
516
|
return;
|
|
517
|
+
await this.hooks.onBeforeTTSInit?.({
|
|
518
|
+
phase: "tts-init",
|
|
519
|
+
details: {
|
|
520
|
+
backend: config?.backend,
|
|
521
|
+
},
|
|
522
|
+
});
|
|
523
|
+
await this.emitTelemetry("tts-init-start", {
|
|
524
|
+
backend: config?.backend ??
|
|
525
|
+
this.config.tools?.providers?.tts
|
|
526
|
+
?.backend ??
|
|
527
|
+
"browser",
|
|
528
|
+
});
|
|
529
|
+
const resolvedToolConfig = config ||
|
|
530
|
+
(this.config.tools?.providers?.tts || {});
|
|
531
|
+
const resolvedBackend = resolvedToolConfig.backend || "browser";
|
|
532
|
+
const runtimeProvider = resolvedToolConfig.serverProvider ||
|
|
533
|
+
resolvedToolConfig.provider ||
|
|
534
|
+
(resolvedBackend === "polly" || resolvedBackend === "google"
|
|
535
|
+
? resolvedBackend
|
|
536
|
+
: undefined);
|
|
537
|
+
const runtimeTTSConfig = {
|
|
538
|
+
voice: resolvedToolConfig.defaultVoice,
|
|
539
|
+
rate: resolvedToolConfig.rate,
|
|
540
|
+
pitch: resolvedToolConfig.pitch,
|
|
541
|
+
providerOptions: {
|
|
542
|
+
...(resolvedToolConfig.language
|
|
543
|
+
? {
|
|
544
|
+
locale: resolvedToolConfig.language,
|
|
545
|
+
}
|
|
546
|
+
: {}),
|
|
547
|
+
...(resolvedBackend === "polly" && resolvedToolConfig.engine
|
|
548
|
+
? {
|
|
549
|
+
engine: resolvedToolConfig.engine,
|
|
550
|
+
}
|
|
551
|
+
: {}),
|
|
552
|
+
...(resolvedBackend === "polly" &&
|
|
553
|
+
typeof resolvedToolConfig.sampleRate === "number"
|
|
554
|
+
? {
|
|
555
|
+
sampleRate: resolvedToolConfig.sampleRate,
|
|
556
|
+
}
|
|
557
|
+
: {}),
|
|
558
|
+
...(resolvedBackend === "polly" && resolvedToolConfig.format
|
|
559
|
+
? {
|
|
560
|
+
format: resolvedToolConfig.format,
|
|
561
|
+
}
|
|
562
|
+
: {}),
|
|
563
|
+
...(resolvedBackend === "polly"
|
|
564
|
+
? {
|
|
565
|
+
speechMarkTypes: resolvedToolConfig.speechMarksMode === "word+sentence"
|
|
566
|
+
? ["word", "sentence"]
|
|
567
|
+
: ["word"],
|
|
568
|
+
}
|
|
569
|
+
: {}),
|
|
570
|
+
},
|
|
571
|
+
apiEndpoint: resolvedToolConfig.apiEndpoint,
|
|
572
|
+
provider: runtimeProvider,
|
|
573
|
+
language: resolvedToolConfig.language,
|
|
574
|
+
};
|
|
149
575
|
// Try to use TTS provider from registry if available
|
|
150
576
|
if (this.toolProviderRegistry.has("tts")) {
|
|
151
577
|
try {
|
|
152
|
-
const ttsProvider = await this.
|
|
578
|
+
const ttsProvider = await this.ensureProviderReady("tts");
|
|
153
579
|
const providerInstance = await ttsProvider.createInstance();
|
|
154
|
-
await this.ttsService.initialize(providerInstance);
|
|
580
|
+
await this.ttsService.initialize(providerInstance, runtimeTTSConfig);
|
|
155
581
|
this.ttsService.setCatalogResolver(this.catalogResolver);
|
|
156
582
|
this.ttsInitialized = true;
|
|
583
|
+
await this.hooks.onTTSReady?.();
|
|
584
|
+
await this.emitTelemetry("tts-init-success", {
|
|
585
|
+
provider: "registry",
|
|
586
|
+
});
|
|
157
587
|
console.log("[ToolkitCoordinator] TTS initialized via ToolProviderRegistry");
|
|
158
588
|
return;
|
|
159
589
|
}
|
|
@@ -163,14 +593,24 @@ export class ToolkitCoordinator {
|
|
|
163
593
|
}
|
|
164
594
|
// Fallback to browser provider
|
|
165
595
|
const provider = new BrowserTTSProvider();
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
596
|
+
try {
|
|
597
|
+
await this.ttsService.initialize(provider, runtimeTTSConfig);
|
|
598
|
+
this.ttsService.setCatalogResolver(this.catalogResolver);
|
|
599
|
+
this.ttsInitialized = true;
|
|
600
|
+
await this.hooks.onTTSReady?.();
|
|
601
|
+
await this.emitTelemetry("tts-init-success", {
|
|
602
|
+
provider: "browser-fallback",
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
catch (error) {
|
|
606
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
607
|
+
this.hooks.onTTSError?.(normalized, { phase: "tts-init" });
|
|
608
|
+
this.handleError(normalized, { phase: "tts-init" });
|
|
609
|
+
await this.emitTelemetry("tts-init-error", {
|
|
610
|
+
message: normalized.message,
|
|
611
|
+
});
|
|
612
|
+
throw normalized;
|
|
172
613
|
}
|
|
173
|
-
this.ttsInitialized = true;
|
|
174
614
|
}
|
|
175
615
|
/**
|
|
176
616
|
* Get all services as a bundle for section player convenience.
|
|
@@ -193,7 +633,48 @@ export class ToolkitCoordinator {
|
|
|
193
633
|
* @returns Tool provider instance
|
|
194
634
|
*/
|
|
195
635
|
async getToolProvider(providerId) {
|
|
196
|
-
return this.
|
|
636
|
+
return this.ensureProviderReady(providerId);
|
|
637
|
+
}
|
|
638
|
+
async waitUntilReady() {
|
|
639
|
+
if (this.isReady())
|
|
640
|
+
return;
|
|
641
|
+
if (this.coordinatorReadyPromise)
|
|
642
|
+
return this.coordinatorReadyPromise;
|
|
643
|
+
this.coordinatorReadyPromise = (async () => {
|
|
644
|
+
await this.ensureStateLoaded();
|
|
645
|
+
const ttsConfig = this.config.tools?.providers?.tts;
|
|
646
|
+
if (ttsConfig?.enabled !== false) {
|
|
647
|
+
await this.ensureTTSReady(ttsConfig);
|
|
648
|
+
}
|
|
649
|
+
if (!this.coordinatorReadyNotified) {
|
|
650
|
+
this.coordinatorReadyNotified = true;
|
|
651
|
+
await this.hooks.onCoordinatorReady?.(this);
|
|
652
|
+
await this.emitTelemetry("coordinator-ready", {
|
|
653
|
+
assessmentId: this.assessmentId,
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
})().finally(() => {
|
|
657
|
+
this.coordinatorReadyPromise = undefined;
|
|
658
|
+
});
|
|
659
|
+
return this.coordinatorReadyPromise;
|
|
660
|
+
}
|
|
661
|
+
isReady() {
|
|
662
|
+
return this.getInitStatus().coordinator;
|
|
663
|
+
}
|
|
664
|
+
getInitStatus() {
|
|
665
|
+
const providers = {};
|
|
666
|
+
for (const providerId of this.toolProviderRegistry.getProviderIds()) {
|
|
667
|
+
providers[providerId] = this.toolProviderRegistry.isInitialized(providerId);
|
|
668
|
+
}
|
|
669
|
+
return {
|
|
670
|
+
tts: this.ttsInitialized,
|
|
671
|
+
stateLoaded: this.stateLoaded || !this.hooks.loadToolState,
|
|
672
|
+
coordinator: (this.stateLoaded || !this.hooks.loadToolState) &&
|
|
673
|
+
(this.ttsInitialized ||
|
|
674
|
+
this.config.tools?.providers?.tts
|
|
675
|
+
?.enabled === false),
|
|
676
|
+
providers,
|
|
677
|
+
};
|
|
197
678
|
}
|
|
198
679
|
/**
|
|
199
680
|
* Check if a tool is enabled.
|
|
@@ -203,7 +684,7 @@ export class ToolkitCoordinator {
|
|
|
203
684
|
* @returns True if tool is enabled
|
|
204
685
|
*/
|
|
205
686
|
isToolEnabled(toolId) {
|
|
206
|
-
const toolConfig = this.config.tools?.[toolId];
|
|
687
|
+
const toolConfig = this.config.tools?.providers?.[toolId];
|
|
207
688
|
// Enabled by default unless explicitly set to false
|
|
208
689
|
return toolConfig?.enabled !== false;
|
|
209
690
|
}
|
|
@@ -214,7 +695,7 @@ export class ToolkitCoordinator {
|
|
|
214
695
|
* @returns Tool configuration or null if not configured
|
|
215
696
|
*/
|
|
216
697
|
getToolConfig(toolId) {
|
|
217
|
-
return this.config.tools?.[toolId] || null;
|
|
698
|
+
return this.config.tools?.providers?.[toolId] || null;
|
|
218
699
|
}
|
|
219
700
|
/**
|
|
220
701
|
* Update tool configuration.
|
|
@@ -227,9 +708,12 @@ export class ToolkitCoordinator {
|
|
|
227
708
|
// Update config
|
|
228
709
|
const current = this.getToolConfig(toolId) || {};
|
|
229
710
|
if (!this.config.tools) {
|
|
230
|
-
this.config.tools =
|
|
711
|
+
this.config.tools = normalizeToolsConfig();
|
|
712
|
+
}
|
|
713
|
+
if (!this.config.tools.providers) {
|
|
714
|
+
this.config.tools.providers = {};
|
|
231
715
|
}
|
|
232
|
-
this.config.tools[toolId] = { ...current, ...updates };
|
|
716
|
+
this.config.tools.providers[toolId] = { ...current, ...updates };
|
|
233
717
|
// Apply configuration changes to services
|
|
234
718
|
this._applyToolConfigChange(toolId, updates);
|
|
235
719
|
}
|
|
@@ -241,12 +725,12 @@ export class ToolkitCoordinator {
|
|
|
241
725
|
*/
|
|
242
726
|
updateFloatingTools(toolIds) {
|
|
243
727
|
if (!this.config.tools) {
|
|
244
|
-
this.config.tools =
|
|
728
|
+
this.config.tools = normalizeToolsConfig();
|
|
245
729
|
}
|
|
246
|
-
if (!this.config.tools.
|
|
247
|
-
this.config.tools.
|
|
730
|
+
if (!this.config.tools.placement) {
|
|
731
|
+
this.config.tools.placement = normalizeToolsConfig().placement;
|
|
248
732
|
}
|
|
249
|
-
this.config.tools.
|
|
733
|
+
this.config.tools.placement.section = [...toolIds];
|
|
250
734
|
// Notify listener of change
|
|
251
735
|
if (this.floatingToolsChangeCallback) {
|
|
252
736
|
this.floatingToolsChangeCallback(toolIds);
|
|
@@ -258,7 +742,9 @@ export class ToolkitCoordinator {
|
|
|
258
742
|
* @returns Array of enabled tool IDs
|
|
259
743
|
*/
|
|
260
744
|
getFloatingTools() {
|
|
261
|
-
|
|
745
|
+
if (!this.config.tools)
|
|
746
|
+
return [];
|
|
747
|
+
return resolveToolsForLevel(this.config.tools, "section");
|
|
262
748
|
}
|
|
263
749
|
/**
|
|
264
750
|
* Set a callback to be notified when floating tools change.
|
|
@@ -279,12 +765,16 @@ export class ToolkitCoordinator {
|
|
|
279
765
|
* Apply tool configuration changes to underlying services.
|
|
280
766
|
* Called after updateToolConfig().
|
|
281
767
|
*/
|
|
282
|
-
_applyToolConfigChange(toolId,
|
|
768
|
+
_applyToolConfigChange(toolId, _updates) {
|
|
283
769
|
// Apply configuration changes based on tool
|
|
284
770
|
switch (toolId) {
|
|
285
771
|
case "tts":
|
|
286
|
-
|
|
287
|
-
|
|
772
|
+
void this._reconfigureTTSProvider().then(async () => {
|
|
773
|
+
const ttsConfig = this.config.tools?.providers?.tts;
|
|
774
|
+
if (!this.lazyInit && ttsConfig?.enabled !== false) {
|
|
775
|
+
await this.ensureTTSReady(ttsConfig);
|
|
776
|
+
}
|
|
777
|
+
});
|
|
288
778
|
break;
|
|
289
779
|
case "answerEliminator":
|
|
290
780
|
// Future: Could notify answer eliminator tools of strategy change
|
|
@@ -292,5 +782,50 @@ export class ToolkitCoordinator {
|
|
|
292
782
|
// Add cases for other tools as needed
|
|
293
783
|
}
|
|
294
784
|
}
|
|
785
|
+
async _reconfigureTTSProvider() {
|
|
786
|
+
this.ttsInitialized = false;
|
|
787
|
+
this.ttsInitPromise = undefined;
|
|
788
|
+
try {
|
|
789
|
+
this.ttsService.stop();
|
|
790
|
+
}
|
|
791
|
+
catch {
|
|
792
|
+
// noop: stop best effort
|
|
793
|
+
}
|
|
794
|
+
if (this.toolProviderRegistry.has("tts")) {
|
|
795
|
+
await this.toolProviderRegistry.unregister("tts");
|
|
796
|
+
}
|
|
797
|
+
const ttsConfig = this.config.tools?.providers?.tts;
|
|
798
|
+
if (ttsConfig?.enabled === false)
|
|
799
|
+
return;
|
|
800
|
+
const backend = ttsConfig?.backend || "browser";
|
|
801
|
+
const serverProvider = ttsConfig?.serverProvider ||
|
|
802
|
+
ttsConfig?.provider ||
|
|
803
|
+
(backend === "polly" || backend === "google" ? backend : undefined);
|
|
804
|
+
await this.registerProvider("tts", {
|
|
805
|
+
provider: new TTSToolProvider(backend),
|
|
806
|
+
config: {
|
|
807
|
+
backend,
|
|
808
|
+
apiEndpoint: ttsConfig?.apiEndpoint,
|
|
809
|
+
serverProvider,
|
|
810
|
+
providerOptions: backend === "polly"
|
|
811
|
+
? {
|
|
812
|
+
...(ttsConfig?.engine ? { engine: ttsConfig.engine } : {}),
|
|
813
|
+
...(typeof ttsConfig?.sampleRate === "number"
|
|
814
|
+
? { sampleRate: ttsConfig.sampleRate }
|
|
815
|
+
: {}),
|
|
816
|
+
...(ttsConfig?.format ? { format: ttsConfig.format } : {}),
|
|
817
|
+
speechMarkTypes: ttsConfig?.speechMarksMode === "word+sentence"
|
|
818
|
+
? ["word", "sentence"]
|
|
819
|
+
: ["word"],
|
|
820
|
+
}
|
|
821
|
+
: undefined,
|
|
822
|
+
voice: ttsConfig?.defaultVoice,
|
|
823
|
+
rate: ttsConfig?.rate,
|
|
824
|
+
pitch: ttsConfig?.pitch,
|
|
825
|
+
},
|
|
826
|
+
lazy: true,
|
|
827
|
+
authFetcher: ttsConfig?.authFetcher,
|
|
828
|
+
});
|
|
829
|
+
}
|
|
295
830
|
}
|
|
296
831
|
//# sourceMappingURL=ToolkitCoordinator.js.map
|