axiom 0.18.0 → 0.19.0

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.
package/dist/bin.cjs CHANGED
@@ -255,10 +255,10 @@ async function askConfirmation(message) {
255
255
  input: process.stdin,
256
256
  output: process.stdout
257
257
  });
258
- return new Promise((resolve2) => {
258
+ return new Promise((resolve3) => {
259
259
  rl.question(`${message} (y/N): `, (answer) => {
260
260
  rl.close();
261
- resolve2(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
261
+ resolve3(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
262
262
  });
263
263
  });
264
264
  }
@@ -646,6 +646,9 @@ var AxiomReporter = class {
646
646
  }
647
647
  async onTestSuiteReady(_testSuite) {
648
648
  const meta = _testSuite.meta();
649
+ if (_testSuite.state() === "skipped") {
650
+ return;
651
+ }
649
652
  const baseline = meta.evaluation.baseline;
650
653
  if (baseline) {
651
654
  this.baseline = await findEvaluationCases(baseline.id);
@@ -673,9 +676,13 @@ var AxiomReporter = class {
673
676
  }
674
677
  onTestCaseReady(test) {
675
678
  const meta = test.meta();
679
+ if (!meta.case) return;
676
680
  console.log(u.blue(` \u2713 evaluating case ${meta.case.index}`));
677
681
  }
678
682
  onTestSuiteResult(testSuite) {
683
+ if (testSuite.state() === "skipped") {
684
+ return;
685
+ }
679
686
  const duration = Number((performance.now() - this.start) / 1e3).toFixed(2);
680
687
  console.log(" ");
681
688
  console.log(" ", u.dim("Cases"), testSuite.children.size);
@@ -736,6 +743,20 @@ var AxiomReporter = class {
736
743
  }
737
744
  return [k, scoreValue];
738
745
  });
746
+ if (testMeta.case.outOfScopeFlags && testMeta.case.outOfScopeFlags.length > 0) {
747
+ const pickedFlagsText = testMeta.case.pickedFlags ? `(picked: ${testMeta.case.pickedFlags.map((f2) => `'${f2}'`).join(", ")})` : "(none)";
748
+ console.log(" ", u.yellow(`\u26A0 Out-of-scope flags: ${pickedFlagsText}`));
749
+ testMeta.case.outOfScopeFlags.forEach((flag) => {
750
+ const timeStr = new Date(flag.accessedAt).toLocaleTimeString();
751
+ console.log(" ", `${flag.flagPath} (at ${timeStr})`);
752
+ if (flag.stackTrace && flag.stackTrace.length > 0) {
753
+ flag.stackTrace.forEach((frame, i) => {
754
+ const prefix = i === flag.stackTrace.length - 1 ? " \u2514\u2500" : " \u251C\u2500";
755
+ console.log(" ", u.dim(`${prefix} ${frame}`));
756
+ });
757
+ }
758
+ });
759
+ }
739
760
  }
740
761
  };
741
762
 
@@ -751,7 +772,7 @@ var import_api2 = require("@opentelemetry/api");
751
772
  // package.json
752
773
  var package_default = {
753
774
  name: "axiom",
754
- version: "0.18.0",
775
+ version: "0.19.0",
755
776
  type: "module",
756
777
  author: "Axiom, Inc.",
757
778
  contributors: [
@@ -823,14 +844,12 @@ var package_default = {
823
844
  "@sinclair/typebox": "^0.34.37",
824
845
  commander: "^14.0.0",
825
846
  "console-table-printer": "^2.14.6",
826
- esbuild: "^0.25.8",
827
847
  handlebars: "^4.7.8",
828
- nanoid: "^5.1.5",
829
- vitest: "catalog:",
830
- zod: "catalog:"
848
+ nanoid: "^5.1.5"
831
849
  },
832
850
  peerDependencies: {
833
- "@opentelemetry/api": "^1.9.0"
851
+ "@opentelemetry/api": "^1.9.0",
852
+ zod: "^3.25.0 || ^4.0.0"
834
853
  },
835
854
  devDependencies: {
836
855
  "@ai-sdk/anthropicv1": "npm:@ai-sdk/anthropic@^1.2.12",
@@ -848,17 +867,19 @@ var package_default = {
848
867
  "@vitest/coverage-v8": "^3.2.4",
849
868
  aiv4: "npm:ai@^4.3.19",
850
869
  aiv5: "npm:ai@^5.0.0",
870
+ esbuild: "^0.25.8",
851
871
  eslint: "catalog:",
852
872
  prettier: "catalog:",
853
873
  tinyrainbow: "^2.0.0",
854
874
  tsup: "catalog:",
855
875
  typescript: "catalog:",
856
- vitest: "catalog:"
876
+ vitest: "catalog:",
877
+ zod: "catalog:"
857
878
  },
858
879
  files: [
859
880
  "dist"
860
881
  ],
861
- packageManager: "pnpm@10.11.1"
882
+ packageManager: "pnpm@10.16.1"
862
883
  };
863
884
 
864
885
  // src/otel/utils/redaction.ts
@@ -913,12 +934,12 @@ var processor = new import_sdk_trace_node.BatchSpanProcessor(exporter, {
913
934
  var provider = new import_sdk_trace_node.NodeTracerProvider({
914
935
  resource: (0, import_resources.resourceFromAttributes)({
915
936
  ["service.name"]: "axiom",
916
- ["service.version"]: "0.18.0"
937
+ ["service.version"]: "0.19.0"
917
938
  }),
918
939
  spanProcessors: [processor]
919
940
  });
920
941
  provider.register();
921
- var tracer = import_api3.trace.getTracer("axiom", "0.18.0");
942
+ var tracer = import_api3.trace.getTracer("axiom", "0.19.0");
922
943
  var flush = async () => {
923
944
  await provider.forceFlush();
924
945
  };
@@ -931,7 +952,8 @@ var runVitest = async (dir, opts) => {
931
952
  root: dir ? dir : process.cwd(),
932
953
  mode: "test",
933
954
  include: opts.include,
934
- reporters: [new AxiomReporter()],
955
+ testNamePattern: opts.testNamePattern,
956
+ reporters: ["verbose", new AxiomReporter()],
935
957
  environment: "node",
936
958
  browser: void 0,
937
959
  watch: opts.watch,
@@ -958,30 +980,347 @@ var runVitest = async (dir, opts) => {
958
980
 
959
981
  // src/cli/commands/eval.command.ts
960
982
  var import_node_fs = require("fs");
961
- var loadEvalCommand = (program2) => {
983
+
984
+ // src/evals/context/storage.ts
985
+ var import_api10 = require("@opentelemetry/api");
986
+
987
+ // src/evals/context/manager.ts
988
+ var import_meta = {};
989
+ var CONTEXT_MANAGER_SYMBOL = Symbol.for("axiom.context_manager");
990
+ function getGlobalContextManager() {
991
+ return globalThis[CONTEXT_MANAGER_SYMBOL];
992
+ }
993
+ function setGlobalContextManager(manager2) {
994
+ globalThis[CONTEXT_MANAGER_SYMBOL] = manager2;
995
+ }
996
+ var isNodeJS = typeof process !== "undefined" && process.versions && process.versions.node;
997
+ function getContextManager() {
998
+ const existing = getGlobalContextManager();
999
+ if (existing) {
1000
+ return existing;
1001
+ }
1002
+ let manager;
1003
+ if (isNodeJS) {
1004
+ try {
1005
+ let AsyncLocalStorage;
1006
+ try {
1007
+ const requireFn = eval("require");
1008
+ AsyncLocalStorage = requireFn("async_hooks").AsyncLocalStorage;
1009
+ } catch (directError) {
1010
+ try {
1011
+ const requireFn = eval("require");
1012
+ const { createRequire } = requireFn("module");
1013
+ const dynamicRequire = createRequire(import_meta.url);
1014
+ AsyncLocalStorage = dynamicRequire("async_hooks").AsyncLocalStorage;
1015
+ } catch (_createRequireError) {
1016
+ throw directError;
1017
+ }
1018
+ }
1019
+ manager = new AsyncLocalStorage();
1020
+ } catch (error) {
1021
+ console.warn("AsyncLocalStorage not available, using fallback context manager:", error);
1022
+ manager = createFallbackManager();
1023
+ }
1024
+ } else {
1025
+ manager = createFallbackManager();
1026
+ }
1027
+ setGlobalContextManager(manager);
1028
+ return manager;
1029
+ }
1030
+ function createFallbackManager() {
1031
+ let currentContext = null;
1032
+ return {
1033
+ getStore: () => currentContext,
1034
+ run: (value, fn) => {
1035
+ const prev = currentContext;
1036
+ currentContext = value;
1037
+ try {
1038
+ return fn();
1039
+ } finally {
1040
+ currentContext = prev;
1041
+ }
1042
+ }
1043
+ };
1044
+ }
1045
+ function createAsyncHook(_name) {
1046
+ return {
1047
+ get() {
1048
+ const manager2 = getContextManager();
1049
+ if (manager2.getStore) {
1050
+ return manager2.getStore();
1051
+ }
1052
+ return void 0;
1053
+ },
1054
+ run(value, fn) {
1055
+ const manager2 = getContextManager();
1056
+ return manager2.run(value, fn);
1057
+ }
1058
+ };
1059
+ }
1060
+ function __resetContextManagerForTests() {
1061
+ delete globalThis[CONTEXT_MANAGER_SYMBOL];
1062
+ }
1063
+
1064
+ // src/evals/context/global-flags.ts
1065
+ var GLOBAL_OVERRIDES_SYMBOL = Symbol.for("axiom.global_flag_overrides");
1066
+ function setRoot(val) {
1067
+ globalThis[GLOBAL_OVERRIDES_SYMBOL] = val;
1068
+ }
1069
+ function setGlobalFlagOverrides(overrides2) {
1070
+ setRoot(overrides2);
1071
+ }
1072
+
1073
+ // src/validate-flags.ts
1074
+ var import_zod3 = require("zod");
1075
+
1076
+ // src/cli/utils/format-zod-errors.ts
1077
+ var import_zod = require("zod");
1078
+
1079
+ // src/util/dot-path.ts
1080
+ var import_zod2 = require("zod");
1081
+
1082
+ // src/app-scope.ts
1083
+ var import_api9 = require("@opentelemetry/api");
1084
+ var import_zod4 = require("zod");
1085
+
1086
+ // src/otel/utils/to-otel-attribute.ts
1087
+ var import_api4 = require("@opentelemetry/api");
1088
+
1089
+ // src/otel/withSpan.ts
1090
+ var import_api7 = require("@opentelemetry/api");
1091
+
1092
+ // src/otel/utils/wrapperUtils.ts
1093
+ var import_api6 = require("@opentelemetry/api");
1094
+
1095
+ // src/otel/semconv/attributes.ts
1096
+ var import_semantic_conventions = require("@opentelemetry/semantic-conventions");
1097
+ var import_incubating = require("@opentelemetry/semantic-conventions/incubating");
1098
+
1099
+ // src/otel/startActiveSpan.ts
1100
+ var import_api5 = require("@opentelemetry/api");
1101
+
1102
+ // src/otel/middleware.ts
1103
+ var import_api8 = require("@opentelemetry/api");
1104
+
1105
+ // src/evals/context/storage.ts
1106
+ var EVAL_CONTEXT = createAsyncHook("eval-context");
1107
+ function getEvalContext() {
1108
+ const ctx = EVAL_CONTEXT.get();
1109
+ if (!ctx) {
1110
+ return {
1111
+ flags: {},
1112
+ facts: {},
1113
+ pickedFlags: void 0,
1114
+ outOfScopeFlags: void 0
1115
+ };
1116
+ }
1117
+ return {
1118
+ flags: ctx.flags,
1119
+ facts: ctx.facts,
1120
+ pickedFlags: ctx.pickedFlags,
1121
+ outOfScopeFlags: ctx.outOfScopeFlags,
1122
+ parent: ctx.parent,
1123
+ overrides: ctx.overrides
1124
+ };
1125
+ }
1126
+ function putOnSpan(kind, key, value) {
1127
+ const span = import_api10.trace.getActiveSpan();
1128
+ if (span?.isRecording()) {
1129
+ span.setAttributes({ [`${kind}.${key}`]: value });
1130
+ }
1131
+ }
1132
+ function withEvalContext(options = {}, fn) {
1133
+ const { initialFlags = {}, pickedFlags = [] } = options;
1134
+ return EVAL_CONTEXT.run(
1135
+ { flags: { ...initialFlags }, facts: {}, pickedFlags, outOfScopeFlags: [] },
1136
+ fn
1137
+ );
1138
+ }
1139
+
1140
+ // src/context.ts
1141
+ function overrideFlags(partial) {
1142
+ const current = getEvalContext();
1143
+ if (!current) {
1144
+ if (process.env.NODE_ENV !== "test") {
1145
+ console.warn("overrideFlags called outside of evaluation context");
1146
+ }
1147
+ return;
1148
+ }
1149
+ const overlayContext = {
1150
+ ...current,
1151
+ flags: { ...current.flags, ...partial },
1152
+ // Merge for backwards compatibility
1153
+ parent: current,
1154
+ overrides: { ...partial }
1155
+ };
1156
+ const currentCtx = EVAL_CONTEXT.get();
1157
+ if (currentCtx) {
1158
+ Object.assign(currentCtx, overlayContext);
1159
+ }
1160
+ for (const [key, value] of Object.entries(partial)) {
1161
+ putOnSpan("flag", key, value);
1162
+ }
1163
+ }
1164
+
1165
+ // src/cli/utils/eval-context-runner.ts
1166
+ async function runEvalWithContext(overrides2, runFn) {
1167
+ setGlobalFlagOverrides(overrides2);
1168
+ return withEvalContext({ initialFlags: overrides2 }, async () => {
1169
+ if (Object.keys(overrides2).length > 0) {
1170
+ overrideFlags(overrides2);
1171
+ }
1172
+ return runFn();
1173
+ });
1174
+ }
1175
+
1176
+ // src/cli/utils/glob-utils.ts
1177
+ function isGlob(str) {
1178
+ return /[*?[\]{}!]/.test(str);
1179
+ }
1180
+
1181
+ // src/cli/commands/eval.command.ts
1182
+ var loadEvalCommand = (program2, flagOverrides = {}) => {
962
1183
  return program2.addCommand(
963
1184
  new import_commander3.Command("eval").description("run evals locally").addArgument(
964
- new import_commander3.Argument("[path]", "path of base directory or *.eval.ts file").default(
1185
+ new import_commander3.Argument("[target]", "file, directory, glob pattern, or eval name").default(
965
1186
  ".",
966
1187
  "any *.eval.ts file in current directory"
967
1188
  )
968
- ).option("-w, --watch true", "keep server running and watch for changes", false).option("-t, --token <TOKEN>", "axiom token", process.env.AXIOM_TOKEN).option("-d, --dataset <DATASET>", "axiom dataset name", process.env.AXIOM_DATASET).option("-u, --url <AXIOM URL>", "axiom url", process.env.AXIOM_URL ?? "https://api.axiom.co").option("-b, --baseline <BASELINE ID>", "id of baseline evaluation to compare against").action(async (path3, options) => {
1189
+ ).option("-w, --watch true", "keep server running and watch for changes", false).option("-t, --token <TOKEN>", "axiom token", process.env.AXIOM_TOKEN).option("-d, --dataset <DATASET>", "axiom dataset name", process.env.AXIOM_DATASET).option("-u, --url <AXIOM URL>", "axiom url", process.env.AXIOM_URL ?? "https://api.axiom.co").option("-b, --baseline <BASELINE ID>", "id of baseline evaluation to compare against").action(async (target, options) => {
969
1190
  if (!options.token || !options.dataset) {
970
1191
  throw new Error("AXIOM_TOKEN, and AXIOM_DATASET must be set");
971
1192
  }
972
- const f2 = (0, import_node_fs.lstatSync)(path3);
973
- const isDir = f2.isDirectory();
974
- const targetPath = isDir ? path3 : ".";
975
- const includedfiles = isDir ? ["**/*.eval.ts"] : [path3];
976
- await runVitest(targetPath, {
977
- watch: options.watch,
978
- baseline: options.baseline,
979
- include: includedfiles
1193
+ let targetPath = ".";
1194
+ let include = ["**/*.eval.ts"];
1195
+ let testNamePattern;
1196
+ const isGlobPattern = isGlob(target);
1197
+ if (isGlobPattern) {
1198
+ include = [target];
1199
+ } else {
1200
+ try {
1201
+ const stat = (0, import_node_fs.lstatSync)(target);
1202
+ if (stat.isDirectory()) {
1203
+ targetPath = target;
1204
+ include = ["**/*.eval.ts"];
1205
+ } else {
1206
+ include = [target];
1207
+ }
1208
+ } catch {
1209
+ testNamePattern = new RegExp(target, "i");
1210
+ }
1211
+ }
1212
+ await runEvalWithContext(flagOverrides, async () => {
1213
+ return runVitest(targetPath, {
1214
+ watch: options.watch,
1215
+ baseline: options.baseline,
1216
+ include,
1217
+ testNamePattern
1218
+ });
980
1219
  });
981
1220
  })
982
1221
  );
983
1222
  };
984
1223
 
1224
+ // src/cli/utils/parse-flag-overrides.ts
1225
+ var import_zod5 = require("zod");
1226
+ var import_node_fs2 = require("fs");
1227
+ var import_node_path3 = require("path");
1228
+ var FLAG_RE = /^--flag\.([^=]+)(?:=(.*))?$/;
1229
+ var CONFIG_RE = /^--flags-config(?:=(.*))?$/;
1230
+ function ensureNoSpaceSeparatedSyntax(flagName, value, nextToken, flagType) {
1231
+ if (value === void 0 && nextToken !== void 0) {
1232
+ if (flagType === "flag" && !nextToken.startsWith("-") && nextToken !== "true" && nextToken !== "false") {
1233
+ console.error(`\u274C Invalid syntax: --flag.${flagName} ${nextToken}`);
1234
+ console.error(`\u{1F4A1} Use: --flag.${flagName}=${nextToken}`);
1235
+ process.exit(1);
1236
+ } else if (flagType === "config" && !nextToken.startsWith("-")) {
1237
+ console.error(`\u274C Invalid syntax: --flags-config ${nextToken}`);
1238
+ console.error(`\u{1F4A1} Use: --flags-config=${nextToken}`);
1239
+ process.exit(1);
1240
+ }
1241
+ }
1242
+ }
1243
+ function coerceValue(raw) {
1244
+ if (raw === "true") return true;
1245
+ if (raw === "false") return false;
1246
+ const num = Number(raw);
1247
+ if (!Number.isNaN(num) && raw.trim() === num.toString()) {
1248
+ return num;
1249
+ }
1250
+ try {
1251
+ return JSON.parse(raw);
1252
+ } catch {
1253
+ return raw;
1254
+ }
1255
+ }
1256
+ function loadConfigFile(path3) {
1257
+ const abs = (0, import_node_path3.resolve)(process.cwd(), path3);
1258
+ try {
1259
+ const contents = (0, import_node_fs2.readFileSync)(abs, "utf8");
1260
+ const parsed = JSON.parse(contents);
1261
+ if (typeof parsed !== "object" || Array.isArray(parsed) || parsed === null) {
1262
+ console.error(
1263
+ `\u274C Flags config must be a JSON object, got ${Array.isArray(parsed) ? "array" : typeof parsed}`
1264
+ );
1265
+ process.exit(1);
1266
+ }
1267
+ return parsed;
1268
+ } catch (err) {
1269
+ console.error(`\u274C Could not read or parse flags config "${path3}": ${err.message}`);
1270
+ process.exit(1);
1271
+ }
1272
+ }
1273
+ function extractOverrides(argv) {
1274
+ const cleanedArgv2 = [];
1275
+ const overrides2 = {};
1276
+ let configPath = null;
1277
+ let hasCliFlags = false;
1278
+ let configPathCount = 0;
1279
+ for (let i = 0; i < argv.length; i++) {
1280
+ const token2 = argv[i];
1281
+ const configMatch = token2.match(CONFIG_RE);
1282
+ const flagMatch = token2.match(FLAG_RE);
1283
+ if (configMatch) {
1284
+ configPathCount++;
1285
+ if (configPathCount > 1) {
1286
+ console.error("\u274C Only one --flags-config can be supplied.");
1287
+ process.exit(1);
1288
+ }
1289
+ const value = configMatch[1];
1290
+ const nextToken = argv.length > i + 1 ? argv[i + 1] : void 0;
1291
+ ensureNoSpaceSeparatedSyntax("flags-config", value, nextToken, "config");
1292
+ if (!value) {
1293
+ console.error("\u274C --flags-config requires a file path");
1294
+ console.error("\u{1F4A1} Use: --flags-config=path/to/config.json");
1295
+ process.exit(1);
1296
+ }
1297
+ configPath = value;
1298
+ } else if (flagMatch) {
1299
+ hasCliFlags = true;
1300
+ const key = flagMatch[1];
1301
+ const value = flagMatch[2];
1302
+ const nextToken = argv.length > i + 1 ? argv[i + 1] : void 0;
1303
+ ensureNoSpaceSeparatedSyntax(key, value, nextToken, "flag");
1304
+ const finalValue = value === void 0 ? "true" : value;
1305
+ overrides2[key] = coerceValue(finalValue);
1306
+ } else {
1307
+ cleanedArgv2.push(token2);
1308
+ }
1309
+ }
1310
+ if (configPath && hasCliFlags) {
1311
+ console.error("\u274C Cannot use both --flags-config and --flag.* arguments together.");
1312
+ console.error("Choose one approach:");
1313
+ console.error(" \u2022 Config file: --flags-config=my-flags.json");
1314
+ console.error(" \u2022 CLI flags: --flag.temperature=0.9 --flag.model=gpt-4o");
1315
+ process.exit(1);
1316
+ }
1317
+ if (configPath) {
1318
+ const configOverrides = loadConfigFile(configPath);
1319
+ return { cleanedArgv: cleanedArgv2, overrides: configOverrides };
1320
+ }
1321
+ return { cleanedArgv: cleanedArgv2, overrides: overrides2 };
1322
+ }
1323
+
985
1324
  // src/bin.ts
986
1325
  var import_env = __toESM(require("@next/env"), 1);
987
1326
 
@@ -990,7 +1329,7 @@ var import_commander4 = require("commander");
990
1329
  var loadVersionCommand = (program2) => {
991
1330
  return program2.addCommand(
992
1331
  new import_commander4.Command("version").description("cli version").action(() => {
993
- console.log("0.18.0");
1332
+ console.log("0.19.0");
994
1333
  })
995
1334
  );
996
1335
  };
@@ -998,13 +1337,14 @@ var loadVersionCommand = (program2) => {
998
1337
  // src/bin.ts
999
1338
  var { loadEnvConfig } = import_env.default;
1000
1339
  loadEnvConfig(process.cwd());
1340
+ var { cleanedArgv, overrides } = extractOverrides(process.argv.slice(2));
1001
1341
  var program = new import_commander5.Command();
1002
- program.name("axiom").description("Axiom's CLI to manage your objects and run evals").version("0.18.0");
1342
+ program.name("axiom").description("Axiom's CLI to manage your objects and run evals").version("0.19.0");
1003
1343
  loadPushCommand(program);
1004
1344
  loadPullCommand(program);
1005
- loadEvalCommand(program);
1345
+ loadEvalCommand(program, overrides);
1006
1346
  loadVersionCommand(program);
1007
- program.parse();
1347
+ program.parse(["node", "axiom", ...cleanedArgv]);
1008
1348
  // Annotate the CommonJS export names for ESM import in node:
1009
1349
  0 && (module.exports = {
1010
1350
  program