Repository: twosixlabs/armory
Issue: Error running scenario_configs/eval5/asr_librispeech/ configs

To reproduce:
```
python -m armory run scenario_configs/eval5/asr_librispeech/defended_entailment.json --num-eval-batches 2
```
from the `develop` branch. Note that this error does _not_ occur on the `dev_0.15.2` branch, so it doesn't affect eval 5

Error message:
```
  File "/workspace/armory/scenarios/scenario.py", line 364, in evaluate_current
    self.run_benign()
    │    └ <function Scenario.run_benign at 0x7f7d89edd8b0>
    └ <armory.scenarios.audio_asr.AutomaticSpeechRecognition object at 0x7f7d8b020820>

  File "/workspace/armory/scenarios/scenario.py", line 310, in run_benign
    self.probe.update(y_pred=y_pred)
    │    │     │             └ array(['BRIGHTER THAN EARLY DAWN THE MOST BRILLIANT DYE ARE BLOWN CLEAR BAND OF COLOR THROUGH THE SKY THAT SWIRL AND SWEEP AN...
    │    │     └ <function Probe.update at 0x7f7d8a1468b0>
    │    └ <armory.instrument.instrument.Probe object at 0x7f7d8a2882b0>
    └ <armory.scenarios.audio_asr.AutomaticSpeechRecognition object at 0x7f7d8b020820>

  File "/workspace/armory/instrument/instrument.py", line 117, in update
    self.sink.update(name, value)
    │    │    │      │     └ array(['BRIGHTER THAN EARLY DAWN THE MOST BRILLIANT DYE ARE BLOWN CLEAR BAND OF COLOR THROUGH THE SKY THAT SWIRL AND SWEEP AN...
    │    │    │      └ 'scenario.y_pred'
    │    │    └ <function Hub.update at 0x7f7d8a14e4c0>
    │    └ <armory.instrument.instrument.Hub object at 0x7f7d8a2882e0>
    └ <armory.instrument.instrument.Probe object at 0x7f7d8a2882b0>

  File "/workspace/armory/instrument/instrument.py", line 357, in update
    meter.set(arg, value, self.context["batch"])
    │     │   │    │      │    └ {'batch': 0, 'stage': 'benign'}
    │     │   │    │      └ <armory.instrument.instrument.Hub object at 0x7f7d8a2882e0>
    │     │   │    └ array(['BRIGHTER THAN EARLY DAWN THE MOST BRILLIANT DYE ARE BLOWN CLEAR BAND OF COLOR THROUGH THE SKY THAT SWIRL AND SWEEP AN...
    │     │   └ 'scenario.y_pred'
    │     └ <function Meter.set at 0x7f7d8a14ea60>
    └ <armory.instrument.instrument.Meter object at 0x7f7d7c1c0370>

  File "/workspace/armory/instrument/instrument.py", line 549, in set
    self.measure()
    │    └ <function Meter.measure at 0x7f7d8a14eb80>
    └ <armory.instrument.instrument.Meter object at 0x7f7d7c1c0370>

  File "/workspace/armory/instrument/instrument.py", line 579, in measure
    writer.write(record)
    │      │     └ ('benign_word_error_rate', 0, array([[ 7., 33.]]))
    │      └ <function Writer.write at 0x7f7d8a14eee0>
    └ <armory.instrument.config.ResultsLogWriter object at 0x7f7d4505c130>

  File "/workspace/armory/instrument/instrument.py", line 686, in write
    return self._write(name, batch, result)
           │    │      │     │      └ array([[ 7., 33.]])
           │    │      │     └ 0
           │    │      └ 'benign_word_error_rate'
           │    └ <function ResultsLogWriter._write at 0x7f7d8a150ca0>
           └ <armory.instrument.config.ResultsLogWriter object at 0x7f7d4505c130>

  File "/workspace/armory/instrument/config.py", line 196, in _write
    result = metrics.get("total_wer")(result)
             │       │                └ array([[ 7., 33.]])
             │       └ <function get at 0x7f7d8a1368b0>
             └ <module 'armory.metrics' from '/workspace/armory/metrics/__init__.py'>

  File "/workspace/armory/metrics/task.py", line 192, in total_wer
    raise ValueError("Inputs must be tuples of size 2: (edit distance, length)")

ValueError: Inputs must be tuples of size 2: (edit distance, length)

```

Changed files:
armory/metrics/task.py

diff --git a/armory/metrics/task.py b/armory/metrics/task.py
index 5f0f7680..11dd7d5e 100644
--- a/armory/metrics/task.py
+++ b/armory/metrics/task.py
@@ -185,11 +185,24 @@ def total_entailment(sample_results):
 @aggregator
 def total_wer(sample_wers):
     """
-    Aggregate a list of per-sample word error rate tuples (edit_distance, words)
+    Aggregate an array or list of per-sample word error rate [edit_distance, words]
         Return global_wer, (total_edit_distance, total_words)
+
+    sample_wers: a list of 2-tuples, or an array of shape (N, 2)
     """
-    if not all(isinstance(wer_tuple, tuple) for wer_tuple in sample_wers):
-        raise ValueError("Inputs must be tuples of size 2: (edit distance, length)")
+
+    if isinstance(sample_wers, list):
+        if not all(isinstance(wer_tuple, tuple) for wer_tuple in sample_wers):
+            raise ValueError("Inputs must be tuples of size 2: (edit distance, length)")
+    elif isinstance(sample_wers, np.ndarray):
+        if sample_wers.ndim != 2 or sample_wers.shape[-1] != 2:
+            raise ValueError(
+                f"sample_wers must be an array of shape (N, 2). Received shape {sample_wers.shape}"
+            )
+    else:
+        raise ValueError(
+            f"Expected sample_wers to be a list or numpy array. Received type {type(sample_wers)}"
+        )
 
     total_edit_distance = 0
     total_words = 0