@juspay/neurolink 9.94.6 → 9.95.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.
Files changed (34) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/analytics/index.d.ts +7 -0
  3. package/dist/analytics/index.js +7 -0
  4. package/dist/analytics/parseQualityScore.d.ts +9 -0
  5. package/dist/analytics/parseQualityScore.js +29 -0
  6. package/dist/analytics/pricing.d.ts +24 -0
  7. package/dist/analytics/pricing.js +37 -0
  8. package/dist/analytics/service.d.ts +36 -0
  9. package/dist/analytics/service.js +393 -0
  10. package/dist/analytics/storage.d.ts +19 -0
  11. package/dist/analytics/storage.js +32 -0
  12. package/dist/browser/neurolink.min.js +389 -389
  13. package/dist/cli/factories/commandFactory.d.ts +0 -2
  14. package/dist/cli/factories/commandFactory.js +36 -45
  15. package/dist/cli/loop/session.js +21 -0
  16. package/dist/cli/utils/inputValidation.d.ts +38 -0
  17. package/dist/cli/utils/inputValidation.js +143 -0
  18. package/dist/lib/analytics/index.d.ts +7 -0
  19. package/dist/lib/analytics/index.js +8 -0
  20. package/dist/lib/analytics/parseQualityScore.d.ts +9 -0
  21. package/dist/lib/analytics/parseQualityScore.js +30 -0
  22. package/dist/lib/analytics/pricing.d.ts +24 -0
  23. package/dist/lib/analytics/pricing.js +38 -0
  24. package/dist/lib/analytics/service.d.ts +36 -0
  25. package/dist/lib/analytics/service.js +394 -0
  26. package/dist/lib/analytics/storage.d.ts +19 -0
  27. package/dist/lib/analytics/storage.js +33 -0
  28. package/dist/lib/neurolink.d.ts +54 -0
  29. package/dist/lib/neurolink.js +302 -204
  30. package/dist/lib/types/analytics.d.ts +127 -0
  31. package/dist/neurolink.d.ts +54 -0
  32. package/dist/neurolink.js +302 -204
  33. package/dist/types/analytics.d.ts +127 -0
  34. package/package.json +2 -1
@@ -24,8 +24,6 @@ export declare class CLICommandFactory {
24
24
  private static processCliPDFFiles;
25
25
  private static processCliFiles;
26
26
  private static processCliVideoFiles;
27
- private static isNonLocalFileReference;
28
- private static validateCliInputFiles;
29
27
  private static processOptions;
30
28
  /**
31
29
  * Validate Anthropic subscription options
@@ -24,6 +24,7 @@ import { initializeCliParser } from "../parser.js";
24
24
  import { formatFileSize, saveAudioToFile } from "../utils/audioFileUtils.js";
25
25
  import { playAudio } from "../utils/audioPlayer.js";
26
26
  import { resolveFilePaths } from "../utils/pathResolver.js";
27
+ import { validateCliInputFiles, validateCsvMaxRows, } from "../utils/inputValidation.js";
27
28
  import { animatedWrite } from "../utils/typewriter.js";
28
29
  import { createStreamAbortHandler } from "../utils/abortHandler.js";
29
30
  import { formatVideoFileSize, getVideoMetadataSummary, saveVideoToFile, } from "../utils/videoFileUtils.js";
@@ -187,7 +188,7 @@ export class CLICommandFactory {
187
188
  csvMaxRows: {
188
189
  type: "number",
189
190
  default: 1000,
190
- description: "Maximum number of CSV rows to process",
191
+ description: "Maximum number of CSV rows to process (positive integer, range 1-100000, default 1000)",
191
192
  },
192
193
  csvFormat: {
193
194
  type: "string",
@@ -805,42 +806,6 @@ export class CLICommandFactory {
805
806
  // URLs are preserved as-is by resolveFilePaths
806
807
  return resolveFilePaths(paths);
807
808
  }
808
- static isNonLocalFileReference(filePath) {
809
- const lower = filePath.toLowerCase();
810
- return (lower.startsWith("http://") ||
811
- lower.startsWith("https://") ||
812
- lower.startsWith("file://") ||
813
- lower.startsWith("data:"));
814
- }
815
- static validateCliInputFiles(argv) {
816
- const fileArgs = [
817
- { option: "--image", value: argv.image },
818
- { option: "--csv", value: argv.csv },
819
- { option: "--pdf", value: argv.pdf },
820
- { option: "--video", value: argv.video },
821
- { option: "--file", value: argv.file },
822
- ];
823
- const missingPaths = [];
824
- for (const { option, value } of fileArgs) {
825
- if (!value) {
826
- continue;
827
- }
828
- const rawPaths = Array.isArray(value) ? value : [value];
829
- const resolvedPaths = resolveFilePaths(rawPaths);
830
- for (let i = 0; i < resolvedPaths.length; i++) {
831
- const resolvedPath = resolvedPaths[i];
832
- if (CLICommandFactory.isNonLocalFileReference(resolvedPath)) {
833
- continue;
834
- }
835
- if (!fs.existsSync(resolvedPath)) {
836
- missingPaths.push(`${option} path not found: ${rawPaths[i]} (resolved to ${resolvedPath})`);
837
- }
838
- }
839
- }
840
- if (missingPaths.length > 0) {
841
- throw new Error(`One or more input files do not exist:\n${missingPaths.join("\n")}`);
842
- }
843
- }
844
809
  // Helper method to process common options
845
810
  static processOptions(argv) {
846
811
  // Handle noColor option by disabling chalk
@@ -1538,7 +1503,12 @@ export class CLICommandFactory {
1538
1503
  .example('$0 generate "Smooth camera movement" --image ./input.jpg --provider vertex --model veo-3.1-generate-001 --outputMode video --videoResolution 720p --videoLength 6 --videoAspectRatio 16:9 --videoOutput ./output.mp4', "Video generation with full options")
1539
1504
  .example('$0 generate "AI in Healthcare" --pptPages 10', "Generate a PowerPoint presentation")
1540
1505
  .example('$0 generate "Company Q4 Results" --pptPages 15 --pptTheme corporate --pptAudience business', "Generate presentation with options")
1541
- .example('$0 generate "Machine Learning 101" --pptTheme minimal --pptTone educational --pptNoImages', "Generate educational slides without AI images"));
1506
+ .example('$0 generate "Machine Learning 101" --pptTheme minimal --pptTone educational --pptNoImages', "Generate educational slides without AI images")
1507
+ .example('$0 generate "Describe this image" --image ./photo.jpg', "Analyze an image (multimodal input)")
1508
+ .example('$0 generate "Summarize this report" --pdf ./report.pdf', "Analyze a PDF document")
1509
+ .example('$0 generate "Key trends?" --csv large-data.csv --csvMaxRows 100', "Analyze a CSV with a row limit")
1510
+ .example('$0 generate "Compare the chart and the report" --image ./chart.png --pdf ./report.pdf', "Combine multiple file types in one prompt")
1511
+ .example('$0 generate "What is in this file?" --file ./data.json', "Auto-detect a file type with --file"));
1542
1512
  },
1543
1513
  handler: async (argv) => await CLICommandFactory.executeGenerate(argv),
1544
1514
  };
@@ -1560,7 +1530,10 @@ export class CLICommandFactory {
1560
1530
  .example('$0 stream "Explain machine learning" -p anthropic', "Stream with specific provider")
1561
1531
  .example('$0 stream "Code walkthrough" --output story.txt', "Stream to file")
1562
1532
  .example('echo "Live demo" | $0 stream', "Stream from stdin")
1563
- .example('$0 stream "Narrate this video" --video path/to/video.mp4', "Stream video analysis"));
1533
+ .example('$0 stream "Narrate this video" --video path/to/video.mp4', "Stream video analysis")
1534
+ .example('$0 stream "Describe this image" --image ./photo.jpg', "Stream image analysis (multimodal input)")
1535
+ .example('$0 stream "Summarize this document" --pdf ./report.pdf', "Stream PDF analysis")
1536
+ .example('$0 stream "Explain this dataset" --csv ./data.csv --csv-format markdown', "Stream CSV analysis"));
1564
1537
  },
1565
1538
  handler: async (argv) => await CLICommandFactory.executeStream(argv),
1566
1539
  };
@@ -2277,7 +2250,11 @@ export class CLICommandFactory {
2277
2250
  if (isSttOnly) {
2278
2251
  return "";
2279
2252
  }
2280
- throw new Error("No input received from stdin");
2253
+ throw new Error("No input received from stdin.\n" +
2254
+ "💡 Try this:\n" +
2255
+ ' neurolink generate "your prompt"\n' +
2256
+ ' echo "your prompt" | neurolink generate\n' +
2257
+ ' neurolink generate "Describe this image" --image ./photo.jpg');
2281
2258
  }
2282
2259
  return trimmedData;
2283
2260
  }
@@ -2285,7 +2262,11 @@ export class CLICommandFactory {
2285
2262
  if (isSttOnly) {
2286
2263
  return "";
2287
2264
  }
2288
- throw new Error('Input required. Use: neurolink generate "your prompt" or echo "prompt" | neurolink generate');
2265
+ throw new Error("❌ Input required.\n" +
2266
+ "💡 Try this:\n" +
2267
+ ' neurolink generate "your prompt"\n' +
2268
+ ' echo "your prompt" | neurolink generate\n' +
2269
+ ' neurolink generate "Describe this image" --image ./photo.jpg');
2289
2270
  }
2290
2271
  return argv.input;
2291
2272
  }
@@ -2544,7 +2525,8 @@ export class CLICommandFactory {
2544
2525
  if (options.delay) {
2545
2526
  await new Promise((resolve) => setTimeout(resolve, options.delay));
2546
2527
  }
2547
- CLICommandFactory.validateCliInputFiles(argv);
2528
+ validateCliInputFiles(argv);
2529
+ validateCsvMaxRows(argv);
2548
2530
  // Process context
2549
2531
  const { inputText, contextMetadata } = CLICommandFactory.processGenerateContext(rawInput, options);
2550
2532
  // Handle dry-run mode for testing
@@ -3304,7 +3286,11 @@ export class CLICommandFactory {
3304
3286
  argv.input = "";
3305
3287
  return;
3306
3288
  }
3307
- throw new Error("No input received from stdin");
3289
+ throw new Error("No input received from stdin.\n" +
3290
+ "💡 Try this:\n" +
3291
+ ' neurolink stream "your prompt"\n' +
3292
+ ' echo "your prompt" | neurolink stream\n' +
3293
+ ' neurolink stream "Describe this image" --image ./photo.jpg');
3308
3294
  }
3309
3295
  }
3310
3296
  else if (!argv.input) {
@@ -3312,7 +3298,11 @@ export class CLICommandFactory {
3312
3298
  argv.input = "";
3313
3299
  return;
3314
3300
  }
3315
- throw new Error('Input required. Use: neurolink stream "your prompt" or echo "prompt" | neurolink stream');
3301
+ throw new Error("❌ Input required.\n" +
3302
+ "💡 Try this:\n" +
3303
+ ' neurolink stream "your prompt"\n' +
3304
+ ' echo "your prompt" | neurolink stream\n' +
3305
+ ' neurolink stream "Describe this image" --image ./photo.jpg');
3316
3306
  }
3317
3307
  }
3318
3308
  /**
@@ -3331,7 +3321,8 @@ export class CLICommandFactory {
3331
3321
  if (options.delay) {
3332
3322
  await new Promise((resolve) => setTimeout(resolve, options.delay));
3333
3323
  }
3334
- CLICommandFactory.validateCliInputFiles(argv);
3324
+ validateCliInputFiles(argv);
3325
+ validateCsvMaxRows(argv);
3335
3326
  const { inputText, contextMetadata } = await CLICommandFactory.processStreamContext(argv, options);
3336
3327
  // Handle dry-run mode for testing
3337
3328
  if (options.dryRun) {
@@ -414,6 +414,22 @@ export class LoopSession {
414
414
  logger.always(chalk.yellow(` ${c.cmd.padEnd(20)}`) + `${c.desc}`);
415
415
  });
416
416
  logger.always("\nAny other command will be executed as a standard neurolink CLI command.");
417
+ // #315: multimodal file flags are buried in the raw yargs dump below — call
418
+ // them out explicitly so loop users know PDFs/images/CSVs/video are supported.
419
+ logger.always(chalk.cyan("\nMultimodal file inputs (per-command flags):"));
420
+ [
421
+ ["--image <path|url>", "Attach an image for analysis (repeatable)"],
422
+ ["--pdf <path|url>", "Attach a PDF document (repeatable)"],
423
+ [
424
+ "--csv <path|url>",
425
+ "Attach a CSV file (see --csv-format, --csv-max-rows)",
426
+ ],
427
+ ["--video <path|url>", "Attach a video for analysis"],
428
+ ["--file <path|url>", "Attach a file and auto-detect its type"],
429
+ ].forEach(([flag, desc]) => {
430
+ logger.always(chalk.yellow(` ${flag.padEnd(20)}`) + `${desc}`);
431
+ });
432
+ logger.always(chalk.gray(' e.g. generate "Describe this" --image ./photo.jpg --pdf ./report.pdf'));
417
433
  // Also show the standard help output
418
434
  this.initializeCliParser().showHelp("log");
419
435
  }
@@ -429,6 +445,11 @@ export class LoopSession {
429
445
  logger.always(chalk.gray(` Type: ${schema.type}`));
430
446
  }
431
447
  }
448
+ // #350: multimodal file inputs are per-command flags, not session variables,
449
+ // so they don't appear above — point users to them explicitly.
450
+ logger.always(chalk.gray("\n Note: file inputs (--image/--pdf/--csv/--video/--file) are per-command\n" +
451
+ " flags, not session variables — pass them directly on a generate/stream line,\n" +
452
+ ' e.g. generate "Summarize this" --pdf ./report.pdf'));
432
453
  }
433
454
  /**
434
455
  * Get command input with history support using readline
@@ -0,0 +1,38 @@
1
+ /**
2
+ * CLI Input Validation Utilities
3
+ *
4
+ * Validates multimodal file flags (--image/--csv/--pdf/--video/--file) and
5
+ * --csv-max-rows before a generate/stream/batch run starts, so bad input
6
+ * fails fast with a clear message instead of surfacing deep inside provider
7
+ * calls. Exported as standalone functions (rather than private statics on
8
+ * CLICommandFactory) so callers — including tests — can import and invoke
9
+ * them directly, without reaching into the class via an `unknown` cast.
10
+ */
11
+ /**
12
+ * Soft (warn-only) size thresholds for CLI multimodal flags. Intentionally
13
+ * defined here rather than reusing `lib/processors/config/sizeLimits.ts`'s
14
+ * hard processor caps (e.g. PDF's 100MB hard limit): reusing those names/
15
+ * values would couple the CLI layer to processor internals across the
16
+ * CLI/SDK boundary, and would blur the line between a soft "this may be
17
+ * slow" warning and the actual hard cap the processor enforces.
18
+ */
19
+ export declare const CLI_SOFT_LIMITS_MB: {
20
+ readonly IMAGE_MAX_MB: 10;
21
+ readonly CSV_MAX_MB: 50;
22
+ readonly PDF_MAX_MB: 100;
23
+ readonly VIDEO_MAX_MB: 500;
24
+ };
25
+ /**
26
+ * Validate --image/--csv/--pdf/--video/--file inputs before a CLI run
27
+ * starts: every local path must exist, must not be a directory, and — for
28
+ * the flags with a soft limit — triggers a size warning above the
29
+ * threshold. URLs/data URIs skip both existence and size checks (#319).
30
+ */
31
+ export declare function validateCliInputFiles(argv: Record<string, unknown>): void;
32
+ /**
33
+ * #310: validate --csv-max-rows is a positive integer in the range
34
+ * 1-100000, matching the documented option range (the value previously
35
+ * flowed through silently, or — above 100000 — only warned instead of
36
+ * being rejected, contradicting the documented hard range).
37
+ */
38
+ export declare function validateCsvMaxRows(argv: Record<string, unknown>): void;
@@ -0,0 +1,143 @@
1
+ /**
2
+ * CLI Input Validation Utilities
3
+ *
4
+ * Validates multimodal file flags (--image/--csv/--pdf/--video/--file) and
5
+ * --csv-max-rows before a generate/stream/batch run starts, so bad input
6
+ * fails fast with a clear message instead of surfacing deep inside provider
7
+ * calls. Exported as standalone functions (rather than private statics on
8
+ * CLICommandFactory) so callers — including tests — can import and invoke
9
+ * them directly, without reaching into the class via an `unknown` cast.
10
+ */
11
+ import fs from "node:fs";
12
+ import chalk from "chalk";
13
+ import { logger } from "../../lib/utils/logger.js";
14
+ import { resolveFilePaths } from "./pathResolver.js";
15
+ /**
16
+ * Soft (warn-only) size thresholds for CLI multimodal flags. Intentionally
17
+ * defined here rather than reusing `lib/processors/config/sizeLimits.ts`'s
18
+ * hard processor caps (e.g. PDF's 100MB hard limit): reusing those names/
19
+ * values would couple the CLI layer to processor internals across the
20
+ * CLI/SDK boundary, and would blur the line between a soft "this may be
21
+ * slow" warning and the actual hard cap the processor enforces.
22
+ */
23
+ export const CLI_SOFT_LIMITS_MB = {
24
+ IMAGE_MAX_MB: 10,
25
+ CSV_MAX_MB: 50,
26
+ PDF_MAX_MB: 100,
27
+ VIDEO_MAX_MB: 500,
28
+ };
29
+ function isNonLocalFileReference(filePath) {
30
+ const lower = filePath.toLowerCase();
31
+ return (lower.startsWith("http://") ||
32
+ lower.startsWith("https://") ||
33
+ lower.startsWith("file://") ||
34
+ lower.startsWith("data:"));
35
+ }
36
+ /**
37
+ * Validate --image/--csv/--pdf/--video/--file inputs before a CLI run
38
+ * starts: every local path must exist, must not be a directory, and — for
39
+ * the flags with a soft limit — triggers a size warning above the
40
+ * threshold. URLs/data URIs skip both existence and size checks (#319).
41
+ */
42
+ export function validateCliInputFiles(argv) {
43
+ const fileArgs = [
44
+ {
45
+ option: "--image",
46
+ value: argv.image,
47
+ warnAtMB: CLI_SOFT_LIMITS_MB.IMAGE_MAX_MB,
48
+ },
49
+ {
50
+ option: "--csv",
51
+ value: argv.csv,
52
+ warnAtMB: CLI_SOFT_LIMITS_MB.CSV_MAX_MB,
53
+ },
54
+ {
55
+ option: "--pdf",
56
+ value: argv.pdf,
57
+ warnAtMB: CLI_SOFT_LIMITS_MB.PDF_MAX_MB,
58
+ },
59
+ {
60
+ option: "--video",
61
+ value: argv.video,
62
+ warnAtMB: CLI_SOFT_LIMITS_MB.VIDEO_MAX_MB,
63
+ },
64
+ { option: "--file", value: argv.file },
65
+ ];
66
+ const missingPaths = [];
67
+ for (const { option, value, warnAtMB } of fileArgs) {
68
+ if (!value) {
69
+ continue;
70
+ }
71
+ const rawPaths = Array.isArray(value) ? value : [value];
72
+ const resolvedPaths = resolveFilePaths(rawPaths);
73
+ for (let i = 0; i < resolvedPaths.length; i++) {
74
+ const resolvedPath = resolvedPaths[i];
75
+ // URLs / data: refs skip both existence and size checks (#319).
76
+ if (isNonLocalFileReference(resolvedPath)) {
77
+ continue;
78
+ }
79
+ // Single statSync covers existence, directory rejection, and the
80
+ // size warning below — avoids a second syscall and closes the
81
+ // TOCTOU gap a separate existsSync()+statSync() pair would leave.
82
+ let stat;
83
+ try {
84
+ stat = fs.statSync(resolvedPath);
85
+ }
86
+ catch (err) {
87
+ // Only ENOENT actually means "not found" — EACCES (permission
88
+ // denied), ELOOP (symlink loop), ENOTDIR, etc. are real failures
89
+ // that get masked (and made undebuggable) if we blanket-label
90
+ // every statSync error the same way.
91
+ const code = err?.code;
92
+ if (code === "ENOENT") {
93
+ missingPaths.push(`${option} path not found: ${rawPaths[i]} (resolved to ${resolvedPath})`);
94
+ }
95
+ else {
96
+ const reason = err instanceof Error ? err.message : String(err);
97
+ missingPaths.push(`${option} path could not be accessed: ${rawPaths[i]} (resolved to ${resolvedPath}) — ` +
98
+ `${code ? `${code}: ` : ""}${reason}`);
99
+ }
100
+ continue;
101
+ }
102
+ if (stat.isDirectory()) {
103
+ missingPaths.push(`${option} path is a directory, not a file: ${rawPaths[i]} (resolved to ${resolvedPath})`);
104
+ continue;
105
+ }
106
+ // #319: warn (not error) when a local multimodal file is unusually
107
+ // large so the user isn't surprised by slow processing / token blowups.
108
+ if (warnAtMB !== undefined) {
109
+ const sizeMB = stat.size / (1024 * 1024);
110
+ if (sizeMB > warnAtMB) {
111
+ logger.always(chalk.yellow(`⚠️ ${option} file ${rawPaths[i]} is ${sizeMB.toFixed(1)}MB ` +
112
+ `(above the ${warnAtMB}MB soft limit) — processing may be slow ` +
113
+ `or exceed provider token/size limits.`));
114
+ }
115
+ }
116
+ }
117
+ }
118
+ if (missingPaths.length > 0) {
119
+ throw new Error("❌ One or more input files are invalid:\n" +
120
+ `${missingPaths.join("\n")}\n` +
121
+ "💡 Check the path is correct and relative to your current directory, " +
122
+ "or pass an absolute path / URL.");
123
+ }
124
+ }
125
+ /**
126
+ * #310: validate --csv-max-rows is a positive integer in the range
127
+ * 1-100000, matching the documented option range (the value previously
128
+ * flowed through silently, or — above 100000 — only warned instead of
129
+ * being rejected, contradicting the documented hard range).
130
+ */
131
+ export function validateCsvMaxRows(argv) {
132
+ const raw = argv.csvMaxRows;
133
+ if (raw === undefined) {
134
+ return;
135
+ }
136
+ const value = Number(raw);
137
+ if (!Number.isInteger(value) || value < 1 || value > 100000) {
138
+ throw new Error(`Invalid --csv-max-rows (--csvMaxRows) value: ${String(raw)}. ` +
139
+ `Must be a positive integer in the range 1-100000. ` +
140
+ `Example: --csv-max-rows 500`);
141
+ }
142
+ }
143
+ //# sourceMappingURL=inputValidation.js.map
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Analytics Module Exports (runtime values only — types live in src/lib/types)
3
+ */
4
+ export { calculateAdvancedCost } from "./pricing.js";
5
+ export { InMemoryAnalyticsStorage, DEFAULT_ANALYTICS_STORAGE_MAX_RECORDS, } from "./storage.js";
6
+ export { AnalyticsService } from "./service.js";
7
+ export { parseAnalyticsQualityScore } from "./parseQualityScore.js";
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Analytics Module Exports (runtime values only — types live in src/lib/types)
3
+ */
4
+ export { calculateAdvancedCost } from "./pricing.js";
5
+ export { InMemoryAnalyticsStorage, DEFAULT_ANALYTICS_STORAGE_MAX_RECORDS, } from "./storage.js";
6
+ export { AnalyticsService } from "./service.js";
7
+ export { parseAnalyticsQualityScore } from "./parseQualityScore.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Safe narrowing of unknown event payloads into AnalyticsQualityScore.
3
+ */
4
+ import type { AnalyticsQualityScore } from "../types/index.js";
5
+ /**
6
+ * Convert an unknown quality-score payload into AnalyticsQualityScore.
7
+ * Returns undefined for missing, null, or malformed values.
8
+ */
9
+ export declare function parseAnalyticsQualityScore(value: unknown): AnalyticsQualityScore | undefined;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Safe narrowing of unknown event payloads into AnalyticsQualityScore.
3
+ */
4
+ /**
5
+ * Convert an unknown quality-score payload into AnalyticsQualityScore.
6
+ * Returns undefined for missing, null, or malformed values.
7
+ */
8
+ export function parseAnalyticsQualityScore(value) {
9
+ if (!value || typeof value !== "object") {
10
+ return undefined;
11
+ }
12
+ const record = value;
13
+ // Reject NaN/Infinity — typeof NaN === "number"
14
+ if (!Number.isFinite(record.overall) ||
15
+ !Number.isFinite(record.relevance) ||
16
+ !Number.isFinite(record.accuracy) ||
17
+ !Number.isFinite(record.completeness)) {
18
+ return undefined;
19
+ }
20
+ return {
21
+ overall: record.overall,
22
+ relevance: record.relevance,
23
+ accuracy: record.accuracy,
24
+ completeness: record.completeness,
25
+ ...(typeof record.reasoning === "string"
26
+ ? { reasoning: record.reasoning }
27
+ : {}),
28
+ };
29
+ }
30
+ //# sourceMappingURL=parseQualityScore.js.map
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Analytics cost helpers.
3
+ *
4
+ * Prefer costs already computed by the telemetry/pricing pipeline
5
+ * (`utils/pricing.calculateCost` / span `ai.cost.total`). This module
6
+ * is a thin compatibility wrapper — it does not maintain a second
7
+ * hardcoded pricing table.
8
+ */
9
+ /**
10
+ * Calculate cost from token counts using the canonical SDK pricing table.
11
+ * Returns 0 when the provider/model combination is unknown.
12
+ *
13
+ * Partial model-name matching uses longest-prefix preference inside
14
+ * `calculateCost` / `findRates` — shorter keys (e.g. `gpt-4o`) never
15
+ * win over more specific ones (e.g. `gpt-4o-mini`).
16
+ *
17
+ * @param model - Model name
18
+ * @param inputTokens - Number of input tokens
19
+ * @param outputTokens - Number of output tokens
20
+ * @param provider - Provider name (improves rate lookup; defaults to
21
+ * cross-provider search via openrouter alias)
22
+ * @returns Total cost in USD
23
+ */
24
+ export declare function calculateAdvancedCost(model: string | undefined, inputTokens: number, outputTokens: number, provider?: string): number;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Analytics cost helpers.
3
+ *
4
+ * Prefer costs already computed by the telemetry/pricing pipeline
5
+ * (`utils/pricing.calculateCost` / span `ai.cost.total`). This module
6
+ * is a thin compatibility wrapper — it does not maintain a second
7
+ * hardcoded pricing table.
8
+ */
9
+ import { calculateCost } from "../utils/pricing.js";
10
+ /**
11
+ * Calculate cost from token counts using the canonical SDK pricing table.
12
+ * Returns 0 when the provider/model combination is unknown.
13
+ *
14
+ * Partial model-name matching uses longest-prefix preference inside
15
+ * `calculateCost` / `findRates` — shorter keys (e.g. `gpt-4o`) never
16
+ * win over more specific ones (e.g. `gpt-4o-mini`).
17
+ *
18
+ * @param model - Model name
19
+ * @param inputTokens - Number of input tokens
20
+ * @param outputTokens - Number of output tokens
21
+ * @param provider - Provider name (improves rate lookup; defaults to
22
+ * cross-provider search via openrouter alias)
23
+ * @returns Total cost in USD
24
+ */
25
+ export function calculateAdvancedCost(model, inputTokens, outputTokens, provider) {
26
+ if (!model) {
27
+ return 0;
28
+ }
29
+ // When provider is unknown, use the openrouter/litellm cross-provider
30
+ // search path in findRates (PROVIDER_ALIASES → __cross_provider__).
31
+ const resolvedProvider = provider && provider.length > 0 ? provider : "openrouter";
32
+ return calculateCost(resolvedProvider, model, {
33
+ input: inputTokens,
34
+ output: outputTokens,
35
+ total: inputTokens + outputTokens,
36
+ });
37
+ }
38
+ //# sourceMappingURL=pricing.js.map
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Advanced Analytics Service Layer
3
+ * Handles metrics collection, aggregation, cost analysis, and projections.
4
+ */
5
+ import type { AnalyticsStorage, CostAnalysisOptions, CostAnalysisResult, ProviderMetricsOptions, ProviderMetricsResult, TeamAnalyticsOptions, TeamAnalyticsResult, TelemetryRecord } from "../types/index.js";
6
+ export declare class AnalyticsService {
7
+ private storage;
8
+ constructor(storage?: AnalyticsStorage);
9
+ /**
10
+ * Helper to parse dynamic time range formats safely
11
+ */
12
+ private parseTimeRange;
13
+ /**
14
+ * Capture a request lifecycle record
15
+ */
16
+ trackRequest(record: Omit<TelemetryRecord, "id" | "cost"> & {
17
+ cost?: number;
18
+ }): Promise<void>;
19
+ /**
20
+ * getProviderMetrics implementation
21
+ */
22
+ getProviderMetrics(options?: ProviderMetricsOptions): Promise<ProviderMetricsResult>;
23
+ /**
24
+ * Helper to format timestamps for date groupings.
25
+ * Clones the Date so week arithmetic never mutates the caller's value.
26
+ */
27
+ private formatGroupDate;
28
+ /**
29
+ * getCostAnalysis implementation
30
+ */
31
+ getCostAnalysis(options?: CostAnalysisOptions): Promise<CostAnalysisResult>;
32
+ /**
33
+ * getTeamAnalytics implementation
34
+ */
35
+ getTeamAnalytics(options?: TeamAnalyticsOptions): Promise<TeamAnalyticsResult>;
36
+ }