@vibexnpm/talkx 7.0.0 → 7.1.0
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/index.d.ts +20 -3
- package/dist/talkflow-sdk.esm.js +1 -1
- package/dist/talkflow-sdk.esm.js.map +1 -1
- package/dist/talkflow-sdk.standalone.js +1 -1
- package/dist/talkflow-sdk.standalone.js.map +1 -1
- package/dist/talkflow-sdk.umd.js +1 -1
- package/dist/talkflow-sdk.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/chat/ChatClient.js +50 -6
- package/types/index.d.ts +20 -3
package/package.json
CHANGED
package/src/chat/ChatClient.js
CHANGED
|
@@ -142,6 +142,7 @@ class ChatClient extends EventEmitter {
|
|
|
142
142
|
THINKING: '생각 중…',
|
|
143
143
|
SEARCHING: '검색 중…',
|
|
144
144
|
PLANNING: '기획 중…',
|
|
145
|
+
DOCUMENT_ANALYSIS: '문서 분석 중…',
|
|
145
146
|
WRITING: '작성 중…'
|
|
146
147
|
};
|
|
147
148
|
// PHASE 의 expiresAt 누락/이상 시 fallback TTL (ms) — 서버 ttl(최소 30s) 보다 약간 길게.
|
|
@@ -3078,12 +3079,19 @@ class ChatClient extends EventEmitter {
|
|
|
3078
3079
|
return;
|
|
3079
3080
|
}
|
|
3080
3081
|
const phase = event.phase || null;
|
|
3082
|
+
const samePhase = Boolean(prevSlot && prevSlot.phase === phase);
|
|
3083
|
+
// 같은 phase의 일반 heartbeat에는 progress가 없다. 그 틱이 직전 실측 숫자를 지우지 않게
|
|
3084
|
+
// 보존하고, phase가 바뀔 때만 이전 작업의 숫자를 폐기한다.
|
|
3085
|
+
const progress = event.progress
|
|
3086
|
+
? this._normalizeAssistantProgress(event.progress)
|
|
3087
|
+
: (samePhase && prevSlot ? prevSlot.progress : null);
|
|
3081
3088
|
// 같은 stream 의 PHASE 재발행(B-3 synthesis heartbeat 등)이면 누적 상태를 보존한다.
|
|
3082
3089
|
// ⚠️ 새 stream 의 커서는 <b>-1</b> 로 연다. DeltaCoalescer 의 첫 청크가 seq=0 이라 0 으로 열면
|
|
3083
3090
|
// 첫 글자가 "중복"으로 버려진다 (설계 §5.5.0).
|
|
3084
3091
|
const prev = prevSlot;
|
|
3085
3092
|
const slot = this._setAssistantSlot(roomId, streamId, event, {
|
|
3086
3093
|
phase,
|
|
3094
|
+
progress,
|
|
3087
3095
|
phaseUpdatedAt: createdAt !== null ? createdAt : (prev ? prev.phaseUpdatedAt : 0),
|
|
3088
3096
|
text: prev ? (prev.text || '') : '',
|
|
3089
3097
|
lastSeq: prev ? this._seqOf(prev.lastSeq) : -1,
|
|
@@ -3132,7 +3140,9 @@ class ChatClient extends EventEmitter {
|
|
|
3132
3140
|
lastSeq = seq;
|
|
3133
3141
|
}
|
|
3134
3142
|
|
|
3135
|
-
const slot = this._setAssistantSlot(roomId, streamId, event, {
|
|
3143
|
+
const slot = this._setAssistantSlot(roomId, streamId, event, {
|
|
3144
|
+
phase, progress: prev ? (prev.progress || null) : null, text, lastSeq, textAvailable
|
|
3145
|
+
});
|
|
3136
3146
|
this._emitAssistantStream(roomId, streamId, slot, {
|
|
3137
3147
|
active: true,
|
|
3138
3148
|
text: slot.textAvailable ? slot.text : null, // 누적 전체(버블 렌더용)
|
|
@@ -3153,6 +3163,7 @@ class ChatClient extends EventEmitter {
|
|
|
3153
3163
|
active: false,
|
|
3154
3164
|
personaId: event.personaId || (slot && slot.personaId) || null,
|
|
3155
3165
|
phase: null,
|
|
3166
|
+
progress: null,
|
|
3156
3167
|
label: null,
|
|
3157
3168
|
status: event.status || null,
|
|
3158
3169
|
messageId: event.messageId || null
|
|
@@ -3242,7 +3253,7 @@ class ChatClient extends EventEmitter {
|
|
|
3242
3253
|
// 유예 구간에 걸린 종료분. 진행 표시를 켜지 않고 최종 메시지로 바로 확정시킨다.
|
|
3243
3254
|
this._tombstoneAssistantStream(roomId, streamId);
|
|
3244
3255
|
this._emitAssistantStream(roomId, streamId, null, {
|
|
3245
|
-
active: false, phase: null, label: null, personaId: s.personaId || null,
|
|
3256
|
+
active: false, phase: null, progress: null, label: null, personaId: s.personaId || null,
|
|
3246
3257
|
status: s.doneStatus || null, messageId: s.messageId || null, resumed: true
|
|
3247
3258
|
});
|
|
3248
3259
|
return;
|
|
@@ -3253,6 +3264,7 @@ class ChatClient extends EventEmitter {
|
|
|
3253
3264
|
personaId: s.personaId, expiresAt: undefined
|
|
3254
3265
|
}, {
|
|
3255
3266
|
phase: s.phase || null,
|
|
3267
|
+
progress: this._normalizeAssistantProgress(s.progress),
|
|
3256
3268
|
phaseUpdatedAt: typeof s.phaseUpdatedAt === 'number' ? s.phaseUpdatedAt : 0,
|
|
3257
3269
|
text: textAvailable ? (s.text || '') : '',
|
|
3258
3270
|
lastSeq: this._seqOf(s.lastSeq),
|
|
@@ -3452,6 +3464,7 @@ class ChatClient extends EventEmitter {
|
|
|
3452
3464
|
const slot = {
|
|
3453
3465
|
personaId: event.personaId || (prev && prev.personaId) || null,
|
|
3454
3466
|
phase: fields.phase,
|
|
3467
|
+
progress: fields.progress || null,
|
|
3455
3468
|
// 마지막으로 <b>적용된</b> PHASE 의 발행 시각 — pending PHASE 역행 판정 커서(§6.2).
|
|
3456
3469
|
phaseUpdatedAt: fields.phaseUpdatedAt !== undefined
|
|
3457
3470
|
? fields.phaseUpdatedAt
|
|
@@ -3476,7 +3489,7 @@ class ChatClient extends EventEmitter {
|
|
|
3476
3489
|
this._deleteAssistantSlot(roomId, streamId);
|
|
3477
3490
|
this.logger.debug(`Assistant progress auto-cleared (timeout): room=${roomId}, stream=${streamId}`);
|
|
3478
3491
|
this._emitAssistantStream(roomId, streamId, slot, {
|
|
3479
|
-
active: false, phase: null, label: null, reason: 'timeout'
|
|
3492
|
+
active: false, phase: null, progress: null, label: null, reason: 'timeout'
|
|
3480
3493
|
});
|
|
3481
3494
|
}
|
|
3482
3495
|
|
|
@@ -3491,12 +3504,14 @@ class ChatClient extends EventEmitter {
|
|
|
3491
3504
|
*/
|
|
3492
3505
|
_emitAssistantStream(roomId, streamId, slot, overrides) {
|
|
3493
3506
|
const phase = 'phase' in overrides ? overrides.phase : (slot ? slot.phase : null);
|
|
3507
|
+
const progress = 'progress' in overrides ? overrides.progress : (slot ? slot.progress : null);
|
|
3494
3508
|
this.emit('assistantProgress', {
|
|
3495
3509
|
roomId,
|
|
3496
3510
|
streamId,
|
|
3497
3511
|
personaId: slot ? slot.personaId : null,
|
|
3498
3512
|
phase,
|
|
3499
|
-
|
|
3513
|
+
progress,
|
|
3514
|
+
label: this._assistantPhaseLabel(phase, progress),
|
|
3500
3515
|
textAvailable: slot ? slot.textAvailable : true,
|
|
3501
3516
|
lastSeq: slot ? slot.lastSeq : -1,
|
|
3502
3517
|
...overrides
|
|
@@ -3528,6 +3543,7 @@ class ChatClient extends EventEmitter {
|
|
|
3528
3543
|
return {
|
|
3529
3544
|
roomId, active: false, streamCount: 0,
|
|
3530
3545
|
streamId: null, personaId: null, phase: null, label: null, text: null, textAvailable: true,
|
|
3546
|
+
progress: null,
|
|
3531
3547
|
streams: []
|
|
3532
3548
|
};
|
|
3533
3549
|
}
|
|
@@ -3539,7 +3555,8 @@ class ChatClient extends EventEmitter {
|
|
|
3539
3555
|
streamId,
|
|
3540
3556
|
personaId: slot.personaId,
|
|
3541
3557
|
phase: slot.phase,
|
|
3542
|
-
|
|
3558
|
+
progress: slot.progress || null,
|
|
3559
|
+
label: this._assistantPhaseLabel(slot.phase, slot.progress),
|
|
3543
3560
|
text: slot.textAvailable ? (slot.text || null) : null,
|
|
3544
3561
|
textAvailable: slot.textAvailable,
|
|
3545
3562
|
// stream 단위 UI(권장 (B))가 마운트 즉시 버블을 복원할 수 있도록 전부 싣는다.
|
|
@@ -3550,7 +3567,8 @@ class ChatClient extends EventEmitter {
|
|
|
3550
3567
|
streamId: id,
|
|
3551
3568
|
personaId: s.personaId,
|
|
3552
3569
|
phase: s.phase,
|
|
3553
|
-
|
|
3570
|
+
progress: s.progress || null,
|
|
3571
|
+
label: this._assistantPhaseLabel(s.phase, s.progress),
|
|
3554
3572
|
text: s.textAvailable ? (s.text || null) : null,
|
|
3555
3573
|
textAvailable: s.textAvailable,
|
|
3556
3574
|
resumed: s.resumed === true
|
|
@@ -3558,6 +3576,32 @@ class ChatClient extends EventEmitter {
|
|
|
3558
3576
|
};
|
|
3559
3577
|
}
|
|
3560
3578
|
|
|
3579
|
+
/** @private */
|
|
3580
|
+
_normalizeAssistantProgress(progress) {
|
|
3581
|
+
if (!progress || !['ANALYZING', 'SUMMARIZING'].includes(progress.stage)) return null;
|
|
3582
|
+
if (progress.stage === 'SUMMARIZING') {
|
|
3583
|
+
return { stage: 'SUMMARIZING', current: null, total: null, unit: null };
|
|
3584
|
+
}
|
|
3585
|
+
const current = Number.isInteger(progress.current) ? progress.current : null;
|
|
3586
|
+
const total = Number.isInteger(progress.total) ? progress.total : null;
|
|
3587
|
+
const unit = ['DOCUMENT', 'PAGE', 'TEXT_CHUNK'].includes(progress.unit) ? progress.unit : null;
|
|
3588
|
+
if (current === null || total === null || !unit || current < 0 || total < 1 || current > total) {
|
|
3589
|
+
return null;
|
|
3590
|
+
}
|
|
3591
|
+
return { stage: 'ANALYZING', current, total, unit };
|
|
3592
|
+
}
|
|
3593
|
+
|
|
3594
|
+
/** @private */
|
|
3595
|
+
_assistantPhaseLabel(phase, progress) {
|
|
3596
|
+
if (!phase) return null;
|
|
3597
|
+
if (phase !== 'DOCUMENT_ANALYSIS') return this._assistantPhaseLabels[phase] || null;
|
|
3598
|
+
if (progress && progress.stage === 'SUMMARIZING') return '분석 결과 종합 중…';
|
|
3599
|
+
if (!progress || progress.stage !== 'ANALYZING') return this._assistantPhaseLabels[phase];
|
|
3600
|
+
const suffix = progress.unit === 'PAGE' ? '페이지'
|
|
3601
|
+
: progress.unit === 'TEXT_CHUNK' ? '구간' : '개';
|
|
3602
|
+
return `문서 분석 중… (${progress.current}/${progress.total}${suffix})`;
|
|
3603
|
+
}
|
|
3604
|
+
|
|
3561
3605
|
/**
|
|
3562
3606
|
* 방의 <b>현재</b> AI 진행 상태를 즉시 반환 (네트워크 호출 없음).
|
|
3563
3607
|
*
|
package/types/index.d.ts
CHANGED
|
@@ -1264,6 +1264,18 @@ export interface TypingEvent {
|
|
|
1264
1264
|
senderType: 'USER' | 'ASSISTANT';
|
|
1265
1265
|
}
|
|
1266
1266
|
|
|
1267
|
+
/** 문서 분석의 재입장 복원 가능한 실측 진행률. */
|
|
1268
|
+
export interface DocumentAnalysisProgress {
|
|
1269
|
+
/** ANALYZING=페이지/텍스트 구간 처리, SUMMARIZING=확보한 근거 종합. */
|
|
1270
|
+
stage: 'ANALYZING' | 'SUMMARIZING';
|
|
1271
|
+
/** ANALYZING일 때 처리 완료 수. */
|
|
1272
|
+
current: number | null;
|
|
1273
|
+
/** ANALYZING일 때 전체 수. */
|
|
1274
|
+
total: number | null;
|
|
1275
|
+
/** 진행 단위. SUMMARIZING일 때 null. */
|
|
1276
|
+
unit: 'DOCUMENT' | 'PAGE' | 'TEXT_CHUNK' | null;
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1267
1279
|
/**
|
|
1268
1280
|
* AI 진행 표시 이벤트 — `/topic/chat/{roomId}/assistant-stream` 의 PHASE/DONE.
|
|
1269
1281
|
*
|
|
@@ -1281,6 +1293,7 @@ export interface TypingEvent {
|
|
|
1281
1293
|
*
|
|
1282
1294
|
* 현재는 PM_BACKSTAGE(방당 단일 PM) 만 phase 를 발행 — 멘션/버튼/14인 경로는 본 이벤트 없이 typing 만.
|
|
1283
1295
|
*/
|
|
1296
|
+
|
|
1284
1297
|
export interface AssistantProgressEvent {
|
|
1285
1298
|
/** 채팅방 ID. */
|
|
1286
1299
|
roomId: string;
|
|
@@ -1294,7 +1307,9 @@ export interface AssistantProgressEvent {
|
|
|
1294
1307
|
/** 진행 주체 페르소나 ID. */
|
|
1295
1308
|
personaId: string | null;
|
|
1296
1309
|
/** 진행 단계 — `active:true` 일 때. 종료 시 null. */
|
|
1297
|
-
phase: 'THINKING' | 'SEARCHING' | 'PLANNING' | 'WRITING' | null;
|
|
1310
|
+
phase: 'THINKING' | 'SEARCHING' | 'PLANNING' | 'DOCUMENT_ANALYSIS' | 'WRITING' | null;
|
|
1311
|
+
/** DOCUMENT_ANALYSIS의 실측 진행률. 같은 phase heartbeat 사이와 재입장 후에도 보존된다. */
|
|
1312
|
+
progress?: DocumentAnalysisProgress | null;
|
|
1298
1313
|
/** phase 의 기본 표시 라벨(예: "생각 중…"). SDK 매핑 — 커스텀하려면 `phase` 사용. 종료 시 null. */
|
|
1299
1314
|
label: string | null;
|
|
1300
1315
|
/** true=진행중(PHASE/DELTA) / false=종료(DONE·TTL). */
|
|
@@ -1360,7 +1375,8 @@ export interface AssistantRoomProgressEvent {
|
|
|
1360
1375
|
/** 표시 기준이 된 stream (가장 최근 갱신). 없으면 null. */
|
|
1361
1376
|
streamId: string | null;
|
|
1362
1377
|
personaId: string | null;
|
|
1363
|
-
phase: 'THINKING' | 'SEARCHING' | 'PLANNING' | 'WRITING' | null;
|
|
1378
|
+
phase: 'THINKING' | 'SEARCHING' | 'PLANNING' | 'DOCUMENT_ANALYSIS' | 'WRITING' | null;
|
|
1379
|
+
progress?: DocumentAnalysisProgress | null;
|
|
1364
1380
|
label: string | null;
|
|
1365
1381
|
/** 표시 기준 stream 의 누적 본문. `textAvailable:false` 거나 진행 중이 아니면 null. */
|
|
1366
1382
|
text?: string | null;
|
|
@@ -1372,7 +1388,8 @@ export interface AssistantRoomProgressEvent {
|
|
|
1372
1388
|
export interface AssistantProgressStreamSnapshot {
|
|
1373
1389
|
streamId: string;
|
|
1374
1390
|
personaId: string | null;
|
|
1375
|
-
phase: 'THINKING' | 'SEARCHING' | 'PLANNING' | 'WRITING' | null;
|
|
1391
|
+
phase: 'THINKING' | 'SEARCHING' | 'PLANNING' | 'DOCUMENT_ANALYSIS' | 'WRITING' | null;
|
|
1392
|
+
progress?: DocumentAnalysisProgress | null;
|
|
1376
1393
|
label: string | null;
|
|
1377
1394
|
text: string | null;
|
|
1378
1395
|
textAvailable: boolean;
|