@apart-tech/intelligence-core 1.21.1 → 1.22.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 (57) hide show
  1. package/dist/db/tenant.d.ts.map +1 -1
  2. package/dist/db/tenant.js +2 -0
  3. package/dist/db/tenant.js.map +1 -1
  4. package/dist/index.d.ts +7 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +3 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/services/__tests__/search-service.test.js +3 -0
  9. package/dist/services/__tests__/search-service.test.js.map +1 -1
  10. package/dist/services/chat-capture-service.d.ts +6 -2
  11. package/dist/services/chat-capture-service.d.ts.map +1 -1
  12. package/dist/services/chat-capture-service.js +56 -20
  13. package/dist/services/chat-capture-service.js.map +1 -1
  14. package/dist/services/chat-context-service.d.ts.map +1 -1
  15. package/dist/services/chat-context-service.js +5 -0
  16. package/dist/services/chat-context-service.js.map +1 -1
  17. package/dist/services/chat-cost-limit-service.d.ts +44 -0
  18. package/dist/services/chat-cost-limit-service.d.ts.map +1 -0
  19. package/dist/services/chat-cost-limit-service.js +95 -0
  20. package/dist/services/chat-cost-limit-service.js.map +1 -0
  21. package/dist/services/chat-cost-limit-service.test.d.ts +2 -0
  22. package/dist/services/chat-cost-limit-service.test.d.ts.map +1 -0
  23. package/dist/services/chat-cost-limit-service.test.js +184 -0
  24. package/dist/services/chat-cost-limit-service.test.js.map +1 -0
  25. package/dist/services/chat-usage-service.d.ts +15 -0
  26. package/dist/services/chat-usage-service.d.ts.map +1 -1
  27. package/dist/services/chat-usage-service.js +65 -0
  28. package/dist/services/chat-usage-service.js.map +1 -1
  29. package/dist/services/dedup-service.d.ts +132 -0
  30. package/dist/services/dedup-service.d.ts.map +1 -0
  31. package/dist/services/dedup-service.js +434 -0
  32. package/dist/services/dedup-service.js.map +1 -0
  33. package/dist/services/dedup-service.test.d.ts +2 -0
  34. package/dist/services/dedup-service.test.d.ts.map +1 -0
  35. package/dist/services/dedup-service.test.js +160 -0
  36. package/dist/services/dedup-service.test.js.map +1 -0
  37. package/dist/services/edge-service.d.ts +1 -0
  38. package/dist/services/edge-service.d.ts.map +1 -1
  39. package/dist/services/edge-service.js +9 -0
  40. package/dist/services/edge-service.js.map +1 -1
  41. package/dist/services/model-pricing-service.d.ts +39 -0
  42. package/dist/services/model-pricing-service.d.ts.map +1 -0
  43. package/dist/services/model-pricing-service.js +96 -0
  44. package/dist/services/model-pricing-service.js.map +1 -0
  45. package/dist/services/model-pricing-service.test.d.ts +2 -0
  46. package/dist/services/model-pricing-service.test.d.ts.map +1 -0
  47. package/dist/services/model-pricing-service.test.js +134 -0
  48. package/dist/services/model-pricing-service.test.js.map +1 -0
  49. package/dist/services/org-llm-provider-service.d.ts +1 -1
  50. package/dist/services/org-llm-provider-service.d.ts.map +1 -1
  51. package/dist/services/search-service.d.ts.map +1 -1
  52. package/dist/services/search-service.js +34 -2
  53. package/dist/services/search-service.js.map +1 -1
  54. package/dist/types/index.d.ts +7 -0
  55. package/dist/types/index.d.ts.map +1 -1
  56. package/package.json +1 -1
  57. package/prisma/schema.prisma +61 -4
@@ -0,0 +1,184 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { ChatCostLimitService } from "./chat-cost-limit-service.js";
3
+ const ORG_ID = "org-1";
4
+ const USER_ID = "user-1";
5
+ function makeMockChatUsageSvc(totalCostCents = 0) {
6
+ return {
7
+ getUserUsage: vi.fn(async () => ({
8
+ totalMessages: 10,
9
+ totalTokensIn: 1000,
10
+ totalTokensOut: 500,
11
+ totalCostCents,
12
+ })),
13
+ };
14
+ }
15
+ function makeMockDb(opts) {
16
+ return {
17
+ chatUserCostLimit: {
18
+ findUnique: vi.fn(async () => opts?.userLimit ?? null),
19
+ upsert: vi.fn(async ({ create }) => ({
20
+ id: "limit-1",
21
+ ...create,
22
+ updatedAt: new Date(),
23
+ })),
24
+ deleteMany: vi.fn(async () => ({ count: 1 })),
25
+ findMany: vi.fn(async () => opts?.userLimit
26
+ ? [{ id: "limit-1", organizationId: ORG_ID, userId: USER_ID, ...opts.userLimit, updatedAt: new Date(), updatedBy: "admin" }]
27
+ : []),
28
+ },
29
+ organization: {
30
+ findUnique: vi.fn(async () => ({
31
+ metadata: opts?.orgMetadata ?? {},
32
+ })),
33
+ },
34
+ };
35
+ }
36
+ describe("ChatCostLimitService", () => {
37
+ describe("getEffectiveLimit", () => {
38
+ it("returns null when no config exists (unlimited)", async () => {
39
+ const db = makeMockDb();
40
+ const usageSvc = makeMockChatUsageSvc();
41
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
42
+ const svc = new ChatCostLimitService(db, usageSvc);
43
+ const limit = await svc.getEffectiveLimit(ORG_ID, USER_ID);
44
+ expect(limit).toBeNull();
45
+ });
46
+ it("returns org default when no user override exists", async () => {
47
+ const db = makeMockDb({
48
+ orgMetadata: { chatLimits: { defaultUserCostCentsPerMonth: 500 } },
49
+ });
50
+ const usageSvc = makeMockChatUsageSvc();
51
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
52
+ const svc = new ChatCostLimitService(db, usageSvc);
53
+ const limit = await svc.getEffectiveLimit(ORG_ID, USER_ID);
54
+ expect(limit).toBe(500);
55
+ });
56
+ it("returns user override when it exists (takes precedence over org default)", async () => {
57
+ const db = makeMockDb({
58
+ userLimit: { costLimitCents: 1000 },
59
+ orgMetadata: { chatLimits: { defaultUserCostCentsPerMonth: 500 } },
60
+ });
61
+ const usageSvc = makeMockChatUsageSvc();
62
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
63
+ const svc = new ChatCostLimitService(db, usageSvc);
64
+ const limit = await svc.getEffectiveLimit(ORG_ID, USER_ID);
65
+ expect(limit).toBe(1000);
66
+ });
67
+ });
68
+ describe("checkChatCostQuota", () => {
69
+ it("allows when no limit is set", async () => {
70
+ const db = makeMockDb();
71
+ const usageSvc = makeMockChatUsageSvc(200);
72
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
+ const svc = new ChatCostLimitService(db, usageSvc);
74
+ const result = await svc.checkChatCostQuota(ORG_ID, USER_ID);
75
+ expect(result.allowed).toBe(true);
76
+ expect(result.warning).toBe(false);
77
+ expect(result.limitCents).toBeNull();
78
+ expect(result.currentCostCents).toBe(200);
79
+ });
80
+ it("allows when under limit with no warning", async () => {
81
+ const db = makeMockDb({
82
+ orgMetadata: { chatLimits: { defaultUserCostCentsPerMonth: 500 } },
83
+ });
84
+ const usageSvc = makeMockChatUsageSvc(200); // 200/500 = 40%
85
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
86
+ const svc = new ChatCostLimitService(db, usageSvc);
87
+ const result = await svc.checkChatCostQuota(ORG_ID, USER_ID);
88
+ expect(result.allowed).toBe(true);
89
+ expect(result.warning).toBe(false);
90
+ expect(result.limitCents).toBe(500);
91
+ });
92
+ it("warns at 80% threshold", async () => {
93
+ const db = makeMockDb({
94
+ orgMetadata: { chatLimits: { defaultUserCostCentsPerMonth: 500 } },
95
+ });
96
+ const usageSvc = makeMockChatUsageSvc(400); // 400/500 = 80%
97
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
98
+ const svc = new ChatCostLimitService(db, usageSvc);
99
+ const result = await svc.checkChatCostQuota(ORG_ID, USER_ID);
100
+ expect(result.allowed).toBe(true);
101
+ expect(result.warning).toBe(true);
102
+ expect(result.currentCostCents).toBe(400);
103
+ });
104
+ it("blocks at 100%", async () => {
105
+ const db = makeMockDb({
106
+ orgMetadata: { chatLimits: { defaultUserCostCentsPerMonth: 500 } },
107
+ });
108
+ const usageSvc = makeMockChatUsageSvc(500); // 500/500 = 100%
109
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
110
+ const svc = new ChatCostLimitService(db, usageSvc);
111
+ const result = await svc.checkChatCostQuota(ORG_ID, USER_ID);
112
+ expect(result.allowed).toBe(false);
113
+ expect(result.warning).toBe(true);
114
+ expect(result.limitCents).toBe(500);
115
+ });
116
+ it("blocks when over limit", async () => {
117
+ const db = makeMockDb({
118
+ orgMetadata: { chatLimits: { defaultUserCostCentsPerMonth: 500 } },
119
+ });
120
+ const usageSvc = makeMockChatUsageSvc(600); // 600/500 = 120%
121
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
122
+ const svc = new ChatCostLimitService(db, usageSvc);
123
+ const result = await svc.checkChatCostQuota(ORG_ID, USER_ID);
124
+ expect(result.allowed).toBe(false);
125
+ expect(result.warning).toBe(true);
126
+ });
127
+ it("uses user override for limit check", async () => {
128
+ const db = makeMockDb({
129
+ userLimit: { costLimitCents: 1000 },
130
+ orgMetadata: { chatLimits: { defaultUserCostCentsPerMonth: 500 } },
131
+ });
132
+ const usageSvc = makeMockChatUsageSvc(600); // 600/1000 = 60%, under 80%
133
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
134
+ const svc = new ChatCostLimitService(db, usageSvc);
135
+ const result = await svc.checkChatCostQuota(ORG_ID, USER_ID);
136
+ expect(result.allowed).toBe(true);
137
+ expect(result.warning).toBe(false);
138
+ expect(result.limitCents).toBe(1000);
139
+ });
140
+ });
141
+ describe("setUserLimit", () => {
142
+ it("upserts a per-user limit", async () => {
143
+ const db = makeMockDb();
144
+ const usageSvc = makeMockChatUsageSvc();
145
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
146
+ const svc = new ChatCostLimitService(db, usageSvc);
147
+ const result = await svc.setUserLimit(ORG_ID, USER_ID, 1000, "admin@test.com");
148
+ expect(result.costLimitCents).toBe(1000);
149
+ expect(db.chatUserCostLimit.upsert).toHaveBeenCalled();
150
+ });
151
+ });
152
+ describe("removeUserLimit", () => {
153
+ it("deletes the per-user limit", async () => {
154
+ const db = makeMockDb({ userLimit: { costLimitCents: 1000 } });
155
+ const usageSvc = makeMockChatUsageSvc();
156
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
157
+ const svc = new ChatCostLimitService(db, usageSvc);
158
+ await svc.removeUserLimit(ORG_ID, USER_ID);
159
+ expect(db.chatUserCostLimit.deleteMany).toHaveBeenCalledWith({
160
+ where: { organizationId: ORG_ID, userId: USER_ID },
161
+ });
162
+ });
163
+ });
164
+ describe("listUserLimits", () => {
165
+ it("returns all overrides for org", async () => {
166
+ const db = makeMockDb({ userLimit: { costLimitCents: 1000 } });
167
+ const usageSvc = makeMockChatUsageSvc();
168
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
169
+ const svc = new ChatCostLimitService(db, usageSvc);
170
+ const limits = await svc.listUserLimits(ORG_ID);
171
+ expect(limits).toHaveLength(1);
172
+ expect(limits[0].costLimitCents).toBe(1000);
173
+ });
174
+ it("returns empty array when no overrides", async () => {
175
+ const db = makeMockDb();
176
+ const usageSvc = makeMockChatUsageSvc();
177
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
178
+ const svc = new ChatCostLimitService(db, usageSvc);
179
+ const limits = await svc.listUserLimits(ORG_ID);
180
+ expect(limits).toHaveLength(0);
181
+ });
182
+ });
183
+ });
184
+ //# sourceMappingURL=chat-cost-limit-service.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-cost-limit-service.test.js","sourceRoot":"","sources":["../../src/services/chat-cost-limit-service.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAc,MAAM,QAAQ,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAGpE,MAAM,MAAM,GAAG,OAAO,CAAC;AACvB,MAAM,OAAO,GAAG,QAAQ,CAAC;AAEzB,SAAS,oBAAoB,CAAC,cAAc,GAAG,CAAC;IAC9C,OAAO;QACL,YAAY,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;YAC/B,aAAa,EAAE,EAAE;YACjB,aAAa,EAAE,IAAI;YACnB,cAAc,EAAE,GAAG;YACnB,cAAc;SACf,CAAC,CAAC;KAC2B,CAAC;AACnC,CAAC;AAED,SAAS,UAAU,CAAC,IAGnB;IACC,OAAO;QACL,iBAAiB,EAAE;YACjB,UAAU,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,IAAI,EAAE,SAAS,IAAI,IAAI,CAAC;YACtD,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE,MAAM,EAAuC,EAAE,EAAE,CAAC,CAAC;gBACxE,EAAE,EAAE,SAAS;gBACb,GAAG,MAAM;gBACT,SAAS,EAAE,IAAI,IAAI,EAAE;aACtB,CAAC,CAAC;YACH,UAAU,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;YAC7C,QAAQ,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CACzB,IAAI,EAAE,SAAS;gBACb,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;gBAC5H,CAAC,CAAC,EAAE,CACP;SACF;QACD,YAAY,EAAE;YACZ,UAAU,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;gBAC7B,QAAQ,EAAE,IAAI,EAAE,WAAW,IAAI,EAAE;aAClC,CAAC,CAAC;SACJ;KACF,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;QACjC,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;YAC9D,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;YACxB,MAAM,QAAQ,GAAG,oBAAoB,EAAE,CAAC;YACxC,8DAA8D;YAC9D,MAAM,GAAG,GAAG,IAAI,oBAAoB,CAAC,EAAS,EAAE,QAAQ,CAAC,CAAC;YAE1D,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,iBAAiB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC3D,MAAM,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC3B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;YAChE,MAAM,EAAE,GAAG,UAAU,CAAC;gBACpB,WAAW,EAAE,EAAE,UAAU,EAAE,EAAE,4BAA4B,EAAE,GAAG,EAAE,EAAE;aACnE,CAAC,CAAC;YACH,MAAM,QAAQ,GAAG,oBAAoB,EAAE,CAAC;YACxC,8DAA8D;YAC9D,MAAM,GAAG,GAAG,IAAI,oBAAoB,CAAC,EAAS,EAAE,QAAQ,CAAC,CAAC;YAE1D,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,iBAAiB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC3D,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0EAA0E,EAAE,KAAK,IAAI,EAAE;YACxF,MAAM,EAAE,GAAG,UAAU,CAAC;gBACpB,SAAS,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE;gBACnC,WAAW,EAAE,EAAE,UAAU,EAAE,EAAE,4BAA4B,EAAE,GAAG,EAAE,EAAE;aACnE,CAAC,CAAC;YACH,MAAM,QAAQ,GAAG,oBAAoB,EAAE,CAAC;YACxC,8DAA8D;YAC9D,MAAM,GAAG,GAAG,IAAI,oBAAoB,CAAC,EAAS,EAAE,QAAQ,CAAC,CAAC;YAE1D,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,iBAAiB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC3D,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAClC,EAAE,CAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;YAC3C,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;YACxB,MAAM,QAAQ,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC;YAC3C,8DAA8D;YAC9D,MAAM,GAAG,GAAG,IAAI,oBAAoB,CAAC,EAAS,EAAE,QAAQ,CAAC,CAAC;YAE1D,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC7D,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,QAAQ,EAAE,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;YACvD,MAAM,EAAE,GAAG,UAAU,CAAC;gBACpB,WAAW,EAAE,EAAE,UAAU,EAAE,EAAE,4BAA4B,EAAE,GAAG,EAAE,EAAE;aACnE,CAAC,CAAC;YACH,MAAM,QAAQ,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,gBAAgB;YAC5D,8DAA8D;YAC9D,MAAM,GAAG,GAAG,IAAI,oBAAoB,CAAC,EAAS,EAAE,QAAQ,CAAC,CAAC;YAE1D,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC7D,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wBAAwB,EAAE,KAAK,IAAI,EAAE;YACtC,MAAM,EAAE,GAAG,UAAU,CAAC;gBACpB,WAAW,EAAE,EAAE,UAAU,EAAE,EAAE,4BAA4B,EAAE,GAAG,EAAE,EAAE;aACnE,CAAC,CAAC;YACH,MAAM,QAAQ,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,gBAAgB;YAC5D,8DAA8D;YAC9D,MAAM,GAAG,GAAG,IAAI,oBAAoB,CAAC,EAAS,EAAE,QAAQ,CAAC,CAAC;YAE1D,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC7D,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gBAAgB,EAAE,KAAK,IAAI,EAAE;YAC9B,MAAM,EAAE,GAAG,UAAU,CAAC;gBACpB,WAAW,EAAE,EAAE,UAAU,EAAE,EAAE,4BAA4B,EAAE,GAAG,EAAE,EAAE;aACnE,CAAC,CAAC;YACH,MAAM,QAAQ,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,iBAAiB;YAC7D,8DAA8D;YAC9D,MAAM,GAAG,GAAG,IAAI,oBAAoB,CAAC,EAAS,EAAE,QAAQ,CAAC,CAAC;YAE1D,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC7D,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wBAAwB,EAAE,KAAK,IAAI,EAAE;YACtC,MAAM,EAAE,GAAG,UAAU,CAAC;gBACpB,WAAW,EAAE,EAAE,UAAU,EAAE,EAAE,4BAA4B,EAAE,GAAG,EAAE,EAAE;aACnE,CAAC,CAAC;YACH,MAAM,QAAQ,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,iBAAiB;YAC7D,8DAA8D;YAC9D,MAAM,GAAG,GAAG,IAAI,oBAAoB,CAAC,EAAS,EAAE,QAAQ,CAAC,CAAC;YAE1D,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC7D,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;YAClD,MAAM,EAAE,GAAG,UAAU,CAAC;gBACpB,SAAS,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE;gBACnC,WAAW,EAAE,EAAE,UAAU,EAAE,EAAE,4BAA4B,EAAE,GAAG,EAAE,EAAE;aACnE,CAAC,CAAC;YACH,MAAM,QAAQ,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,4BAA4B;YACxE,8DAA8D;YAC9D,MAAM,GAAG,GAAG,IAAI,oBAAoB,CAAC,EAAS,EAAE,QAAQ,CAAC,CAAC;YAE1D,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC7D,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;QAC5B,EAAE,CAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;YACxC,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;YACxB,MAAM,QAAQ,GAAG,oBAAoB,EAAE,CAAC;YACxC,8DAA8D;YAC9D,MAAM,GAAG,GAAG,IAAI,oBAAoB,CAAC,EAAS,EAAE,QAAQ,CAAC,CAAC;YAE1D,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC;YAC/E,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACzC,MAAM,CAAC,EAAE,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,gBAAgB,EAAE,CAAC;QACzD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;QAC/B,EAAE,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;YAC1C,MAAM,EAAE,GAAG,UAAU,CAAC,EAAE,SAAS,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;YAC/D,MAAM,QAAQ,GAAG,oBAAoB,EAAE,CAAC;YACxC,8DAA8D;YAC9D,MAAM,GAAG,GAAG,IAAI,oBAAoB,CAAC,EAAS,EAAE,QAAQ,CAAC,CAAC;YAE1D,MAAM,GAAG,CAAC,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC3C,MAAM,CAAC,EAAE,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC,oBAAoB,CAAC;gBAC3D,KAAK,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE;aACnD,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;QAC9B,EAAE,CAAC,+BAA+B,EAAE,KAAK,IAAI,EAAE;YAC7C,MAAM,EAAE,GAAG,UAAU,CAAC,EAAE,SAAS,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;YAC/D,MAAM,QAAQ,GAAG,oBAAoB,EAAE,CAAC;YACxC,8DAA8D;YAC9D,MAAM,GAAG,GAAG,IAAI,oBAAoB,CAAC,EAAS,EAAE,QAAQ,CAAC,CAAC;YAE1D,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;YAChD,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC/B,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;YACrD,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;YACxB,MAAM,QAAQ,GAAG,oBAAoB,EAAE,CAAC;YACxC,8DAA8D;YAC9D,MAAM,GAAG,GAAG,IAAI,oBAAoB,CAAC,EAAS,EAAE,QAAQ,CAAC,CAAC;YAE1D,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;YAChD,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -11,6 +11,13 @@ export interface ChatUsageSummary {
11
11
  totalTokensOut: number;
12
12
  totalCostCents: number;
13
13
  }
14
+ export interface ChatModelUsage {
15
+ model: string;
16
+ totalMessages: number;
17
+ totalTokensIn: number;
18
+ totalTokensOut: number;
19
+ totalCostCents: number;
20
+ }
14
21
  /**
15
22
  * Extends UsageService with chat-specific metrics. Uses the existing
16
23
  * incrementMetric() pattern from UsageService to track:
@@ -32,5 +39,13 @@ export declare class ChatUsageService {
32
39
  start: Date;
33
40
  end: Date;
34
41
  }): Promise<ChatUsageSummary>;
42
+ getUserUsageByModel(organizationId: string, userId: string, period?: {
43
+ start: Date;
44
+ end: Date;
45
+ }): Promise<ChatModelUsage[]>;
46
+ getOrgUsageByModel(organizationId: string, period?: {
47
+ start: Date;
48
+ end: Date;
49
+ }): Promise<ChatModelUsage[]>;
35
50
  }
36
51
  //# sourceMappingURL=chat-usage-service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"chat-usage-service.d.ts","sourceRoot":"","sources":["../../src/services/chat-usage-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,qBAAa,gBAAgB;IAEzB,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,EAAE;gBADF,YAAY,EAAE,YAAY,EAC1B,EAAE,EAAE,OAAO,gBAAgB,EAAE,YAAY;IAG7C,kBAAkB,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAS/D,YAAY,CAChB,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,IAAI,CAAC;QAAC,GAAG,EAAE,IAAI,CAAA;KAAE,GAClC,OAAO,CAAC,gBAAgB,CAAC;IAqCtB,WAAW,CACf,cAAc,EAAE,MAAM,EACtB,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,IAAI,CAAC;QAAC,GAAG,EAAE,IAAI,CAAA;KAAE,GAClC,OAAO,CAAC,gBAAgB,CAAC;CAgC7B"}
1
+ {"version":3,"file":"chat-usage-service.d.ts","sourceRoot":"","sources":["../../src/services/chat-usage-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,qBAAa,gBAAgB;IAEzB,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,EAAE;gBADF,YAAY,EAAE,YAAY,EAC1B,EAAE,EAAE,OAAO,gBAAgB,EAAE,YAAY;IAG7C,kBAAkB,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAS/D,YAAY,CAChB,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,IAAI,CAAC;QAAC,GAAG,EAAE,IAAI,CAAA;KAAE,GAClC,OAAO,CAAC,gBAAgB,CAAC;IAqCtB,WAAW,CACf,cAAc,EAAE,MAAM,EACtB,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,IAAI,CAAC;QAAC,GAAG,EAAE,IAAI,CAAA;KAAE,GAClC,OAAO,CAAC,gBAAgB,CAAC;IAiCtB,mBAAmB,CACvB,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,IAAI,CAAC;QAAC,GAAG,EAAE,IAAI,CAAA;KAAE,GAClC,OAAO,CAAC,cAAc,EAAE,CAAC;IAoCtB,kBAAkB,CACtB,cAAc,EAAE,MAAM,EACtB,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,IAAI,CAAC;QAAC,GAAG,EAAE,IAAI,CAAA;KAAE,GAClC,OAAO,CAAC,cAAc,EAAE,CAAC;CAkC7B"}
@@ -85,5 +85,70 @@ export class ChatUsageService {
85
85
  totalCostCents: result._sum.costCents ?? 0,
86
86
  };
87
87
  }
88
+ async getUserUsageByModel(organizationId, userId, period) {
89
+ const where = {
90
+ organizationId,
91
+ session: { userId },
92
+ model: { not: null },
93
+ };
94
+ if (period) {
95
+ where.createdAt = { gte: period.start, lte: period.end };
96
+ }
97
+ else {
98
+ const now = new Date();
99
+ where.createdAt = {
100
+ gte: new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1)),
101
+ };
102
+ }
103
+ const groups = await this.db.chatMessage.groupBy({
104
+ by: ["model"],
105
+ where,
106
+ _count: { id: true },
107
+ _sum: {
108
+ tokensIn: true,
109
+ tokensOut: true,
110
+ costCents: true,
111
+ },
112
+ });
113
+ return groups.map((g) => ({
114
+ model: g.model,
115
+ totalMessages: g._count.id,
116
+ totalTokensIn: g._sum.tokensIn ?? 0,
117
+ totalTokensOut: g._sum.tokensOut ?? 0,
118
+ totalCostCents: g._sum.costCents ?? 0,
119
+ }));
120
+ }
121
+ async getOrgUsageByModel(organizationId, period) {
122
+ const where = {
123
+ organizationId,
124
+ model: { not: null },
125
+ };
126
+ if (period) {
127
+ where.createdAt = { gte: period.start, lte: period.end };
128
+ }
129
+ else {
130
+ const now = new Date();
131
+ where.createdAt = {
132
+ gte: new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1)),
133
+ };
134
+ }
135
+ const groups = await this.db.chatMessage.groupBy({
136
+ by: ["model"],
137
+ where,
138
+ _count: { id: true },
139
+ _sum: {
140
+ tokensIn: true,
141
+ tokensOut: true,
142
+ costCents: true,
143
+ },
144
+ });
145
+ return groups.map((g) => ({
146
+ model: g.model,
147
+ totalMessages: g._count.id,
148
+ totalTokensIn: g._sum.tokensIn ?? 0,
149
+ totalTokensOut: g._sum.tokensOut ?? 0,
150
+ totalCostCents: g._sum.costCents ?? 0,
151
+ }));
152
+ }
88
153
  }
89
154
  //# sourceMappingURL=chat-usage-service.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"chat-usage-service.js","sourceRoot":"","sources":["../../src/services/chat-usage-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"chat-usage-service.js","sourceRoot":"","sources":["../../src/services/chat-usage-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,132 @@
1
+ import type { PrismaClient } from "@prisma/client";
2
+ import { type TenantContext } from "../db/tenant.js";
3
+ import type { NodeService } from "./node-service.js";
4
+ import type { EdgeService } from "./edge-service.js";
5
+ import type { TagService } from "./tag-service.js";
6
+ export interface SimilarityPair {
7
+ nodeAId: string;
8
+ nodeBId: string;
9
+ similarity: number;
10
+ }
11
+ export interface MergeCluster {
12
+ nodeIds: string[];
13
+ minSimilarity: number;
14
+ avgSimilarity: number;
15
+ topicLabel: string;
16
+ }
17
+ export interface ScanResult {
18
+ proposalIds: string[];
19
+ clustersFound: number;
20
+ nodesScanned: number;
21
+ }
22
+ export interface MergeResult {
23
+ mergedNodeId: string;
24
+ archivedNodeIds: string[];
25
+ edgesTransferred: number;
26
+ }
27
+ export declare class DedupService {
28
+ private db;
29
+ private tenantCtx;
30
+ constructor(db: PrismaClient, tenantCtx?: TenantContext);
31
+ /**
32
+ * Scan chat nodes for similarity clusters and create merge proposals.
33
+ */
34
+ scanChatNodes(opts: {
35
+ threshold?: number;
36
+ limit?: number;
37
+ createdBy: string;
38
+ llmLabeler?: (titles: string[]) => Promise<string>;
39
+ }): Promise<ScanResult>;
40
+ /**
41
+ * Union-find clustering of similarity pairs.
42
+ * Returns clusters with min/avg similarity.
43
+ */
44
+ computeClusters(pairs: SimilarityPair[]): MergeCluster[];
45
+ /**
46
+ * List merge proposals with optional status filter and pagination.
47
+ */
48
+ listProposals(opts?: {
49
+ status?: string;
50
+ limit?: number;
51
+ offset?: number;
52
+ }): Promise<{
53
+ proposals: any[];
54
+ total: number;
55
+ }>;
56
+ /**
57
+ * Get a single proposal with full source node data.
58
+ */
59
+ getProposal(id: string): Promise<{
60
+ sourceNodes: {
61
+ id: string;
62
+ metadata: import("@prisma/client/runtime/library").JsonValue;
63
+ createdAt: Date;
64
+ type: string;
65
+ title: string;
66
+ content: string;
67
+ status: string;
68
+ createdBy: string;
69
+ updatedAt: Date;
70
+ version: number;
71
+ hasPii: boolean;
72
+ embeddingPiiMode: string | null;
73
+ domainId: string | null;
74
+ organizationId: string;
75
+ }[];
76
+ id: string;
77
+ metadata: import("@prisma/client/runtime/library").JsonValue;
78
+ createdAt: Date;
79
+ status: string;
80
+ createdBy: string;
81
+ organizationId: string;
82
+ topicLabel: string;
83
+ sourceNodeIds: string[];
84
+ mergedNodeId: string | null;
85
+ similarity: number;
86
+ resolvedBy: string | null;
87
+ resolvedAt: Date | null;
88
+ } | null>;
89
+ /**
90
+ * Update proposal status with transition validation.
91
+ * Allowed: pending→approved, pending→rejected, approved→merged.
92
+ */
93
+ updateProposalStatus(id: string, status: string, resolvedBy: string): Promise<{
94
+ id: string;
95
+ metadata: import("@prisma/client/runtime/library").JsonValue;
96
+ createdAt: Date;
97
+ status: string;
98
+ createdBy: string;
99
+ organizationId: string;
100
+ topicLabel: string;
101
+ sourceNodeIds: string[];
102
+ mergedNodeId: string | null;
103
+ similarity: number;
104
+ resolvedBy: string | null;
105
+ resolvedAt: Date | null;
106
+ }>;
107
+ /**
108
+ * Execute a merge: create merged node, transfer edges, archive sources.
109
+ */
110
+ executeMerge(proposalId: string, opts: {
111
+ mergedBy: string;
112
+ nodeService: NodeService;
113
+ edgeService: EdgeService;
114
+ title?: string;
115
+ content?: string;
116
+ llmSummarizer?: (contents: string[]) => Promise<string>;
117
+ }): Promise<MergeResult>;
118
+ /**
119
+ * Mark a node as canonical by applying the `canonical` tag.
120
+ */
121
+ markCanonical(nodeId: string, tagService: TagService, taggedBy: string): Promise<void>;
122
+ /**
123
+ * Remove canonical tag from a node.
124
+ */
125
+ removeCanonical(nodeId: string): Promise<void>;
126
+ /**
127
+ * Find similarity pairs among chat nodes using the embedding self-join.
128
+ * Scoped to nodes with capturedFromSession metadata.
129
+ */
130
+ private findChatSimilarityPairs;
131
+ }
132
+ //# sourceMappingURL=dedup-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dedup-service.d.ts","sourceRoot":"","sources":["../../src/services/dedup-service.ts"],"names":[],"mappings":"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"}