@meyicloud/meyi-insight-server 1.1.3 → 1.1.5

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 (92) hide show
  1. package/dist/controllers/tenant.controller.js +1 -1
  2. package/dist/controllers/tenant.controller.js.map +1 -1
  3. package/dist/features/ai-pricing/ai-pricing.controller.d.ts +9 -0
  4. package/dist/features/ai-pricing/ai-pricing.controller.d.ts.map +1 -0
  5. package/dist/features/ai-pricing/ai-pricing.controller.js +73 -0
  6. package/dist/features/ai-pricing/ai-pricing.controller.js.map +1 -0
  7. package/dist/features/ai-pricing/ai-pricing.model.d.ts +44 -0
  8. package/dist/features/ai-pricing/ai-pricing.model.d.ts.map +1 -0
  9. package/dist/features/ai-pricing/ai-pricing.model.js +229 -0
  10. package/dist/features/ai-pricing/ai-pricing.model.js.map +1 -0
  11. package/dist/features/ai-pricing/ai-pricing.routes.d.ts +10 -0
  12. package/dist/features/ai-pricing/ai-pricing.routes.d.ts.map +1 -0
  13. package/dist/features/ai-pricing/ai-pricing.routes.js +18 -0
  14. package/dist/features/ai-pricing/ai-pricing.routes.js.map +1 -0
  15. package/dist/features/ai-pricing/ai-pricing.service.d.ts +23 -0
  16. package/dist/features/ai-pricing/ai-pricing.service.d.ts.map +1 -0
  17. package/dist/features/ai-pricing/ai-pricing.service.js +87 -0
  18. package/dist/features/ai-pricing/ai-pricing.service.js.map +1 -0
  19. package/dist/features/ai-pricing/ai-pricing.sync.d.ts +48 -0
  20. package/dist/features/ai-pricing/ai-pricing.sync.d.ts.map +1 -0
  21. package/dist/features/ai-pricing/ai-pricing.sync.js +119 -0
  22. package/dist/features/ai-pricing/ai-pricing.sync.js.map +1 -0
  23. package/dist/features/ai-pricing/ai-pricing.types.d.ts +66 -0
  24. package/dist/features/ai-pricing/ai-pricing.types.d.ts.map +1 -0
  25. package/dist/features/ai-pricing/ai-pricing.types.js +16 -0
  26. package/dist/features/ai-pricing/ai-pricing.types.js.map +1 -0
  27. package/dist/features/ai-pricing/ai-pricing.worker.d.ts +47 -0
  28. package/dist/features/ai-pricing/ai-pricing.worker.d.ts.map +1 -0
  29. package/dist/features/ai-pricing/ai-pricing.worker.js +121 -0
  30. package/dist/features/ai-pricing/ai-pricing.worker.js.map +1 -0
  31. package/dist/features/ai-pricing/index.d.ts +8 -0
  32. package/dist/features/ai-pricing/index.d.ts.map +1 -0
  33. package/dist/features/ai-pricing/index.js +8 -0
  34. package/dist/features/ai-pricing/index.js.map +1 -0
  35. package/dist/features/ai-pricing/litellm.mapper.d.ts +11 -0
  36. package/dist/features/ai-pricing/litellm.mapper.d.ts.map +1 -0
  37. package/dist/features/ai-pricing/litellm.mapper.js +104 -0
  38. package/dist/features/ai-pricing/litellm.mapper.js.map +1 -0
  39. package/dist/features/ai-pricing/pricing.lookup.d.ts +39 -0
  40. package/dist/features/ai-pricing/pricing.lookup.d.ts.map +1 -0
  41. package/dist/features/ai-pricing/pricing.lookup.js +88 -0
  42. package/dist/features/ai-pricing/pricing.lookup.js.map +1 -0
  43. package/dist/features/ai-pricing/pricing.plan.d.ts +33 -0
  44. package/dist/features/ai-pricing/pricing.plan.d.ts.map +1 -0
  45. package/dist/features/ai-pricing/pricing.plan.js +63 -0
  46. package/dist/features/ai-pricing/pricing.plan.js.map +1 -0
  47. package/dist/features/ai-pricing/pricing.reprice.d.ts +25 -0
  48. package/dist/features/ai-pricing/pricing.reprice.d.ts.map +1 -0
  49. package/dist/features/ai-pricing/pricing.reprice.js +65 -0
  50. package/dist/features/ai-pricing/pricing.reprice.js.map +1 -0
  51. package/dist/features/ai-usage/ai-usage.controller.d.ts +1 -0
  52. package/dist/features/ai-usage/ai-usage.controller.d.ts.map +1 -1
  53. package/dist/features/ai-usage/ai-usage.controller.js +9 -0
  54. package/dist/features/ai-usage/ai-usage.controller.js.map +1 -1
  55. package/dist/features/ai-usage/ai-usage.model.d.ts +2 -0
  56. package/dist/features/ai-usage/ai-usage.model.d.ts.map +1 -1
  57. package/dist/features/ai-usage/ai-usage.model.js +16 -0
  58. package/dist/features/ai-usage/ai-usage.model.js.map +1 -1
  59. package/dist/features/ai-usage/ai-usage.query.d.ts.map +1 -1
  60. package/dist/features/ai-usage/ai-usage.query.js +8 -0
  61. package/dist/features/ai-usage/ai-usage.query.js.map +1 -1
  62. package/dist/features/ai-usage/ai-usage.routes.d.ts.map +1 -1
  63. package/dist/features/ai-usage/ai-usage.routes.js +1 -0
  64. package/dist/features/ai-usage/ai-usage.routes.js.map +1 -1
  65. package/dist/features/ai-usage/ai-usage.service.d.ts +2 -1
  66. package/dist/features/ai-usage/ai-usage.service.d.ts.map +1 -1
  67. package/dist/features/ai-usage/ai-usage.service.js +11 -0
  68. package/dist/features/ai-usage/ai-usage.service.js.map +1 -1
  69. package/dist/features/ai-usage/ai-usage.types.d.ts +26 -0
  70. package/dist/features/ai-usage/ai-usage.types.d.ts.map +1 -1
  71. package/dist/features/ai-usage/ai-usage.types.js.map +1 -1
  72. package/dist/migrations/run.d.ts.map +1 -1
  73. package/dist/migrations/run.js +103 -15
  74. package/dist/migrations/run.js.map +1 -1
  75. package/dist/routes/index.d.ts +2 -0
  76. package/dist/routes/index.d.ts.map +1 -1
  77. package/dist/routes/index.js +7 -2
  78. package/dist/routes/index.js.map +1 -1
  79. package/dist/server.d.ts.map +1 -1
  80. package/dist/server.js +4 -0
  81. package/dist/server.js.map +1 -1
  82. package/dist/service.d.ts +5 -0
  83. package/dist/service.d.ts.map +1 -1
  84. package/dist/service.js +15 -0
  85. package/dist/service.js.map +1 -1
  86. package/dist/services/tenant.service.js +2 -2
  87. package/dist/services/tenant.service.js.map +1 -1
  88. package/dist/tenancy.d.ts +1 -0
  89. package/dist/tenancy.d.ts.map +1 -1
  90. package/dist/tenancy.js +9 -3
  91. package/dist/tenancy.js.map +1 -1
  92. package/package.json +2 -2
@@ -7,7 +7,7 @@ export function createTenantController(service) {
7
7
  return res.json({
8
8
  ok: true,
9
9
  multiTenantEnabled: isMultiTenantEnabled(),
10
- defaultTenantId: DEFAULT_TENANT_ID,
10
+ defaultTenantId: isMultiTenantEnabled() ? null : DEFAULT_TENANT_ID,
11
11
  tenants,
12
12
  });
13
13
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tenant.controller.js","sourceRoot":"","sources":["../../src/controllers/tenant.controller.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAExE,MAAM,UAAU,sBAAsB,CAAC,OAAsB;IAC3D,OAAO;QACL,KAAK,CAAC,IAAI,CAAC,GAAY,EAAE,GAAa;YACpC,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;gBACrC,OAAO,GAAG,CAAC,IAAI,CAAC;oBACd,EAAE,EAAE,IAAI;oBACR,kBAAkB,EAAE,oBAAoB,EAAE;oBAC1C,eAAe,EAAE,iBAAiB;oBAClC,OAAO;iBACR,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,CAAM,EAAE,CAAC;gBAChB,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;YACxH,CAAC;QACH,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,GAAY,EAAE,GAAa;YACtC,IAAI,CAAC;gBACH,IAAI,CAAC,oBAAoB,EAAE,EAAE,CAAC;oBAC5B,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,+BAA+B,EAAE,CAAC,CAAC;gBACrF,CAAC;gBACD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;gBAChF,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;YACpD,CAAC;YAAC,OAAO,CAAM,EAAE,CAAC;gBAChB,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,KAAK,uBAAuB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;gBACjE,OAAO,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;YAC3H,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"tenant.controller.js","sourceRoot":"","sources":["../../src/controllers/tenant.controller.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAExE,MAAM,UAAU,sBAAsB,CAAC,OAAsB;IAC3D,OAAO;QACL,KAAK,CAAC,IAAI,CAAC,GAAY,EAAE,GAAa;YACpC,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;gBACrC,OAAO,GAAG,CAAC,IAAI,CAAC;oBACd,EAAE,EAAE,IAAI;oBACR,kBAAkB,EAAE,oBAAoB,EAAE;oBAC1C,eAAe,EAAE,oBAAoB,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,iBAAiB;oBAClE,OAAO;iBACR,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,CAAM,EAAE,CAAC;gBAChB,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;YACxH,CAAC;QACH,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,GAAY,EAAE,GAAa;YACtC,IAAI,CAAC;gBACH,IAAI,CAAC,oBAAoB,EAAE,EAAE,CAAC;oBAC5B,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,+BAA+B,EAAE,CAAC,CAAC;gBACrF,CAAC;gBACD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;gBAChF,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;YACpD,CAAC;YAAC,OAAO,CAAM,EAAE,CAAC;gBAChB,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,KAAK,uBAAuB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;gBACjE,OAAO,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;YAC3H,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { Request, Response } from "express";
2
+ import type { AiPricingService } from "./ai-pricing.service.js";
3
+ export declare function createAiPricingController(service: AiPricingService): {
4
+ list(req: Request, res: Response): Promise<Response<any, Record<string, any>>>;
5
+ reviews(req: Request, res: Response): Promise<Response<any, Record<string, any>>>;
6
+ decide(req: Request, res: Response): Promise<Response<any, Record<string, any>>>;
7
+ setManual(req: Request, res: Response): Promise<Response<any, Record<string, any>>>;
8
+ };
9
+ //# sourceMappingURL=ai-pricing.controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-pricing.controller.d.ts","sourceRoot":"","sources":["../../../src/features/ai-pricing/ai-pricing.controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AA2BhE,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB;cAE/C,OAAO,OAAO,QAAQ;iBAYnB,OAAO,OAAO,QAAQ;gBAQvB,OAAO,OAAO,QAAQ;mBASnB,OAAO,OAAO,QAAQ;EAU9C"}
@@ -0,0 +1,73 @@
1
+ import { AiPricingError } from "./ai-pricing.types.js";
2
+ function fail(res, e) {
3
+ if (e instanceof AiPricingError)
4
+ return res.status(e.status).json({ ok: false, error: e.message });
5
+ return res.status(500).json({ ok: false, error: e?.message ?? "Internal error" });
6
+ }
7
+ function actor(req) {
8
+ const u = req.user;
9
+ return u ? String(u.email || u.id || u.name || "") || null : null;
10
+ }
11
+ /**
12
+ * Prices are platform-wide, so changing them is for platform admins only. Fails
13
+ * closed: no authenticated super_admin (including standalone mode, where the
14
+ * host never populates req.user) means no write.
15
+ */
16
+ function isPlatformAdmin(req) {
17
+ return req.user?.role === "super_admin";
18
+ }
19
+ function first(v) {
20
+ if (Array.isArray(v))
21
+ return first(v[0]);
22
+ return typeof v === "string" && v.trim() ? v.trim() : undefined;
23
+ }
24
+ export function createAiPricingController(service) {
25
+ return {
26
+ async list(req, res) {
27
+ try {
28
+ const result = await service.list({
29
+ provider: first(req.query.provider),
30
+ search: first(req.query.search),
31
+ limit: Number(first(req.query.limit)),
32
+ });
33
+ return res.json({ ok: true, ...result });
34
+ }
35
+ catch (e) {
36
+ return fail(res, e);
37
+ }
38
+ },
39
+ async reviews(req, res) {
40
+ if (!isPlatformAdmin(req))
41
+ return res.status(403).json({ ok: false, error: "Platform admin only" });
42
+ try {
43
+ return res.json({ ok: true, reviews: await service.listReviews(first(req.query.status) ?? "pending") });
44
+ }
45
+ catch (e) {
46
+ return fail(res, e);
47
+ }
48
+ },
49
+ async decide(req, res) {
50
+ if (!isPlatformAdmin(req))
51
+ return res.status(403).json({ ok: false, error: "Platform admin only" });
52
+ try {
53
+ await service.decideReview(req.params.id, String(req.body?.action ?? ""), actor(req));
54
+ return res.json({ ok: true });
55
+ }
56
+ catch (e) {
57
+ return fail(res, e);
58
+ }
59
+ },
60
+ async setManual(req, res) {
61
+ if (!isPlatformAdmin(req))
62
+ return res.status(403).json({ ok: false, error: "Platform admin only" });
63
+ try {
64
+ await service.setManual(req.body ?? {}, actor(req));
65
+ return res.json({ ok: true });
66
+ }
67
+ catch (e) {
68
+ return fail(res, e);
69
+ }
70
+ },
71
+ };
72
+ }
73
+ //# sourceMappingURL=ai-pricing.controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-pricing.controller.js","sourceRoot":"","sources":["../../../src/features/ai-pricing/ai-pricing.controller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import type { PricingPlan } from "./pricing.plan.js";
2
+ import type { PriceAlias, PriceEntry, PriceRow, SyncState } from "./ai-pricing.types.js";
3
+ export declare function toPriceRow(r: Record<string, any>): PriceRow;
4
+ /** Persistence for platform-level AI prices. Not tenant-scoped by design. */
5
+ export declare class AiPricingModel {
6
+ private db;
7
+ constructor(db: any);
8
+ loadPrices(currentOnly?: boolean): Promise<PriceRow[]>;
9
+ loadAliases(): Promise<PriceAlias[]>;
10
+ getState(source: string): Promise<SyncState | null>;
11
+ /** Upsert only the fields present in *patch*; the rest keep their stored values. */
12
+ saveState(source: string, patch: Partial<Omit<SyncState, "source">>): Promise<void>;
13
+ pendingReviewKeys(): Promise<Set<string>>;
14
+ private insertEntries;
15
+ /**
16
+ * Apply a sync plan atomically. Returns false (nothing written) when another
17
+ * replica holds the sync lock.
18
+ */
19
+ applyPlan(plan: PricingPlan, sourceRef: string | null): Promise<boolean>;
20
+ /** Close the current row for this model/region (if any) and make *entry* current. */
21
+ setPrice(entry: PriceEntry, source: "manual" | "litellm", sourceRef: string | null, updatedBy: string | null): Promise<void>;
22
+ listCurrent(filter: {
23
+ provider?: string;
24
+ search?: string;
25
+ limit: number;
26
+ }): Promise<PriceRow[]>;
27
+ listReviews(status: string): Promise<Record<string, unknown>[]>;
28
+ getReview(id: string): Promise<Record<string, any> | null>;
29
+ closeReview(id: string, status: "applied" | "rejected", decidedBy: string | null): Promise<boolean>;
30
+ tryLockForReprice<T>(work: (tx: any) => Promise<T>): Promise<T | null>;
31
+ /** Unpriced rows with tokens, oldest first, after a keyset cursor. */
32
+ unpricedBatch(tx: any, since: Date, after: {
33
+ createdAt: Date;
34
+ id: string;
35
+ } | null, limit: number): Promise<any[]>;
36
+ saveRepriced(tx: any, id: string, update: {
37
+ models: unknown;
38
+ totalCost: number;
39
+ reason: string | null;
40
+ totalInputTokens: number;
41
+ totalTokens: number;
42
+ }): Promise<void>;
43
+ }
44
+ //# sourceMappingURL=ai-pricing.model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-pricing.model.d.ts","sourceRoot":"","sources":["../../../src/features/ai-pricing/ai-pricing.model.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAwBzF,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,QAAQ,CAiB3D;AAkBD,6EAA6E;AAC7E,qBAAa,cAAc;IACb,OAAO,CAAC,EAAE;gBAAF,EAAE,EAAE,GAAG;IAErB,UAAU,CAAC,WAAW,UAAO,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAKnD,WAAW,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IAKpC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAczD,oFAAoF;IAC9E,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBnF,iBAAiB,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAOjC,aAAa;IAgB3B;;;OAGG;IACG,SAAS,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC;IAyC9E,qFAAqF;IAC/E,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAY5H,WAAW,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAW/F,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAM/D,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;IAI1D,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,UAAU,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC;IAUnG,iBAAiB,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAO5E,sEAAsE;IAChE,aAAa,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE;QAAE,SAAS,EAAE,IAAI,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM;IAahG,YAAY,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE;QAC9C,MAAM,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;KAC1G,GAAG,OAAO,CAAC,IAAI,CAAC;CAQlB"}
@@ -0,0 +1,229 @@
1
+ import { sql } from "drizzle-orm";
2
+ const PRICING = sql.raw(`"meyiconnect"."ai_model_pricing"`);
3
+ const ALIASES = sql.raw(`"meyiconnect"."ai_model_aliases"`);
4
+ const STATE = sql.raw(`"meyiconnect"."ai_pricing_sync_state"`);
5
+ const REVIEW = sql.raw(`"meyiconnect"."ai_model_pricing_review"`);
6
+ const USAGE = sql.raw(`"meyiconnect"."ai_usage_log"`);
7
+ // A model's first row applies to all earlier usage too: there is no older price to
8
+ // prefer, and without it the re-pricing job could not price rows written before
9
+ // the model entered the table. Later changes take effect from now().
10
+ const FIRST_SEEN = new Date("1970-01-01T00:00:00Z");
11
+ // One sync/re-price at a time across replicas (transaction-scoped, so it is
12
+ // released on commit/rollback even if the process dies).
13
+ const LOCK = sql.raw(`pg_try_advisory_xact_lock(hashtext('ai_pricing_sync'))`);
14
+ function rowsOf(res) {
15
+ return res?.rows ?? (Array.isArray(res) ? res : []);
16
+ }
17
+ const num = (v) => (v === null || v === undefined ? null : Number(v));
18
+ const date = (v) => (v ? new Date(v) : null);
19
+ export function toPriceRow(r) {
20
+ return {
21
+ id: String(r.id),
22
+ provider: String(r.provider),
23
+ modelKey: String(r.model_key),
24
+ region: String(r.region),
25
+ inputPerMillion: Number(r.input_per_million),
26
+ outputPerMillion: Number(r.output_per_million),
27
+ cacheReadPerMillion: num(r.cache_read_per_million),
28
+ cacheWritePerMillion: num(r.cache_write_per_million),
29
+ currency: String(r.currency || "USD"),
30
+ effectiveFrom: new Date(r.effective_from),
31
+ effectiveTo: date(r.effective_to),
32
+ source: r.source,
33
+ sourceRef: r.source_ref ?? null,
34
+ verifiedAt: date(r.verified_at),
35
+ };
36
+ }
37
+ function entriesJson(entries) {
38
+ return JSON.stringify(entries.map((e) => ({
39
+ provider: e.provider,
40
+ model_key: e.modelKey,
41
+ region: e.region,
42
+ input_per_million: e.inputPerMillion,
43
+ output_per_million: e.outputPerMillion,
44
+ cache_read_per_million: e.cacheReadPerMillion,
45
+ cache_write_per_million: e.cacheWritePerMillion,
46
+ })));
47
+ }
48
+ function idsJson(ids) {
49
+ return sql `(SELECT jsonb_array_elements_text(${JSON.stringify(ids)}::jsonb)::uuid)`;
50
+ }
51
+ /** Persistence for platform-level AI prices. Not tenant-scoped by design. */
52
+ export class AiPricingModel {
53
+ db;
54
+ constructor(db) {
55
+ this.db = db;
56
+ }
57
+ async loadPrices(currentOnly = true) {
58
+ const where = currentOnly ? sql `WHERE effective_to IS NULL` : sql ``;
59
+ return rowsOf(await this.db.execute(sql `SELECT * FROM ${PRICING} ${where}`)).map(toPriceRow);
60
+ }
61
+ async loadAliases() {
62
+ return rowsOf(await this.db.execute(sql `SELECT provider, alias, model_key FROM ${ALIASES}`))
63
+ .map((r) => ({ provider: r.provider, alias: r.alias, modelKey: r.model_key }));
64
+ }
65
+ async getState(source) {
66
+ const r = rowsOf(await this.db.execute(sql `SELECT * FROM ${STATE} WHERE source = ${source}`))[0];
67
+ if (!r)
68
+ return null;
69
+ return {
70
+ source: r.source,
71
+ sourceRef: r.source_ref ?? null,
72
+ etag: r.etag ?? null,
73
+ lastCheckedAt: date(r.last_checked_at),
74
+ lastChangedAt: date(r.last_changed_at),
75
+ lastRepricedAt: date(r.last_repriced_at),
76
+ lastError: r.last_error ?? null,
77
+ };
78
+ }
79
+ /** Upsert only the fields present in *patch*; the rest keep their stored values. */
80
+ async saveState(source, patch) {
81
+ const columns = [
82
+ ["sourceRef", "source_ref"], ["etag", "etag"], ["lastCheckedAt", "last_checked_at"],
83
+ ["lastChangedAt", "last_changed_at"], ["lastRepricedAt", "last_repriced_at"], ["lastError", "last_error"],
84
+ ];
85
+ const present = columns.filter(([field]) => field in patch);
86
+ if (!present.length)
87
+ return;
88
+ const names = sql.raw(present.map(([, column]) => column).join(", "));
89
+ const values = sql.join(present.map(([field]) => sql `${patch[field] ?? null}`), sql `, `);
90
+ const updates = sql.raw(present.map(([, column]) => `${column} = EXCLUDED.${column}`).join(", "));
91
+ await this.db.execute(sql `
92
+ INSERT INTO ${STATE} (source, ${names}) VALUES (${source}, ${values})
93
+ ON CONFLICT (source) DO UPDATE SET ${updates}
94
+ `);
95
+ }
96
+ async pendingReviewKeys() {
97
+ const rows = rowsOf(await this.db.execute(sql `
98
+ SELECT provider, model_key, region FROM ${REVIEW} WHERE status = 'pending'
99
+ `));
100
+ return new Set(rows.map((r) => `${r.provider}|${r.model_key}|${r.region}`));
101
+ }
102
+ async insertEntries(tx, entries, source, sourceRef, effectiveFrom, updatedBy) {
103
+ if (!entries.length)
104
+ return;
105
+ await tx.execute(sql `
106
+ INSERT INTO ${PRICING}
107
+ (provider, model_key, region, input_per_million, output_per_million,
108
+ cache_read_per_million, cache_write_per_million, effective_from, source, source_ref, verified_at, updated_by)
109
+ SELECT x.provider, x.model_key, x.region, x.input_per_million, x.output_per_million,
110
+ x.cache_read_per_million, x.cache_write_per_million,
111
+ coalesce(${effectiveFrom}::timestamptz, now()), ${source}, ${sourceRef}, now(), ${updatedBy}
112
+ FROM jsonb_to_recordset(${entriesJson(entries)}::jsonb) AS x(
113
+ provider text, model_key text, region text, input_per_million numeric, output_per_million numeric,
114
+ cache_read_per_million numeric, cache_write_per_million numeric)
115
+ `);
116
+ }
117
+ /**
118
+ * Apply a sync plan atomically. Returns false (nothing written) when another
119
+ * replica holds the sync lock.
120
+ */
121
+ async applyPlan(plan, sourceRef) {
122
+ return this.db.transaction(async (tx) => {
123
+ const locked = rowsOf(await tx.execute(sql `SELECT ${LOCK} AS locked`))[0]?.locked;
124
+ if (!locked)
125
+ return false;
126
+ if (plan.verifies.length) {
127
+ await tx.execute(sql `
128
+ UPDATE ${PRICING} SET verified_at = now(), source_ref = ${sourceRef}
129
+ WHERE id IN ${idsJson(plan.verifies.map((r) => r.id))}
130
+ `);
131
+ }
132
+ if (plan.replaces.length) {
133
+ await tx.execute(sql `
134
+ UPDATE ${PRICING} SET effective_to = now()
135
+ WHERE id IN ${idsJson(plan.replaces.map((r) => r.current.id))} AND effective_to IS NULL
136
+ `);
137
+ await this.insertEntries(tx, plan.replaces.map((r) => r.next), "litellm", sourceRef, null, null);
138
+ }
139
+ await this.insertEntries(tx, plan.inserts, "litellm", sourceRef, FIRST_SEEN, null);
140
+ if (plan.reviews.length) {
141
+ await tx.execute(sql `
142
+ INSERT INTO ${REVIEW} (provider, model_key, region, current, proposed, reason, source_ref)
143
+ SELECT x.provider, x.model_key, x.region, x.current, x.proposed, x.reason, ${sourceRef}
144
+ FROM jsonb_to_recordset(${JSON.stringify(plan.reviews.map((r) => ({
145
+ provider: r.proposed.provider,
146
+ model_key: r.proposed.modelKey,
147
+ region: r.proposed.region,
148
+ current: r.current ? {
149
+ id: r.current.id, source: r.current.source,
150
+ inputPerMillion: r.current.inputPerMillion, outputPerMillion: r.current.outputPerMillion,
151
+ cacheReadPerMillion: r.current.cacheReadPerMillion, cacheWritePerMillion: r.current.cacheWritePerMillion,
152
+ } : null,
153
+ proposed: r.proposed,
154
+ reason: r.reason,
155
+ })))}::jsonb) AS x(provider text, model_key text, region text, current jsonb, proposed jsonb, reason text)
156
+ `);
157
+ }
158
+ return true;
159
+ });
160
+ }
161
+ /** Close the current row for this model/region (if any) and make *entry* current. */
162
+ async setPrice(entry, source, sourceRef, updatedBy) {
163
+ await this.db.transaction(async (tx) => {
164
+ const closed = rowsOf(await tx.execute(sql `
165
+ UPDATE ${PRICING} SET effective_to = now()
166
+ WHERE provider = ${entry.provider} AND model_key = ${entry.modelKey} AND region = ${entry.region}
167
+ AND effective_to IS NULL
168
+ RETURNING id
169
+ `));
170
+ await this.insertEntries(tx, [entry], source, sourceRef, closed.length ? null : FIRST_SEEN, updatedBy);
171
+ });
172
+ }
173
+ async listCurrent(filter) {
174
+ const parts = [sql `effective_to IS NULL`];
175
+ if (filter.provider)
176
+ parts.push(sql `provider = ${filter.provider}`);
177
+ if (filter.search)
178
+ parts.push(sql `model_key ILIKE ${`%${filter.search.replace(/[\\%_]/g, (c) => `\\${c}`)}%`}`);
179
+ return rowsOf(await this.db.execute(sql `
180
+ SELECT * FROM ${PRICING} WHERE ${sql.join(parts, sql ` AND `)}
181
+ ORDER BY provider, model_key, region
182
+ LIMIT ${filter.limit}
183
+ `)).map(toPriceRow);
184
+ }
185
+ async listReviews(status) {
186
+ return rowsOf(await this.db.execute(sql `
187
+ SELECT * FROM ${REVIEW} WHERE status = ${status} ORDER BY created_at DESC LIMIT 500
188
+ `));
189
+ }
190
+ async getReview(id) {
191
+ return rowsOf(await this.db.execute(sql `SELECT * FROM ${REVIEW} WHERE id = ${id}::uuid`))[0] ?? null;
192
+ }
193
+ async closeReview(id, status, decidedBy) {
194
+ const rows = rowsOf(await this.db.execute(sql `
195
+ UPDATE ${REVIEW} SET status = ${status}, decided_by = ${decidedBy}, decided_at = now()
196
+ WHERE id = ${id}::uuid AND status = 'pending' RETURNING id
197
+ `));
198
+ return rows.length > 0;
199
+ }
200
+ // ── re-pricing ─────────────────────────────────────────────────────────────
201
+ async tryLockForReprice(work) {
202
+ return this.db.transaction(async (tx) => {
203
+ const locked = rowsOf(await tx.execute(sql `SELECT ${LOCK} AS locked`))[0]?.locked;
204
+ return locked ? work(tx) : null;
205
+ });
206
+ }
207
+ /** Unpriced rows with tokens, oldest first, after a keyset cursor. */
208
+ async unpricedBatch(tx, since, after, limit) {
209
+ const cursor = after
210
+ ? sql `AND (created_at, id) > (${after.createdAt}::timestamptz, ${after.id}::uuid)`
211
+ : sql ``;
212
+ return rowsOf(await tx.execute(sql `
213
+ SELECT id, models, created_at, total_output_tokens, total_cache_read_tokens, total_cache_write_tokens
214
+ FROM ${USAGE}
215
+ WHERE priced = false AND created_at >= ${since} AND total_tokens > 0 ${cursor}
216
+ ORDER BY created_at, id
217
+ LIMIT ${limit}
218
+ `));
219
+ }
220
+ async saveRepriced(tx, id, update) {
221
+ await tx.execute(sql `
222
+ UPDATE ${USAGE}
223
+ SET models = ${JSON.stringify(update.models)}::jsonb, total_cost = ${update.totalCost}, priced = true,
224
+ reason = ${update.reason}, total_input_tokens = ${update.totalInputTokens}, total_tokens = ${update.totalTokens}
225
+ WHERE id = ${id}::uuid AND priced = false
226
+ `);
227
+ }
228
+ }
229
+ //# sourceMappingURL=ai-pricing.model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-pricing.model.js","sourceRoot":"","sources":["../../../src/features/ai-pricing/ai-pricing.model.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { Router } from "express";
2
+ import type { AiPricingService } from "./ai-pricing.service.js";
3
+ /**
4
+ * GET / current list prices + sync status (tenant admins; role "user" is blocked in routes/index.ts)
5
+ * GET /review price changes held for review (platform admins)
6
+ * POST /review/:id { action: "apply" | "reject" } (platform admins)
7
+ * PUT /manual set a manual price (always wins over the sync) (platform admins)
8
+ */
9
+ export declare function createAiPricingRouter(service: AiPricingService): Router;
10
+ //# sourceMappingURL=ai-pricing.routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-pricing.routes.d.ts","sourceRoot":"","sources":["../../../src/features/ai-pricing/ai-pricing.routes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAUvE"}
@@ -0,0 +1,18 @@
1
+ import { Router } from "express";
2
+ import { createAiPricingController } from "./ai-pricing.controller.js";
3
+ /**
4
+ * GET / current list prices + sync status (tenant admins; role "user" is blocked in routes/index.ts)
5
+ * GET /review price changes held for review (platform admins)
6
+ * POST /review/:id { action: "apply" | "reject" } (platform admins)
7
+ * PUT /manual set a manual price (always wins over the sync) (platform admins)
8
+ */
9
+ export function createAiPricingRouter(service) {
10
+ const router = Router();
11
+ const ctrl = createAiPricingController(service);
12
+ router.get("/", ctrl.list.bind(ctrl));
13
+ router.get("/review", ctrl.reviews.bind(ctrl));
14
+ router.post("/review/:id", ctrl.decide.bind(ctrl));
15
+ router.put("/manual", ctrl.setManual.bind(ctrl));
16
+ return router;
17
+ }
18
+ //# sourceMappingURL=ai-pricing.routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-pricing.routes.js","sourceRoot":"","sources":["../../../src/features/ai-pricing/ai-pricing.routes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAGvE;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAyB;IAC7D,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;IACxB,MAAM,IAAI,GAAG,yBAAyB,CAAC,OAAO,CAAC,CAAC;IAEhD,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACtC,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/C,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACnD,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAEjD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { AiPricingModel } from "./ai-pricing.model.js";
2
+ import { type PriceEntry, type PriceRow, type SyncState } from "./ai-pricing.types.js";
3
+ /** Validates a manual price from a platform admin. */
4
+ export declare function parseManualPrice(body: Record<string, unknown>): {
5
+ entry: PriceEntry;
6
+ sourceRef: string | null;
7
+ };
8
+ export declare class AiPricingService {
9
+ private model;
10
+ constructor(model: AiPricingModel);
11
+ list(filter: {
12
+ provider?: string;
13
+ search?: string;
14
+ limit?: number;
15
+ }): Promise<{
16
+ prices: PriceRow[];
17
+ sync: SyncState | null;
18
+ }>;
19
+ listReviews(status?: string): Promise<Record<string, unknown>[]>;
20
+ decideReview(id: string, action: string, decidedBy: string | null): Promise<void>;
21
+ setManual(body: Record<string, unknown>, updatedBy: string | null): Promise<void>;
22
+ }
23
+ //# sourceMappingURL=ai-pricing.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-pricing.service.d.ts","sourceRoot":"","sources":["../../../src/features/ai-pricing/ai-pricing.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAkB,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAevG,sDAAsD;AACtD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAoB/G;AAED,qBAAa,gBAAgB;IACf,OAAO,CAAC,KAAK;gBAAL,KAAK,EAAE,cAAc;IAEnC,IAAI,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;QAAC,IAAI,EAAE,SAAS,GAAG,IAAI,CAAA;KAAE,CAAC;IASnI,WAAW,CAAC,MAAM,SAAY;IAKxB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBjF,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;CAIxF"}
@@ -0,0 +1,87 @@
1
+ import { AiPricingError } from "./ai-pricing.types.js";
2
+ const PROVIDERS = new Set(["bedrock", "openai", "anthropic", "gemini", "meyi_model"]);
3
+ const REGION = /^(\*|[a-z]{2}(-gov)?-[a-z]+-\d)$/;
4
+ function rate(value, field, required) {
5
+ if (value === undefined || value === null || value === "") {
6
+ if (required)
7
+ throw new AiPricingError(`${field} is required`);
8
+ return null;
9
+ }
10
+ const n = Number(value);
11
+ if (!Number.isFinite(n) || n < 0 || n > 1_000_000)
12
+ throw new AiPricingError(`${field} must be a non-negative number`);
13
+ return Math.round(n * 1e6) / 1e6;
14
+ }
15
+ /** Validates a manual price from a platform admin. */
16
+ export function parseManualPrice(body) {
17
+ const provider = String(body.provider ?? "").trim().toLowerCase();
18
+ if (!PROVIDERS.has(provider))
19
+ throw new AiPricingError(`provider must be one of ${[...PROVIDERS].join(", ")}`);
20
+ const modelKey = String(body.modelKey ?? "").trim();
21
+ if (!modelKey || modelKey.length > 200)
22
+ throw new AiPricingError("modelKey is required (max 200 characters)");
23
+ const region = String(body.region ?? "*").trim() || "*";
24
+ if (!REGION.test(region))
25
+ throw new AiPricingError("region must be '*' or an AWS region code");
26
+ const sourceRef = body.sourceRef === undefined || body.sourceRef === null ? null : String(body.sourceRef).slice(0, 500);
27
+ return {
28
+ entry: {
29
+ provider,
30
+ modelKey,
31
+ region,
32
+ inputPerMillion: rate(body.inputPerMillion, "inputPerMillion", true),
33
+ outputPerMillion: rate(body.outputPerMillion, "outputPerMillion", true),
34
+ cacheReadPerMillion: rate(body.cacheReadPerMillion, "cacheReadPerMillion", false),
35
+ cacheWritePerMillion: rate(body.cacheWritePerMillion, "cacheWritePerMillion", false),
36
+ },
37
+ sourceRef,
38
+ };
39
+ }
40
+ export class AiPricingService {
41
+ model;
42
+ constructor(model) {
43
+ this.model = model;
44
+ }
45
+ async list(filter) {
46
+ const limit = Math.min(Math.max(Number(filter.limit) || 500, 1), 2000);
47
+ const [prices, sync] = await Promise.all([
48
+ this.model.listCurrent({ provider: filter.provider?.toLowerCase(), search: filter.search, limit }),
49
+ this.model.getState("litellm"),
50
+ ]);
51
+ return { prices, sync };
52
+ }
53
+ listReviews(status = "pending") {
54
+ if (!["pending", "applied", "rejected"].includes(status))
55
+ throw new AiPricingError("status is invalid");
56
+ return this.model.listReviews(status);
57
+ }
58
+ async decideReview(id, action, decidedBy) {
59
+ if (!/^[0-9a-f-]{36}$/i.test(id))
60
+ throw new AiPricingError("review id is invalid");
61
+ if (action !== "apply" && action !== "reject")
62
+ throw new AiPricingError("action must be apply or reject");
63
+ const review = await this.model.getReview(id);
64
+ if (!review)
65
+ throw new AiPricingError("review not found", 404);
66
+ if (review.status !== "pending")
67
+ throw new AiPricingError("review already decided", 409);
68
+ if (action === "apply") {
69
+ const p = review.proposed;
70
+ await this.model.setPrice({
71
+ provider: review.provider,
72
+ modelKey: review.model_key,
73
+ region: review.region,
74
+ inputPerMillion: Number(p.inputPerMillion),
75
+ outputPerMillion: Number(p.outputPerMillion),
76
+ cacheReadPerMillion: p.cacheReadPerMillion ?? null,
77
+ cacheWritePerMillion: p.cacheWritePerMillion ?? null,
78
+ }, "litellm", review.source_ref ?? null, decidedBy);
79
+ }
80
+ await this.model.closeReview(id, action === "apply" ? "applied" : "rejected", decidedBy);
81
+ }
82
+ async setManual(body, updatedBy) {
83
+ const { entry, sourceRef } = parseManualPrice(body);
84
+ await this.model.setPrice(entry, "manual", sourceRef, updatedBy);
85
+ }
86
+ }
87
+ //# sourceMappingURL=ai-pricing.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-pricing.service.js","sourceRoot":"","sources":["../../../src/features/ai-pricing/ai-pricing.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import type { AiPricingModel } from "./ai-pricing.model.js";
2
+ import type { SyncMode, SyncResult } from "./ai-pricing.types.js";
3
+ export declare const DEFAULT_LITELLM_REF = "159b5b2f7243bba757c08e4ddc45a2514e4cd037";
4
+ export interface SyncConfig {
5
+ mode: SyncMode;
6
+ ref: string;
7
+ url: string;
8
+ timeoutMs: number;
9
+ /** Uncompressed body limit; the file is ~3 MB today. */
10
+ maxBytes: number;
11
+ /** Relative change above which a price goes to review instead of applying. */
12
+ threshold: number;
13
+ }
14
+ export declare function syncConfigFromEnv(env?: NodeJS.ProcessEnv): SyncConfig;
15
+ type FetchLike = (url: string, init: {
16
+ headers: Record<string, string>;
17
+ signal: AbortSignal;
18
+ }) => Promise<{
19
+ status: number;
20
+ ok: boolean;
21
+ headers: {
22
+ get(name: string): string | null;
23
+ };
24
+ body: ReadableStream<Uint8Array> | null;
25
+ }>;
26
+ /** GET the price file with gzip + ETag, a timeout and a size cap. */
27
+ export declare function fetchPriceFile(config: Pick<SyncConfig, "url" | "timeoutMs" | "maxBytes">, etag: string | null, fetchImpl?: FetchLike): Promise<{
28
+ notModified: true;
29
+ } | {
30
+ notModified: false;
31
+ data: unknown;
32
+ etag: string | null;
33
+ }>;
34
+ type Logger = {
35
+ info?: (m: string) => void;
36
+ warn?: (m: string) => void;
37
+ error?: (e: any, m: string) => void;
38
+ };
39
+ export declare class AiPricingSync {
40
+ private model;
41
+ private config;
42
+ private logger;
43
+ private fetchImpl?;
44
+ constructor(model: AiPricingModel, config: SyncConfig, logger?: Logger, fetchImpl?: FetchLike);
45
+ run(): Promise<SyncResult>;
46
+ }
47
+ export {};
48
+ //# sourceMappingURL=ai-pricing.sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-pricing.sync.d.ts","sourceRoot":"","sources":["../../../src/features/ai-pricing/ai-pricing.sync.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAKlE,eAAO,MAAM,mBAAmB,6CAA6C,CAAC;AAG9E,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,QAAQ,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,UAAU,CAalF;AAED,KAAK,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,KAAK,OAAO,CAAC;IACxG,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE;QAAE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAC9C,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC;CACzC,CAAC,CAAC;AAEH,qEAAqE;AACrE,wBAAsB,cAAc,CAClC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,KAAK,GAAG,WAAW,GAAG,UAAU,CAAC,EAC1D,IAAI,EAAE,MAAM,GAAG,IAAI,EACnB,SAAS,GAAE,SAAyC,GACnD,OAAO,CAAC;IAAE,WAAW,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,WAAW,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAuB7F;AAED,KAAK,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,CAAC;AAE9G,qBAAa,aAAa;IAEtB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,SAAS,CAAC;gBAHV,KAAK,EAAE,cAAc,EACrB,MAAM,EAAE,UAAU,EAClB,MAAM,GAAE,MAAuB,EAC/B,SAAS,CAAC,EAAE,SAAS;IAGzB,GAAG,IAAI,OAAO,CAAC,UAAU,CAAC;CA2DjC"}