Tobias Brugger commited on
Commit
7245f26
·
1 Parent(s): 5d4b4ba

add grading templates answers

Browse files
src/lib/DatasetViewer.svelte CHANGED
@@ -4,7 +4,11 @@
4
  fetchDatasetConfigYaml,
5
  type DatasetConfigYaml,
6
  } from "./huggingfaceApi";
7
- import type { TermDefinition, EquivalencyScore } from "./types";
 
 
 
 
8
  import { marked } from "marked";
9
  import { config, getDatasetName } from "./config";
10
 
@@ -12,6 +16,7 @@
12
  const availableJurisdictions = config.jurisdictions;
13
  const dataset = getDatasetName("structured-answers");
14
  const scoresDataset = getDatasetName("equivalency-scores");
 
15
  const directTranslations = config.directTranslations;
16
  const equivalencyScoreLabels = config.equivalencyScoreLabels;
17
 
@@ -33,6 +38,8 @@
33
  let selectedTerm1 = $state<string | null>(null);
34
  let selectedTerm2 = $state<string | null>(null);
35
  let datasetConfig = $state<DatasetConfigYaml | null>(null);
 
 
36
 
37
  // Track previous selected terms for detecting user-initiated changes (loop prevention)
38
  let prevSelectedTerm1 = $state<string | null>(null);
@@ -42,6 +49,7 @@
42
  let loading = $state(false);
43
  let error = $state("");
44
  let hasLoaded = $state(false);
 
45
  let selectedCategory = $state<string>("All");
46
 
47
  // Track previous values to detect changes
@@ -77,6 +85,9 @@
77
  prevSelectedTerm2 = null;
78
  hasLoaded = false;
79
 
 
 
 
80
  try {
81
  // Determine the config name for equivalency scores (alphabetically sorted)
82
  const sortedJurisdictions = [jurisdiction1, jurisdiction2].sort();
@@ -97,10 +108,6 @@
97
  );
98
  equivalencyScores = scoresRows as EquivalencyScore[];
99
 
100
- console.log("Jurisdiction 1 data:", data1);
101
- console.log("Jurisdiction 2 data:", data2);
102
- console.log("Equivalency scores:", equivalencyScores);
103
-
104
  hasLoaded = true;
105
 
106
  // Fetch config.yaml for this jurisdiction pair
@@ -147,6 +154,41 @@
147
  console.error("Jurisdiction loading error:", e);
148
  } finally {
149
  loading = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  }
151
  }
152
 
@@ -162,7 +204,8 @@
162
  return found?.definition || "";
163
  }
164
 
165
- function normalizeTermForComparison(term: string): string {
 
166
  // Replace underscores with spaces and normalize whitespace for comparison
167
  return term.replace(/_/g, " ").replace(/\s+/g, " ").trim().toLowerCase();
168
  }
@@ -303,6 +346,67 @@
303
  if (selectedCategory === "All") return score.comparisons;
304
  return score.comparisons.filter((c) => c.category === selectedCategory);
305
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306
  </script>
307
 
308
  <div class="jurisdiction-comparison">
@@ -511,67 +615,111 @@
511
  <div class="comparisons-section">
512
  <h3>Detailed Comparisons</h3>
513
 
514
- <!-- Category Filter Pills -->
515
- {#if categories.length > 0}
516
- <div class="category-filters">
517
- <button
518
- class="filter-pill"
519
- class:active={selectedCategory === "All"}
520
- onclick={() => (selectedCategory = "All")}
521
- >
522
- All ({score.comparisons.length})
523
- </button>
524
- {#each categories as category}
525
- {@const count = score.comparisons.filter(
526
- (c) => c.category === category
527
- ).length}
528
  <button
529
  class="filter-pill"
530
- class:active={selectedCategory === category}
531
- onclick={() => (selectedCategory = category)}
532
  >
533
- {formatCategoryName(category)} ({count})
534
  </button>
535
- {/each}
536
- </div>
537
- {/if}
538
-
539
- <div class="comparisons-list">
540
- {#each filteredComparisons as comparison}
541
- <div class="comparison-item">
542
- <div class="comparison-header">
543
- <span class="comparison-category">
544
- {formatCategoryName(comparison.category)}
545
- </span>
546
- {#if comparison.subcategory}
547
- <span class="comparison-subcategory">
548
- {formatCategoryName(comparison.subcategory)}
 
 
 
 
 
 
 
549
  </span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
550
  {/if}
551
- <span
552
- class={"comparison-score " +
553
- getScoreClass(comparison.similarity_score)}
554
- >{comparison.similarity_score.toFixed(2)}</span
555
- >
556
- </div>
557
- <div class="comparison-meta">
558
- <span class="comparison-weight"
559
- >Weight: {comparison.weight.toFixed(2)}</span
560
- >
561
- <span class="comparison-weighted-score"
562
- >Weighted Score: {comparison.weighted_similarity_score.toFixed(
563
- 2
564
- )}</span
565
- >
566
- </div>
567
- {#if comparison.reasoning}
568
- <div class="comparison-reasoning">
569
- {comparison.reasoning}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
570
  </div>
571
- {/if}
572
- </div>
573
- {/each}
574
- </div>
575
  </div>
576
  {/if}
577
  </div>
@@ -975,6 +1123,54 @@
975
  flex-wrap: wrap;
976
  }
977
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
978
  .comparison-category {
979
  font-weight: 600;
980
  color: #646cff;
@@ -987,13 +1183,29 @@
987
  }
988
 
989
  .comparison-score {
990
- margin-left: auto;
991
  padding: 0.25rem 0.75rem;
992
  border-radius: 12px;
993
  font-weight: 600;
994
  font-size: 0.9rem;
995
  }
996
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
997
  /* Score color variants */
998
  .comparison-score.score-high {
999
  background: #1a7f37;
@@ -1117,7 +1329,7 @@
1117
  .dataset-model-info.subtle {
1118
  background: none;
1119
  border: none;
1120
- padding: 0.25rem 0;
1121
  margin: 0 0 0.5rem 0;
1122
  max-width: 100%;
1123
  color: #888;
@@ -1175,4 +1387,30 @@
1175
  border-bottom: 1px solid #555;
1176
  padding-bottom: 0.4rem;
1177
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1178
  </style>
 
4
  fetchDatasetConfigYaml,
5
  type DatasetConfigYaml,
6
  } from "./huggingfaceApi";
7
+ import type {
8
+ TermDefinition,
9
+ EquivalencyScore,
10
+ GradingTemplateEntry,
11
+ } from "./types";
12
  import { marked } from "marked";
13
  import { config, getDatasetName } from "./config";
14
 
 
16
  const availableJurisdictions = config.jurisdictions;
17
  const dataset = getDatasetName("structured-answers");
18
  const scoresDataset = getDatasetName("equivalency-scores");
19
+ const gradingTemplatesDataset = getDatasetName("grading-templates");
20
  const directTranslations = config.directTranslations;
21
  const equivalencyScoreLabels = config.equivalencyScoreLabels;
22
 
 
38
  let selectedTerm1 = $state<string | null>(null);
39
  let selectedTerm2 = $state<string | null>(null);
40
  let datasetConfig = $state<DatasetConfigYaml | null>(null);
41
+ let gradingTemplates_j1 = $state<GradingTemplateEntry[]>([]);
42
+ let gradingTemplates_j2 = $state<GradingTemplateEntry[]>([]);
43
 
44
  // Track previous selected terms for detecting user-initiated changes (loop prevention)
45
  let prevSelectedTerm1 = $state<string | null>(null);
 
49
  let loading = $state(false);
50
  let error = $state("");
51
  let hasLoaded = $state(false);
52
+ let loadingDetails = $state(false);
53
  let selectedCategory = $state<string>("All");
54
 
55
  // Track previous values to detect changes
 
85
  prevSelectedTerm2 = null;
86
  hasLoaded = false;
87
 
88
+ // Start loading grading templates/details concurrently to reduce wait time
89
+ const detailsPromise = loadDetails();
90
+
91
  try {
92
  // Determine the config name for equivalency scores (alphabetically sorted)
93
  const sortedJurisdictions = [jurisdiction1, jurisdiction2].sort();
 
108
  );
109
  equivalencyScores = scoresRows as EquivalencyScore[];
110
 
 
 
 
 
111
  hasLoaded = true;
112
 
113
  // Fetch config.yaml for this jurisdiction pair
 
154
  console.error("Jurisdiction loading error:", e);
155
  } finally {
156
  loading = false;
157
+ // Ensure details loading finishes (any error already handled in loadDetails)
158
+ try {
159
+ await detailsPromise;
160
+ } catch (e) {
161
+ console.warn("loadDetails failed:", e);
162
+ }
163
+ }
164
+ }
165
+
166
+ async function loadDetails() {
167
+ loadingDetails = true;
168
+ try {
169
+ const [rows_j1, rows_j2] = await Promise.all([
170
+ fetchAllRows(
171
+ gradingTemplatesDataset,
172
+ jurisdiction1,
173
+ "train",
174
+ undefined
175
+ ),
176
+ fetchAllRows(
177
+ gradingTemplatesDataset,
178
+ jurisdiction2,
179
+ "train",
180
+ undefined
181
+ ),
182
+ ]);
183
+ gradingTemplates_j1 = rows_j1 as GradingTemplateEntry[];
184
+ gradingTemplates_j2 = rows_j2 as GradingTemplateEntry[];
185
+ } catch (e) {
186
+ // Non-fatal: templates may not exist for all jurisdictions
187
+ gradingTemplates_j1 = [];
188
+ gradingTemplates_j2 = [];
189
+ console.warn("Failed to load grading templates:", e);
190
+ } finally {
191
+ loadingDetails = false;
192
  }
193
  }
194
 
 
204
  return found?.definition || "";
205
  }
206
 
207
+ function normalizeTermForComparison(term: string | undefined): string {
208
+ if (term === undefined) return "";
209
  // Replace underscores with spaces and normalize whitespace for comparison
210
  return term.replace(/_/g, " ").replace(/\s+/g, " ").trim().toLowerCase();
211
  }
 
346
  if (selectedCategory === "All") return score.comparisons;
347
  return score.comparisons.filter((c) => c.category === selectedCategory);
348
  }
349
+
350
+ // Find a grading template entry matching a comparison's category/subcategory and optional term
351
+ function findGradingTemplate(
352
+ category: string,
353
+ subcategory: string | undefined,
354
+ templates: GradingTemplateEntry[],
355
+ term?: string
356
+ ): GradingTemplateEntry | null {
357
+ if (!templates || templates.length === 0) return null;
358
+
359
+ const normalizedTarget = term ? normalizeTermForComparison(term) : null;
360
+
361
+ return (
362
+ templates.find((t) => {
363
+ const catMatch =
364
+ t.category === category &&
365
+ (t.subcategory || "") === (subcategory || "");
366
+ if (!catMatch) return false;
367
+ if (!normalizedTarget) return true;
368
+ return normalizeTermForComparison(t.term) === normalizedTarget;
369
+ }) || null
370
+ );
371
+ }
372
+
373
+ // Helper getters for templates + question text to avoid {@const} in template
374
+ function getTemplateJ1(comparison: {
375
+ category: string;
376
+ subcategory?: string;
377
+ term_j1?: string;
378
+ }) {
379
+ return findGradingTemplate(
380
+ comparison.category,
381
+ comparison.subcategory,
382
+ gradingTemplates_j1,
383
+ comparison.term_j1
384
+ );
385
+ }
386
+ function getTemplateJ2(comparison: {
387
+ category: string;
388
+ subcategory?: string;
389
+ term_j2?: string;
390
+ }) {
391
+ return findGradingTemplate(
392
+ comparison.category,
393
+ comparison.subcategory,
394
+ gradingTemplates_j2,
395
+ comparison.term_j2
396
+ );
397
+ }
398
+ function getQuestionText(comparison: {
399
+ category: string;
400
+ subcategory?: string;
401
+ term_j1?: string;
402
+ term_j2?: string;
403
+ }) {
404
+ return (
405
+ getTemplateJ1(comparison)?.question ||
406
+ getTemplateJ2(comparison)?.question ||
407
+ ""
408
+ );
409
+ }
410
  </script>
411
 
412
  <div class="jurisdiction-comparison">
 
615
  <div class="comparisons-section">
616
  <h3>Detailed Comparisons</h3>
617
 
618
+ {#if loadingDetails}
619
+ <div class="details-loading">
620
+ <div class="spinner" aria-hidden="true"></div>
621
+ <div class="spinner-label">Loading details…</div>
622
+ </div>
623
+ {:else}
624
+ <!-- Category Filter Pills -->
625
+ {#if categories.length > 0}
626
+ <div class="category-filters">
 
 
 
 
 
627
  <button
628
  class="filter-pill"
629
+ class:active={selectedCategory === "All"}
630
+ onclick={() => (selectedCategory = "All")}
631
  >
632
+ All ({score.comparisons.length})
633
  </button>
634
+ {#each categories as category}
635
+ {@const count = score.comparisons.filter(
636
+ (c) => c.category === category
637
+ ).length}
638
+ <button
639
+ class="filter-pill"
640
+ class:active={selectedCategory === category}
641
+ onclick={() => (selectedCategory = category)}
642
+ >
643
+ {formatCategoryName(category)} ({count})
644
+ </button>
645
+ {/each}
646
+ </div>
647
+ {/if}
648
+
649
+ <div class="comparisons-list">
650
+ {#each filteredComparisons as comparison}
651
+ <div class="comparison-item">
652
+ <div class="comparison-header">
653
+ <span class="comparison-category">
654
+ {formatCategoryName(comparison.category)}
655
  </span>
656
+ {#if comparison.subcategory}
657
+ <span class="comparison-subcategory">
658
+ › {formatCategoryName(comparison.subcategory)}
659
+ </span>
660
+ {/if}
661
+ <div class="category-meta">
662
+ <span class="category-weight"
663
+ >Weight: {comparison.weight.toFixed(2)}</span
664
+ >
665
+ <span class="category-weighted"
666
+ >Weighted Score: {comparison.weighted_similarity_score.toFixed(
667
+ 2
668
+ )}</span
669
+ >
670
+ </div>
671
+ <span
672
+ class={"comparison-score " +
673
+ getScoreClass(comparison.similarity_score)}
674
+ >{comparison.similarity_score.toFixed(2)}</span
675
+ >
676
+ </div>
677
+
678
+ {#if getQuestionText(comparison)}
679
+ <div class="detail-question-single">
680
+ {getQuestionText(comparison)}
681
+ </div>
682
  {/if}
683
+
684
+ <div class="comparison-details">
685
+ <div class="detail-column left">
686
+ {#if getTemplateJ1(comparison)}
687
+ <div class="detail-term">
688
+ {getTemplateJ1(comparison)?.term}
689
+ </div>
690
+ <div class="detail-answer">
691
+ {getTemplateJ1(comparison)?.answer}
692
+ </div>
693
+ {:else}
694
+ <div class="detail-empty">—</div>
695
+ {/if}
696
+ </div>
697
+
698
+ <div class="detail-column center">
699
+ {#if comparison.reasoning}
700
+ <div class="comparison-reasoning">
701
+ {comparison.reasoning}
702
+ </div>
703
+ {/if}
704
+ </div>
705
+
706
+ <div class="detail-column right">
707
+ {#if getTemplateJ2(comparison)}
708
+ <div class="detail-term">
709
+ {getTemplateJ2(comparison)?.term}
710
+ </div>
711
+ <div class="detail-answer">
712
+ {getTemplateJ2(comparison)?.answer}
713
+ </div>
714
+ {:else}
715
+ <div class="detail-empty">—</div>
716
+ {/if}
717
+ </div>
718
  </div>
719
+ </div>
720
+ {/each}
721
+ </div>
722
+ {/if}
723
  </div>
724
  {/if}
725
  </div>
 
1123
  flex-wrap: wrap;
1124
  }
1125
 
1126
+ .comparison-details {
1127
+ display: grid;
1128
+ grid-template-columns: 1fr 1fr 1fr;
1129
+ gap: 1rem;
1130
+ margin-top: 0.75rem;
1131
+ align-items: start;
1132
+ }
1133
+
1134
+ .detail-column {
1135
+ background: #fff;
1136
+ padding: 0.75rem;
1137
+ border-radius: 6px;
1138
+ border: 1px solid #f0f0f0;
1139
+ min-height: 70px;
1140
+ }
1141
+
1142
+ .detail-column.left {
1143
+ border-left: 3px solid #646cff;
1144
+ }
1145
+ .detail-column.right {
1146
+ border-right: 3px solid #646cff;
1147
+ }
1148
+
1149
+ .detail-term {
1150
+ font-weight: 600;
1151
+ margin-bottom: 0.35rem;
1152
+ color: #333;
1153
+ }
1154
+ .detail-question {
1155
+ font-size: 0.95rem;
1156
+ color: #555;
1157
+ margin-bottom: 0.5rem;
1158
+ }
1159
+ .detail-answer {
1160
+ font-size: 0.9rem;
1161
+ color: #444;
1162
+ }
1163
+ .detail-empty {
1164
+ color: #888;
1165
+ font-style: italic;
1166
+ }
1167
+
1168
+ @media (max-width: 900px) {
1169
+ .comparison-details {
1170
+ grid-template-columns: 1fr;
1171
+ }
1172
+ }
1173
+
1174
  .comparison-category {
1175
  font-weight: 600;
1176
  color: #646cff;
 
1183
  }
1184
 
1185
  .comparison-score {
 
1186
  padding: 0.25rem 0.75rem;
1187
  border-radius: 12px;
1188
  font-weight: 600;
1189
  font-size: 0.9rem;
1190
  }
1191
 
1192
+ .category-meta {
1193
+ margin-left: auto;
1194
+ display: flex;
1195
+ gap: 0.5rem;
1196
+ align-items: center;
1197
+ color: #666;
1198
+ font-size: 0.9rem;
1199
+ }
1200
+ .category-weight,
1201
+ .category-weighted {
1202
+ background: #f0f0f0;
1203
+ padding: 0.15rem 0.5rem;
1204
+ border-radius: 6px;
1205
+ font-weight: 600;
1206
+ color: #444;
1207
+ }
1208
+
1209
  /* Score color variants */
1210
  .comparison-score.score-high {
1211
  background: #1a7f37;
 
1329
  .dataset-model-info.subtle {
1330
  background: none;
1331
  border: none;
1332
+ padding: 2rem 0;
1333
  margin: 0 0 0.5rem 0;
1334
  max-width: 100%;
1335
  color: #888;
 
1387
  border-bottom: 1px solid #555;
1388
  padding-bottom: 0.4rem;
1389
  }
1390
+ /* Details loading spinner */
1391
+ .details-loading {
1392
+ display: flex;
1393
+ align-items: center;
1394
+ gap: 0.75rem;
1395
+ padding: 1rem 0;
1396
+ justify-content: center;
1397
+ color: #666;
1398
+ }
1399
+ .spinner {
1400
+ width: 28px;
1401
+ height: 28px;
1402
+ border: 4px solid #e6e6ff;
1403
+ border-top-color: #646cff;
1404
+ border-radius: 50%;
1405
+ animation: spin 1s linear infinite;
1406
+ }
1407
+ .spinner-label {
1408
+ font-size: 0.95rem;
1409
+ color: #444;
1410
+ }
1411
+ @keyframes spin {
1412
+ to {
1413
+ transform: rotate(360deg);
1414
+ }
1415
+ }
1416
  </style>
src/lib/config.ts CHANGED
@@ -52,7 +52,7 @@ export const config = {
52
 
53
  // Helper to build dataset names
54
  export function getDatasetName(
55
- datasetType: "structured-answers" | "equivalency-scores"
56
  ): string {
57
  return `${config.api.huggingfaceOrg}/${datasetType}`;
58
  }
 
52
 
53
  // Helper to build dataset names
54
  export function getDatasetName(
55
+ datasetType: "structured-answers" | "equivalency-scores" | "grading-templates"
56
  ): string {
57
  return `${config.api.huggingfaceOrg}/${datasetType}`;
58
  }
src/lib/types.ts CHANGED
@@ -97,3 +97,11 @@ export interface EquivalencyScore {
97
  comparative_law_note: string;
98
  comparisons?: Comparison[];
99
  }
 
 
 
 
 
 
 
 
 
97
  comparative_law_note: string;
98
  comparisons?: Comparison[];
99
  }
100
+
101
+ export interface GradingTemplateEntry {
102
+ term: string;
103
+ category: string;
104
+ subcategory: string;
105
+ question: string;
106
+ answer: string;
107
+ }