@agentfare/core 0.1.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 (102) hide show
  1. package/README.md +24 -0
  2. package/dist/analyzer/auto-model-selector.d.ts +3 -0
  3. package/dist/analyzer/auto-model-selector.d.ts.map +1 -0
  4. package/dist/analyzer/auto-model-selector.js +15 -0
  5. package/dist/analyzer/auto-model-selector.js.map +1 -0
  6. package/dist/analyzer/cache.d.ts +16 -0
  7. package/dist/analyzer/cache.d.ts.map +1 -0
  8. package/dist/analyzer/cache.js +113 -0
  9. package/dist/analyzer/cache.js.map +1 -0
  10. package/dist/analyzer/llm-analyzer.d.ts +4 -0
  11. package/dist/analyzer/llm-analyzer.d.ts.map +1 -0
  12. package/dist/analyzer/llm-analyzer.js +94 -0
  13. package/dist/analyzer/llm-analyzer.js.map +1 -0
  14. package/dist/analyzer/rules.d.ts +3 -0
  15. package/dist/analyzer/rules.d.ts.map +1 -0
  16. package/dist/analyzer/rules.js +223 -0
  17. package/dist/analyzer/rules.js.map +1 -0
  18. package/dist/analyzer/types.d.ts +52 -0
  19. package/dist/analyzer/types.d.ts.map +1 -0
  20. package/dist/analyzer/types.js +35 -0
  21. package/dist/analyzer/types.js.map +1 -0
  22. package/dist/config/defaults.d.ts +3 -0
  23. package/dist/config/defaults.d.ts.map +1 -0
  24. package/dist/config/defaults.js +41 -0
  25. package/dist/config/defaults.js.map +1 -0
  26. package/dist/config/enterprise.d.ts +7 -0
  27. package/dist/config/enterprise.d.ts.map +1 -0
  28. package/dist/config/enterprise.js +25 -0
  29. package/dist/config/enterprise.js.map +1 -0
  30. package/dist/config/loader.d.ts +10 -0
  31. package/dist/config/loader.d.ts.map +1 -0
  32. package/dist/config/loader.js +134 -0
  33. package/dist/config/loader.js.map +1 -0
  34. package/dist/config/types.d.ts +81 -0
  35. package/dist/config/types.d.ts.map +1 -0
  36. package/dist/config/types.js +3 -0
  37. package/dist/config/types.js.map +1 -0
  38. package/dist/errors.d.ts +14 -0
  39. package/dist/errors.d.ts.map +1 -0
  40. package/dist/errors.js +25 -0
  41. package/dist/errors.js.map +1 -0
  42. package/dist/index.d.ts +29 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +63 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/optimizer/eval-runner.d.ts +23 -0
  47. package/dist/optimizer/eval-runner.d.ts.map +1 -0
  48. package/dist/optimizer/eval-runner.js +115 -0
  49. package/dist/optimizer/eval-runner.js.map +1 -0
  50. package/dist/optimizer/online-learning.d.ts +37 -0
  51. package/dist/optimizer/online-learning.d.ts.map +1 -0
  52. package/dist/optimizer/online-learning.js +128 -0
  53. package/dist/optimizer/online-learning.js.map +1 -0
  54. package/dist/optimizer/pipeline-parser.d.ts +4 -0
  55. package/dist/optimizer/pipeline-parser.d.ts.map +1 -0
  56. package/dist/optimizer/pipeline-parser.js +72 -0
  57. package/dist/optimizer/pipeline-parser.js.map +1 -0
  58. package/dist/optimizer/search.d.ts +7 -0
  59. package/dist/optimizer/search.d.ts.map +1 -0
  60. package/dist/optimizer/search.js +323 -0
  61. package/dist/optimizer/search.js.map +1 -0
  62. package/dist/optimizer/types.d.ts +31 -0
  63. package/dist/optimizer/types.d.ts.map +1 -0
  64. package/dist/optimizer/types.js +11 -0
  65. package/dist/optimizer/types.js.map +1 -0
  66. package/dist/routing/cross-provider.d.ts +8 -0
  67. package/dist/routing/cross-provider.d.ts.map +1 -0
  68. package/dist/routing/cross-provider.js +22 -0
  69. package/dist/routing/cross-provider.js.map +1 -0
  70. package/dist/routing/enterprise.d.ts +7 -0
  71. package/dist/routing/enterprise.d.ts.map +1 -0
  72. package/dist/routing/enterprise.js +16 -0
  73. package/dist/routing/enterprise.js.map +1 -0
  74. package/dist/routing/router.d.ts +19 -0
  75. package/dist/routing/router.d.ts.map +1 -0
  76. package/dist/routing/router.js +125 -0
  77. package/dist/routing/router.js.map +1 -0
  78. package/dist/routing/same-provider.d.ts +4 -0
  79. package/dist/routing/same-provider.d.ts.map +1 -0
  80. package/dist/routing/same-provider.js +24 -0
  81. package/dist/routing/same-provider.js.map +1 -0
  82. package/dist/tracker/cost-tracker.d.ts +13 -0
  83. package/dist/tracker/cost-tracker.d.ts.map +1 -0
  84. package/dist/tracker/cost-tracker.js +39 -0
  85. package/dist/tracker/cost-tracker.js.map +1 -0
  86. package/dist/tracker/database.d.ts +64 -0
  87. package/dist/tracker/database.d.ts.map +1 -0
  88. package/dist/tracker/database.js +194 -0
  89. package/dist/tracker/database.js.map +1 -0
  90. package/dist/tracker/quality-signal.d.ts +29 -0
  91. package/dist/tracker/quality-signal.d.ts.map +1 -0
  92. package/dist/tracker/quality-signal.js +95 -0
  93. package/dist/tracker/quality-signal.js.map +1 -0
  94. package/dist/tracker/report-exporter.d.ts +28 -0
  95. package/dist/tracker/report-exporter.d.ts.map +1 -0
  96. package/dist/tracker/report-exporter.js +39 -0
  97. package/dist/tracker/report-exporter.js.map +1 -0
  98. package/dist/utils/tokens.d.ts +10 -0
  99. package/dist/utils/tokens.d.ts.map +1 -0
  100. package/dist/utils/tokens.js +26 -0
  101. package/dist/utils/tokens.js.map +1 -0
  102. package/package.json +22 -0
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findSameProviderModel = findSameProviderModel;
4
+ function findSameProviderModel(registry, provider, tier, strategy) {
5
+ const candidates = registry.getByProvider(provider).filter((m) => m.tier === tier);
6
+ if (candidates.length === 0) {
7
+ const allSameProvider = registry.getByProvider(provider);
8
+ if (allSameProvider.length > 0)
9
+ return allSameProvider[0];
10
+ return undefined;
11
+ }
12
+ if (candidates.length === 1)
13
+ return candidates[0];
14
+ switch (strategy) {
15
+ case "cost-optimal":
16
+ return candidates.reduce((min, m) => m.pricing.outputPerMillion < min.pricing.outputPerMillion ? m : min);
17
+ case "quality-first":
18
+ return candidates.reduce((best, m) => m.capabilities.codeGeneration > best.capabilities.codeGeneration ? m : best);
19
+ case "balanced":
20
+ default:
21
+ return candidates[0];
22
+ }
23
+ }
24
+ //# sourceMappingURL=same-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"same-provider.js","sourceRoot":"","sources":["../../src/routing/same-provider.ts"],"names":[],"mappings":";;AAGA,sDA2BC;AA3BD,SAAgB,qBAAqB,CACnC,QAAuB,EACvB,QAAgB,EAChB,IAAe,EACf,QAAuD;IAEvD,MAAM,UAAU,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IACnF,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,eAAe,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QACzD,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,eAAe,CAAC,CAAC,CAAC,CAAC;QAC1D,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC;IAElD,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,cAAc;YACjB,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAClC,CAAC,CAAC,OAAO,CAAC,gBAAgB,GAAG,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CACpE,CAAC;QACJ,KAAK,eAAe;YAClB,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CACnC,CAAC,CAAC,YAAY,CAAC,cAAc,GAAG,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAC5E,CAAC;QACJ,KAAK,UAAU,CAAC;QAChB;YACE,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { TrackingDatabase } from "./database.js";
2
+ import type { StepAnalysis } from "../analyzer/types.js";
3
+ import type { ModelEntry } from "@agentfare/models";
4
+ export declare class CostTracker {
5
+ private db;
6
+ constructor(db: TrackingDatabase);
7
+ record(analysis: StepAnalysis, originalModel: string, originalModelEntry: ModelEntry | undefined, targetModel: ModelEntry, sessionId: string, tool: string, tokenUsage: {
8
+ input: number;
9
+ output: number;
10
+ }): void;
11
+ private calculateCostFromEntry;
12
+ }
13
+ //# sourceMappingURL=cost-tracker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cost-tracker.d.ts","sourceRoot":"","sources":["../../src/tracker/cost-tracker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAmB,MAAM,eAAe,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,qBAAa,WAAW;IACV,OAAO,CAAC,EAAE;gBAAF,EAAE,EAAE,gBAAgB;IAExC,MAAM,CACJ,QAAQ,EAAE,YAAY,EACtB,aAAa,EAAE,MAAM,EACrB,kBAAkB,EAAE,UAAU,GAAG,SAAS,EAC1C,WAAW,EAAE,UAAU,EACvB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAC5C,IAAI;IA0BP,OAAO,CAAC,sBAAsB;CAU/B"}
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CostTracker = void 0;
4
+ class CostTracker {
5
+ db;
6
+ constructor(db) {
7
+ this.db = db;
8
+ }
9
+ record(analysis, originalModel, originalModelEntry, targetModel, sessionId, tool, tokenUsage) {
10
+ const originalCost = originalModelEntry
11
+ ? this.calculateCostFromEntry(originalModelEntry, tokenUsage)
12
+ : 0;
13
+ const actualCost = this.calculateCostFromEntry(targetModel, tokenUsage);
14
+ const savings = originalCost - actualCost;
15
+ const entry = {
16
+ sessionId,
17
+ tool,
18
+ stepType: analysis.stepType,
19
+ originalModel,
20
+ routedModel: targetModel.id,
21
+ difficulty: analysis.difficulty,
22
+ confidence: analysis.confidence,
23
+ reasoning: analysis.reasoning,
24
+ inputTokens: tokenUsage.input,
25
+ outputTokens: tokenUsage.output,
26
+ originalCost,
27
+ actualCost,
28
+ savings,
29
+ };
30
+ this.db.insertRoutingLog(entry);
31
+ }
32
+ calculateCostFromEntry(model, tokens) {
33
+ const inputCost = (tokens.input / 1_000_000) * model.pricing.inputPerMillion;
34
+ const outputCost = (tokens.output / 1_000_000) * model.pricing.outputPerMillion;
35
+ return inputCost + outputCost;
36
+ }
37
+ }
38
+ exports.CostTracker = CostTracker;
39
+ //# sourceMappingURL=cost-tracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cost-tracker.js","sourceRoot":"","sources":["../../src/tracker/cost-tracker.ts"],"names":[],"mappings":";;;AAIA,MAAa,WAAW;IACF;IAApB,YAAoB,EAAoB;QAApB,OAAE,GAAF,EAAE,CAAkB;IAAG,CAAC;IAE5C,MAAM,CACJ,QAAsB,EACtB,aAAqB,EACrB,kBAA0C,EAC1C,WAAuB,EACvB,SAAiB,EACjB,IAAY,EACZ,UAA6C;QAE7C,MAAM,YAAY,GAAG,kBAAkB;YACrC,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC,kBAAkB,EAAE,UAAU,CAAC;YAC7D,CAAC,CAAC,CAAC,CAAC;QACN,MAAM,UAAU,GAAG,IAAI,CAAC,sBAAsB,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;QACxE,MAAM,OAAO,GAAG,YAAY,GAAG,UAAU,CAAC;QAE1C,MAAM,KAAK,GAAoB;YAC7B,SAAS;YACT,IAAI;YACJ,QAAQ,EAAE,QAAQ,CAAC,QAAQ;YAC3B,aAAa;YACb,WAAW,EAAE,WAAW,CAAC,EAAE;YAC3B,UAAU,EAAE,QAAQ,CAAC,UAAU;YAC/B,UAAU,EAAE,QAAQ,CAAC,UAAU;YAC/B,SAAS,EAAE,QAAQ,CAAC,SAAS;YAC7B,WAAW,EAAE,UAAU,CAAC,KAAK;YAC7B,YAAY,EAAE,UAAU,CAAC,MAAM;YAC/B,YAAY;YACZ,UAAU;YACV,OAAO;SACR,CAAC;QAEF,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;IAEO,sBAAsB,CAC5B,KAAiB,EACjB,MAAyC;QAEzC,MAAM,SAAS,GACb,CAAC,MAAM,CAAC,KAAK,GAAG,SAAS,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC;QAC7D,MAAM,UAAU,GACd,CAAC,MAAM,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC;QAC/D,OAAO,SAAS,GAAG,UAAU,CAAC;IAChC,CAAC;CACF;AA/CD,kCA+CC"}
@@ -0,0 +1,64 @@
1
+ export interface RoutingLogEntry {
2
+ sessionId: string;
3
+ tool: string;
4
+ stepType: string;
5
+ originalModel: string;
6
+ routedModel: string;
7
+ difficulty: number;
8
+ confidence: number;
9
+ reasoning: string;
10
+ inputTokens: number;
11
+ outputTokens: number;
12
+ originalCost: number;
13
+ actualCost: number;
14
+ savings: number;
15
+ qualitySignal?: string | null;
16
+ }
17
+ export interface CostSummary {
18
+ totalRequests: number;
19
+ totalOriginalCost: number;
20
+ totalActualCost: number;
21
+ totalSavings: number;
22
+ }
23
+ export interface RoutingLogRow {
24
+ id: number;
25
+ timestamp: string;
26
+ session_id: string;
27
+ tool: string;
28
+ step_type: string;
29
+ original_model: string;
30
+ routed_model: string;
31
+ difficulty: number | null;
32
+ confidence: number | null;
33
+ reasoning: string | null;
34
+ input_tokens: number;
35
+ output_tokens: number;
36
+ original_cost: number;
37
+ actual_cost: number;
38
+ savings: number;
39
+ quality_signal: string | null;
40
+ }
41
+ export interface StepToolSummary {
42
+ key: string;
43
+ count: number;
44
+ totalCost: number;
45
+ totalSavings: number;
46
+ }
47
+ export declare class TrackingDatabase {
48
+ private db;
49
+ constructor(dbPath: string);
50
+ listTables(): string[];
51
+ insertRoutingLog(entry: RoutingLogEntry): void;
52
+ queryLogs(filter: {
53
+ sessionId?: string;
54
+ tool?: string;
55
+ stepType?: string;
56
+ }): RoutingLogRow[];
57
+ getStepSummary(timeRange?: string): StepToolSummary[];
58
+ getToolSummary(timeRange?: string): StepToolSummary[];
59
+ private static readonly ALLOWED_AGG_COLUMNS;
60
+ private aggregateBy;
61
+ getCostSummary(timeRange?: string): CostSummary;
62
+ close(): void;
63
+ }
64
+ //# sourceMappingURL=database.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/tracker/database.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;CACtB;AAGD,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAGD,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AA4CD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,EAAE,CAAoB;gBAElB,MAAM,EAAE,MAAM;IAQ1B,UAAU,IAAI,MAAM,EAAE;IAStB,gBAAgB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI;IA4B9C,SAAS,CAAC,MAAM,EAAE;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,aAAa,EAAE;IAyBnB,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,eAAe,EAAE;IAIrD,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,eAAe,EAAE;IAKrD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAkC;IAE7E,OAAO,CAAC,WAAW;IAkBnB,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,WAAW;IA8B/C,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,194 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.TrackingDatabase = void 0;
40
+ const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
41
+ const path = __importStar(require("node:path"));
42
+ const fs = __importStar(require("node:fs"));
43
+ const SCHEMA = `
44
+ CREATE TABLE IF NOT EXISTS routing_logs (
45
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
46
+ timestamp TEXT NOT NULL DEFAULT (datetime('now')),
47
+ session_id TEXT NOT NULL,
48
+ tool TEXT NOT NULL,
49
+ step_type TEXT NOT NULL,
50
+ original_model TEXT NOT NULL,
51
+ routed_model TEXT NOT NULL,
52
+ difficulty REAL,
53
+ confidence REAL,
54
+ reasoning TEXT,
55
+ input_tokens INTEGER DEFAULT 0,
56
+ output_tokens INTEGER DEFAULT 0,
57
+ original_cost REAL DEFAULT 0,
58
+ actual_cost REAL DEFAULT 0,
59
+ savings REAL DEFAULT 0,
60
+ quality_signal TEXT DEFAULT NULL
61
+ );
62
+
63
+ CREATE TABLE IF NOT EXISTS model_scores (
64
+ model TEXT NOT NULL,
65
+ step_type TEXT NOT NULL,
66
+ avg_accuracy REAL DEFAULT 0.5,
67
+ avg_latency_ms INTEGER DEFAULT 0,
68
+ avg_cost_per_task REAL DEFAULT 0,
69
+ sample_count INTEGER DEFAULT 0,
70
+ last_updated TEXT NOT NULL DEFAULT (datetime('now')),
71
+ PRIMARY KEY (model, step_type)
72
+ );
73
+
74
+ CREATE TABLE IF NOT EXISTS pipeline_combos (
75
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
76
+ pipeline_name TEXT NOT NULL,
77
+ combo_json TEXT NOT NULL,
78
+ estimated_accuracy REAL,
79
+ estimated_cost REAL,
80
+ pareto_type TEXT,
81
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
82
+ );
83
+ `;
84
+ class TrackingDatabase {
85
+ db;
86
+ constructor(dbPath) {
87
+ const dir = path.dirname(dbPath);
88
+ if (!fs.existsSync(dir))
89
+ fs.mkdirSync(dir, { recursive: true });
90
+ this.db = new better_sqlite3_1.default(dbPath);
91
+ this.db.pragma("journal_mode = WAL");
92
+ this.db.exec(SCHEMA);
93
+ }
94
+ listTables() {
95
+ const rows = this.db
96
+ .prepare("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name")
97
+ .all();
98
+ return rows.map((r) => r.name);
99
+ }
100
+ insertRoutingLog(entry) {
101
+ this.db
102
+ .prepare(`
103
+ INSERT INTO routing_logs (session_id, tool, step_type, original_model, routed_model,
104
+ difficulty, confidence, reasoning, input_tokens, output_tokens,
105
+ original_cost, actual_cost, savings, quality_signal)
106
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
107
+ `)
108
+ .run(entry.sessionId, entry.tool, entry.stepType, entry.originalModel, entry.routedModel, entry.difficulty, entry.confidence, entry.reasoning, entry.inputTokens, entry.outputTokens, entry.originalCost, entry.actualCost, entry.savings, entry.qualitySignal ?? null);
109
+ }
110
+ queryLogs(filter) {
111
+ const conditions = [];
112
+ const params = [];
113
+ if (filter.sessionId) {
114
+ conditions.push("session_id = ?");
115
+ params.push(filter.sessionId);
116
+ }
117
+ if (filter.tool) {
118
+ conditions.push("tool = ?");
119
+ params.push(filter.tool);
120
+ }
121
+ if (filter.stepType) {
122
+ conditions.push("step_type = ?");
123
+ params.push(filter.stepType);
124
+ }
125
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
126
+ return this.db
127
+ .prepare(`SELECT * FROM routing_logs ${where} ORDER BY timestamp DESC`)
128
+ .all(...params);
129
+ }
130
+ // ISSUE-036: SQL-level aggregation to avoid loading all rows into memory
131
+ getStepSummary(timeRange) {
132
+ return this.aggregateBy("step_type", timeRange);
133
+ }
134
+ getToolSummary(timeRange) {
135
+ return this.aggregateBy("tool", timeRange);
136
+ }
137
+ // ISSUE-062: whitelist allowed aggregation columns to prevent SQL injection
138
+ static ALLOWED_AGG_COLUMNS = ["step_type", "tool"];
139
+ aggregateBy(column, timeRange) {
140
+ if (!TrackingDatabase.ALLOWED_AGG_COLUMNS.includes(column)) {
141
+ throw new Error(`Invalid aggregation column: ${column}`);
142
+ }
143
+ const timeCondition = timeRange
144
+ ? `WHERE timestamp >= datetime('now', ?)`
145
+ : "";
146
+ const stmt = timeRange
147
+ ? this.db.prepare(`SELECT ${column} as key, COUNT(*) as count, COALESCE(SUM(actual_cost), 0) as totalCost, COALESCE(SUM(savings), 0) as totalSavings FROM routing_logs ${timeCondition} GROUP BY ${column} ORDER BY totalCost DESC`)
148
+ : this.db.prepare(`SELECT ${column} as key, COUNT(*) as count, COALESCE(SUM(actual_cost), 0) as totalCost, COALESCE(SUM(savings), 0) as totalSavings FROM routing_logs GROUP BY ${column} ORDER BY totalCost DESC`);
149
+ const params = timeRange ? [`-${parseTimeRange(timeRange)}`] : [];
150
+ return stmt.all(...params);
151
+ }
152
+ getCostSummary(timeRange) {
153
+ // ISSUE-008: use parameterized query instead of string interpolation
154
+ const stmt = timeRange
155
+ ? this.db.prepare(`SELECT
156
+ COUNT(*) as totalRequests,
157
+ COALESCE(SUM(original_cost), 0) as totalOriginalCost,
158
+ COALESCE(SUM(actual_cost), 0) as totalActualCost,
159
+ COALESCE(SUM(savings), 0) as totalSavings
160
+ FROM routing_logs
161
+ WHERE timestamp >= datetime('now', ?)`)
162
+ : this.db.prepare(`SELECT
163
+ COUNT(*) as totalRequests,
164
+ COALESCE(SUM(original_cost), 0) as totalOriginalCost,
165
+ COALESCE(SUM(actual_cost), 0) as totalActualCost,
166
+ COALESCE(SUM(savings), 0) as totalSavings
167
+ FROM routing_logs`);
168
+ const params = timeRange ? [`-${parseTimeRange(timeRange)}`] : [];
169
+ const row = stmt.get(...params);
170
+ return {
171
+ totalRequests: row.totalRequests,
172
+ totalOriginalCost: row.totalOriginalCost,
173
+ totalActualCost: row.totalActualCost,
174
+ totalSavings: row.totalSavings,
175
+ };
176
+ }
177
+ close() {
178
+ this.db.close();
179
+ }
180
+ }
181
+ exports.TrackingDatabase = TrackingDatabase;
182
+ function parseTimeRange(range) {
183
+ const match = range.match(/^(\d+)([dhm])$/);
184
+ if (!match)
185
+ throw new Error(`Invalid timeRange: "${range}". Expected format: <number><d|h|m> (e.g. "7d", "24h", "30m")`);
186
+ const [, num, unit] = match;
187
+ // regex guarantees unit is one of d/h/m
188
+ if (unit === "d")
189
+ return `${num} days`;
190
+ if (unit === "h")
191
+ return `${num} hours`;
192
+ return `${num} minutes`;
193
+ }
194
+ //# sourceMappingURL=database.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/tracker/database.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ export type QualitySignal = "success" | "retry" | "manual_switch" | "task_abandoned" | "error";
2
+ export interface QualitySignalEvent {
3
+ sessionId: string;
4
+ signal: QualitySignal;
5
+ model: string;
6
+ stepType: string;
7
+ timestamp: Date;
8
+ }
9
+ export declare class QualitySignalCollector {
10
+ private lastRoutedModels;
11
+ private routedTiers;
12
+ private sessionLastRequest;
13
+ private signals;
14
+ recordRoutedModel(sessionId: string, model: string, tier: string): void;
15
+ recordRequest(sessionId: string, model: string, stepType: string): void;
16
+ detectManualSwitch(sessionId: string, currentModel: string): boolean;
17
+ detectRetry(sessionId: string): boolean;
18
+ detectAbandoned(sessionId: string): boolean;
19
+ recordSignal(model: string, stepType: string, signal: QualitySignal, sessionId?: string): void;
20
+ getSignals(): Array<{
21
+ sessionId: string;
22
+ model: string;
23
+ stepType: string;
24
+ signal: QualitySignal;
25
+ timestamp: number;
26
+ }>;
27
+ inferFinalSignal(sessionId: string): QualitySignalEvent | null;
28
+ }
29
+ //# sourceMappingURL=quality-signal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quality-signal.d.ts","sourceRoot":"","sources":["../../src/tracker/quality-signal.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GACrB,SAAS,GACT,OAAO,GACP,eAAe,GACf,gBAAgB,GAChB,OAAO,CAAC;AAEZ,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,aAAa,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,qBAAa,sBAAsB;IACjC,OAAO,CAAC,gBAAgB,CAAkC;IAC1D,OAAO,CAAC,WAAW,CAAkC;IACrD,OAAO,CAAC,kBAAkB,CAGZ;IACd,OAAO,CAAC,OAAO,CAMP;IAER,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAKvE,aAAa,CACX,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,GACf,IAAI;IAQP,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO;IAOpE,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAMvC,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAM3C,YAAY,CACV,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,aAAa,EACrB,SAAS,CAAC,EAAE,MAAM,GACjB,IAAI;IAcP,UAAU,IAAI,KAAK,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,aAAa,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IAIF,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI;CAiC/D"}
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.QualitySignalCollector = void 0;
4
+ class QualitySignalCollector {
5
+ lastRoutedModels = new Map();
6
+ routedTiers = new Map();
7
+ sessionLastRequest = new Map();
8
+ signals = [];
9
+ recordRoutedModel(sessionId, model, tier) {
10
+ this.lastRoutedModels.set(sessionId, model);
11
+ this.routedTiers.set(sessionId, tier);
12
+ }
13
+ recordRequest(sessionId, model, stepType) {
14
+ this.sessionLastRequest.set(sessionId, {
15
+ model,
16
+ stepType,
17
+ timestamp: Date.now(),
18
+ });
19
+ }
20
+ detectManualSwitch(sessionId, currentModel) {
21
+ const lastRouted = this.lastRoutedModels.get(sessionId);
22
+ if (!lastRouted)
23
+ return false;
24
+ if (currentModel === lastRouted)
25
+ return false;
26
+ return !isOurRouting(currentModel, lastRouted);
27
+ }
28
+ detectRetry(sessionId) {
29
+ const last = this.sessionLastRequest.get(sessionId);
30
+ if (!last)
31
+ return false;
32
+ return Date.now() - last.timestamp < 10000;
33
+ }
34
+ detectAbandoned(sessionId) {
35
+ const last = this.sessionLastRequest.get(sessionId);
36
+ if (!last)
37
+ return false;
38
+ return Date.now() - last.timestamp > 300000;
39
+ }
40
+ recordSignal(model, stepType, signal, sessionId) {
41
+ this.signals.push({
42
+ sessionId: sessionId ?? "",
43
+ model,
44
+ stepType,
45
+ signal,
46
+ timestamp: Date.now(),
47
+ });
48
+ // Keep bounded to prevent memory leak
49
+ if (this.signals.length > 1000) {
50
+ this.signals = this.signals.slice(-500);
51
+ }
52
+ }
53
+ getSignals() {
54
+ return this.signals;
55
+ }
56
+ inferFinalSignal(sessionId) {
57
+ const last = this.sessionLastRequest.get(sessionId);
58
+ if (!last)
59
+ return null;
60
+ // Find the worst signal for this session: error > task_abandoned > retry > manual_switch > success
61
+ const priority = {
62
+ error: 4,
63
+ task_abandoned: 3,
64
+ retry: 2,
65
+ manual_switch: 1,
66
+ success: 0,
67
+ };
68
+ const sessionSignals = this.signals.filter((s) => s.sessionId === sessionId);
69
+ let worstSignal = "success";
70
+ let worstPriority = 0;
71
+ for (const s of sessionSignals) {
72
+ const p = priority[s.signal] ?? 0;
73
+ if (p > worstPriority) {
74
+ worstPriority = p;
75
+ worstSignal = s.signal;
76
+ }
77
+ }
78
+ return {
79
+ sessionId,
80
+ signal: worstSignal,
81
+ model: last.model,
82
+ stepType: last.stepType,
83
+ timestamp: new Date(),
84
+ };
85
+ }
86
+ }
87
+ exports.QualitySignalCollector = QualitySignalCollector;
88
+ function isOurRouting(currentModel, lastRouted) {
89
+ const currentParts = currentModel.split("/");
90
+ const lastParts = lastRouted.split("/");
91
+ if (currentParts.length < 2 || lastParts.length < 2)
92
+ return false;
93
+ return currentParts[0] === lastParts[0];
94
+ }
95
+ //# sourceMappingURL=quality-signal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quality-signal.js","sourceRoot":"","sources":["../../src/tracker/quality-signal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { TrackingDatabase } from "./database.js";
2
+ export interface StepReport {
3
+ stepType: string;
4
+ count: number;
5
+ totalCost: number;
6
+ totalSavings: number;
7
+ avgSavingsPct: number;
8
+ }
9
+ export interface ToolReport {
10
+ tool: string;
11
+ count: number;
12
+ totalCost: number;
13
+ totalSavings: number;
14
+ }
15
+ export interface CostReport {
16
+ summary: {
17
+ totalRequests: number;
18
+ totalOriginalCost: number;
19
+ totalActualCost: number;
20
+ totalSavings: number;
21
+ savingsPct: number;
22
+ };
23
+ byStep: StepReport[];
24
+ byTool: ToolReport[];
25
+ generatedAt: string;
26
+ }
27
+ export declare function generateReport(db: TrackingDatabase, timeRange?: string): CostReport;
28
+ //# sourceMappingURL=report-exporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-exporter.d.ts","sourceRoot":"","sources":["../../src/tracker/report-exporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE;QACP,aAAa,EAAE,MAAM,CAAC;QACtB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,eAAe,EAAE,MAAM,CAAC;QACxB,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,gBAAgB,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,UAAU,CAsCnF"}
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateReport = generateReport;
4
+ function generateReport(db, timeRange) {
5
+ const summary = db.getCostSummary(timeRange);
6
+ const savingsPct = summary.totalOriginalCost > 0
7
+ ? (summary.totalSavings / summary.totalOriginalCost) * 100
8
+ : 0;
9
+ // ISSUE-036: Use SQL-level aggregation instead of loading all rows into memory
10
+ const stepRows = db.getStepSummary(timeRange);
11
+ const toolRows = db.getToolSummary(timeRange);
12
+ const byStep = stepRows.map((row) => ({
13
+ stepType: row.key,
14
+ count: row.count,
15
+ totalCost: row.totalCost,
16
+ totalSavings: row.totalSavings,
17
+ // ISSUE-037: guard against division by zero
18
+ avgSavingsPct: (() => { const denom = row.totalCost + row.totalSavings; return Math.abs(denom) > 0.01 ? (row.totalSavings / denom) * 100 : 0; })(),
19
+ }));
20
+ const byTool = toolRows.map((row) => ({
21
+ tool: row.key,
22
+ count: row.count,
23
+ totalCost: row.totalCost,
24
+ totalSavings: row.totalSavings,
25
+ }));
26
+ return {
27
+ summary: {
28
+ totalRequests: summary.totalRequests,
29
+ totalOriginalCost: summary.totalOriginalCost,
30
+ totalActualCost: summary.totalActualCost,
31
+ totalSavings: summary.totalSavings,
32
+ savingsPct,
33
+ },
34
+ byStep,
35
+ byTool,
36
+ generatedAt: new Date().toISOString(),
37
+ };
38
+ }
39
+ //# sourceMappingURL=report-exporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-exporter.js","sourceRoot":"","sources":["../../src/tracker/report-exporter.ts"],"names":[],"mappings":";;AA8BA,wCAsCC;AAtCD,SAAgB,cAAc,CAAC,EAAoB,EAAE,SAAkB;IACrE,MAAM,OAAO,GAAG,EAAE,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;IAC7C,MAAM,UAAU,GAAG,OAAO,CAAC,iBAAiB,GAAG,CAAC;QAC9C,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,GAAG,OAAO,CAAC,iBAAiB,CAAC,GAAG,GAAG;QAC1D,CAAC,CAAC,CAAC,CAAC;IAEN,+EAA+E;IAC/E,MAAM,QAAQ,GAAG,EAAE,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,EAAE,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;IAE9C,MAAM,MAAM,GAAiB,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAClD,QAAQ,EAAE,GAAG,CAAC,GAAG;QACjB,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,YAAY,EAAE,GAAG,CAAC,YAAY;QAC9B,4CAA4C;QAC5C,aAAa,EAAE,CAAC,GAAG,EAAE,GAAG,MAAM,KAAK,GAAG,GAAG,CAAC,SAAS,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;KACnJ,CAAC,CAAC,CAAC;IAEJ,MAAM,MAAM,GAAiB,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAClD,IAAI,EAAE,GAAG,CAAC,GAAG;QACb,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,YAAY,EAAE,GAAG,CAAC,YAAY;KAC/B,CAAC,CAAC,CAAC;IAEJ,OAAO;QACL,OAAO,EAAE;YACP,aAAa,EAAE,OAAO,CAAC,aAAa;YACpC,iBAAiB,EAAE,OAAO,CAAC,iBAAiB;YAC5C,eAAe,EAAE,OAAO,CAAC,eAAe;YACxC,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,UAAU;SACX;QACD,MAAM;QACN,MAAM;QACN,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACtC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Shared token estimation utility.
3
+ * Estimates token count from message arrays using ~4 chars/token heuristic.
4
+ */
5
+ import type { Message } from "../analyzer/types.js";
6
+ export declare function estimateTokensFromMessages(messages: Message[]): {
7
+ input: number;
8
+ output: number;
9
+ };
10
+ //# sourceMappingURL=tokens.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/utils/tokens.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAEpD,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG;IAC/D,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAmBA"}
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.estimateTokensFromMessages = estimateTokensFromMessages;
4
+ function estimateTokensFromMessages(messages) {
5
+ let totalChars = 0;
6
+ for (const m of messages) {
7
+ if (typeof m.content === "string") {
8
+ totalChars += m.content.length;
9
+ }
10
+ else if (Array.isArray(m.content)) {
11
+ for (const block of m.content) {
12
+ if (block.text)
13
+ totalChars += block.text.length;
14
+ }
15
+ }
16
+ if (m.tool_calls) {
17
+ for (const tc of m.tool_calls) {
18
+ totalChars += tc.function.arguments.length;
19
+ }
20
+ }
21
+ }
22
+ const inputTokens = Math.ceil(totalChars / 4);
23
+ const outputTokens = Math.ceil(inputTokens * 0.3);
24
+ return { input: inputTokens, output: outputTokens };
25
+ }
26
+ //# sourceMappingURL=tokens.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../src/utils/tokens.ts"],"names":[],"mappings":";;AAMA,gEAsBC;AAtBD,SAAgB,0BAA0B,CAAC,QAAmB;IAI5D,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;YAClC,UAAU,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;QACjC,CAAC;aAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;YACpC,KAAK,MAAM,KAAK,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;gBAC9B,IAAI,KAAK,CAAC,IAAI;oBAAE,UAAU,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC;YAClD,CAAC;QACH,CAAC;QACD,IAAI,CAAC,CAAC,UAAU,EAAE,CAAC;YACjB,KAAK,MAAM,EAAE,IAAI,CAAC,CAAC,UAAU,EAAE,CAAC;gBAC9B,UAAU,IAAI,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC;YAC7C,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;IAC9C,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC,CAAC;IAClD,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;AACtD,CAAC"}
package/package.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@agentfare/core",
3
+ "version": "0.1.0",
4
+ "license": "MIT",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": ["dist"],
8
+ "scripts": {
9
+ "build": "tsc",
10
+ "test": "vitest run",
11
+ "test:watch": "vitest"
12
+ },
13
+ "dependencies": {
14
+ "@agentfare/models": "workspace:*",
15
+ "better-sqlite3": "^11.0.0"
16
+ },
17
+ "devDependencies": {
18
+ "@types/better-sqlite3": "^7.6.0",
19
+ "typescript": "^5.8",
20
+ "vitest": "^3.2"
21
+ }
22
+ }