@@ -898,6 +898,310 @@ export class DataGenerator {
898
898
: JSON . stringify ( this . fileContent . chatMlJson ) ;
899
899
}
900
900
901
+ /**
902
+ * Creates realistic support chat session data with conversational flow.
903
+ * Use for: Demonstrating session-based conversations with tool calls and scoring.
904
+ */
905
+ generateSupportChatSessionData ( projectId : string ) : {
906
+ traces : TraceRecordInsertType [ ] ;
907
+ observations : ObservationRecordInsertType [ ] ;
908
+ scores : ScoreRecordInsertType [ ] ;
909
+ } {
910
+ // Domain: Art museum membership support (non-tech)
911
+ const dialogues = [
912
+ {
913
+ user : "Hi—my family membership at the City Art Museum was charged twice this month." ,
914
+ assistant :
915
+ "Sorry about that. I can help. Could you share your membership ID and the last 4 digits of the card?" ,
916
+ } ,
917
+ {
918
+ user : "Membership ID is CAM-48291, card ending 4417." ,
919
+ assistant : "Thanks. I'll check your billing records now." ,
920
+ tool : {
921
+ name : "billing.lookup" ,
922
+ input : { membershipId : "CAM-48291" , last4 : "4417" } ,
923
+ output : {
924
+ duplicateCharge : true ,
925
+ amounts : [ 95.0 , 95.0 ] ,
926
+ date : "2025-08-28" ,
927
+ } ,
928
+ } ,
929
+ } ,
930
+ {
931
+ user : "Okay." ,
932
+ assistant :
933
+ "I found a duplicate charge on 2025-08-28 for $95. I can issue a refund to the original payment method. Shall I proceed?" ,
934
+ } ,
935
+ {
936
+ user : "Yes, please issue the refund." ,
937
+ assistant : "Processing your refund now. One moment." ,
938
+ tool : {
939
+ name : "billing.refund" ,
940
+ input : { membershipId : "CAM-48291" , amount : 95.0 } ,
941
+ output : { status : "success" , refundId : "RFND-20931" } ,
942
+ } ,
943
+ } ,
944
+ {
945
+ user : "Thank you!" ,
946
+ assistant :
947
+ "Refund RFND-20931 has been issued. You'll see it on your statement within 3–5 business days." ,
948
+ } ,
949
+ {
950
+ user : "No, that's all. Appreciate the quick help!" ,
951
+ assistant : "Happy to help. Enjoy your next visit to the museum!" ,
952
+ } ,
953
+ // a couple more lightweight turns for scrolling realism
954
+ {
955
+ user : "Oh, and do members get early access to exhibitions?" ,
956
+ assistant :
957
+ "Yes—members get a 48-hour early booking window and a preview evening invite." ,
958
+ } ,
959
+ {
960
+ user : "Perfect." ,
961
+ assistant : "You're all set. Have a great day!" ,
962
+ } ,
963
+ ] ;
964
+
965
+ const now = Date . now ( ) ;
966
+ const traces : TraceRecordInsertType [ ] = dialogues . map ( ( d , index ) => ( {
967
+ id : `support-chat-${ index } -${ projectId . slice ( - 8 ) } ` ,
968
+ timestamp : now + index * 1000 ,
969
+ name : "SupportChatSession" ,
970
+ user_id : null ,
971
+ metadata : { scenario : "support-chat" } ,
972
+ release : null ,
973
+ version : null ,
974
+ project_id : projectId ,
975
+ environment : "default" ,
976
+ public : false ,
977
+ bookmarked : false ,
978
+ tags : [ "support" , "chat" , "session" ] ,
979
+ input : JSON . stringify (
980
+ d . tool
981
+ ? {
982
+ messages : [
983
+ { role : "user" , content : d . user } ,
984
+ { role : "assistant" , content : d . assistant } ,
985
+ {
986
+ role : "tool" ,
987
+ name : d . tool . name ,
988
+ content : d . tool . output ,
989
+ } ,
990
+ ] ,
991
+ }
992
+ : { messages : [ { role : "user" , content : d . user } ] } ,
993
+ ) ,
994
+ output : JSON . stringify ( { role : "assistant" , content : d . assistant } ) ,
995
+ session_id : "support-chat-session" ,
996
+ created_at : now + index * 1000 ,
997
+ updated_at : now + index * 1000 + 500 ,
998
+ event_ts : now + index * 1000 ,
999
+ is_deleted : 0 ,
1000
+ } ) ) ;
1001
+
1002
+ // Create one GENERATION observation per trace
1003
+ const observations : ObservationRecordInsertType [ ] = dialogues
1004
+ . map ( ( d , index ) => {
1005
+ const start = now + index * 1000 + 50 ;
1006
+ const end = start + 400 + Math . floor ( Math . random ( ) * 400 ) ;
1007
+ const inputTokens = 80 + Math . floor ( Math . random ( ) * 60 ) ;
1008
+ const outputTokens = 60 + Math . floor ( Math . random ( ) * 60 ) ;
1009
+ const totalTokens = inputTokens + outputTokens ;
1010
+
1011
+ const baseGen : ObservationRecordInsertType = {
1012
+ id : `support-chat-${ index } -${ projectId . slice ( - 8 ) } -gen` ,
1013
+ trace_id : `support-chat-${ index } -${ projectId . slice ( - 8 ) } ` ,
1014
+ project_id : projectId ,
1015
+ type : "GENERATION" ,
1016
+ parent_observation_id : null ,
1017
+ environment : "default" ,
1018
+ start_time : start ,
1019
+ end_time : end ,
1020
+ name : "llm-generation" ,
1021
+ metadata : { } ,
1022
+ level : "DEFAULT" ,
1023
+ status_message : null ,
1024
+ version : null ,
1025
+ input : JSON . stringify ( {
1026
+ messages : [
1027
+ { role : "user" , content : d . user } ,
1028
+ d . tool
1029
+ ? {
1030
+ role : "tool" ,
1031
+ name : d . tool . name ,
1032
+ content : d . tool . output ,
1033
+ }
1034
+ : undefined ,
1035
+ ] . filter ( Boolean ) ,
1036
+ } ) ,
1037
+ output : JSON . stringify ( { role : "assistant" , content : d . assistant } ) ,
1038
+ provided_model_name : "gpt-4o" ,
1039
+ internal_model_id : null ,
1040
+ model_parameters : JSON . stringify ( { temperature : 0.2 } ) ,
1041
+ provided_usage_details : {
1042
+ input : inputTokens ,
1043
+ output : outputTokens ,
1044
+ total : totalTokens ,
1045
+ } ,
1046
+ usage_details : {
1047
+ input : inputTokens ,
1048
+ output : outputTokens ,
1049
+ total : totalTokens ,
1050
+ } ,
1051
+ provided_cost_details : {
1052
+ input : Math . round ( inputTokens * 2 ) / 1_000_000 ,
1053
+ output : Math . round ( outputTokens * 3 ) / 1_000_000 ,
1054
+ total : Math . round ( totalTokens * 5 ) / 1_000_000 ,
1055
+ } ,
1056
+ cost_details : {
1057
+ input : Math . round ( inputTokens * 2 ) / 1_000_000 ,
1058
+ output : Math . round ( outputTokens * 3 ) / 1_000_000 ,
1059
+ total : Math . round ( totalTokens * 5 ) / 1_000_000 ,
1060
+ } ,
1061
+ total_cost : Math . round ( totalTokens * 5 ) / 1_000_000 ,
1062
+ completion_start_time : start + 120 ,
1063
+ prompt_id : null ,
1064
+ prompt_name : null ,
1065
+ prompt_version : null ,
1066
+ created_at : start ,
1067
+ updated_at : end ,
1068
+ event_ts : start ,
1069
+ is_deleted : 0 ,
1070
+ } ;
1071
+
1072
+ if ( ! d . tool ) return [ baseGen ] ;
1073
+
1074
+ const toolObs : ObservationRecordInsertType = {
1075
+ id : `support-chat-${ index } -${ projectId . slice ( - 8 ) } -tool` ,
1076
+ trace_id : `support-chat-${ index } -${ projectId . slice ( - 8 ) } ` ,
1077
+ project_id : projectId ,
1078
+ type : "TOOL" ,
1079
+ parent_observation_id : null ,
1080
+ environment : "default" ,
1081
+ start_time : start - 40 ,
1082
+ end_time : start - 5 ,
1083
+ name : d . tool . name ,
1084
+ metadata : { } ,
1085
+ level : "DEFAULT" ,
1086
+ status_message : null ,
1087
+ version : null ,
1088
+ input : JSON . stringify ( d . tool . input ) ,
1089
+ output : JSON . stringify ( d . tool . output ) ,
1090
+ provided_model_name : null ,
1091
+ internal_model_id : null ,
1092
+ model_parameters : null ,
1093
+ provided_usage_details : { } ,
1094
+ usage_details : { } ,
1095
+ provided_cost_details : { } ,
1096
+ cost_details : { } ,
1097
+ total_cost : null ,
1098
+ completion_start_time : null ,
1099
+ prompt_id : null ,
1100
+ prompt_name : null ,
1101
+ prompt_version : null ,
1102
+ created_at : start - 40 ,
1103
+ updated_at : start - 5 ,
1104
+ event_ts : start - 40 ,
1105
+ is_deleted : 0 ,
1106
+ } ;
1107
+
1108
+ return [ toolObs , baseGen ] ;
1109
+ } )
1110
+ . flat ( ) ;
1111
+
1112
+ // Create a couple of scores per trace
1113
+ const scores : ScoreRecordInsertType [ ] = dialogues
1114
+ . map ( ( _ , index ) => {
1115
+ const baseTs = now + index * 1000 + 600 ;
1116
+ const helpfulness : ScoreRecordInsertType = {
1117
+ id : `support-chat-${ index } -${ projectId . slice ( - 8 ) } -score-helpfulness` ,
1118
+ project_id : projectId ,
1119
+ trace_id : `support-chat-${ index } -${ projectId . slice ( - 8 ) } ` ,
1120
+ session_id : null ,
1121
+ dataset_run_id : null ,
1122
+ observation_id : null ,
1123
+ environment : "default" ,
1124
+ name : "helpfulness" ,
1125
+ value : 70 + Math . random ( ) * 25 ,
1126
+ source : "API" ,
1127
+ comment : "Heuristic helpfulness score" ,
1128
+ metadata : { } ,
1129
+ author_user_id : null ,
1130
+ config_id : null ,
1131
+ data_type : "NUMERIC" ,
1132
+ string_value : null ,
1133
+ queue_id : null ,
1134
+ created_at : baseTs ,
1135
+ updated_at : baseTs ,
1136
+ timestamp : baseTs ,
1137
+ event_ts : baseTs ,
1138
+ is_deleted : 0 ,
1139
+ } ;
1140
+
1141
+ const safeVal = Math . random ( ) > 0.1 ? 1 : 0 ;
1142
+ const safety : ScoreRecordInsertType = {
1143
+ id : `support-chat-${ index } -${ projectId . slice ( - 8 ) } -score-safety` ,
1144
+ project_id : projectId ,
1145
+ trace_id : `support-chat-${ index } -${ projectId . slice ( - 8 ) } ` ,
1146
+ session_id : null ,
1147
+ dataset_run_id : null ,
1148
+ observation_id : null ,
1149
+ environment : "default" ,
1150
+ name : "safe" ,
1151
+ value : safeVal ,
1152
+ source : "API" ,
1153
+ comment : "Content safety" ,
1154
+ metadata : { } ,
1155
+ author_user_id : null ,
1156
+ config_id : null ,
1157
+ data_type : "BOOLEAN" ,
1158
+ string_value : safeVal === 1 ? "true" : "false" ,
1159
+ queue_id : null ,
1160
+ created_at : baseTs + 10 ,
1161
+ updated_at : baseTs + 10 ,
1162
+ timestamp : baseTs + 10 ,
1163
+ event_ts : baseTs + 10 ,
1164
+ is_deleted : 0 ,
1165
+ } ;
1166
+
1167
+ // Optional: resolution score on last turn
1168
+ const isFinal = index === dialogues . length - 1 ;
1169
+ const resolved : ScoreRecordInsertType | null = isFinal
1170
+ ? {
1171
+ id : `support-chat-${ index } -${ projectId . slice ( - 8 ) } -score-resolved` ,
1172
+ project_id : projectId ,
1173
+ trace_id : `support-chat-${ index } -${ projectId . slice ( - 8 ) } ` ,
1174
+ session_id : null ,
1175
+ dataset_run_id : null ,
1176
+ observation_id : null ,
1177
+ environment : "default" ,
1178
+ name : "resolved" ,
1179
+ value : 1 ,
1180
+ source : "API" ,
1181
+ comment : "Conversation resolved" ,
1182
+ metadata : { } ,
1183
+ author_user_id : null ,
1184
+ config_id : null ,
1185
+ data_type : "BOOLEAN" ,
1186
+ string_value : "true" ,
1187
+ queue_id : null ,
1188
+ created_at : baseTs + 20 ,
1189
+ updated_at : baseTs + 20 ,
1190
+ timestamp : baseTs + 20 ,
1191
+ event_ts : baseTs + 20 ,
1192
+ is_deleted : 0 ,
1193
+ }
1194
+ : null ;
1195
+
1196
+ return [ helpfulness , safety , resolved ] . filter (
1197
+ Boolean ,
1198
+ ) as ScoreRecordInsertType [ ] ;
1199
+ } )
1200
+ . flat ( ) ;
1201
+
1202
+ return { traces, observations, scores } ;
1203
+ }
1204
+
901
1205
/**
902
1206
* Creates exactly one score per evaluation trace with prefixed IDs.
903
1207
* Use for: Evaluation traces that need score validation, evaluator testing.
0 commit comments