Skip to content

Commit 5860d06

Browse files
author
=
committed
add support for reading streaming data in entities
1 parent 7d7adb8 commit 5860d06

1 file changed

Lines changed: 68 additions & 58 deletions

File tree

packages/core/src/utils/getActivityLivestreamingMetadata.ts

Lines changed: 68 additions & 58 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,75 +13,73 @@ 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

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

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

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

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

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

115125
return Object.freeze(
116-
output.channelData.streamType === 'final'
126+
streamData.streamType === 'final'
117127
? {
118128
sequenceNumber: Infinity,
119129
sessionId,
120130
type: 'final activity'
121131
}
122132
: {
123-
sequenceNumber: output.channelData.streamSequence,
133+
sequenceNumber: streamData.streamSequence,
124134
sessionId,
125135
type: !(output.text || output.attachments?.length)
126136
? 'contentless'
127-
: output.channelData.streamType === 'informative'
137+
: streamData.streamType === 'informative'
128138
? 'informative message'
129139
: 'interim activity'
130140
}

0 commit comments

Comments
 (0)