Skip to content

Commit b09797d

Browse files
authored
Merge branch 'feature/entities-streaming-support' into main
2 parents 5838525 + 5860d06 commit b09797d

2 files changed

Lines changed: 72 additions & 59 deletions

File tree

packages/core/src/reducers/createActivitiesReducer.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ function patchActivity(
102102
return 'unknown';
103103
});
104104
}
105+
// if !metadata && type == typing
106+
else if (activity.type === 'typing') {
107+
activity = updateIn(activity, ['text'], () => '');
108+
}
105109

106110
let sequenceId: number;
107111

packages/core/src/utils/getActivityLivestreamingMetadata.ts

Lines changed: 68 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import {
22
any,
33
array,
4+
empty,
45
integer,
6+
length,
57
literal,
68
minValue,
79
nonEmpty,
@@ -11,77 +13,74 @@ import {
1113
pipe,
1214
safeParse,
1315
string,
16+
transform,
1417
undefinedable,
15-
union
18+
union,
19+
unknown
1620
} from 'valibot';
1721

1822
import { type WebChatActivity } from '../types/WebChatActivity';
1923
import getOrgSchemaMessage from './getOrgSchemaMessage';
2024

2125
const EMPTY_ARRAY = Object.freeze([]);
2226

27+
type StreamData = {
28+
streamId?: string;
29+
streamSequence?: number;
30+
streamType?: string;
31+
};
32+
2333
const streamSequenceSchema = pipe(number(), integer(), minValue(1));
2434

25-
const livestreamingActivitySchema = union([
26-
// Interim.
35+
const streamInfoSchema = union([
2736
object({
28-
attachments: optional(array(any()), EMPTY_ARRAY),
29-
channelData: object({
30-
// "streamId" is optional for the very first activity in the session.
31-
streamId: optional(undefinedable(string())),
32-
streamSequence: streamSequenceSchema,
33-
streamType: literal('streaming')
34-
}),
35-
id: string(),
36-
// "text" is optional. If not set or empty, it presents a contentless activity.
37-
text: optional(undefinedable(string())),
38-
type: literal('typing')
39-
}),
40-
// Informative message.
41-
object({
42-
attachments: optional(array(any()), EMPTY_ARRAY),
43-
channelData: object({
44-
// "streamId" is optional for the very first activity in the session.
45-
streamId: optional(undefinedable(string())),
46-
streamSequence: streamSequenceSchema,
47-
streamType: literal('informative')
48-
}),
49-
id: string(),
50-
// Informative may not have "text", but should have abstract instead (checked later)
51-
text: optional(undefinedable(string())),
52-
type: literal('typing'),
53-
entities: optional(array(any()), EMPTY_ARRAY)
37+
// "streamId" is optional for the very first activity in the session.
38+
streamId: optional(undefinedable(string())),
39+
streamSequence: streamSequenceSchema,
40+
streamType: union([literal('streaming'), literal('informative')])
5441
}),
55-
// Conclude with a message.
5642
object({
57-
attachments: optional(array(any()), EMPTY_ARRAY),
58-
channelData: object({
59-
// "streamId" is required for the final activity in the session.
60-
// The final activity must not be the sole activity in the session.
61-
streamId: pipe(string(), nonEmpty()),
62-
streamType: literal('final')
63-
}),
64-
id: string(),
65-
// If "text" is empty, it represents "regretting" the livestream.
66-
text: optional(undefinedable(string())),
67-
type: literal('message')
68-
}),
69-
// Conclude without a message.
70-
object({
71-
attachments: optional(array(any()), EMPTY_ARRAY),
72-
channelData: object({
73-
// "streamId" is required for the final activity in the session.
74-
// The final activity must not be the sole activity in the session.
75-
streamId: pipe(string(), nonEmpty()),
76-
streamType: literal('final')
77-
}),
78-
id: string(),
79-
// If "text" is not set or empty, it represents "regretting" the livestream.
80-
text: optional(undefinedable(literal(''))),
81-
type: literal('typing')
43+
// "streamId" is required for the final activity in the session.
44+
// The final activity must not be the sole activity in the session.
45+
streamId: pipe(string(), nonEmpty()),
46+
streamType: literal('final')
8247
})
8348
]);
8449

50+
const validEntitiesSchema = union([
51+
pipe(
52+
array(streamInfoSchema),
53+
length(1),
54+
transform(data => data[0])
55+
),
56+
pipe(
57+
array(unknown()),
58+
empty(),
59+
transform(() => undefined)
60+
)
61+
]);
62+
63+
const validChannelDataSchema = union([
64+
streamInfoSchema,
65+
pipe(
66+
object({
67+
webChat: object({
68+
receivedAt: number()
69+
})
70+
}),
71+
transform(() => undefined)
72+
)
73+
]);
74+
75+
const livestreamingActivitySchema = object({
76+
entities: validEntitiesSchema,
77+
channelData: validChannelDataSchema,
78+
attachments: optional(array(any()), EMPTY_ARRAY),
79+
id: string(),
80+
text: optional(undefinedable(string())),
81+
type: union([literal('typing'), literal('message')])
82+
});
83+
8584
/**
8685
* Gets the livestreaming metadata of the activity, or `undefined` if the activity is not participating in a livestreaming session.
8786
*
@@ -111,26 +110,36 @@ export default function getActivityLivestreamingMetadata(activity: WebChatActivi
111110
if (result.success) {
112111
const { output } = result;
113112

113+
let streamData: StreamData;
114+
115+
if (output.entities !== undefined) {
116+
streamData = output.entities;
117+
} else if (output.channelData !== undefined) {
118+
streamData = output.channelData;
119+
} else {
120+
return undefined;
121+
}
122+
114123
// If the activity is the first in the session, session ID should be the activity ID.
115-
const sessionId = output.channelData.streamId || output.id;
124+
const sessionId = streamData.streamId || output.id;
116125

117126
return Object.freeze(
118-
output.channelData.streamType === 'final'
127+
streamData.streamType === 'final'
119128
? {
120129
sequenceNumber: Infinity,
121130
sessionId,
122131
type: 'final activity'
123132
}
124133
: {
125-
sequenceNumber: output.channelData.streamSequence,
134+
sequenceNumber: streamData.streamSequence,
126135
sessionId,
127136
type: !(
128137
output.text ||
129138
output.attachments?.length ||
130139
('entities' in output && getOrgSchemaMessage(output.entities)?.abstract)
131140
)
132141
? 'contentless'
133-
: output.channelData.streamType === 'informative'
142+
: streamData.streamType === 'informative'
134143
? 'informative message'
135144
: 'interim activity'
136145
}

0 commit comments

Comments
 (0)