@magmamath/students-features 1.5.3-rc.3 → 1.5.3-rc.5

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.
Files changed (20) hide show
  1. package/dist/commonjs/features/chatbot/model/VoiceTranscriptSnapshotModel.js +35 -8
  2. package/dist/commonjs/features/chatbot/model/VoiceTranscriptSnapshotModel.js.map +1 -1
  3. package/dist/commonjs/lib/effector/createControllerEffect.js +7 -6
  4. package/dist/commonjs/lib/effector/createControllerEffect.js.map +1 -1
  5. package/dist/module/features/chatbot/model/VoiceTranscriptSnapshotModel.js +35 -8
  6. package/dist/module/features/chatbot/model/VoiceTranscriptSnapshotModel.js.map +1 -1
  7. package/dist/module/lib/effector/createControllerEffect.js +7 -6
  8. package/dist/module/lib/effector/createControllerEffect.js.map +1 -1
  9. package/dist/typescript/commonjs/features/chatbot/model/VoiceTranscriptSnapshotModel.d.ts +6 -1
  10. package/dist/typescript/commonjs/features/chatbot/model/VoiceTranscriptSnapshotModel.d.ts.map +1 -1
  11. package/dist/typescript/commonjs/lib/effector/createControllerEffect.d.ts +1 -0
  12. package/dist/typescript/commonjs/lib/effector/createControllerEffect.d.ts.map +1 -1
  13. package/dist/typescript/module/features/chatbot/model/VoiceTranscriptSnapshotModel.d.ts +6 -1
  14. package/dist/typescript/module/features/chatbot/model/VoiceTranscriptSnapshotModel.d.ts.map +1 -1
  15. package/dist/typescript/module/lib/effector/createControllerEffect.d.ts +1 -0
  16. package/dist/typescript/module/lib/effector/createControllerEffect.d.ts.map +1 -1
  17. package/package.json +1 -1
  18. package/src/features/chatbot/model/VoiceTranscriptSnapshotModel.ts +42 -9
  19. package/src/i18n/.generated/schema.json +1 -0
  20. package/src/lib/effector/createControllerEffect.ts +13 -6
@@ -6,8 +6,10 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.VoiceTranscriptSnapshotModel = void 0;
7
7
  var _effector = require("effector");
8
8
  var _types = require("../../voice/types.js");
9
+ var _i18n = require("../../../i18n/i18n.js");
9
10
  const TRANSCRIPT_RETRY_INTERVAL_MS = 2000;
10
11
  const TRANSCRIPT_MAX_RETRIES = 20;
12
+ const CHAT_WITH_VOICE_TIMEOUT_MS = 60_000;
11
13
  // Voice key: `${assignmentId}-${problemId}-${attemptsCount}`
12
14
  // Chat key: `${assignmentId}-${problemId}` — survives attempt increments and voice deletion.
13
15
  const toChatKey = voiceKey => {
@@ -39,6 +41,9 @@ class VoiceTranscriptSnapshotModel {
39
41
  };
40
42
  wrapRequestHint = apiFn => {
41
43
  return async (params, requestConfig) => {
44
+ if (this.hasPendingVoice()) {
45
+ requestConfig?.extendTimeoutMs?.(CHAT_WITH_VOICE_TIMEOUT_MS);
46
+ }
42
47
  const voiceTranscript = await this.getTranscriptForCurrent();
43
48
  return apiFn(voiceTranscript ? {
44
49
  ...params,
@@ -46,12 +51,21 @@ class VoiceTranscriptSnapshotModel {
46
51
  } : params, requestConfig);
47
52
  };
48
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
+ }
49
61
  constructor({
50
62
  voiceRecord,
51
- api
63
+ api,
64
+ errorHandler
52
65
  }) {
53
66
  this.voiceRecord = voiceRecord;
54
67
  this.api = api;
68
+ this.errorHandler = errorHandler;
55
69
  (0, _effector.sample)({
56
70
  clock: voiceRecord.setCurrentRecord,
57
71
  source: voiceRecord.$currentKey,
@@ -62,26 +76,39 @@ class VoiceTranscriptSnapshotModel {
62
76
  }),
63
77
  target: this.captureSnapshotFx
64
78
  });
79
+ (0, _effector.sample)({
80
+ clock: voiceRecord.deleteCurrentRecording,
81
+ source: voiceRecord.$currentKey,
82
+ target: this.clearSnapshotFx
83
+ });
65
84
  }
66
85
  transcriptByAudioFileId = new Map();
67
86
  snapshotByChatKey = new Map();
68
- captureSnapshotFx = (0, _effector.createEffect)(async ({
87
+ captureSnapshotFx = (0, _effector.createEffect)(({
69
88
  voiceKey,
70
89
  record
71
90
  }) => {
72
91
  const chatKey = toChatKey(voiceKey);
73
92
  if (!chatKey) return;
74
- const audioFileId = await this.resolveAudioFileId(record);
75
- if (!audioFileId) return;
76
- this.snapshotByChatKey.set(chatKey, this.fetchTranscript(audioFileId));
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);
77
99
  });
78
100
  async pollTranscript(audioFileId) {
79
101
  for (let attempt = 0; attempt < TRANSCRIPT_MAX_RETRIES; attempt++) {
80
102
  await new Promise(resolve => setTimeout(resolve, TRANSCRIPT_RETRY_INTERVAL_MS));
81
- const response = await this.api.getAudioFileTranscript(audioFileId);
82
- if (response.status === _types.TranscriptionStatus.COMPLETED) return response.text;
83
- if (response.status === _types.TranscriptionStatus.FAILED) return undefined;
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
+ }
84
110
  }
111
+ this.errorHandler?.((0, _i18n.getText)('voice.transcriptionError'));
85
112
  return undefined;
86
113
  }
87
114
  async resolveAudioFileId(record) {
@@ -1 +1 @@
1
- {"version":3,"names":["_effector","require","_types","TRANSCRIPT_RETRY_INTERVAL_MS","TRANSCRIPT_MAX_RETRIES","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","voiceTranscript","constructor","api","sample","clock","setCurrentRecord","source","filter","_","fn","target","captureSnapshotFx","Map","createEffect","attempt","Promise","resolve","setTimeout","response","getAudioFileTranscript","status","TranscriptionStatus","COMPLETED","text","FAILED","id","audioUploadPromise","result","exports"],"sourceRoot":"../../../../../src","sources":["features/chatbot/model/VoiceTranscriptSnapshotModel.ts"],"mappings":"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","ignoreList":[]}
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":[]}
@@ -13,14 +13,15 @@ function controlledRequest(request) {
13
13
  abortAfter = 20000
14
14
  }) => {
15
15
  let timeout = null;
16
- if (abortAfter) {
17
- timeout = setTimeout(() => {
18
- controller.abort('timeout');
19
- }, abortAfter);
20
- }
16
+ const scheduleAbort = ms => {
17
+ if (timeout !== null) clearTimeout(timeout);
18
+ timeout = ms > 0 ? setTimeout(() => controller.abort('timeout'), ms) : null;
19
+ };
20
+ if (abortAfter) scheduleAbort(abortAfter);
21
21
  try {
22
22
  const response = await request(data, {
23
- signal: controller?.signal
23
+ signal: controller?.signal,
24
+ extendTimeoutMs: scheduleAbort
24
25
  });
25
26
  if (timeout !== null) clearTimeout(timeout);
26
27
  return response;
@@ -1 +1 @@
1
- {"version":3,"names":["_effector","require","controlledRequest","request","data","controller","AbortController","abortAfter","timeout","setTimeout","abort","response","signal","clearTimeout","error","code","reason","Error","createControllerEffect","fn","effect","createEffect","fail","watch","exports"],"sourceRoot":"../../../../src","sources":["lib/effector/createControllerEffect.ts"],"mappings":";;;;;;;AAAA,IAAAA,SAAA,GAAAC,OAAA;AAkBO,SAASC,iBAAiBA,CAC/BC,OAAiE,EACjE;EACA,OAAO,OAAO;IACZC,IAAI;IACJC,UAAU,GAAG,IAAIC,eAAe,CAAC,CAAC;IAClCC,UAAU,GAAG;EACY,CAAC,KAAK;IAC/B,IAAIC,OAA6C,GAAG,IAAI;IAExD,IAAID,UAAU,EAAE;MACdC,OAAO,GAAGC,UAAU,CAAC,MAAM;QACzBJ,UAAU,CAACK,KAAK,CAAC,SAAS,CAAC;MAC7B,CAAC,EAAEH,UAAU,CAAC;IAChB;IAEA,IAAI;MACF,MAAMI,QAAQ,GAAG,MAAMR,OAAO,CAACC,IAAI,EAAE;QAAEQ,MAAM,EAAEP,UAAU,EAAEO;MAAO,CAAC,CAAC;MACpE,IAAIJ,OAAO,KAAK,IAAI,EAAEK,YAAY,CAACL,OAAO,CAAC;MAC3C,OAAOG,QAAQ;IACjB,CAAC,CAAC,OAAOG,KAAc,EAAE;MACvB,IAAKA,KAAK,CAAuBC,IAAI,KAAK,cAAc,EAAE;QACxD,IAAIV,UAAU,CAACO,MAAM,CAACI,MAAM,KAAK,SAAS,EAAE;UAC1C,MAAM,IAAIC,KAAK,CAAC,mBAAmB,CAAC;QACtC;QACA,MAAM,IAAIA,KAAK,CAAC,0BAA0B,CAAC;MAC7C;MAEA,MAAMH,KAAK;IACb;EACF,CAAC;AACH;AAEO,MAAMI,sBAAsB,GACjCC,EAA4D,IACzD;EACH,MAAMhB,OAAO,GAAGD,iBAAiB,CAAOiB,EAAE,CAAC;EAC3C,MAAMC,MAAM,GAAG,IAAAC,sBAAY,EAAEjB,IAAO,IAClCD,OAAO,CAAC;IAAEC,IAAI;IAAEC,UAAU,EAAEe,MAAM,CAACf;EAAW,CAAC,CACjD,CAA2B;EAC3Be,MAAM,CAACf,UAAU,GAAG,IAAIC,eAAe,CAAC,CAAC;EAEzCc,MAAM,CAACE,IAAI,CAACC,KAAK,CAAC,MAAM;IACtBH,MAAM,CAACf,UAAU,GAAG,IAAIC,eAAe,CAAC,CAAC;EAC3C,CAAC,CAAC;EAEF,OAAOc,MAAM;AACf,CAAC;AAAAI,OAAA,CAAAN,sBAAA,GAAAA,sBAAA","ignoreList":[]}
1
+ {"version":3,"names":["_effector","require","controlledRequest","request","data","controller","AbortController","abortAfter","timeout","scheduleAbort","ms","clearTimeout","setTimeout","abort","response","signal","extendTimeoutMs","error","code","reason","Error","createControllerEffect","fn","effect","createEffect","fail","watch","exports"],"sourceRoot":"../../../../src","sources":["lib/effector/createControllerEffect.ts"],"mappings":";;;;;;;AAAA,IAAAA,SAAA,GAAAC,OAAA;AAqBO,SAASC,iBAAiBA,CAC/BC,OAAiE,EACjE;EACA,OAAO,OAAO;IACZC,IAAI;IACJC,UAAU,GAAG,IAAIC,eAAe,CAAC,CAAC;IAClCC,UAAU,GAAG;EACY,CAAC,KAAK;IAC/B,IAAIC,OAA6C,GAAG,IAAI;IAExD,MAAMC,aAAa,GAAIC,EAAU,IAAK;MACpC,IAAIF,OAAO,KAAK,IAAI,EAAEG,YAAY,CAACH,OAAO,CAAC;MAC3CA,OAAO,GAAGE,EAAE,GAAG,CAAC,GAAGE,UAAU,CAAC,MAAMP,UAAU,CAACQ,KAAK,CAAC,SAAS,CAAC,EAAEH,EAAE,CAAC,GAAG,IAAI;IAC7E,CAAC;IAED,IAAIH,UAAU,EAAEE,aAAa,CAACF,UAAU,CAAC;IAEzC,IAAI;MACF,MAAMO,QAAQ,GAAG,MAAMX,OAAO,CAACC,IAAI,EAAE;QACnCW,MAAM,EAAEV,UAAU,EAAEU,MAAM;QAC1BC,eAAe,EAAEP;MACnB,CAAC,CAAC;MACF,IAAID,OAAO,KAAK,IAAI,EAAEG,YAAY,CAACH,OAAO,CAAC;MAC3C,OAAOM,QAAQ;IACjB,CAAC,CAAC,OAAOG,KAAc,EAAE;MACvB,IAAKA,KAAK,CAAuBC,IAAI,KAAK,cAAc,EAAE;QACxD,IAAIb,UAAU,CAACU,MAAM,CAACI,MAAM,KAAK,SAAS,EAAE;UAC1C,MAAM,IAAIC,KAAK,CAAC,mBAAmB,CAAC;QACtC;QACA,MAAM,IAAIA,KAAK,CAAC,0BAA0B,CAAC;MAC7C;MAEA,MAAMH,KAAK;IACb;EACF,CAAC;AACH;AAEO,MAAMI,sBAAsB,GACjCC,EAA4D,IACzD;EACH,MAAMnB,OAAO,GAAGD,iBAAiB,CAAOoB,EAAE,CAAC;EAC3C,MAAMC,MAAM,GAAG,IAAAC,sBAAY,EAAEpB,IAAO,IAClCD,OAAO,CAAC;IAAEC,IAAI;IAAEC,UAAU,EAAEkB,MAAM,CAAClB;EAAW,CAAC,CACjD,CAA2B;EAC3BkB,MAAM,CAAClB,UAAU,GAAG,IAAIC,eAAe,CAAC,CAAC;EAEzCiB,MAAM,CAACE,IAAI,CAACC,KAAK,CAAC,MAAM;IACtBH,MAAM,CAAClB,UAAU,GAAG,IAAIC,eAAe,CAAC,CAAC;EAC3C,CAAC,CAAC;EAEF,OAAOiB,MAAM;AACf,CAAC;AAAAI,OAAA,CAAAN,sBAAA,GAAAA,sBAAA","ignoreList":[]}
@@ -2,8 +2,10 @@
2
2
 
3
3
  import { createEffect, sample } from 'effector';
4
4
  import { TranscriptionStatus } from "../../voice/types.js";
5
+ import { getText } from "../../../i18n/i18n.js";
5
6
  const TRANSCRIPT_RETRY_INTERVAL_MS = 2000;
6
7
  const TRANSCRIPT_MAX_RETRIES = 20;
8
+ const CHAT_WITH_VOICE_TIMEOUT_MS = 60_000;
7
9
  // Voice key: `${assignmentId}-${problemId}-${attemptsCount}`
8
10
  // Chat key: `${assignmentId}-${problemId}` — survives attempt increments and voice deletion.
9
11
  const toChatKey = voiceKey => {
@@ -35,6 +37,9 @@ export class VoiceTranscriptSnapshotModel {
35
37
  };
36
38
  wrapRequestHint = apiFn => {
37
39
  return async (params, requestConfig) => {
40
+ if (this.hasPendingVoice()) {
41
+ requestConfig?.extendTimeoutMs?.(CHAT_WITH_VOICE_TIMEOUT_MS);
42
+ }
38
43
  const voiceTranscript = await this.getTranscriptForCurrent();
39
44
  return apiFn(voiceTranscript ? {
40
45
  ...params,
@@ -42,12 +47,21 @@ export class VoiceTranscriptSnapshotModel {
42
47
  } : params, requestConfig);
43
48
  };
44
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
+ }
45
57
  constructor({
46
58
  voiceRecord,
47
- api
59
+ api,
60
+ errorHandler
48
61
  }) {
49
62
  this.voiceRecord = voiceRecord;
50
63
  this.api = api;
64
+ this.errorHandler = errorHandler;
51
65
  sample({
52
66
  clock: voiceRecord.setCurrentRecord,
53
67
  source: voiceRecord.$currentKey,
@@ -58,26 +72,39 @@ export class VoiceTranscriptSnapshotModel {
58
72
  }),
59
73
  target: this.captureSnapshotFx
60
74
  });
75
+ sample({
76
+ clock: voiceRecord.deleteCurrentRecording,
77
+ source: voiceRecord.$currentKey,
78
+ target: this.clearSnapshotFx
79
+ });
61
80
  }
62
81
  transcriptByAudioFileId = new Map();
63
82
  snapshotByChatKey = new Map();
64
- captureSnapshotFx = createEffect(async ({
83
+ captureSnapshotFx = createEffect(({
65
84
  voiceKey,
66
85
  record
67
86
  }) => {
68
87
  const chatKey = toChatKey(voiceKey);
69
88
  if (!chatKey) return;
70
- const audioFileId = await this.resolveAudioFileId(record);
71
- if (!audioFileId) return;
72
- this.snapshotByChatKey.set(chatKey, this.fetchTranscript(audioFileId));
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);
73
95
  });
74
96
  async pollTranscript(audioFileId) {
75
97
  for (let attempt = 0; attempt < TRANSCRIPT_MAX_RETRIES; attempt++) {
76
98
  await new Promise(resolve => setTimeout(resolve, TRANSCRIPT_RETRY_INTERVAL_MS));
77
- const response = await this.api.getAudioFileTranscript(audioFileId);
78
- if (response.status === TranscriptionStatus.COMPLETED) return response.text;
79
- if (response.status === TranscriptionStatus.FAILED) return undefined;
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
+ }
80
106
  }
107
+ this.errorHandler?.(getText('voice.transcriptionError'));
81
108
  return undefined;
82
109
  }
83
110
  async resolveAudioFileId(record) {
@@ -1 +1 @@
1
- {"version":3,"names":["createEffect","sample","TranscriptionStatus","TRANSCRIPT_RETRY_INTERVAL_MS","TRANSCRIPT_MAX_RETRIES","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","voiceTranscript","constructor","api","clock","setCurrentRecord","source","filter","_","fn","target","captureSnapshotFx","Map","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
+ {"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":[]}
@@ -8,14 +8,15 @@ export function controlledRequest(request) {
8
8
  abortAfter = 20000
9
9
  }) => {
10
10
  let timeout = null;
11
- if (abortAfter) {
12
- timeout = setTimeout(() => {
13
- controller.abort('timeout');
14
- }, abortAfter);
15
- }
11
+ const scheduleAbort = ms => {
12
+ if (timeout !== null) clearTimeout(timeout);
13
+ timeout = ms > 0 ? setTimeout(() => controller.abort('timeout'), ms) : null;
14
+ };
15
+ if (abortAfter) scheduleAbort(abortAfter);
16
16
  try {
17
17
  const response = await request(data, {
18
- signal: controller?.signal
18
+ signal: controller?.signal,
19
+ extendTimeoutMs: scheduleAbort
19
20
  });
20
21
  if (timeout !== null) clearTimeout(timeout);
21
22
  return response;
@@ -1 +1 @@
1
- {"version":3,"names":["createEffect","controlledRequest","request","data","controller","AbortController","abortAfter","timeout","setTimeout","abort","response","signal","clearTimeout","error","code","reason","Error","createControllerEffect","fn","effect","fail","watch"],"sourceRoot":"../../../../src","sources":["lib/effector/createControllerEffect.ts"],"mappings":";;AAAA,SAASA,YAAY,QAAqB,UAAU;AAkBpD,OAAO,SAASC,iBAAiBA,CAC/BC,OAAiE,EACjE;EACA,OAAO,OAAO;IACZC,IAAI;IACJC,UAAU,GAAG,IAAIC,eAAe,CAAC,CAAC;IAClCC,UAAU,GAAG;EACY,CAAC,KAAK;IAC/B,IAAIC,OAA6C,GAAG,IAAI;IAExD,IAAID,UAAU,EAAE;MACdC,OAAO,GAAGC,UAAU,CAAC,MAAM;QACzBJ,UAAU,CAACK,KAAK,CAAC,SAAS,CAAC;MAC7B,CAAC,EAAEH,UAAU,CAAC;IAChB;IAEA,IAAI;MACF,MAAMI,QAAQ,GAAG,MAAMR,OAAO,CAACC,IAAI,EAAE;QAAEQ,MAAM,EAAEP,UAAU,EAAEO;MAAO,CAAC,CAAC;MACpE,IAAIJ,OAAO,KAAK,IAAI,EAAEK,YAAY,CAACL,OAAO,CAAC;MAC3C,OAAOG,QAAQ;IACjB,CAAC,CAAC,OAAOG,KAAc,EAAE;MACvB,IAAKA,KAAK,CAAuBC,IAAI,KAAK,cAAc,EAAE;QACxD,IAAIV,UAAU,CAACO,MAAM,CAACI,MAAM,KAAK,SAAS,EAAE;UAC1C,MAAM,IAAIC,KAAK,CAAC,mBAAmB,CAAC;QACtC;QACA,MAAM,IAAIA,KAAK,CAAC,0BAA0B,CAAC;MAC7C;MAEA,MAAMH,KAAK;IACb;EACF,CAAC;AACH;AAEA,OAAO,MAAMI,sBAAsB,GACjCC,EAA4D,IACzD;EACH,MAAMhB,OAAO,GAAGD,iBAAiB,CAAOiB,EAAE,CAAC;EAC3C,MAAMC,MAAM,GAAGnB,YAAY,CAAEG,IAAO,IAClCD,OAAO,CAAC;IAAEC,IAAI;IAAEC,UAAU,EAAEe,MAAM,CAACf;EAAW,CAAC,CACjD,CAA2B;EAC3Be,MAAM,CAACf,UAAU,GAAG,IAAIC,eAAe,CAAC,CAAC;EAEzCc,MAAM,CAACC,IAAI,CAACC,KAAK,CAAC,MAAM;IACtBF,MAAM,CAACf,UAAU,GAAG,IAAIC,eAAe,CAAC,CAAC;EAC3C,CAAC,CAAC;EAEF,OAAOc,MAAM;AACf,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["createEffect","controlledRequest","request","data","controller","AbortController","abortAfter","timeout","scheduleAbort","ms","clearTimeout","setTimeout","abort","response","signal","extendTimeoutMs","error","code","reason","Error","createControllerEffect","fn","effect","fail","watch"],"sourceRoot":"../../../../src","sources":["lib/effector/createControllerEffect.ts"],"mappings":";;AAAA,SAASA,YAAY,QAAqB,UAAU;AAqBpD,OAAO,SAASC,iBAAiBA,CAC/BC,OAAiE,EACjE;EACA,OAAO,OAAO;IACZC,IAAI;IACJC,UAAU,GAAG,IAAIC,eAAe,CAAC,CAAC;IAClCC,UAAU,GAAG;EACY,CAAC,KAAK;IAC/B,IAAIC,OAA6C,GAAG,IAAI;IAExD,MAAMC,aAAa,GAAIC,EAAU,IAAK;MACpC,IAAIF,OAAO,KAAK,IAAI,EAAEG,YAAY,CAACH,OAAO,CAAC;MAC3CA,OAAO,GAAGE,EAAE,GAAG,CAAC,GAAGE,UAAU,CAAC,MAAMP,UAAU,CAACQ,KAAK,CAAC,SAAS,CAAC,EAAEH,EAAE,CAAC,GAAG,IAAI;IAC7E,CAAC;IAED,IAAIH,UAAU,EAAEE,aAAa,CAACF,UAAU,CAAC;IAEzC,IAAI;MACF,MAAMO,QAAQ,GAAG,MAAMX,OAAO,CAACC,IAAI,EAAE;QACnCW,MAAM,EAAEV,UAAU,EAAEU,MAAM;QAC1BC,eAAe,EAAEP;MACnB,CAAC,CAAC;MACF,IAAID,OAAO,KAAK,IAAI,EAAEG,YAAY,CAACH,OAAO,CAAC;MAC3C,OAAOM,QAAQ;IACjB,CAAC,CAAC,OAAOG,KAAc,EAAE;MACvB,IAAKA,KAAK,CAAuBC,IAAI,KAAK,cAAc,EAAE;QACxD,IAAIb,UAAU,CAACU,MAAM,CAACI,MAAM,KAAK,SAAS,EAAE;UAC1C,MAAM,IAAIC,KAAK,CAAC,mBAAmB,CAAC;QACtC;QACA,MAAM,IAAIA,KAAK,CAAC,0BAA0B,CAAC;MAC7C;MAEA,MAAMH,KAAK;IACb;EACF,CAAC;AACH;AAEA,OAAO,MAAMI,sBAAsB,GACjCC,EAA4D,IACzD;EACH,MAAMnB,OAAO,GAAGD,iBAAiB,CAAOoB,EAAE,CAAC;EAC3C,MAAMC,MAAM,GAAGtB,YAAY,CAAEG,IAAO,IAClCD,OAAO,CAAC;IAAEC,IAAI;IAAEC,UAAU,EAAEkB,MAAM,CAAClB;EAAW,CAAC,CACjD,CAA2B;EAC3BkB,MAAM,CAAClB,UAAU,GAAG,IAAIC,eAAe,CAAC,CAAC;EAEzCiB,MAAM,CAACC,IAAI,CAACC,KAAK,CAAC,MAAM;IACtBF,MAAM,CAAClB,UAAU,GAAG,IAAIC,eAAe,CAAC,CAAC;EAC3C,CAAC,CAAC;EAEF,OAAOiB,MAAM;AACf,CAAC","ignoreList":[]}
@@ -4,9 +4,11 @@ import { RequestOptionalConfig } from '../../../lib/effector/createControllerEff
4
4
  type GetTranscriptApi = {
5
5
  getAudioFileTranscript: (audioFileId: string) => Promise<AudioTranscriptResponse>;
6
6
  };
7
+ export type VoiceTranscriptErrorHandler = (errorMessage: string) => void;
7
8
  type VoiceTranscriptSnapshotModelProps = {
8
9
  voiceRecord: VoiceRecordModel;
9
10
  api: GetTranscriptApi;
11
+ errorHandler?: VoiceTranscriptErrorHandler;
10
12
  };
11
13
  export declare class VoiceTranscriptSnapshotModel {
12
14
  readonly fetchTranscript: (audioFileId: string) => Promise<string | undefined>;
@@ -14,12 +16,15 @@ export declare class VoiceTranscriptSnapshotModel {
14
16
  readonly wrapRequestHint: <P extends object, R>(apiFn: (params: P & {
15
17
  voiceTranscript?: string;
16
18
  }, requestConfig?: RequestOptionalConfig) => Promise<R>) => (params: P, requestConfig?: RequestOptionalConfig) => Promise<R>;
17
- constructor({ voiceRecord, api }: VoiceTranscriptSnapshotModelProps);
19
+ private hasPendingVoice;
20
+ constructor({ voiceRecord, api, errorHandler }: VoiceTranscriptSnapshotModelProps);
18
21
  private readonly voiceRecord;
19
22
  private readonly api;
23
+ private readonly errorHandler?;
20
24
  private readonly transcriptByAudioFileId;
21
25
  private readonly snapshotByChatKey;
22
26
  private readonly captureSnapshotFx;
27
+ private readonly clearSnapshotFx;
23
28
  private pollTranscript;
24
29
  private resolveAudioFileId;
25
30
  }
@@ -1 +1 @@
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;AAKpF,KAAK,gBAAgB,GAAG;IACtB,sBAAsB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,uBAAuB,CAAC,CAAA;CAClF,CAAA;AAOD,KAAK,iCAAiC,GAAG;IACvC,WAAW,EAAE,gBAAgB,CAAA;IAC7B,GAAG,EAAE,gBAAgB,CAAA;CACtB,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,CAI5E;gBAEW,EAAE,WAAW,EAAE,GAAG,EAAE,EAAE,iCAAiC;IAanE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkB;IAC9C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkB;IACtC,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAiD;IACzF,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAiD;IAEnF,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAQhC;YAEY,cAAc;YAYd,kBAAkB;CAUjC"}
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"}
@@ -9,6 +9,7 @@ type ControlledRequestProps<P> = {
9
9
  };
10
10
  export type RequestOptionalConfig = {
11
11
  signal?: AbortSignal;
12
+ extendTimeoutMs?: (ms: number) => void;
12
13
  };
13
14
  export declare function controlledRequest<R = void, P = void>(request: (props: P, config?: RequestOptionalConfig) => Promise<R>): ({ data, controller, abortAfter, }: ControlledRequestProps<P>) => Promise<R>;
14
15
  export declare const createControllerEffect: <T, P = void>(fn: (props: P, config?: RequestOptionalConfig) => Promise<T>) => ControlledEffect<P, T>;
@@ -1 +1 @@
1
- {"version":3,"file":"createControllerEffect.d.ts","sourceRoot":"","sources":["../../../../../src/lib/effector/createControllerEffect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,UAAU,CAAA;AAMpD,MAAM,MAAM,gBAAgB,CAAC,CAAC,EAAE,CAAC,IAAI,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG;IAClD,UAAU,EAAE,eAAe,CAAA;CAC5B,CAAA;AAED,KAAK,sBAAsB,CAAC,CAAC,IAAI;IAC/B,IAAI,EAAE,CAAC,CAAA;IACP,UAAU,CAAC,EAAE,eAAe,CAAA;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IAAE,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,CAAA;AAE5D,wBAAgB,iBAAiB,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,IAAI,EAClD,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,qBAAqB,KAAK,OAAO,CAAC,CAAC,CAAC,uCAM9D,sBAAsB,CAAC,CAAC,CAAC,gBAwB7B;AAED,eAAO,MAAM,sBAAsB,GAAI,CAAC,EAAE,CAAC,aACrC,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,qBAAqB,KAAK,OAAO,CAAC,CAAC,CAAC,2BAa7D,CAAA"}
1
+ {"version":3,"file":"createControllerEffect.d.ts","sourceRoot":"","sources":["../../../../../src/lib/effector/createControllerEffect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,UAAU,CAAA;AAMpD,MAAM,MAAM,gBAAgB,CAAC,CAAC,EAAE,CAAC,IAAI,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG;IAClD,UAAU,EAAE,eAAe,CAAA;CAC5B,CAAA;AAED,KAAK,sBAAsB,CAAC,CAAC,IAAI;IAC/B,IAAI,EAAE,CAAC,CAAA;IACP,UAAU,CAAC,EAAE,eAAe,CAAA;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;CACvC,CAAA;AAED,wBAAgB,iBAAiB,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,IAAI,EAClD,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,qBAAqB,KAAK,OAAO,CAAC,CAAC,CAAC,uCAM9D,sBAAsB,CAAC,CAAC,CAAC,gBA4B7B;AAED,eAAO,MAAM,sBAAsB,GAAI,CAAC,EAAE,CAAC,aACrC,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,qBAAqB,KAAK,OAAO,CAAC,CAAC,CAAC,2BAa7D,CAAA"}
@@ -4,9 +4,11 @@ import { RequestOptionalConfig } from '../../../lib/effector/createControllerEff
4
4
  type GetTranscriptApi = {
5
5
  getAudioFileTranscript: (audioFileId: string) => Promise<AudioTranscriptResponse>;
6
6
  };
7
+ export type VoiceTranscriptErrorHandler = (errorMessage: string) => void;
7
8
  type VoiceTranscriptSnapshotModelProps = {
8
9
  voiceRecord: VoiceRecordModel;
9
10
  api: GetTranscriptApi;
11
+ errorHandler?: VoiceTranscriptErrorHandler;
10
12
  };
11
13
  export declare class VoiceTranscriptSnapshotModel {
12
14
  readonly fetchTranscript: (audioFileId: string) => Promise<string | undefined>;
@@ -14,12 +16,15 @@ export declare class VoiceTranscriptSnapshotModel {
14
16
  readonly wrapRequestHint: <P extends object, R>(apiFn: (params: P & {
15
17
  voiceTranscript?: string;
16
18
  }, requestConfig?: RequestOptionalConfig) => Promise<R>) => (params: P, requestConfig?: RequestOptionalConfig) => Promise<R>;
17
- constructor({ voiceRecord, api }: VoiceTranscriptSnapshotModelProps);
19
+ private hasPendingVoice;
20
+ constructor({ voiceRecord, api, errorHandler }: VoiceTranscriptSnapshotModelProps);
18
21
  private readonly voiceRecord;
19
22
  private readonly api;
23
+ private readonly errorHandler?;
20
24
  private readonly transcriptByAudioFileId;
21
25
  private readonly snapshotByChatKey;
22
26
  private readonly captureSnapshotFx;
27
+ private readonly clearSnapshotFx;
23
28
  private pollTranscript;
24
29
  private resolveAudioFileId;
25
30
  }
@@ -1 +1 @@
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;AAKpF,KAAK,gBAAgB,GAAG;IACtB,sBAAsB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,uBAAuB,CAAC,CAAA;CAClF,CAAA;AAOD,KAAK,iCAAiC,GAAG;IACvC,WAAW,EAAE,gBAAgB,CAAA;IAC7B,GAAG,EAAE,gBAAgB,CAAA;CACtB,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,CAI5E;gBAEW,EAAE,WAAW,EAAE,GAAG,EAAE,EAAE,iCAAiC;IAanE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkB;IAC9C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkB;IACtC,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAiD;IACzF,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAiD;IAEnF,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAQhC;YAEY,cAAc;YAYd,kBAAkB;CAUjC"}
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"}
@@ -9,6 +9,7 @@ type ControlledRequestProps<P> = {
9
9
  };
10
10
  export type RequestOptionalConfig = {
11
11
  signal?: AbortSignal;
12
+ extendTimeoutMs?: (ms: number) => void;
12
13
  };
13
14
  export declare function controlledRequest<R = void, P = void>(request: (props: P, config?: RequestOptionalConfig) => Promise<R>): ({ data, controller, abortAfter, }: ControlledRequestProps<P>) => Promise<R>;
14
15
  export declare const createControllerEffect: <T, P = void>(fn: (props: P, config?: RequestOptionalConfig) => Promise<T>) => ControlledEffect<P, T>;
@@ -1 +1 @@
1
- {"version":3,"file":"createControllerEffect.d.ts","sourceRoot":"","sources":["../../../../../src/lib/effector/createControllerEffect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,UAAU,CAAA;AAMpD,MAAM,MAAM,gBAAgB,CAAC,CAAC,EAAE,CAAC,IAAI,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG;IAClD,UAAU,EAAE,eAAe,CAAA;CAC5B,CAAA;AAED,KAAK,sBAAsB,CAAC,CAAC,IAAI;IAC/B,IAAI,EAAE,CAAC,CAAA;IACP,UAAU,CAAC,EAAE,eAAe,CAAA;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IAAE,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,CAAA;AAE5D,wBAAgB,iBAAiB,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,IAAI,EAClD,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,qBAAqB,KAAK,OAAO,CAAC,CAAC,CAAC,uCAM9D,sBAAsB,CAAC,CAAC,CAAC,gBAwB7B;AAED,eAAO,MAAM,sBAAsB,GAAI,CAAC,EAAE,CAAC,aACrC,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,qBAAqB,KAAK,OAAO,CAAC,CAAC,CAAC,2BAa7D,CAAA"}
1
+ {"version":3,"file":"createControllerEffect.d.ts","sourceRoot":"","sources":["../../../../../src/lib/effector/createControllerEffect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,UAAU,CAAA;AAMpD,MAAM,MAAM,gBAAgB,CAAC,CAAC,EAAE,CAAC,IAAI,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG;IAClD,UAAU,EAAE,eAAe,CAAA;CAC5B,CAAA;AAED,KAAK,sBAAsB,CAAC,CAAC,IAAI;IAC/B,IAAI,EAAE,CAAC,CAAA;IACP,UAAU,CAAC,EAAE,eAAe,CAAA;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;CACvC,CAAA;AAED,wBAAgB,iBAAiB,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,IAAI,EAClD,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,qBAAqB,KAAK,OAAO,CAAC,CAAC,CAAC,uCAM9D,sBAAsB,CAAC,CAAC,CAAC,gBA4B7B;AAED,eAAO,MAAM,sBAAsB,GAAI,CAAC,EAAE,CAAC,aACrC,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,qBAAqB,KAAK,OAAO,CAAC,CAAC,CAAC,2BAa7D,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magmamath/students-features",
3
- "version": "1.5.3-rc.3",
3
+ "version": "1.5.3-rc.5",
4
4
  "description": "Magmamath features library",
5
5
  "source": "src/index.ts",
6
6
  "main": "src/index.ts",
@@ -6,9 +6,11 @@ import {
6
6
  } from '../../voice/types'
7
7
  import { VoiceRecordModel } from '../../voice/recording/model/VoiceRecord.model'
8
8
  import { RequestOptionalConfig } from '../../../lib/effector/createControllerEffect'
9
+ import { getText } from '../../../i18n/i18n'
9
10
 
10
11
  const TRANSCRIPT_RETRY_INTERVAL_MS = 2000
11
12
  const TRANSCRIPT_MAX_RETRIES = 20
13
+ const CHAT_WITH_VOICE_TIMEOUT_MS = 60_000
12
14
 
13
15
  type GetTranscriptApi = {
14
16
  getAudioFileTranscript: (audioFileId: string) => Promise<AudioTranscriptResponse>
@@ -19,9 +21,12 @@ type CaptureParams = {
19
21
  record: VoiceRecordCollectionItem
20
22
  }
21
23
 
24
+ export type VoiceTranscriptErrorHandler = (errorMessage: string) => void
25
+
22
26
  type VoiceTranscriptSnapshotModelProps = {
23
27
  voiceRecord: VoiceRecordModel
24
28
  api: GetTranscriptApi
29
+ errorHandler?: VoiceTranscriptErrorHandler
25
30
  }
26
31
 
27
32
  // Voice key: `${assignmentId}-${problemId}-${attemptsCount}`
@@ -68,14 +73,26 @@ export class VoiceTranscriptSnapshotModel {
68
73
  ) => Promise<R>,
69
74
  ) => {
70
75
  return async (params: P, requestConfig?: RequestOptionalConfig): Promise<R> => {
76
+ if (this.hasPendingVoice()) {
77
+ requestConfig?.extendTimeoutMs?.(CHAT_WITH_VOICE_TIMEOUT_MS)
78
+ }
71
79
  const voiceTranscript = await this.getTranscriptForCurrent()
72
80
  return apiFn(voiceTranscript ? { ...params, voiceTranscript } : params, requestConfig)
73
81
  }
74
82
  }
75
83
 
76
- constructor({ voiceRecord, api }: VoiceTranscriptSnapshotModelProps) {
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) {
77
93
  this.voiceRecord = voiceRecord
78
94
  this.api = api
95
+ this.errorHandler = errorHandler
79
96
 
80
97
  sample({
81
98
  clock: voiceRecord.setCurrentRecord,
@@ -84,32 +101,48 @@ export class VoiceTranscriptSnapshotModel {
84
101
  fn: (voiceKey, record) => ({ voiceKey, record }),
85
102
  target: this.captureSnapshotFx,
86
103
  })
104
+
105
+ sample({
106
+ clock: voiceRecord.deleteCurrentRecording,
107
+ source: voiceRecord.$currentKey,
108
+ target: this.clearSnapshotFx,
109
+ })
87
110
  }
88
111
 
89
112
  private readonly voiceRecord: VoiceRecordModel
90
113
  private readonly api: GetTranscriptApi
114
+ private readonly errorHandler?: VoiceTranscriptErrorHandler
91
115
  private readonly transcriptByAudioFileId = new Map<string, Promise<string | undefined>>()
92
116
  private readonly snapshotByChatKey = new Map<string, Promise<string | undefined>>()
93
117
 
94
- private readonly captureSnapshotFx = createEffect(async ({ voiceKey, record }: CaptureParams) => {
118
+ private readonly captureSnapshotFx = createEffect(({ voiceKey, record }: CaptureParams) => {
95
119
  const chatKey = toChatKey(voiceKey)
96
120
  if (!chatKey) return
97
121
 
98
- const audioFileId = await this.resolveAudioFileId(record)
99
- if (!audioFileId) return
122
+ const transcriptPromise = this.resolveAudioFileId(record).then((audioFileId) =>
123
+ audioFileId ? this.fetchTranscript(audioFileId) : undefined,
124
+ )
125
+ this.snapshotByChatKey.set(chatKey, transcriptPromise)
126
+ })
100
127
 
101
- this.snapshotByChatKey.set(chatKey, this.fetchTranscript(audioFileId))
128
+ private readonly clearSnapshotFx = createEffect((voiceKey: string) => {
129
+ const chatKey = toChatKey(voiceKey)
130
+ if (chatKey) this.snapshotByChatKey.delete(chatKey)
102
131
  })
103
132
 
104
133
  private async pollTranscript(audioFileId: string): Promise<string | undefined> {
105
134
  for (let attempt = 0; attempt < TRANSCRIPT_MAX_RETRIES; attempt++) {
106
135
  await new Promise((resolve) => setTimeout(resolve, TRANSCRIPT_RETRY_INTERVAL_MS))
107
136
 
108
- const response = await this.api.getAudioFileTranscript(audioFileId)
109
-
110
- if (response.status === TranscriptionStatus.COMPLETED) return response.text
111
- if (response.status === TranscriptionStatus.FAILED) return undefined
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
+ }
112
144
  }
145
+ this.errorHandler?.(getText('voice.transcriptionError'))
113
146
  return undefined
114
147
  }
115
148
 
@@ -144,6 +144,7 @@
144
144
  "redo": "Redo",
145
145
  "redoRecording": "Redo recording?",
146
146
  "thisWillRemoveRecording": "This will remove your previous recording.",
147
+ "transcriptionError": "Transcription request error",
147
148
  "transcriptions": "Transcriptions",
148
149
  "transcriptNotAvailable": "Transcript not available",
149
150
  "uploadFailed": "Upload failed. Please try again.",
@@ -14,7 +14,10 @@ type ControlledRequestProps<P> = {
14
14
  abortAfter?: number
15
15
  }
16
16
 
17
- export type RequestOptionalConfig = { signal?: AbortSignal }
17
+ export type RequestOptionalConfig = {
18
+ signal?: AbortSignal
19
+ extendTimeoutMs?: (ms: number) => void
20
+ }
18
21
 
19
22
  export function controlledRequest<R = void, P = void>(
20
23
  request: (props: P, config?: RequestOptionalConfig) => Promise<R>,
@@ -26,14 +29,18 @@ export function controlledRequest<R = void, P = void>(
26
29
  }: ControlledRequestProps<P>) => {
27
30
  let timeout: ReturnType<typeof setTimeout> | null = null
28
31
 
29
- if (abortAfter) {
30
- timeout = setTimeout(() => {
31
- controller.abort('timeout')
32
- }, abortAfter)
32
+ const scheduleAbort = (ms: number) => {
33
+ if (timeout !== null) clearTimeout(timeout)
34
+ timeout = ms > 0 ? setTimeout(() => controller.abort('timeout'), ms) : null
33
35
  }
34
36
 
37
+ if (abortAfter) scheduleAbort(abortAfter)
38
+
35
39
  try {
36
- const response = await request(data, { signal: controller?.signal })
40
+ const response = await request(data, {
41
+ signal: controller?.signal,
42
+ extendTimeoutMs: scheduleAbort,
43
+ })
37
44
  if (timeout !== null) clearTimeout(timeout)
38
45
  return response
39
46
  } catch (error: unknown) {