@magmamath/students-features 1.6.4 → 1.7.0-rc.1
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/commonjs/features/chatbot/index.js +0 -7
- package/dist/commonjs/features/chatbot/index.js.map +1 -1
- package/dist/commonjs/features/keyboard/constants/groups/currency.constants.js +3 -0
- package/dist/commonjs/features/keyboard/constants/groups/currency.constants.js.map +1 -1
- package/dist/commonjs/features/keyboard/constants/keyboard.helpers.js +2 -1
- package/dist/commonjs/features/keyboard/constants/keyboard.helpers.js.map +1 -1
- package/dist/commonjs/features/keyboard/constants/keys/units.constants.js +12 -4
- package/dist/commonjs/features/keyboard/constants/keys/units.constants.js.map +1 -1
- package/dist/commonjs/features/voice/recording/components/VoiceRecordButton.js +1 -2
- package/dist/commonjs/features/voice/recording/components/VoiceRecordButton.js.map +1 -1
- package/dist/commonjs/lib/effector/createControllerEffect.js +6 -7
- package/dist/commonjs/lib/effector/createControllerEffect.js.map +1 -1
- package/dist/commonjs/shared/icons/keyboard/DinarIcon.js +34 -0
- package/dist/commonjs/shared/icons/keyboard/DinarIcon.js.map +1 -0
- package/dist/commonjs/shared/icons/keyboard/FrancIcon.js +1 -1
- package/dist/commonjs/shared/icons/keyboard/RappenIcon.js +2 -2
- package/dist/commonjs/shared/icons/keyboard/RappenIcon.js.map +1 -1
- package/dist/module/features/chatbot/index.js +0 -1
- package/dist/module/features/chatbot/index.js.map +1 -1
- package/dist/module/features/keyboard/constants/groups/currency.constants.js +3 -0
- package/dist/module/features/keyboard/constants/groups/currency.constants.js.map +1 -1
- package/dist/module/features/keyboard/constants/keyboard.helpers.js +2 -1
- package/dist/module/features/keyboard/constants/keyboard.helpers.js.map +1 -1
- package/dist/module/features/keyboard/constants/keys/units.constants.js +12 -4
- package/dist/module/features/keyboard/constants/keys/units.constants.js.map +1 -1
- package/dist/module/features/voice/recording/components/VoiceRecordButton.js +1 -2
- package/dist/module/features/voice/recording/components/VoiceRecordButton.js.map +1 -1
- package/dist/module/lib/effector/createControllerEffect.js +6 -7
- package/dist/module/lib/effector/createControllerEffect.js.map +1 -1
- package/dist/module/shared/icons/keyboard/DinarIcon.js +26 -0
- package/dist/module/shared/icons/keyboard/DinarIcon.js.map +1 -0
- package/dist/module/shared/icons/keyboard/FrancIcon.js +1 -1
- package/dist/module/shared/icons/keyboard/RappenIcon.js +2 -2
- package/dist/module/shared/icons/keyboard/RappenIcon.js.map +1 -1
- package/dist/typescript/commonjs/features/chatbot/index.d.ts +1 -2
- package/dist/typescript/commonjs/features/chatbot/index.d.ts.map +1 -1
- package/dist/typescript/commonjs/features/chatbot/types/api.types.d.ts +0 -1
- package/dist/typescript/commonjs/features/chatbot/types/api.types.d.ts.map +1 -1
- package/dist/typescript/commonjs/features/keyboard/constants/groups/currency.constants.d.ts.map +1 -1
- package/dist/typescript/commonjs/features/keyboard/constants/keyboard.helpers.d.ts.map +1 -1
- package/dist/typescript/commonjs/features/keyboard/constants/keys/units.constants.d.ts.map +1 -1
- package/dist/typescript/commonjs/features/pmProgress/shared/pmProgress.constants.d.ts +1 -1
- package/dist/typescript/commonjs/features/voice/recording/components/VoiceRecordButton.d.ts.map +1 -1
- package/dist/typescript/commonjs/lib/effector/createControllerEffect.d.ts +0 -1
- package/dist/typescript/commonjs/lib/effector/createControllerEffect.d.ts.map +1 -1
- package/dist/typescript/commonjs/shared/icons/keyboard/DinarIcon.d.ts +4 -0
- package/dist/typescript/commonjs/shared/icons/keyboard/DinarIcon.d.ts.map +1 -0
- package/dist/typescript/module/features/chatbot/index.d.ts +1 -2
- package/dist/typescript/module/features/chatbot/index.d.ts.map +1 -1
- package/dist/typescript/module/features/chatbot/types/api.types.d.ts +0 -1
- package/dist/typescript/module/features/chatbot/types/api.types.d.ts.map +1 -1
- package/dist/typescript/module/features/keyboard/constants/groups/currency.constants.d.ts.map +1 -1
- package/dist/typescript/module/features/keyboard/constants/keyboard.helpers.d.ts.map +1 -1
- package/dist/typescript/module/features/keyboard/constants/keys/units.constants.d.ts.map +1 -1
- package/dist/typescript/module/features/pmProgress/shared/pmProgress.constants.d.ts +1 -1
- package/dist/typescript/module/features/voice/recording/components/VoiceRecordButton.d.ts.map +1 -1
- package/dist/typescript/module/lib/effector/createControllerEffect.d.ts +0 -1
- package/dist/typescript/module/lib/effector/createControllerEffect.d.ts.map +1 -1
- package/dist/typescript/module/shared/icons/keyboard/DinarIcon.d.ts +4 -0
- package/dist/typescript/module/shared/icons/keyboard/DinarIcon.d.ts.map +1 -0
- package/package.json +3 -3
- package/src/features/chatbot/index.ts +0 -2
- package/src/features/chatbot/types/api.types.ts +0 -1
- package/src/features/keyboard/constants/groups/currency.constants.ts +1 -0
- package/src/features/keyboard/constants/keyboard.helpers.ts +1 -0
- package/src/features/keyboard/constants/keys/units.constants.tsx +10 -4
- package/src/features/voice/recording/components/VoiceRecordButton.tsx +3 -2
- package/src/i18n/.generated/schema.json +6 -7
- package/src/lib/effector/createControllerEffect.ts +6 -13
- package/src/shared/icons/keyboard/DinarIcon.tsx +20 -0
- package/src/shared/icons/keyboard/FrancIcon.tsx +1 -1
- package/src/shared/icons/keyboard/RappenIcon.tsx +2 -2
- package/dist/commonjs/features/chatbot/model/VoiceTranscriptSnapshotModel.js +0 -126
- package/dist/commonjs/features/chatbot/model/VoiceTranscriptSnapshotModel.js.map +0 -1
- package/dist/module/features/chatbot/model/VoiceTranscriptSnapshotModel.js +0 -121
- package/dist/module/features/chatbot/model/VoiceTranscriptSnapshotModel.js.map +0 -1
- package/dist/typescript/commonjs/features/chatbot/model/VoiceTranscriptSnapshotModel.d.ts +0 -32
- package/dist/typescript/commonjs/features/chatbot/model/VoiceTranscriptSnapshotModel.d.ts.map +0 -1
- package/dist/typescript/module/features/chatbot/model/VoiceTranscriptSnapshotModel.d.ts +0 -32
- package/dist/typescript/module/features/chatbot/model/VoiceTranscriptSnapshotModel.d.ts.map +0 -1
- package/src/features/chatbot/model/VoiceTranscriptSnapshotModel.ts +0 -159
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.VoiceTranscriptSnapshotModel = void 0;
|
|
7
|
-
var _effector = require("effector");
|
|
8
|
-
var _types = require("../../voice/types.js");
|
|
9
|
-
var _i18n = require("../../../i18n/i18n.js");
|
|
10
|
-
const TRANSCRIPT_RETRY_INTERVAL_MS = 2000;
|
|
11
|
-
const TRANSCRIPT_MAX_RETRIES = 20;
|
|
12
|
-
const CHAT_WITH_VOICE_TIMEOUT_MS = 60_000;
|
|
13
|
-
// Voice key: `${assignmentId}-${problemId}-${attemptsCount}`
|
|
14
|
-
// Chat key: `${assignmentId}-${problemId}` — survives attempt increments and voice deletion.
|
|
15
|
-
const toChatKey = voiceKey => {
|
|
16
|
-
const lastDashIndex = voiceKey.lastIndexOf('-');
|
|
17
|
-
if (lastDashIndex <= 0) return null;
|
|
18
|
-
return voiceKey.slice(0, lastDashIndex);
|
|
19
|
-
};
|
|
20
|
-
class VoiceTranscriptSnapshotModel {
|
|
21
|
-
fetchTranscript = audioFileId => {
|
|
22
|
-
const cached = this.transcriptByAudioFileId.get(audioFileId);
|
|
23
|
-
if (cached) return cached;
|
|
24
|
-
const promise = this.pollTranscript(audioFileId).catch(() => undefined);
|
|
25
|
-
this.transcriptByAudioFileId.set(audioFileId, promise);
|
|
26
|
-
return promise;
|
|
27
|
-
};
|
|
28
|
-
getTranscriptForCurrent = async () => {
|
|
29
|
-
const voiceKey = this.voiceRecord.$currentKey.getState();
|
|
30
|
-
if (!voiceKey) return undefined;
|
|
31
|
-
const chatKey = toChatKey(voiceKey);
|
|
32
|
-
if (chatKey) {
|
|
33
|
-
const snapshot = this.snapshotByChatKey.get(chatKey);
|
|
34
|
-
if (snapshot) return snapshot;
|
|
35
|
-
}
|
|
36
|
-
const record = this.voiceRecord.collection.get(voiceKey);
|
|
37
|
-
if (!record) return undefined;
|
|
38
|
-
const audioFileId = await this.resolveAudioFileId(record);
|
|
39
|
-
if (!audioFileId) return undefined;
|
|
40
|
-
return this.fetchTranscript(audioFileId);
|
|
41
|
-
};
|
|
42
|
-
wrapRequestHint = apiFn => {
|
|
43
|
-
return async (params, requestConfig) => {
|
|
44
|
-
if (this.hasPendingVoice()) {
|
|
45
|
-
requestConfig?.extendTimeoutMs?.(CHAT_WITH_VOICE_TIMEOUT_MS);
|
|
46
|
-
}
|
|
47
|
-
const voiceTranscript = await this.getTranscriptForCurrent();
|
|
48
|
-
return apiFn(voiceTranscript ? {
|
|
49
|
-
...params,
|
|
50
|
-
voiceTranscript
|
|
51
|
-
} : params, requestConfig);
|
|
52
|
-
};
|
|
53
|
-
};
|
|
54
|
-
hasPendingVoice() {
|
|
55
|
-
const voiceKey = this.voiceRecord.$currentKey.getState();
|
|
56
|
-
if (!voiceKey) return false;
|
|
57
|
-
const chatKey = toChatKey(voiceKey);
|
|
58
|
-
if (chatKey && this.snapshotByChatKey.has(chatKey)) return true;
|
|
59
|
-
return !!this.voiceRecord.collection.get(voiceKey);
|
|
60
|
-
}
|
|
61
|
-
constructor({
|
|
62
|
-
voiceRecord,
|
|
63
|
-
api,
|
|
64
|
-
errorHandler
|
|
65
|
-
}) {
|
|
66
|
-
this.voiceRecord = voiceRecord;
|
|
67
|
-
this.api = api;
|
|
68
|
-
this.errorHandler = errorHandler;
|
|
69
|
-
(0, _effector.sample)({
|
|
70
|
-
clock: voiceRecord.setCurrentRecord,
|
|
71
|
-
source: voiceRecord.$currentKey,
|
|
72
|
-
filter: (_, record) => !!record,
|
|
73
|
-
fn: (voiceKey, record) => ({
|
|
74
|
-
voiceKey,
|
|
75
|
-
record
|
|
76
|
-
}),
|
|
77
|
-
target: this.captureSnapshotFx
|
|
78
|
-
});
|
|
79
|
-
(0, _effector.sample)({
|
|
80
|
-
clock: voiceRecord.deleteCurrentRecording,
|
|
81
|
-
source: voiceRecord.$currentKey,
|
|
82
|
-
target: this.clearSnapshotFx
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
transcriptByAudioFileId = new Map();
|
|
86
|
-
snapshotByChatKey = new Map();
|
|
87
|
-
captureSnapshotFx = (0, _effector.createEffect)(({
|
|
88
|
-
voiceKey,
|
|
89
|
-
record
|
|
90
|
-
}) => {
|
|
91
|
-
const chatKey = toChatKey(voiceKey);
|
|
92
|
-
if (!chatKey) return;
|
|
93
|
-
const transcriptPromise = this.resolveAudioFileId(record).then(audioFileId => audioFileId ? this.fetchTranscript(audioFileId) : undefined);
|
|
94
|
-
this.snapshotByChatKey.set(chatKey, transcriptPromise);
|
|
95
|
-
});
|
|
96
|
-
clearSnapshotFx = (0, _effector.createEffect)(voiceKey => {
|
|
97
|
-
const chatKey = toChatKey(voiceKey);
|
|
98
|
-
if (chatKey) this.snapshotByChatKey.delete(chatKey);
|
|
99
|
-
});
|
|
100
|
-
async pollTranscript(audioFileId) {
|
|
101
|
-
for (let attempt = 0; attempt < TRANSCRIPT_MAX_RETRIES; attempt++) {
|
|
102
|
-
await new Promise(resolve => setTimeout(resolve, TRANSCRIPT_RETRY_INTERVAL_MS));
|
|
103
|
-
try {
|
|
104
|
-
const response = await this.api.getAudioFileTranscript(audioFileId);
|
|
105
|
-
if (response.status === _types.TranscriptionStatus.COMPLETED) return response.text;
|
|
106
|
-
if (response.status === _types.TranscriptionStatus.FAILED) break;
|
|
107
|
-
} catch {
|
|
108
|
-
// network/request failure — retry on next iteration
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
this.errorHandler?.((0, _i18n.getText)('voice.transcriptionError'));
|
|
112
|
-
return undefined;
|
|
113
|
-
}
|
|
114
|
-
async resolveAudioFileId(record) {
|
|
115
|
-
if (record.id) return record.id;
|
|
116
|
-
if (!record.audioUploadPromise) return undefined;
|
|
117
|
-
try {
|
|
118
|
-
const result = await record.audioUploadPromise;
|
|
119
|
-
return result?.id;
|
|
120
|
-
} catch {
|
|
121
|
-
return undefined;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
exports.VoiceTranscriptSnapshotModel = VoiceTranscriptSnapshotModel;
|
|
126
|
-
//# sourceMappingURL=VoiceTranscriptSnapshotModel.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["_effector","require","_types","_i18n","TRANSCRIPT_RETRY_INTERVAL_MS","TRANSCRIPT_MAX_RETRIES","CHAT_WITH_VOICE_TIMEOUT_MS","toChatKey","voiceKey","lastDashIndex","lastIndexOf","slice","VoiceTranscriptSnapshotModel","fetchTranscript","audioFileId","cached","transcriptByAudioFileId","get","promise","pollTranscript","catch","undefined","set","getTranscriptForCurrent","voiceRecord","$currentKey","getState","chatKey","snapshot","snapshotByChatKey","record","collection","resolveAudioFileId","wrapRequestHint","apiFn","params","requestConfig","hasPendingVoice","extendTimeoutMs","voiceTranscript","has","constructor","api","errorHandler","sample","clock","setCurrentRecord","source","filter","_","fn","target","captureSnapshotFx","deleteCurrentRecording","clearSnapshotFx","Map","createEffect","transcriptPromise","then","delete","attempt","Promise","resolve","setTimeout","response","getAudioFileTranscript","status","TranscriptionStatus","COMPLETED","text","FAILED","getText","id","audioUploadPromise","result","exports"],"sourceRoot":"../../../../../src","sources":["features/chatbot/model/VoiceTranscriptSnapshotModel.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { createEffect, sample } from 'effector';
|
|
4
|
-
import { TranscriptionStatus } from "../../voice/types.js";
|
|
5
|
-
import { getText } from "../../../i18n/i18n.js";
|
|
6
|
-
const TRANSCRIPT_RETRY_INTERVAL_MS = 2000;
|
|
7
|
-
const TRANSCRIPT_MAX_RETRIES = 20;
|
|
8
|
-
const CHAT_WITH_VOICE_TIMEOUT_MS = 60_000;
|
|
9
|
-
// Voice key: `${assignmentId}-${problemId}-${attemptsCount}`
|
|
10
|
-
// Chat key: `${assignmentId}-${problemId}` — survives attempt increments and voice deletion.
|
|
11
|
-
const toChatKey = voiceKey => {
|
|
12
|
-
const lastDashIndex = voiceKey.lastIndexOf('-');
|
|
13
|
-
if (lastDashIndex <= 0) return null;
|
|
14
|
-
return voiceKey.slice(0, lastDashIndex);
|
|
15
|
-
};
|
|
16
|
-
export class VoiceTranscriptSnapshotModel {
|
|
17
|
-
fetchTranscript = audioFileId => {
|
|
18
|
-
const cached = this.transcriptByAudioFileId.get(audioFileId);
|
|
19
|
-
if (cached) return cached;
|
|
20
|
-
const promise = this.pollTranscript(audioFileId).catch(() => undefined);
|
|
21
|
-
this.transcriptByAudioFileId.set(audioFileId, promise);
|
|
22
|
-
return promise;
|
|
23
|
-
};
|
|
24
|
-
getTranscriptForCurrent = async () => {
|
|
25
|
-
const voiceKey = this.voiceRecord.$currentKey.getState();
|
|
26
|
-
if (!voiceKey) return undefined;
|
|
27
|
-
const chatKey = toChatKey(voiceKey);
|
|
28
|
-
if (chatKey) {
|
|
29
|
-
const snapshot = this.snapshotByChatKey.get(chatKey);
|
|
30
|
-
if (snapshot) return snapshot;
|
|
31
|
-
}
|
|
32
|
-
const record = this.voiceRecord.collection.get(voiceKey);
|
|
33
|
-
if (!record) return undefined;
|
|
34
|
-
const audioFileId = await this.resolveAudioFileId(record);
|
|
35
|
-
if (!audioFileId) return undefined;
|
|
36
|
-
return this.fetchTranscript(audioFileId);
|
|
37
|
-
};
|
|
38
|
-
wrapRequestHint = apiFn => {
|
|
39
|
-
return async (params, requestConfig) => {
|
|
40
|
-
if (this.hasPendingVoice()) {
|
|
41
|
-
requestConfig?.extendTimeoutMs?.(CHAT_WITH_VOICE_TIMEOUT_MS);
|
|
42
|
-
}
|
|
43
|
-
const voiceTranscript = await this.getTranscriptForCurrent();
|
|
44
|
-
return apiFn(voiceTranscript ? {
|
|
45
|
-
...params,
|
|
46
|
-
voiceTranscript
|
|
47
|
-
} : params, requestConfig);
|
|
48
|
-
};
|
|
49
|
-
};
|
|
50
|
-
hasPendingVoice() {
|
|
51
|
-
const voiceKey = this.voiceRecord.$currentKey.getState();
|
|
52
|
-
if (!voiceKey) return false;
|
|
53
|
-
const chatKey = toChatKey(voiceKey);
|
|
54
|
-
if (chatKey && this.snapshotByChatKey.has(chatKey)) return true;
|
|
55
|
-
return !!this.voiceRecord.collection.get(voiceKey);
|
|
56
|
-
}
|
|
57
|
-
constructor({
|
|
58
|
-
voiceRecord,
|
|
59
|
-
api,
|
|
60
|
-
errorHandler
|
|
61
|
-
}) {
|
|
62
|
-
this.voiceRecord = voiceRecord;
|
|
63
|
-
this.api = api;
|
|
64
|
-
this.errorHandler = errorHandler;
|
|
65
|
-
sample({
|
|
66
|
-
clock: voiceRecord.setCurrentRecord,
|
|
67
|
-
source: voiceRecord.$currentKey,
|
|
68
|
-
filter: (_, record) => !!record,
|
|
69
|
-
fn: (voiceKey, record) => ({
|
|
70
|
-
voiceKey,
|
|
71
|
-
record
|
|
72
|
-
}),
|
|
73
|
-
target: this.captureSnapshotFx
|
|
74
|
-
});
|
|
75
|
-
sample({
|
|
76
|
-
clock: voiceRecord.deleteCurrentRecording,
|
|
77
|
-
source: voiceRecord.$currentKey,
|
|
78
|
-
target: this.clearSnapshotFx
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
transcriptByAudioFileId = new Map();
|
|
82
|
-
snapshotByChatKey = new Map();
|
|
83
|
-
captureSnapshotFx = createEffect(({
|
|
84
|
-
voiceKey,
|
|
85
|
-
record
|
|
86
|
-
}) => {
|
|
87
|
-
const chatKey = toChatKey(voiceKey);
|
|
88
|
-
if (!chatKey) return;
|
|
89
|
-
const transcriptPromise = this.resolveAudioFileId(record).then(audioFileId => audioFileId ? this.fetchTranscript(audioFileId) : undefined);
|
|
90
|
-
this.snapshotByChatKey.set(chatKey, transcriptPromise);
|
|
91
|
-
});
|
|
92
|
-
clearSnapshotFx = createEffect(voiceKey => {
|
|
93
|
-
const chatKey = toChatKey(voiceKey);
|
|
94
|
-
if (chatKey) this.snapshotByChatKey.delete(chatKey);
|
|
95
|
-
});
|
|
96
|
-
async pollTranscript(audioFileId) {
|
|
97
|
-
for (let attempt = 0; attempt < TRANSCRIPT_MAX_RETRIES; attempt++) {
|
|
98
|
-
await new Promise(resolve => setTimeout(resolve, TRANSCRIPT_RETRY_INTERVAL_MS));
|
|
99
|
-
try {
|
|
100
|
-
const response = await this.api.getAudioFileTranscript(audioFileId);
|
|
101
|
-
if (response.status === TranscriptionStatus.COMPLETED) return response.text;
|
|
102
|
-
if (response.status === TranscriptionStatus.FAILED) break;
|
|
103
|
-
} catch {
|
|
104
|
-
// network/request failure — retry on next iteration
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
this.errorHandler?.(getText('voice.transcriptionError'));
|
|
108
|
-
return undefined;
|
|
109
|
-
}
|
|
110
|
-
async resolveAudioFileId(record) {
|
|
111
|
-
if (record.id) return record.id;
|
|
112
|
-
if (!record.audioUploadPromise) return undefined;
|
|
113
|
-
try {
|
|
114
|
-
const result = await record.audioUploadPromise;
|
|
115
|
-
return result?.id;
|
|
116
|
-
} catch {
|
|
117
|
-
return undefined;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
//# sourceMappingURL=VoiceTranscriptSnapshotModel.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["createEffect","sample","TranscriptionStatus","getText","TRANSCRIPT_RETRY_INTERVAL_MS","TRANSCRIPT_MAX_RETRIES","CHAT_WITH_VOICE_TIMEOUT_MS","toChatKey","voiceKey","lastDashIndex","lastIndexOf","slice","VoiceTranscriptSnapshotModel","fetchTranscript","audioFileId","cached","transcriptByAudioFileId","get","promise","pollTranscript","catch","undefined","set","getTranscriptForCurrent","voiceRecord","$currentKey","getState","chatKey","snapshot","snapshotByChatKey","record","collection","resolveAudioFileId","wrapRequestHint","apiFn","params","requestConfig","hasPendingVoice","extendTimeoutMs","voiceTranscript","has","constructor","api","errorHandler","clock","setCurrentRecord","source","filter","_","fn","target","captureSnapshotFx","deleteCurrentRecording","clearSnapshotFx","Map","transcriptPromise","then","delete","attempt","Promise","resolve","setTimeout","response","getAudioFileTranscript","status","COMPLETED","text","FAILED","id","audioUploadPromise","result"],"sourceRoot":"../../../../../src","sources":["features/chatbot/model/VoiceTranscriptSnapshotModel.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { AudioTranscriptResponse } from '../../voice/types';
|
|
2
|
-
import { VoiceRecordModel } from '../../voice/recording/model/VoiceRecord.model';
|
|
3
|
-
import { RequestOptionalConfig } from '../../../lib/effector/createControllerEffect';
|
|
4
|
-
type GetTranscriptApi = {
|
|
5
|
-
getAudioFileTranscript: (audioFileId: string) => Promise<AudioTranscriptResponse>;
|
|
6
|
-
};
|
|
7
|
-
export type VoiceTranscriptErrorHandler = (errorMessage: string) => void;
|
|
8
|
-
type VoiceTranscriptSnapshotModelProps = {
|
|
9
|
-
voiceRecord: VoiceRecordModel;
|
|
10
|
-
api: GetTranscriptApi;
|
|
11
|
-
errorHandler?: VoiceTranscriptErrorHandler;
|
|
12
|
-
};
|
|
13
|
-
export declare class VoiceTranscriptSnapshotModel {
|
|
14
|
-
readonly fetchTranscript: (audioFileId: string) => Promise<string | undefined>;
|
|
15
|
-
readonly getTranscriptForCurrent: () => Promise<string | undefined>;
|
|
16
|
-
readonly wrapRequestHint: <P extends object, R>(apiFn: (params: P & {
|
|
17
|
-
voiceTranscript?: string;
|
|
18
|
-
}, requestConfig?: RequestOptionalConfig) => Promise<R>) => (params: P, requestConfig?: RequestOptionalConfig) => Promise<R>;
|
|
19
|
-
private hasPendingVoice;
|
|
20
|
-
constructor({ voiceRecord, api, errorHandler }: VoiceTranscriptSnapshotModelProps);
|
|
21
|
-
private readonly voiceRecord;
|
|
22
|
-
private readonly api;
|
|
23
|
-
private readonly errorHandler?;
|
|
24
|
-
private readonly transcriptByAudioFileId;
|
|
25
|
-
private readonly snapshotByChatKey;
|
|
26
|
-
private readonly captureSnapshotFx;
|
|
27
|
-
private readonly clearSnapshotFx;
|
|
28
|
-
private pollTranscript;
|
|
29
|
-
private resolveAudioFileId;
|
|
30
|
-
}
|
|
31
|
-
export {};
|
|
32
|
-
//# sourceMappingURL=VoiceTranscriptSnapshotModel.d.ts.map
|
package/dist/typescript/commonjs/features/chatbot/model/VoiceTranscriptSnapshotModel.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"VoiceTranscriptSnapshotModel.d.ts","sourceRoot":"","sources":["../../../../../../src/features/chatbot/model/VoiceTranscriptSnapshotModel.ts"],"names":[],"mappings":"AACA,OAAO,EACL,uBAAuB,EAGxB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,+CAA+C,CAAA;AAChF,OAAO,EAAE,qBAAqB,EAAE,MAAM,8CAA8C,CAAA;AAOpF,KAAK,gBAAgB,GAAG;IACtB,sBAAsB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,uBAAuB,CAAC,CAAA;CAClF,CAAA;AAOD,MAAM,MAAM,2BAA2B,GAAG,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAA;AAExE,KAAK,iCAAiC,GAAG;IACvC,WAAW,EAAE,gBAAgB,CAAA;IAC7B,GAAG,EAAE,gBAAgB,CAAA;IACrB,YAAY,CAAC,EAAE,2BAA2B,CAAA;CAC3C,CAAA;AAUD,qBAAa,4BAA4B;IACvC,SAAgB,eAAe,gBAAiB,MAAM,KAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAOnF;IAED,SAAgB,uBAAuB,QAAa,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAiB9E;IAED,SAAgB,eAAe,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAC7C,CACL,MAAM,EAAE,CAAC,GAAG;QAAE,eAAe,CAAC,EAAE,MAAM,CAAA;KAAE,EACxC,aAAa,CAAC,EAAE,qBAAqB,KAClC,OAAO,CAAC,CAAC,CAAC,cAEO,CAAC,kBAAkB,qBAAqB,KAAG,OAAO,CAAC,CAAC,CAAC,CAO5E;IAED,OAAO,CAAC,eAAe;gBAQX,EAAE,WAAW,EAAE,GAAG,EAAE,YAAY,EAAE,EAAE,iCAAiC;IAoBjF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkB;IAC9C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkB;IACtC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAA6B;IAC3D,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAiD;IACzF,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAiD;IAEnF,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAQhC;IAEF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAG9B;YAEY,cAAc;YAgBd,kBAAkB;CAUjC"}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { AudioTranscriptResponse } from '../../voice/types';
|
|
2
|
-
import { VoiceRecordModel } from '../../voice/recording/model/VoiceRecord.model';
|
|
3
|
-
import { RequestOptionalConfig } from '../../../lib/effector/createControllerEffect';
|
|
4
|
-
type GetTranscriptApi = {
|
|
5
|
-
getAudioFileTranscript: (audioFileId: string) => Promise<AudioTranscriptResponse>;
|
|
6
|
-
};
|
|
7
|
-
export type VoiceTranscriptErrorHandler = (errorMessage: string) => void;
|
|
8
|
-
type VoiceTranscriptSnapshotModelProps = {
|
|
9
|
-
voiceRecord: VoiceRecordModel;
|
|
10
|
-
api: GetTranscriptApi;
|
|
11
|
-
errorHandler?: VoiceTranscriptErrorHandler;
|
|
12
|
-
};
|
|
13
|
-
export declare class VoiceTranscriptSnapshotModel {
|
|
14
|
-
readonly fetchTranscript: (audioFileId: string) => Promise<string | undefined>;
|
|
15
|
-
readonly getTranscriptForCurrent: () => Promise<string | undefined>;
|
|
16
|
-
readonly wrapRequestHint: <P extends object, R>(apiFn: (params: P & {
|
|
17
|
-
voiceTranscript?: string;
|
|
18
|
-
}, requestConfig?: RequestOptionalConfig) => Promise<R>) => (params: P, requestConfig?: RequestOptionalConfig) => Promise<R>;
|
|
19
|
-
private hasPendingVoice;
|
|
20
|
-
constructor({ voiceRecord, api, errorHandler }: VoiceTranscriptSnapshotModelProps);
|
|
21
|
-
private readonly voiceRecord;
|
|
22
|
-
private readonly api;
|
|
23
|
-
private readonly errorHandler?;
|
|
24
|
-
private readonly transcriptByAudioFileId;
|
|
25
|
-
private readonly snapshotByChatKey;
|
|
26
|
-
private readonly captureSnapshotFx;
|
|
27
|
-
private readonly clearSnapshotFx;
|
|
28
|
-
private pollTranscript;
|
|
29
|
-
private resolveAudioFileId;
|
|
30
|
-
}
|
|
31
|
-
export {};
|
|
32
|
-
//# sourceMappingURL=VoiceTranscriptSnapshotModel.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"VoiceTranscriptSnapshotModel.d.ts","sourceRoot":"","sources":["../../../../../../src/features/chatbot/model/VoiceTranscriptSnapshotModel.ts"],"names":[],"mappings":"AACA,OAAO,EACL,uBAAuB,EAGxB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,+CAA+C,CAAA;AAChF,OAAO,EAAE,qBAAqB,EAAE,MAAM,8CAA8C,CAAA;AAOpF,KAAK,gBAAgB,GAAG;IACtB,sBAAsB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,uBAAuB,CAAC,CAAA;CAClF,CAAA;AAOD,MAAM,MAAM,2BAA2B,GAAG,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAA;AAExE,KAAK,iCAAiC,GAAG;IACvC,WAAW,EAAE,gBAAgB,CAAA;IAC7B,GAAG,EAAE,gBAAgB,CAAA;IACrB,YAAY,CAAC,EAAE,2BAA2B,CAAA;CAC3C,CAAA;AAUD,qBAAa,4BAA4B;IACvC,SAAgB,eAAe,gBAAiB,MAAM,KAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAOnF;IAED,SAAgB,uBAAuB,QAAa,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAiB9E;IAED,SAAgB,eAAe,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAC7C,CACL,MAAM,EAAE,CAAC,GAAG;QAAE,eAAe,CAAC,EAAE,MAAM,CAAA;KAAE,EACxC,aAAa,CAAC,EAAE,qBAAqB,KAClC,OAAO,CAAC,CAAC,CAAC,cAEO,CAAC,kBAAkB,qBAAqB,KAAG,OAAO,CAAC,CAAC,CAAC,CAO5E;IAED,OAAO,CAAC,eAAe;gBAQX,EAAE,WAAW,EAAE,GAAG,EAAE,YAAY,EAAE,EAAE,iCAAiC;IAoBjF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkB;IAC9C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkB;IACtC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAA6B;IAC3D,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAiD;IACzF,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAiD;IAEnF,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAQhC;IAEF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAG9B;YAEY,cAAc;YAgBd,kBAAkB;CAUjC"}
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import { createEffect, sample } from 'effector'
|
|
2
|
-
import {
|
|
3
|
-
AudioTranscriptResponse,
|
|
4
|
-
TranscriptionStatus,
|
|
5
|
-
VoiceRecordCollectionItem,
|
|
6
|
-
} from '../../voice/types'
|
|
7
|
-
import { VoiceRecordModel } from '../../voice/recording/model/VoiceRecord.model'
|
|
8
|
-
import { RequestOptionalConfig } from '../../../lib/effector/createControllerEffect'
|
|
9
|
-
import { getText } from '../../../i18n/i18n'
|
|
10
|
-
|
|
11
|
-
const TRANSCRIPT_RETRY_INTERVAL_MS = 2000
|
|
12
|
-
const TRANSCRIPT_MAX_RETRIES = 20
|
|
13
|
-
const CHAT_WITH_VOICE_TIMEOUT_MS = 60_000
|
|
14
|
-
|
|
15
|
-
type GetTranscriptApi = {
|
|
16
|
-
getAudioFileTranscript: (audioFileId: string) => Promise<AudioTranscriptResponse>
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
type CaptureParams = {
|
|
20
|
-
voiceKey: string
|
|
21
|
-
record: VoiceRecordCollectionItem
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export type VoiceTranscriptErrorHandler = (errorMessage: string) => void
|
|
25
|
-
|
|
26
|
-
type VoiceTranscriptSnapshotModelProps = {
|
|
27
|
-
voiceRecord: VoiceRecordModel
|
|
28
|
-
api: GetTranscriptApi
|
|
29
|
-
errorHandler?: VoiceTranscriptErrorHandler
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Voice key: `${assignmentId}-${problemId}-${attemptsCount}`
|
|
33
|
-
// Chat key: `${assignmentId}-${problemId}` — survives attempt increments and voice deletion.
|
|
34
|
-
const toChatKey = (voiceKey: string): string | null => {
|
|
35
|
-
const lastDashIndex = voiceKey.lastIndexOf('-')
|
|
36
|
-
if (lastDashIndex <= 0) return null
|
|
37
|
-
return voiceKey.slice(0, lastDashIndex)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export class VoiceTranscriptSnapshotModel {
|
|
41
|
-
public readonly fetchTranscript = (audioFileId: string): Promise<string | undefined> => {
|
|
42
|
-
const cached = this.transcriptByAudioFileId.get(audioFileId)
|
|
43
|
-
if (cached) return cached
|
|
44
|
-
|
|
45
|
-
const promise = this.pollTranscript(audioFileId).catch(() => undefined)
|
|
46
|
-
this.transcriptByAudioFileId.set(audioFileId, promise)
|
|
47
|
-
return promise
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
public readonly getTranscriptForCurrent = async (): Promise<string | undefined> => {
|
|
51
|
-
const voiceKey = this.voiceRecord.$currentKey.getState()
|
|
52
|
-
if (!voiceKey) return undefined
|
|
53
|
-
|
|
54
|
-
const chatKey = toChatKey(voiceKey)
|
|
55
|
-
if (chatKey) {
|
|
56
|
-
const snapshot = this.snapshotByChatKey.get(chatKey)
|
|
57
|
-
if (snapshot) return snapshot
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const record = this.voiceRecord.collection.get(voiceKey)
|
|
61
|
-
if (!record) return undefined
|
|
62
|
-
|
|
63
|
-
const audioFileId = await this.resolveAudioFileId(record)
|
|
64
|
-
if (!audioFileId) return undefined
|
|
65
|
-
|
|
66
|
-
return this.fetchTranscript(audioFileId)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
public readonly wrapRequestHint = <P extends object, R>(
|
|
70
|
-
apiFn: (
|
|
71
|
-
params: P & { voiceTranscript?: string },
|
|
72
|
-
requestConfig?: RequestOptionalConfig,
|
|
73
|
-
) => Promise<R>,
|
|
74
|
-
) => {
|
|
75
|
-
return async (params: P, requestConfig?: RequestOptionalConfig): Promise<R> => {
|
|
76
|
-
if (this.hasPendingVoice()) {
|
|
77
|
-
requestConfig?.extendTimeoutMs?.(CHAT_WITH_VOICE_TIMEOUT_MS)
|
|
78
|
-
}
|
|
79
|
-
const voiceTranscript = await this.getTranscriptForCurrent()
|
|
80
|
-
return apiFn(voiceTranscript ? { ...params, voiceTranscript } : params, requestConfig)
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
private hasPendingVoice(): boolean {
|
|
85
|
-
const voiceKey = this.voiceRecord.$currentKey.getState()
|
|
86
|
-
if (!voiceKey) return false
|
|
87
|
-
const chatKey = toChatKey(voiceKey)
|
|
88
|
-
if (chatKey && this.snapshotByChatKey.has(chatKey)) return true
|
|
89
|
-
return !!this.voiceRecord.collection.get(voiceKey)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
constructor({ voiceRecord, api, errorHandler }: VoiceTranscriptSnapshotModelProps) {
|
|
93
|
-
this.voiceRecord = voiceRecord
|
|
94
|
-
this.api = api
|
|
95
|
-
this.errorHandler = errorHandler
|
|
96
|
-
|
|
97
|
-
sample({
|
|
98
|
-
clock: voiceRecord.setCurrentRecord,
|
|
99
|
-
source: voiceRecord.$currentKey,
|
|
100
|
-
filter: (_, record): record is VoiceRecordCollectionItem => !!record,
|
|
101
|
-
fn: (voiceKey, record) => ({ voiceKey, record }),
|
|
102
|
-
target: this.captureSnapshotFx,
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
sample({
|
|
106
|
-
clock: voiceRecord.deleteCurrentRecording,
|
|
107
|
-
source: voiceRecord.$currentKey,
|
|
108
|
-
target: this.clearSnapshotFx,
|
|
109
|
-
})
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
private readonly voiceRecord: VoiceRecordModel
|
|
113
|
-
private readonly api: GetTranscriptApi
|
|
114
|
-
private readonly errorHandler?: VoiceTranscriptErrorHandler
|
|
115
|
-
private readonly transcriptByAudioFileId = new Map<string, Promise<string | undefined>>()
|
|
116
|
-
private readonly snapshotByChatKey = new Map<string, Promise<string | undefined>>()
|
|
117
|
-
|
|
118
|
-
private readonly captureSnapshotFx = createEffect(({ voiceKey, record }: CaptureParams) => {
|
|
119
|
-
const chatKey = toChatKey(voiceKey)
|
|
120
|
-
if (!chatKey) return
|
|
121
|
-
|
|
122
|
-
const transcriptPromise = this.resolveAudioFileId(record).then((audioFileId) =>
|
|
123
|
-
audioFileId ? this.fetchTranscript(audioFileId) : undefined,
|
|
124
|
-
)
|
|
125
|
-
this.snapshotByChatKey.set(chatKey, transcriptPromise)
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
private readonly clearSnapshotFx = createEffect((voiceKey: string) => {
|
|
129
|
-
const chatKey = toChatKey(voiceKey)
|
|
130
|
-
if (chatKey) this.snapshotByChatKey.delete(chatKey)
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
private async pollTranscript(audioFileId: string): Promise<string | undefined> {
|
|
134
|
-
for (let attempt = 0; attempt < TRANSCRIPT_MAX_RETRIES; attempt++) {
|
|
135
|
-
await new Promise((resolve) => setTimeout(resolve, TRANSCRIPT_RETRY_INTERVAL_MS))
|
|
136
|
-
|
|
137
|
-
try {
|
|
138
|
-
const response = await this.api.getAudioFileTranscript(audioFileId)
|
|
139
|
-
if (response.status === TranscriptionStatus.COMPLETED) return response.text
|
|
140
|
-
if (response.status === TranscriptionStatus.FAILED) break
|
|
141
|
-
} catch {
|
|
142
|
-
// network/request failure — retry on next iteration
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
this.errorHandler?.(getText('voice.transcriptionError'))
|
|
146
|
-
return undefined
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
private async resolveAudioFileId(record: VoiceRecordCollectionItem): Promise<string | undefined> {
|
|
150
|
-
if (record.id) return record.id
|
|
151
|
-
if (!record.audioUploadPromise) return undefined
|
|
152
|
-
try {
|
|
153
|
-
const result = await record.audioUploadPromise
|
|
154
|
-
return result?.id
|
|
155
|
-
} catch {
|
|
156
|
-
return undefined
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|