@reaatech/prompt-version-control-server 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 (136) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/LICENSE +21 -0
  3. package/README.md +261 -0
  4. package/dist/api/routes/deployments.d.ts +4 -0
  5. package/dist/api/routes/deployments.d.ts.map +1 -0
  6. package/dist/api/routes/deployments.js +48 -0
  7. package/dist/api/routes/deployments.js.map +1 -0
  8. package/dist/api/routes/docs.d.ts +4 -0
  9. package/dist/api/routes/docs.d.ts.map +1 -0
  10. package/dist/api/routes/docs.js +75 -0
  11. package/dist/api/routes/docs.js.map +1 -0
  12. package/dist/api/routes/evaluations.d.ts +4 -0
  13. package/dist/api/routes/evaluations.d.ts.map +1 -0
  14. package/dist/api/routes/evaluations.js +63 -0
  15. package/dist/api/routes/evaluations.js.map +1 -0
  16. package/dist/api/routes/metrics.d.ts +4 -0
  17. package/dist/api/routes/metrics.d.ts.map +1 -0
  18. package/dist/api/routes/metrics.js +38 -0
  19. package/dist/api/routes/metrics.js.map +1 -0
  20. package/dist/api/routes/promotions.d.ts +4 -0
  21. package/dist/api/routes/promotions.d.ts.map +1 -0
  22. package/dist/api/routes/promotions.js +95 -0
  23. package/dist/api/routes/promotions.js.map +1 -0
  24. package/dist/api/routes/prompts.d.ts +4 -0
  25. package/dist/api/routes/prompts.d.ts.map +1 -0
  26. package/dist/api/routes/prompts.js +80 -0
  27. package/dist/api/routes/prompts.js.map +1 -0
  28. package/dist/api/routes/render.d.ts +4 -0
  29. package/dist/api/routes/render.d.ts.map +1 -0
  30. package/dist/api/routes/render.js +44 -0
  31. package/dist/api/routes/render.js.map +1 -0
  32. package/dist/api/routes/tags.d.ts +4 -0
  33. package/dist/api/routes/tags.d.ts.map +1 -0
  34. package/dist/api/routes/tags.js +41 -0
  35. package/dist/api/routes/tags.js.map +1 -0
  36. package/dist/api/routes/webhooks.d.ts +4 -0
  37. package/dist/api/routes/webhooks.d.ts.map +1 -0
  38. package/dist/api/routes/webhooks.js +49 -0
  39. package/dist/api/routes/webhooks.js.map +1 -0
  40. package/dist/db/client.d.ts +3 -0
  41. package/dist/db/client.d.ts.map +1 -0
  42. package/dist/db/client.js +21 -0
  43. package/dist/db/client.js.map +1 -0
  44. package/dist/db/redis.d.ts +3 -0
  45. package/dist/db/redis.d.ts.map +1 -0
  46. package/dist/db/redis.js +18 -0
  47. package/dist/db/redis.js.map +1 -0
  48. package/dist/errors.d.ts +22 -0
  49. package/dist/errors.d.ts.map +1 -0
  50. package/dist/errors.js +39 -0
  51. package/dist/errors.js.map +1 -0
  52. package/dist/index.d.ts +4 -0
  53. package/dist/index.d.ts.map +1 -0
  54. package/dist/index.js +83 -0
  55. package/dist/index.js.map +1 -0
  56. package/dist/middleware/audit.d.ts +3 -0
  57. package/dist/middleware/audit.d.ts.map +1 -0
  58. package/dist/middleware/audit.js +39 -0
  59. package/dist/middleware/audit.js.map +1 -0
  60. package/dist/middleware/auth.d.ts +3 -0
  61. package/dist/middleware/auth.d.ts.map +1 -0
  62. package/dist/middleware/auth.js +49 -0
  63. package/dist/middleware/auth.js.map +1 -0
  64. package/dist/middleware/error-handler.d.ts +3 -0
  65. package/dist/middleware/error-handler.d.ts.map +1 -0
  66. package/dist/middleware/error-handler.js +36 -0
  67. package/dist/middleware/error-handler.js.map +1 -0
  68. package/dist/middleware/metrics.d.ts +3 -0
  69. package/dist/middleware/metrics.d.ts.map +1 -0
  70. package/dist/middleware/metrics.js +13 -0
  71. package/dist/middleware/metrics.js.map +1 -0
  72. package/dist/middleware/rate-limit.d.ts +8 -0
  73. package/dist/middleware/rate-limit.d.ts.map +1 -0
  74. package/dist/middleware/rate-limit.js +89 -0
  75. package/dist/middleware/rate-limit.js.map +1 -0
  76. package/dist/middleware/request-id.d.ts +3 -0
  77. package/dist/middleware/request-id.d.ts.map +1 -0
  78. package/dist/middleware/request-id.js +10 -0
  79. package/dist/middleware/request-id.js.map +1 -0
  80. package/dist/services/audit.service.d.ts +6 -0
  81. package/dist/services/audit.service.d.ts.map +1 -0
  82. package/dist/services/audit.service.js +26 -0
  83. package/dist/services/audit.service.js.map +1 -0
  84. package/dist/services/deployment.service.d.ts +101 -0
  85. package/dist/services/deployment.service.d.ts.map +1 -0
  86. package/dist/services/deployment.service.js +127 -0
  87. package/dist/services/deployment.service.js.map +1 -0
  88. package/dist/services/diff.engine.d.ts +13 -0
  89. package/dist/services/diff.engine.d.ts.map +1 -0
  90. package/dist/services/diff.engine.js +101 -0
  91. package/dist/services/diff.engine.js.map +1 -0
  92. package/dist/services/eval.service.d.ts +55 -0
  93. package/dist/services/eval.service.d.ts.map +1 -0
  94. package/dist/services/eval.service.js +151 -0
  95. package/dist/services/eval.service.js.map +1 -0
  96. package/dist/services/event.bus.d.ts +18 -0
  97. package/dist/services/event.bus.d.ts.map +1 -0
  98. package/dist/services/event.bus.js +35 -0
  99. package/dist/services/event.bus.js.map +1 -0
  100. package/dist/services/metric.service.d.ts +33 -0
  101. package/dist/services/metric.service.d.ts.map +1 -0
  102. package/dist/services/metric.service.js +64 -0
  103. package/dist/services/metric.service.js.map +1 -0
  104. package/dist/services/prometheus.service.d.ts +7 -0
  105. package/dist/services/prometheus.service.d.ts.map +1 -0
  106. package/dist/services/prometheus.service.js +27 -0
  107. package/dist/services/prometheus.service.js.map +1 -0
  108. package/dist/services/prompt.service.d.ts +59 -0
  109. package/dist/services/prompt.service.d.ts.map +1 -0
  110. package/dist/services/prompt.service.js +144 -0
  111. package/dist/services/prompt.service.js.map +1 -0
  112. package/dist/services/tag.service.d.ts +46 -0
  113. package/dist/services/tag.service.d.ts.map +1 -0
  114. package/dist/services/tag.service.js +84 -0
  115. package/dist/services/tag.service.js.map +1 -0
  116. package/dist/services/webhook.service.d.ts +50 -0
  117. package/dist/services/webhook.service.d.ts.map +1 -0
  118. package/dist/services/webhook.service.js +196 -0
  119. package/dist/services/webhook.service.js.map +1 -0
  120. package/dist/types/hono.d.ts +10 -0
  121. package/dist/types/hono.d.ts.map +1 -0
  122. package/dist/types/hono.js +2 -0
  123. package/dist/types/hono.js.map +1 -0
  124. package/dist/utils/context.d.ts +3 -0
  125. package/dist/utils/context.d.ts.map +1 -0
  126. package/dist/utils/context.js +9 -0
  127. package/dist/utils/context.js.map +1 -0
  128. package/dist/utils/logger.d.ts +3 -0
  129. package/dist/utils/logger.d.ts.map +1 -0
  130. package/dist/utils/logger.js +20 -0
  131. package/dist/utils/logger.js.map +1 -0
  132. package/dist/utils/pagination.d.ts +10 -0
  133. package/dist/utils/pagination.d.ts.map +1 -0
  134. package/dist/utils/pagination.js +10 -0
  135. package/dist/utils/pagination.js.map +1 -0
  136. package/package.json +74 -0
@@ -0,0 +1,13 @@
1
+ import type { DiffResult } from '@reaatech/prompt-version-control-shared';
2
+ export declare class DiffEngine {
3
+ diff(projectId: string, promptId: string, fromVersion: number, toVersion: number): Promise<DiffResult>;
4
+ /**
5
+ * Simplified line-wise diff for prompt text. Heuristic-based (not a full
6
+ * Myers/LCS), optimized for readability of prompt changes. For production
7
+ * use with fine-grained diffs, consider integrating a dedicated diff library.
8
+ */
9
+ private computeDiff;
10
+ private assessImpact;
11
+ }
12
+ export declare const diffEngine: DiffEngine;
13
+ //# sourceMappingURL=diff.engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.engine.d.ts","sourceRoot":"","sources":["../../src/services/diff.engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAe,MAAM,yCAAyC,CAAC;AAIvF,qBAAa,UAAU;IACf,IAAI,CACR,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,UAAU,CAAC;IA4BtB;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAkDnB,OAAO,CAAC,YAAY;CAwBrB;AAED,eAAO,MAAM,UAAU,YAAmB,CAAC"}
@@ -0,0 +1,101 @@
1
+ import { prisma } from '../db/client.js';
2
+ import { NotFoundError } from '../errors.js';
3
+ export class DiffEngine {
4
+ async diff(projectId, promptId, fromVersion, toVersion) {
5
+ const [from, to] = await Promise.all([
6
+ prisma.version.findFirst({
7
+ where: { promptId, number: fromVersion, prompt: { projectId } },
8
+ }),
9
+ prisma.version.findFirst({
10
+ where: { promptId, number: toVersion, prompt: { projectId } },
11
+ }),
12
+ ]);
13
+ if (!from) {
14
+ throw new NotFoundError('Version', String(fromVersion));
15
+ }
16
+ if (!to) {
17
+ throw new NotFoundError('Version', String(toVersion));
18
+ }
19
+ const sections = this.computeDiff(from.content, to.content);
20
+ const semanticImpact = this.assessImpact(from.content, to.content, sections);
21
+ return {
22
+ fromVersion: from.number,
23
+ toVersion: to.number,
24
+ sections,
25
+ semanticImpact,
26
+ };
27
+ }
28
+ /**
29
+ * Simplified line-wise diff for prompt text. Heuristic-based (not a full
30
+ * Myers/LCS), optimized for readability of prompt changes. For production
31
+ * use with fine-grained diffs, consider integrating a dedicated diff library.
32
+ */
33
+ computeDiff(from, to) {
34
+ const sections = [];
35
+ const fromLines = from.split('\n');
36
+ const toLines = to.split('\n');
37
+ let i = 0;
38
+ let j = 0;
39
+ while (i < fromLines.length || j < toLines.length) {
40
+ if (i >= fromLines.length) {
41
+ // biome-ignore lint/style/noNonNullAssertion: safe — guarded by length check
42
+ sections.push({ type: 'added', value: toLines[j] });
43
+ j++;
44
+ }
45
+ else if (j >= toLines.length) {
46
+ // biome-ignore lint/style/noNonNullAssertion: safe — guarded by length check
47
+ sections.push({ type: 'removed', value: fromLines[i] });
48
+ i++;
49
+ }
50
+ else if (fromLines[i] === toLines[j]) {
51
+ i++;
52
+ j++;
53
+ }
54
+ else {
55
+ // biome-ignore lint/style/noNonNullAssertion: safe — valid array indices
56
+ const nextMatchFrom = toLines.indexOf(fromLines[i], j);
57
+ // biome-ignore lint/style/noNonNullAssertion: safe — valid array indices
58
+ const nextMatchTo = fromLines.indexOf(toLines[j], i);
59
+ if (nextMatchFrom === -1 && nextMatchTo === -1) {
60
+ // biome-ignore lint/style/noNonNullAssertion: safe — valid array indices
61
+ sections.push({ type: 'modified', value: toLines[j], lineStart: i + 1 });
62
+ i++;
63
+ j++;
64
+ }
65
+ else if (nextMatchTo !== -1 && (nextMatchFrom === -1 || nextMatchTo < nextMatchFrom)) {
66
+ for (let k = i; k < nextMatchTo; k++) {
67
+ // biome-ignore lint/style/noNonNullAssertion: safe — k < fromLines.length
68
+ sections.push({ type: 'removed', value: fromLines[k] });
69
+ }
70
+ i = nextMatchTo;
71
+ }
72
+ else {
73
+ for (let k = j; k < nextMatchFrom; k++) {
74
+ // biome-ignore lint/style/noNonNullAssertion: safe — k < toLines.length
75
+ sections.push({ type: 'added', value: toLines[k] });
76
+ }
77
+ j = nextMatchFrom;
78
+ }
79
+ }
80
+ }
81
+ return sections;
82
+ }
83
+ assessImpact(from, to, sections) {
84
+ if (sections.length === 0)
85
+ return 'none';
86
+ const addedLines = sections.filter((s) => s.type === 'added').length;
87
+ const removedLines = sections.filter((s) => s.type === 'removed').length;
88
+ const totalLines = Math.max(from.split('\n').length, to.split('\n').length);
89
+ const changeRatio = (addedLines + removedLines) / totalLines;
90
+ const hasSystemChange = sections.some((s) => s.lineStart !== undefined &&
91
+ s.lineStart <= 3 &&
92
+ /system|you are|role|persona/i.test(s.value));
93
+ if (hasSystemChange || changeRatio > 0.5)
94
+ return 'major';
95
+ if (changeRatio > 0.1)
96
+ return 'minor';
97
+ return 'none';
98
+ }
99
+ }
100
+ export const diffEngine = new DiffEngine();
101
+ //# sourceMappingURL=diff.engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.engine.js","sourceRoot":"","sources":["../../src/services/diff.engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import type { Prisma } from '@prisma/client';
2
+ import type { EvalStatus } from '@reaatech/prompt-version-control-shared';
3
+ export declare class EvalService {
4
+ trigger(projectId: string, versionId: string, harnessId: string): Promise<{
5
+ id: string;
6
+ createdAt: Date;
7
+ metrics: Prisma.JsonValue | null;
8
+ status: import("@prisma/client").$Enums.EvalStatus;
9
+ versionId: string;
10
+ harnessId: string;
11
+ score: number | null;
12
+ startedAt: Date | null;
13
+ completedAt: Date | null;
14
+ }>;
15
+ /**
16
+ * Receive a callback from the eval harness. Scoped strictly by evaluationId
17
+ * (carried in the callback URL we issued); HMAC verified when EVAL_WEBHOOK_SECRET
18
+ * is configured. No API key is required — this endpoint is meant for
19
+ * external harnesses, authenticated via the shared HMAC secret.
20
+ */
21
+ receiveWebhook(evaluationId: string, rawBody: string, signatureHeader: string | undefined, payload: {
22
+ status: EvalStatus;
23
+ score?: number;
24
+ metrics?: Record<string, unknown>;
25
+ }): Promise<{
26
+ id: string;
27
+ createdAt: Date;
28
+ metrics: Prisma.JsonValue | null;
29
+ status: import("@prisma/client").$Enums.EvalStatus;
30
+ versionId: string;
31
+ harnessId: string;
32
+ score: number | null;
33
+ startedAt: Date | null;
34
+ completedAt: Date | null;
35
+ }>;
36
+ listByVersion(projectId: string, versionId: string): Promise<{
37
+ id: string;
38
+ createdAt: Date;
39
+ metrics: Prisma.JsonValue | null;
40
+ status: import("@prisma/client").$Enums.EvalStatus;
41
+ versionId: string;
42
+ harnessId: string;
43
+ score: number | null;
44
+ startedAt: Date | null;
45
+ completedAt: Date | null;
46
+ }[]>;
47
+ getPromotionGateStatus(projectId: string, versionId: string): Promise<{
48
+ canPromote: boolean;
49
+ reason?: string;
50
+ evaluations: Awaited<ReturnType<EvalService['listByVersion']>>;
51
+ }>;
52
+ private buildCallbackUrl;
53
+ }
54
+ export declare const evalService: EvalService;
55
+ //# sourceMappingURL=eval.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eval.service.d.ts","sourceRoot":"","sources":["../../src/services/eval.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yCAAyC,CAAC;AAW1E,qBAAa,WAAW;IAChB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;;;;;;;IAyDrE;;;;;OAKG;IACG,cAAc,CAClB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,MAAM,GAAG,SAAS,EACnC,OAAO,EAAE;QACP,MAAM,EAAE,UAAU,CAAC;QACnB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACnC;;;;;;;;;;;IAsCG,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;;;;;;;IAclD,sBAAsB,CAC1B,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;QACT,UAAU,EAAE,OAAO,CAAC;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;KAChE,CAAC;IAyCF,OAAO,CAAC,gBAAgB;CAIzB;AAED,eAAO,MAAM,WAAW,aAAoB,CAAC"}
@@ -0,0 +1,151 @@
1
+ import { createHmac, timingSafeEqual } from 'node:crypto';
2
+ import { prisma } from '../db/client.js';
3
+ import { NotFoundError, UnauthorizedError } from '../errors.js';
4
+ import { logger } from '../utils/logger.js';
5
+ import { evaluationsCompleted } from './prometheus.service.js';
6
+ // Read at call time so deployments can rotate secrets without a restart and
7
+ // so tests can toggle them between cases.
8
+ const evalHarnessUrl = () => process.env.EVAL_HARNESS_URL;
9
+ const evalWebhookSecret = () => process.env.EVAL_WEBHOOK_SECRET;
10
+ export class EvalService {
11
+ async trigger(projectId, versionId, harnessId) {
12
+ const version = await prisma.version.findFirst({
13
+ where: { id: versionId, prompt: { projectId } },
14
+ });
15
+ if (!version) {
16
+ throw new NotFoundError('Version', versionId);
17
+ }
18
+ const eval_ = await prisma.evaluation.create({
19
+ data: {
20
+ versionId,
21
+ harnessId,
22
+ status: 'pending',
23
+ },
24
+ });
25
+ const harnessUrl = evalHarnessUrl();
26
+ const harnessSecret = evalWebhookSecret();
27
+ if (harnessUrl) {
28
+ try {
29
+ const res = await fetch(`${harnessUrl}/evaluations`, {
30
+ method: 'POST',
31
+ headers: {
32
+ 'Content-Type': 'application/json',
33
+ ...(harnessSecret ? { 'X-Webhook-Secret': harnessSecret } : {}),
34
+ },
35
+ body: JSON.stringify({
36
+ evaluationId: eval_.id,
37
+ versionId,
38
+ harnessId,
39
+ content: version.content,
40
+ template: version.template,
41
+ variables: version.variables,
42
+ callbackUrl: this.buildCallbackUrl(eval_.id),
43
+ }),
44
+ signal: AbortSignal.timeout(10_000),
45
+ });
46
+ if (!res.ok) {
47
+ logger.warn({ evaluationId: eval_.id, status: res.status }, 'eval harness returned non-ok status');
48
+ }
49
+ }
50
+ catch (err) {
51
+ logger.warn({ evaluationId: eval_.id, err }, 'eval harness call failed');
52
+ }
53
+ }
54
+ const updated = await prisma.evaluation.update({
55
+ where: { id: eval_.id },
56
+ data: { status: 'running', startedAt: new Date() },
57
+ });
58
+ return updated;
59
+ }
60
+ /**
61
+ * Receive a callback from the eval harness. Scoped strictly by evaluationId
62
+ * (carried in the callback URL we issued); HMAC verified when EVAL_WEBHOOK_SECRET
63
+ * is configured. No API key is required — this endpoint is meant for
64
+ * external harnesses, authenticated via the shared HMAC secret.
65
+ */
66
+ async receiveWebhook(evaluationId, rawBody, signatureHeader, payload) {
67
+ const secret = evalWebhookSecret();
68
+ if (secret) {
69
+ if (!signatureHeader) {
70
+ throw new UnauthorizedError('Missing webhook signature');
71
+ }
72
+ const expected = createHmac('sha256', secret).update(rawBody).digest('hex');
73
+ const provided = signatureHeader.startsWith('sha256=')
74
+ ? signatureHeader.slice('sha256='.length)
75
+ : signatureHeader;
76
+ const a = Buffer.from(expected, 'hex');
77
+ const b = Buffer.from(provided, 'hex');
78
+ if (a.length !== b.length || !timingSafeEqual(a, b)) {
79
+ throw new UnauthorizedError('Invalid webhook signature');
80
+ }
81
+ }
82
+ const eval_ = await prisma.evaluation.findUnique({ where: { id: evaluationId } });
83
+ if (!eval_) {
84
+ throw new NotFoundError('Evaluation', evaluationId);
85
+ }
86
+ const updated = await prisma.evaluation.update({
87
+ where: { id: eval_.id },
88
+ data: {
89
+ status: payload.status,
90
+ score: payload.score,
91
+ metrics: payload.metrics,
92
+ completedAt: new Date(),
93
+ },
94
+ });
95
+ evaluationsCompleted.inc({ status: payload.status });
96
+ return updated;
97
+ }
98
+ async listByVersion(projectId, versionId) {
99
+ const version = await prisma.version.findFirst({
100
+ where: { id: versionId, prompt: { projectId } },
101
+ select: { id: true },
102
+ });
103
+ if (!version) {
104
+ throw new NotFoundError('Version', versionId);
105
+ }
106
+ return prisma.evaluation.findMany({
107
+ where: { versionId },
108
+ orderBy: { createdAt: 'desc' },
109
+ });
110
+ }
111
+ async getPromotionGateStatus(projectId, versionId) {
112
+ const evals = await this.listByVersion(projectId, versionId);
113
+ if (evals.length === 0) {
114
+ return { canPromote: false, reason: 'No evaluations found', evaluations: evals };
115
+ }
116
+ const failed = evals.filter((e) => e.status === 'failed');
117
+ if (failed.length > 0) {
118
+ return {
119
+ canPromote: false,
120
+ reason: `${failed.length} evaluation(s) failed`,
121
+ evaluations: evals,
122
+ };
123
+ }
124
+ const pending = evals.filter((e) => e.status === 'pending' || e.status === 'running');
125
+ if (pending.length > 0) {
126
+ return {
127
+ canPromote: false,
128
+ reason: `${pending.length} evaluation(s) still pending`,
129
+ evaluations: evals,
130
+ };
131
+ }
132
+ const scores = evals.map((e) => e.score).filter((s) => s !== null);
133
+ if (scores.length > 0) {
134
+ const avgScore = scores.reduce((a, b) => a + b, 0) / scores.length;
135
+ if (avgScore < 0.8) {
136
+ return {
137
+ canPromote: false,
138
+ reason: `Average score ${avgScore.toFixed(2)} below threshold 0.80`,
139
+ evaluations: evals,
140
+ };
141
+ }
142
+ }
143
+ return { canPromote: true, evaluations: evals };
144
+ }
145
+ buildCallbackUrl(evaluationId) {
146
+ const baseUrl = process.env.PUBLIC_API_URL || `http://localhost:${process.env.PORT || 3000}`;
147
+ return `${baseUrl}/api/v1/evaluations/webhook?evaluationId=${evaluationId}`;
148
+ }
149
+ }
150
+ export const evalService = new EvalService();
151
+ //# sourceMappingURL=eval.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eval.service.js","sourceRoot":"","sources":["../../src/services/eval.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export type EventType = 'version.created' | 'tag.moved' | 'eval.completed' | 'promotion.requested' | 'promotion.approved' | 'promotion.rejected';
2
+ export interface AppEvent {
3
+ id: string;
4
+ type: EventType;
5
+ projectId: string;
6
+ payload: Record<string, unknown>;
7
+ createdAt: Date;
8
+ }
9
+ export type EventHandler = (event: AppEvent) => void | Promise<void>;
10
+ declare class EventBus {
11
+ private handlers;
12
+ on(type: EventType, handler: EventHandler): () => void;
13
+ emit(event: AppEvent): void;
14
+ emitAsync(event: AppEvent): Promise<void>;
15
+ }
16
+ export declare const eventBus: EventBus;
17
+ export {};
18
+ //# sourceMappingURL=event.bus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event.bus.d.ts","sourceRoot":"","sources":["../../src/services/event.bus.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,SAAS,GACjB,iBAAiB,GACjB,WAAW,GACX,gBAAgB,GAChB,qBAAqB,GACrB,oBAAoB,GACpB,oBAAoB,CAAC;AAEzB,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,SAAS,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAErE,cAAM,QAAQ;IACZ,OAAO,CAAC,QAAQ,CAA2C;IAE3D,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,YAAY,GAAG,MAAM,IAAI;IAOtD,IAAI,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAW3B,SAAS,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;CAc1C;AAED,eAAO,MAAM,QAAQ,UAAiB,CAAC"}
@@ -0,0 +1,35 @@
1
+ import { logger } from '../utils/logger.js';
2
+ class EventBus {
3
+ handlers = new Map();
4
+ on(type, handler) {
5
+ const set = this.handlers.get(type) ?? new Set();
6
+ set.add(handler);
7
+ this.handlers.set(type, set);
8
+ return () => set.delete(handler);
9
+ }
10
+ emit(event) {
11
+ const set = this.handlers.get(event.type);
12
+ if (!set)
13
+ return;
14
+ for (const handler of set) {
15
+ Promise.resolve()
16
+ .then(() => handler(event))
17
+ .catch((err) => logger.error({ err, eventId: event.id }, 'event handler failed'));
18
+ }
19
+ }
20
+ emitAsync(event) {
21
+ const set = this.handlers.get(event.type);
22
+ if (!set)
23
+ return Promise.resolve();
24
+ return Promise.all(Array.from(set).map(async (handler) => {
25
+ try {
26
+ await handler(event);
27
+ }
28
+ catch (err) {
29
+ logger.error({ err, eventId: event.id }, 'event handler failed');
30
+ }
31
+ })).then(() => undefined);
32
+ }
33
+ }
34
+ export const eventBus = new EventBus();
35
+ //# sourceMappingURL=event.bus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event.bus.js","sourceRoot":"","sources":["../../src/services/event.bus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAoB5C,MAAM,QAAQ;IACJ,QAAQ,GAAG,IAAI,GAAG,EAAgC,CAAC;IAE3D,EAAE,CAAC,IAAe,EAAE,OAAqB;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,EAAE,CAAC;QACjD,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACjB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAC7B,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACnC,CAAC;IAED,IAAI,CAAC,KAAe;QAClB,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,GAAG;YAAE,OAAO;QAEjB,KAAK,MAAM,OAAO,IAAI,GAAG,EAAE,CAAC;YAC1B,OAAO,CAAC,OAAO,EAAE;iBACd,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;iBAC1B,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE,EAAE,sBAAsB,CAAC,CAAC,CAAC;QACtF,CAAC;IACH,CAAC;IAED,SAAS,CAAC,KAAe;QACvB,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,GAAG;YAAE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAEnC,OAAO,OAAO,CAAC,GAAG,CAChB,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;YACpC,IAAI,CAAC;gBACH,MAAM,OAAO,CAAC,KAAK,CAAC,CAAC;YACvB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE,EAAE,sBAAsB,CAAC,CAAC;YACnE,CAAC;QACH,CAAC,CAAC,CACH,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAC1B,CAAC;CACF;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC"}
@@ -0,0 +1,33 @@
1
+ import type { Prisma } from '@prisma/client';
2
+ import type { IngestMetricInput } from '@reaatech/prompt-version-control-shared';
3
+ export declare class MetricService {
4
+ ingest(projectId: string, metrics: IngestMetricInput[]): Promise<{
5
+ ingested: number;
6
+ }>;
7
+ getVersionMetrics(projectId: string, versionId: string, opts: {
8
+ hours?: number;
9
+ }): Promise<{
10
+ id: string;
11
+ name: string;
12
+ versionId: string;
13
+ type: import("@prisma/client").$Enums.MetricType;
14
+ value: number;
15
+ unit: string;
16
+ timestamp: Date;
17
+ dimensions: Prisma.JsonValue | null;
18
+ }[]>;
19
+ getPromptMetrics(projectId: string, promptId: string, opts: {
20
+ hours?: number;
21
+ }): Promise<{
22
+ id: string;
23
+ name: string;
24
+ versionId: string;
25
+ type: import("@prisma/client").$Enums.MetricType;
26
+ value: number;
27
+ unit: string;
28
+ timestamp: Date;
29
+ dimensions: Prisma.JsonValue | null;
30
+ }[]>;
31
+ }
32
+ export declare const metricService: MetricService;
33
+ //# sourceMappingURL=metric.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metric.service.d.ts","sourceRoot":"","sources":["../../src/services/metric.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,KAAK,EAAE,iBAAiB,EAAc,MAAM,yCAAyC,CAAC;AAI7F,qBAAa,aAAa;IAClB,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,EAAE;;;IAgCtD,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;;;;;;;;;;IAgBhF,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;;;;;;;;;;CAkBrF;AAED,eAAO,MAAM,aAAa,eAAsB,CAAC"}
@@ -0,0 +1,64 @@
1
+ import { prisma } from '../db/client.js';
2
+ import { NotFoundError } from '../errors.js';
3
+ export class MetricService {
4
+ async ingest(projectId, metrics) {
5
+ const versionIds = [...new Set(metrics.map((m) => m.versionId))];
6
+ // Confirm every versionId belongs to the caller's project. Cross-tenant
7
+ // attempts surface as NotFound to avoid leaking which IDs exist.
8
+ const versions = await prisma.version.findMany({
9
+ where: { id: { in: versionIds }, prompt: { projectId } },
10
+ select: { id: true },
11
+ });
12
+ const validIds = new Set(versions.map((v) => v.id));
13
+ const invalid = versionIds.filter((id) => !validIds.has(id));
14
+ if (invalid.length > 0) {
15
+ // biome-ignore lint/style/noNonNullAssertion: safe — guarded by length check
16
+ throw new NotFoundError('Version', invalid[0]);
17
+ }
18
+ await prisma.metric.createMany({
19
+ data: metrics.map((m) => ({
20
+ versionId: m.versionId,
21
+ type: m.type,
22
+ name: m.name,
23
+ value: m.value,
24
+ unit: m.unit,
25
+ timestamp: m.timestamp ?? new Date(),
26
+ dimensions: (m.dimensions ?? {}),
27
+ })),
28
+ });
29
+ return { ingested: metrics.length };
30
+ }
31
+ async getVersionMetrics(projectId, versionId, opts) {
32
+ const version = await prisma.version.findFirst({
33
+ where: { id: versionId, prompt: { projectId } },
34
+ select: { id: true },
35
+ });
36
+ if (!version) {
37
+ throw new NotFoundError('Version', versionId);
38
+ }
39
+ const since = new Date(Date.now() - (opts.hours ?? 24) * 60 * 60 * 1000);
40
+ return prisma.metric.findMany({
41
+ where: { versionId, timestamp: { gte: since } },
42
+ orderBy: { timestamp: 'desc' },
43
+ });
44
+ }
45
+ async getPromptMetrics(projectId, promptId, opts) {
46
+ const prompt = await prisma.prompt.findFirst({
47
+ where: { id: promptId, projectId },
48
+ select: { id: true },
49
+ });
50
+ if (!prompt) {
51
+ throw new NotFoundError('Prompt', promptId);
52
+ }
53
+ const since = new Date(Date.now() - (opts.hours ?? 24) * 60 * 60 * 1000);
54
+ return prisma.metric.findMany({
55
+ where: {
56
+ version: { promptId },
57
+ timestamp: { gte: since },
58
+ },
59
+ orderBy: { timestamp: 'desc' },
60
+ });
61
+ }
62
+ }
63
+ export const metricService = new MetricService();
64
+ //# sourceMappingURL=metric.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metric.service.js","sourceRoot":"","sources":["../../src/services/metric.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { Counter, Gauge, Histogram } from 'prom-client';
2
+ export declare const httpRequestDuration: Histogram<"status" | "method" | "route">;
3
+ export declare const promptVersionsCreated: Counter<"project">;
4
+ export declare const evaluationsCompleted: Counter<"status">;
5
+ export declare const deploymentsActive: Gauge<string>;
6
+ export declare function getMetrics(): Promise<string>;
7
+ //# sourceMappingURL=prometheus.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prometheus.service.d.ts","sourceRoot":"","sources":["../../src/services/prometheus.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAY,MAAM,aAAa,CAAC;AAGlE,eAAO,MAAM,mBAAmB,0CAK9B,CAAC;AAGH,eAAO,MAAM,qBAAqB,oBAIhC,CAAC;AAEH,eAAO,MAAM,oBAAoB,mBAI/B,CAAC;AAEH,eAAO,MAAM,iBAAiB,eAG5B,CAAC;AAEH,wBAAsB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAElD"}
@@ -0,0 +1,27 @@
1
+ import { Counter, Gauge, Histogram, register } from 'prom-client';
2
+ // HTTP request duration
3
+ export const httpRequestDuration = new Histogram({
4
+ name: 'pvc_http_request_duration_seconds',
5
+ help: 'HTTP request duration in seconds',
6
+ labelNames: ['method', 'route', 'status'],
7
+ buckets: [0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5],
8
+ });
9
+ // Business metrics
10
+ export const promptVersionsCreated = new Counter({
11
+ name: 'pvc_prompt_versions_created_total',
12
+ help: 'Total number of prompt versions created',
13
+ labelNames: ['project'],
14
+ });
15
+ export const evaluationsCompleted = new Counter({
16
+ name: 'pvc_evaluations_completed_total',
17
+ help: 'Total evaluations completed',
18
+ labelNames: ['status'],
19
+ });
20
+ export const deploymentsActive = new Gauge({
21
+ name: 'pvc_deployments_active',
22
+ help: 'Number of active deployments',
23
+ });
24
+ export async function getMetrics() {
25
+ return register.metrics();
26
+ }
27
+ //# sourceMappingURL=prometheus.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prometheus.service.js","sourceRoot":"","sources":["../../src/services/prometheus.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAElE,wBAAwB;AACxB,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,SAAS,CAAC;IAC/C,IAAI,EAAE,mCAAmC;IACzC,IAAI,EAAE,kCAAkC;IACxC,UAAU,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC;IACzC,OAAO,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,CAAC;CACrD,CAAC,CAAC;AAEH,mBAAmB;AACnB,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,OAAO,CAAC;IAC/C,IAAI,EAAE,mCAAmC;IACzC,IAAI,EAAE,yCAAyC;IAC/C,UAAU,EAAE,CAAC,SAAS,CAAC;CACxB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,OAAO,CAAC;IAC9C,IAAI,EAAE,iCAAiC;IACvC,IAAI,EAAE,6BAA6B;IACnC,UAAU,EAAE,CAAC,QAAQ,CAAC;CACvB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,KAAK,CAAC;IACzC,IAAI,EAAE,wBAAwB;IAC9B,IAAI,EAAE,8BAA8B;CACrC,CAAC,CAAC;AAEH,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,OAAO,QAAQ,CAAC,OAAO,EAAE,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,59 @@
1
+ import type { Prisma, Prompt, Version } from '@prisma/client';
2
+ import type { CreatePromptInput, CreateVersionInput, UpdatePromptInput } from '@reaatech/prompt-version-control-shared';
3
+ export declare class PromptService {
4
+ createPrompt(projectId: string, data: CreatePromptInput): Promise<Prompt>;
5
+ getPrompt(projectId: string, id: string): Promise<Prompt>;
6
+ listPrompts(projectId: string, opts: {
7
+ limit: number;
8
+ cursor?: string;
9
+ }): Promise<{
10
+ data: {
11
+ id: string;
12
+ projectId: string;
13
+ name: string;
14
+ createdAt: Date;
15
+ updatedAt: Date;
16
+ description: string | null;
17
+ template: string;
18
+ variables: Prisma.JsonValue;
19
+ metadata: Prisma.JsonValue | null;
20
+ archived: boolean;
21
+ }[];
22
+ meta: {
23
+ limit: number;
24
+ nextCursor?: string;
25
+ };
26
+ }>;
27
+ updatePrompt(projectId: string, id: string, data: UpdatePromptInput): Promise<Prompt>;
28
+ archivePrompt(projectId: string, id: string): Promise<Prompt>;
29
+ /**
30
+ * Create a new version. Wrapped in a transaction with a unique-violation
31
+ * retry loop so concurrent creates against the same prompt don't both win
32
+ * the same version number.
33
+ */
34
+ createVersion(projectId: string, promptId: string, data: CreateVersionInput): Promise<Version>;
35
+ getVersion(projectId: string, promptId: string, versionId: string): Promise<Version>;
36
+ getVersionByNumber(projectId: string, promptId: string, number: number): Promise<Version>;
37
+ listVersions(projectId: string, promptId: string, opts: {
38
+ limit: number;
39
+ cursor?: string;
40
+ }): Promise<{
41
+ data: {
42
+ number: number;
43
+ id: string;
44
+ createdAt: Date;
45
+ template: string;
46
+ variables: Prisma.JsonValue;
47
+ metadata: Prisma.JsonValue | null;
48
+ promptId: string;
49
+ content: string;
50
+ checksum: string;
51
+ }[];
52
+ meta: {
53
+ limit: number;
54
+ nextCursor?: string;
55
+ };
56
+ }>;
57
+ }
58
+ export declare const promptService: PromptService;
59
+ //# sourceMappingURL=prompt.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.service.d.ts","sourceRoot":"","sources":["../../src/services/prompt.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAE9D,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EAClB,MAAM,yCAAyC,CAAC;AAMjD,qBAAa,aAAa;IAClB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC;IAqBzE,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAYzD,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;;;;;;;;;;;;;;;sBA7CJ,CAAC;;;IAwDpE,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC;IASrF,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAWnE;;;;OAIG;IACG,aAAa,CACjB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,OAAO,CAAC;IAgDb,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAYpF,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAYzF,YAAY,CAChB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;;;;;;;;;;;;;;sBAhK+B,CAAC;;;CAoL3E;AAED,eAAO,MAAM,aAAa,eAAsB,CAAC"}