ai-speedometer 2.1.4 → 2.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/ai-speedometer +115 -30
  2. package/package.json +1 -1
@@ -1762,7 +1762,8 @@ async function benchmarkSingleModelRest(model) {
1762
1762
  messages: [{ role: "user", content: TEST_PROMPT }],
1763
1763
  max_tokens: 500,
1764
1764
  temperature: 0.7,
1765
- stream: true
1765
+ stream: true,
1766
+ stream_options: { include_usage: true }
1766
1767
  };
1767
1768
  if (model.providerType === "google") {
1768
1769
  body["contents"] = [{ parts: [{ text: TEST_PROMPT }] }];
@@ -1770,6 +1771,9 @@ async function benchmarkSingleModelRest(model) {
1770
1771
  delete body["messages"];
1771
1772
  delete body["max_tokens"];
1772
1773
  delete body["stream"];
1774
+ delete body["stream_options"];
1775
+ } else if (model.providerType === "anthropic") {
1776
+ delete body["stream_options"];
1773
1777
  }
1774
1778
  const response = await fetch(url, {
1775
1779
  method: "POST",
@@ -1777,8 +1781,22 @@ async function benchmarkSingleModelRest(model) {
1777
1781
  body: JSON.stringify(body)
1778
1782
  });
1779
1783
  if (!response.ok) {
1780
- await response.text();
1781
- throw new Error(`API request failed: ${response.status} ${response.statusText}`);
1784
+ const errBody = await response.text();
1785
+ let errDetail = "";
1786
+ try {
1787
+ const parsed = JSON.parse(errBody);
1788
+ if (typeof parsed.error === "object" && parsed.error?.message)
1789
+ errDetail = parsed.error.message;
1790
+ else if (typeof parsed.error === "string")
1791
+ errDetail = parsed.error;
1792
+ else if (parsed.message)
1793
+ errDetail = parsed.message;
1794
+ else
1795
+ errDetail = errBody.slice(0, 200);
1796
+ } catch {
1797
+ errDetail = errBody.slice(0, 200);
1798
+ }
1799
+ throw new Error(`${response.status} ${response.statusText}${errDetail ? ": " + errDetail : ""}`);
1782
1800
  }
1783
1801
  const reader = response.body.getReader();
1784
1802
  const decoder = new TextDecoder;
@@ -1805,7 +1823,7 @@ async function benchmarkSingleModelRest(model) {
1805
1823
  if (trimmedLine.startsWith("data: ")) {
1806
1824
  const jsonStr = trimmedLine.slice(6);
1807
1825
  if (jsonStr === "[DONE]")
1808
- break;
1826
+ continue;
1809
1827
  const chunk = JSON.parse(jsonStr);
1810
1828
  const chunkTyped = chunk;
1811
1829
  if (chunkTyped.type === "content_block_delta" && chunkTyped.delta?.text) {
@@ -1846,7 +1864,7 @@ async function benchmarkSingleModelRest(model) {
1846
1864
  if (trimmedLine.startsWith("data: ")) {
1847
1865
  const jsonStr = trimmedLine.slice(6);
1848
1866
  if (jsonStr === "[DONE]")
1849
- break;
1867
+ continue;
1850
1868
  const chunk = JSON.parse(jsonStr);
1851
1869
  if (chunk.choices?.[0]?.delta?.content)
1852
1870
  streamedText += chunk.choices[0].delta.content;
@@ -2161,7 +2179,7 @@ var package_default;
2161
2179
  var init_package = __esm(() => {
2162
2180
  package_default = {
2163
2181
  name: "ai-speedometer",
2164
- version: "2.1.4",
2182
+ version: "2.1.6",
2165
2183
  description: "A comprehensive CLI tool for benchmarking AI models across multiple providers with parallel execution and professional metrics",
2166
2184
  bin: {
2167
2185
  "ai-speedometer": "dist/ai-speedometer",
@@ -2614,7 +2632,7 @@ function ModelSelectScreen() {
2614
2632
  navigate("benchmark");
2615
2633
  }, [dispatch, navigate]);
2616
2634
  usePaste((text) => {
2617
- setSearchQuery((q) => q + text);
2635
+ setSearchQuery((q) => q + text.replace(/[\r\n]/g, ""));
2618
2636
  });
2619
2637
  useKeyboard4((key) => {
2620
2638
  if (key.name === "escape") {
@@ -2658,15 +2676,15 @@ function ModelSelectScreen() {
2658
2676
  }
2659
2677
  return;
2660
2678
  }
2661
- if (!searchQuery && (key.sequence === "A" || key.sequence === "a")) {
2679
+ if (!searchQuery && key.sequence === "A") {
2662
2680
  setSelected(new Set(orderedModels.map((m) => m.key)));
2663
2681
  return;
2664
2682
  }
2665
- if (!searchQuery && (key.sequence === "N" || key.sequence === "n")) {
2683
+ if (!searchQuery && key.sequence === "N") {
2666
2684
  setSelected(new Set);
2667
2685
  return;
2668
2686
  }
2669
- if (!searchQuery && recentCount > 0 && (key.sequence === "R" || key.sequence === "r")) {
2687
+ if (!searchQuery && recentCount > 0 && key.sequence === "R") {
2670
2688
  launchBench(orderedModels.slice(0, recentCount));
2671
2689
  return;
2672
2690
  }
@@ -3307,6 +3325,68 @@ function BenchmarkScreen() {
3307
3325
  }, `ttft-${s.model.id}-${s.model.providerId}`, true, undefined, this));
3308
3326
  }
3309
3327
  }
3328
+ if (allDone && errors.length > 0) {
3329
+ rows.push(/* @__PURE__ */ jsxDEV10("box", {
3330
+ height: 1,
3331
+ backgroundColor: "#292e42"
3332
+ }, "div-errors", false, undefined, this));
3333
+ rows.push(/* @__PURE__ */ jsxDEV10("box", {
3334
+ height: 1,
3335
+ flexDirection: "row",
3336
+ paddingLeft: 1,
3337
+ children: /* @__PURE__ */ jsxDEV10("text", {
3338
+ fg: "#f7768e",
3339
+ children: [
3340
+ " FAILED (",
3341
+ errors.length,
3342
+ ") "
3343
+ ]
3344
+ }, undefined, true, undefined, this)
3345
+ }, "hdr-errors", false, undefined, this));
3346
+ for (const s of errors) {
3347
+ rows.push(/* @__PURE__ */ jsxDEV10("box", {
3348
+ flexDirection: "column",
3349
+ paddingLeft: 2,
3350
+ paddingTop: 1,
3351
+ paddingBottom: 1,
3352
+ children: [
3353
+ /* @__PURE__ */ jsxDEV10("box", {
3354
+ height: 1,
3355
+ flexDirection: "row",
3356
+ children: [
3357
+ /* @__PURE__ */ jsxDEV10("text", {
3358
+ fg: "#f7768e",
3359
+ children: "\u2717 "
3360
+ }, undefined, false, undefined, this),
3361
+ /* @__PURE__ */ jsxDEV10("text", {
3362
+ fg: "#c0caf5",
3363
+ children: [
3364
+ s.model.name,
3365
+ " "
3366
+ ]
3367
+ }, undefined, true, undefined, this),
3368
+ /* @__PURE__ */ jsxDEV10("text", {
3369
+ fg: "#565f89",
3370
+ children: [
3371
+ "(",
3372
+ s.model.providerName,
3373
+ ")"
3374
+ ]
3375
+ }, undefined, true, undefined, this)
3376
+ ]
3377
+ }, undefined, true, undefined, this),
3378
+ /* @__PURE__ */ jsxDEV10("box", {
3379
+ height: 1,
3380
+ paddingLeft: 3,
3381
+ children: /* @__PURE__ */ jsxDEV10("text", {
3382
+ fg: "#f7768e",
3383
+ children: s.error ?? "Unknown error"
3384
+ }, undefined, false, undefined, this)
3385
+ }, undefined, false, undefined, this)
3386
+ ]
3387
+ }, `err-${s.model.id}-${s.model.providerId}`, true, undefined, this));
3388
+ }
3389
+ }
3310
3390
  rows.push(/* @__PURE__ */ jsxDEV10("box", {
3311
3391
  height: 1,
3312
3392
  backgroundColor: "#292e42"
@@ -3515,10 +3595,11 @@ function AddVerifiedScreen() {
3515
3595
  }
3516
3596
  }
3517
3597
  usePaste((text) => {
3598
+ const clean = text.replace(/[\r\n]/g, "");
3518
3599
  if (step === "browse")
3519
- setSearchQuery((q) => q + text);
3600
+ setSearchQuery((q) => q + clean);
3520
3601
  else if (step === "confirm")
3521
- setApiKey((k) => k + text);
3602
+ setApiKey((k) => k + clean);
3522
3603
  });
3523
3604
  useKeyboard6((key) => {
3524
3605
  if (step === "browse") {
@@ -3930,7 +4011,7 @@ function AddCustomScreen() {
3930
4011
  baseUrl: baseUrl.trim(),
3931
4012
  apiKey: apiKey.trim(),
3932
4013
  models: models.map((m) => ({
3933
- id: m.toLowerCase().replace(/[^a-z0-9-]/g, "-"),
4014
+ id: m,
3934
4015
  name: m
3935
4016
  }))
3936
4017
  });
@@ -3945,16 +4026,17 @@ function AddCustomScreen() {
3945
4026
  }
3946
4027
  }
3947
4028
  usePaste((text) => {
4029
+ const clean = text.replace(/[\r\n]/g, "");
3948
4030
  if (step === "id")
3949
- setProviderId((v) => v + text);
4031
+ setProviderId((v) => v + clean);
3950
4032
  else if (step === "name")
3951
- setProviderName((v) => v + text);
4033
+ setProviderName((v) => v + clean);
3952
4034
  else if (step === "url")
3953
- setBaseUrl((v) => v + text);
4035
+ setBaseUrl((v) => v + clean);
3954
4036
  else if (step === "key")
3955
- setApiKey((v) => v + text);
4037
+ setApiKey((v) => v + clean);
3956
4038
  else if (step === "models")
3957
- setModelInput((v) => v + text);
4039
+ setModelInput((v) => v + clean);
3958
4040
  });
3959
4041
  useKeyboard7((key) => {
3960
4042
  if (step === "done") {
@@ -4018,10 +4100,8 @@ function AddCustomScreen() {
4018
4100
  }
4019
4101
  if (step === "name") {
4020
4102
  if (key.name === "return" || key.name === "enter") {
4021
- if (!providerName.trim()) {
4022
- setInputError("Name is required");
4023
- return;
4024
- }
4103
+ if (!providerName.trim())
4104
+ setProviderName(providerId);
4025
4105
  setInputError("");
4026
4106
  setStep("url");
4027
4107
  return;
@@ -4310,8 +4390,12 @@ function AddCustomScreen() {
4310
4390
  height: 1,
4311
4391
  children: /* @__PURE__ */ jsxDEV12("text", {
4312
4392
  fg: "#565f89",
4313
- children: "e.g. My OpenAI"
4314
- }, undefined, false, undefined, this)
4393
+ children: [
4394
+ 'e.g. My OpenAI (Enter to use "',
4395
+ providerId,
4396
+ '")'
4397
+ ]
4398
+ }, undefined, true, undefined, this)
4315
4399
  }, undefined, false, undefined, this)
4316
4400
  ]
4317
4401
  }, undefined, true, undefined, this),
@@ -4485,8 +4569,9 @@ function AddModelsScreen() {
4485
4569
  load();
4486
4570
  }, []);
4487
4571
  usePaste((text) => {
4572
+ const clean = text.replace(/[\r\n]/g, "");
4488
4573
  if (step === "add")
4489
- setModelInput((v) => v + text);
4574
+ setModelInput((v) => v + clean);
4490
4575
  });
4491
4576
  useKeyboard8((key) => {
4492
4577
  if (done) {
@@ -4947,23 +5032,23 @@ function getHints(screen, benchResults) {
4947
5032
  case "main-menu":
4948
5033
  return ["[\u2191\u2193] navigate", "[Enter] select", "[Ctrl+C] quit"];
4949
5034
  case "model-menu":
4950
- return ["[\u2191\u2193] navigate", "[Enter] select", "[q] back"];
5035
+ return ["[\u2191\u2193] navigate", "[Enter] select", "[Q] back"];
4951
5036
  case "model-select":
4952
5037
  return ["[\u2191\u2193] navigate", "[Tab] select", "[Enter] run", "[A] all", "[N] none", "[R] recent", "[Esc] back"];
4953
5038
  case "benchmark": {
4954
5039
  const allDone = benchResults.length > 0 && benchResults.every((r) => r.status === "done" || r.status === "error");
4955
- return allDone ? ["[Enter] back to menu", "[q] back to menu"] : ["Benchmark in progress..."];
5040
+ return allDone ? ["[Enter] back to menu", "[Q] back to menu"] : ["Benchmark in progress..."];
4956
5041
  }
4957
5042
  case "list-providers":
4958
- return ["[\u2191\u2193] scroll", "[q] back"];
5043
+ return ["[\u2191\u2193] scroll", "[Q] back"];
4959
5044
  case "add-verified":
4960
- return ["[\u2191\u2193] navigate", "[Enter] select", "[q] back"];
5045
+ return ["[\u2191\u2193] navigate", "[Enter] select", "[Q] back"];
4961
5046
  case "add-custom":
4962
5047
  return ["[\u2191\u2193] navigate", "[Enter] confirm", "[Esc] back"];
4963
5048
  case "add-models":
4964
5049
  return ["[\u2191\u2193] navigate", "[Enter] add / finish", "[Esc] back"];
4965
5050
  default:
4966
- return ["[q] back"];
5051
+ return ["[Q] back"];
4967
5052
  }
4968
5053
  }
4969
5054
  function ActiveScreen() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-speedometer",
3
- "version": "2.1.4",
3
+ "version": "2.1.6",
4
4
  "description": "A comprehensive CLI tool for benchmarking AI models across multiple providers with parallel execution and professional metrics",
5
5
  "bin": {
6
6
  "ai-speedometer": "dist/ai-speedometer",