@proofhound/core 0.1.8 → 0.1.9
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.
- package/dist/server/channels/mcp/annotation.tools.d.ts.map +1 -1
- package/dist/server/channels/mcp/annotation.tools.js +18 -5
- package/dist/server/channels/mcp/annotation.tools.js.map +1 -1
- package/dist/server/channels/mcp/canary-release.tools.d.ts.map +1 -1
- package/dist/server/channels/mcp/canary-release.tools.js +2 -1
- package/dist/server/channels/mcp/canary-release.tools.js.map +1 -1
- package/dist/server/channels/mcp/dataset.tools.d.ts.map +1 -1
- package/dist/server/channels/mcp/dataset.tools.js +49 -1
- package/dist/server/channels/mcp/dataset.tools.js.map +1 -1
- package/dist/server/channels/mcp/mcp-server.factory.d.ts +9 -1
- package/dist/server/channels/mcp/mcp-server.factory.d.ts.map +1 -1
- package/dist/server/channels/mcp/mcp-server.factory.js +28 -3
- package/dist/server/channels/mcp/mcp-server.factory.js.map +1 -1
- package/dist/server/channels/mcp/prompt.tools.d.ts.map +1 -1
- package/dist/server/channels/mcp/prompt.tools.js +34 -2
- package/dist/server/channels/mcp/prompt.tools.js.map +1 -1
- package/dist/server/channels/mcp/release-line.tools.d.ts.map +1 -1
- package/dist/server/channels/mcp/release-line.tools.js +292 -4
- package/dist/server/channels/mcp/release-line.tools.js.map +1 -1
- package/dist/server/channels/mcp/run-result.tools.d.ts.map +1 -1
- package/dist/server/channels/mcp/run-result.tools.js +7 -5
- package/dist/server/channels/mcp/run-result.tools.js.map +1 -1
- package/dist/server/infrastructure/llm/run-result-writer.js +3 -3
- package/dist/server/modules/annotation/annotation.controller.d.ts +28 -13
- package/dist/server/modules/annotation/annotation.controller.d.ts.map +1 -1
- package/dist/server/modules/annotation/annotation.repository.d.ts +6 -2
- package/dist/server/modules/annotation/annotation.repository.d.ts.map +1 -1
- package/dist/server/modules/annotation/annotation.repository.js +340 -96
- package/dist/server/modules/annotation/annotation.repository.js.map +1 -1
- package/dist/server/modules/annotation/annotation.service.d.ts.map +1 -1
- package/dist/server/modules/annotation/annotation.service.js +62 -10
- package/dist/server/modules/annotation/annotation.service.js.map +1 -1
- package/dist/server/modules/canary-release/canary-release.controller.d.ts +63 -42
- package/dist/server/modules/canary-release/canary-release.controller.d.ts.map +1 -1
- package/dist/server/modules/canary-release/canary-release.repository.d.ts +23 -5
- package/dist/server/modules/canary-release/canary-release.repository.d.ts.map +1 -1
- package/dist/server/modules/canary-release/canary-release.repository.js +28 -12
- package/dist/server/modules/canary-release/canary-release.repository.js.map +1 -1
- package/dist/server/modules/canary-release/canary-release.service.d.ts.map +1 -1
- package/dist/server/modules/canary-release/canary-release.service.js +32 -10
- package/dist/server/modules/canary-release/canary-release.service.js.map +1 -1
- package/dist/server/modules/canary-release/canary-runtime.d.ts +11 -1
- package/dist/server/modules/canary-release/canary-runtime.d.ts.map +1 -1
- package/dist/server/modules/canary-release/canary-runtime.js +63 -8
- package/dist/server/modules/canary-release/canary-runtime.js.map +1 -1
- package/dist/server/modules/dataset/dataset-deletion.hook.d.ts +16 -0
- package/dist/server/modules/dataset/dataset-deletion.hook.d.ts.map +1 -0
- package/dist/server/modules/dataset/dataset-deletion.hook.js +57 -0
- package/dist/server/modules/dataset/dataset-deletion.hook.js.map +1 -0
- package/dist/server/modules/dataset/dataset-import.controller.d.ts +2 -0
- package/dist/server/modules/dataset/dataset-import.controller.d.ts.map +1 -1
- package/dist/server/modules/dataset/dataset.controller.d.ts +98 -0
- package/dist/server/modules/dataset/dataset.controller.d.ts.map +1 -1
- package/dist/server/modules/dataset/dataset.controller.js +36 -0
- package/dist/server/modules/dataset/dataset.controller.js.map +1 -1
- package/dist/server/modules/dataset/dataset.module.d.ts.map +1 -1
- package/dist/server/modules/dataset/dataset.module.js +8 -1
- package/dist/server/modules/dataset/dataset.module.js.map +1 -1
- package/dist/server/modules/dataset/dataset.repository.d.ts +19 -0
- package/dist/server/modules/dataset/dataset.repository.d.ts.map +1 -1
- package/dist/server/modules/dataset/dataset.repository.js +248 -9
- package/dist/server/modules/dataset/dataset.repository.js.map +1 -1
- package/dist/server/modules/dataset/dataset.service.d.ts +33 -1
- package/dist/server/modules/dataset/dataset.service.d.ts.map +1 -1
- package/dist/server/modules/dataset/dataset.service.js +49 -7
- package/dist/server/modules/dataset/dataset.service.js.map +1 -1
- package/dist/server/modules/experiment/experiment.controller.d.ts +8 -8
- package/dist/server/modules/experiment/experiment.repository.d.ts.map +1 -1
- package/dist/server/modules/experiment/experiment.repository.js +28 -0
- package/dist/server/modules/experiment/experiment.repository.js.map +1 -1
- package/dist/server/modules/experiment/experiment.service.d.ts.map +1 -1
- package/dist/server/modules/experiment/experiment.service.js +6 -3
- package/dist/server/modules/experiment/experiment.service.js.map +1 -1
- package/dist/server/modules/model/project-model.controller.d.ts +5 -5
- package/dist/server/modules/monitoring/monitoring.repository.js +1 -1
- package/dist/server/modules/optimization/optimization.controller.d.ts +12 -12
- package/dist/server/modules/optimization/optimization.repository.d.ts +6 -0
- package/dist/server/modules/optimization/optimization.repository.d.ts.map +1 -1
- package/dist/server/modules/optimization/optimization.repository.js +96 -4
- package/dist/server/modules/optimization/optimization.repository.js.map +1 -1
- package/dist/server/modules/optimization/optimization.service.d.ts.map +1 -1
- package/dist/server/modules/optimization/optimization.service.js +13 -4
- package/dist/server/modules/optimization/optimization.service.js.map +1 -1
- package/dist/server/modules/optimization/optimization.workflow.js +1 -1
- package/dist/server/modules/optimization/optimization.workflow.js.map +1 -1
- package/dist/server/modules/production-release/production-release.controller.d.ts +12 -9
- package/dist/server/modules/production-release/production-release.controller.d.ts.map +1 -1
- package/dist/server/modules/production-release/production-release.repository.d.ts +2 -1
- package/dist/server/modules/production-release/production-release.repository.d.ts.map +1 -1
- package/dist/server/modules/production-release/production-release.repository.js +3 -1
- package/dist/server/modules/production-release/production-release.repository.js.map +1 -1
- package/dist/server/modules/production-release/production-release.service.d.ts.map +1 -1
- package/dist/server/modules/production-release/production-release.service.js +10 -1
- package/dist/server/modules/production-release/production-release.service.js.map +1 -1
- package/dist/server/modules/prompt/prompt-deletion.hook.d.ts +18 -0
- package/dist/server/modules/prompt/prompt-deletion.hook.d.ts.map +1 -0
- package/dist/server/modules/prompt/prompt-deletion.hook.js +69 -0
- package/dist/server/modules/prompt/prompt-deletion.hook.js.map +1 -0
- package/dist/server/modules/prompt/prompt.controller.d.ts +146 -38
- package/dist/server/modules/prompt/prompt.controller.d.ts.map +1 -1
- package/dist/server/modules/prompt/prompt.controller.js +24 -0
- package/dist/server/modules/prompt/prompt.controller.js.map +1 -1
- package/dist/server/modules/prompt/prompt.module.d.ts.map +1 -1
- package/dist/server/modules/prompt/prompt.module.js +7 -1
- package/dist/server/modules/prompt/prompt.module.js.map +1 -1
- package/dist/server/modules/prompt/prompt.repository.d.ts +33 -3
- package/dist/server/modules/prompt/prompt.repository.d.ts.map +1 -1
- package/dist/server/modules/prompt/prompt.repository.js +267 -39
- package/dist/server/modules/prompt/prompt.repository.js.map +1 -1
- package/dist/server/modules/prompt/prompt.service.d.ts +78 -6
- package/dist/server/modules/prompt/prompt.service.d.ts.map +1 -1
- package/dist/server/modules/prompt/prompt.service.js +79 -49
- package/dist/server/modules/prompt/prompt.service.js.map +1 -1
- package/dist/server/modules/quick-start/quick-start.controller.d.ts +1 -1
- package/dist/server/modules/quick-start/quick-start.service.d.ts +1 -1
- package/dist/server/modules/release-line/release-line-deletion.hook.d.ts +16 -0
- package/dist/server/modules/release-line/release-line-deletion.hook.d.ts.map +1 -0
- package/dist/server/modules/release-line/release-line-deletion.hook.js +60 -0
- package/dist/server/modules/release-line/release-line-deletion.hook.js.map +1 -0
- package/dist/server/modules/release-line/release-line.controller.d.ts +2503 -82
- package/dist/server/modules/release-line/release-line.controller.d.ts.map +1 -1
- package/dist/server/modules/release-line/release-line.controller.js +169 -0
- package/dist/server/modules/release-line/release-line.controller.js.map +1 -1
- package/dist/server/modules/release-line/release-line.module.d.ts.map +1 -1
- package/dist/server/modules/release-line/release-line.module.js +8 -1
- package/dist/server/modules/release-line/release-line.module.js.map +1 -1
- package/dist/server/modules/release-line/release-line.repository.d.ts +55 -3
- package/dist/server/modules/release-line/release-line.repository.d.ts.map +1 -1
- package/dist/server/modules/release-line/release-line.repository.js +797 -111
- package/dist/server/modules/release-line/release-line.repository.js.map +1 -1
- package/dist/server/modules/release-line/release-line.service.d.ts +22 -5
- package/dist/server/modules/release-line/release-line.service.d.ts.map +1 -1
- package/dist/server/modules/release-line/release-line.service.js +231 -3
- package/dist/server/modules/release-line/release-line.service.js.map +1 -1
- package/dist/server/modules/release-line/release-runner.repository.d.ts +2 -1
- package/dist/server/modules/release-line/release-runner.repository.d.ts.map +1 -1
- package/dist/server/modules/release-line/release-runner.repository.js +14 -10
- package/dist/server/modules/release-line/release-runner.repository.js.map +1 -1
- package/dist/server/modules/release-line/release-runner.service.d.ts +2 -1
- package/dist/server/modules/release-line/release-runner.service.d.ts.map +1 -1
- package/dist/server/modules/release-line/release-runner.service.js +105 -11
- package/dist/server/modules/release-line/release-runner.service.js.map +1 -1
- package/dist/server/modules/release-line/release-variable-mapping.d.ts +9 -0
- package/dist/server/modules/release-line/release-variable-mapping.d.ts.map +1 -0
- package/dist/server/modules/release-line/release-variable-mapping.js +83 -0
- package/dist/server/modules/release-line/release-variable-mapping.js.map +1 -0
- package/dist/server/modules/run-result/run-result.controller.d.ts +10 -7
- package/dist/server/modules/run-result/run-result.controller.d.ts.map +1 -1
- package/dist/server/modules/run-result/run-result.repository.d.ts.map +1 -1
- package/dist/server/modules/run-result/run-result.repository.js +43 -18
- package/dist/server/modules/run-result/run-result.repository.js.map +1 -1
- package/dist/webhook/channels/webhook/webhook.controller.d.ts +4 -0
- package/dist/webhook/channels/webhook/webhook.controller.d.ts.map +1 -1
- package/dist/webhook/channels/webhook/webhook.service.d.ts +2 -0
- package/dist/webhook/channels/webhook/webhook.service.d.ts.map +1 -1
- package/dist/webhook/channels/webhook/webhook.service.js +6 -0
- package/dist/webhook/channels/webhook/webhook.service.js.map +1 -1
- package/dist/worker/consumers/llm.consumer.js +3 -3
- package/dist/worker/consumers/llm.consumer.js.map +1 -1
- package/dist/worker/runners/llm-runner.d.ts.map +1 -1
- package/dist/worker/runners/llm-runner.js +9 -1
- package/dist/worker/runners/llm-runner.js.map +1 -1
- package/dist/worker/runners/run-result-writer.js +3 -3
- package/package.json +12 -12
|
@@ -39,28 +39,78 @@ let AnnotationRepository = class AnnotationRepository {
|
|
|
39
39
|
line.status AS release_line_status,
|
|
40
40
|
line.prompt_name AS prompt_name,
|
|
41
41
|
line.input_connector_name AS input_connector_name,
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
version.id AS release_version_id,
|
|
43
|
+
version.kind AS release_version_kind,
|
|
44
|
+
version.production_version_number,
|
|
45
|
+
version.target_production_version_number,
|
|
46
|
+
version.candidate_number,
|
|
47
|
+
version.prompt_version_id,
|
|
48
|
+
version.prompt_version_number,
|
|
49
|
+
version.prompt_version_snapshot,
|
|
50
|
+
COALESCE(version.model_snapshot->>'name', model.name) AS model_name,
|
|
48
51
|
COALESCE(
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
version.model_snapshot->>'providerType',
|
|
53
|
+
version.model_snapshot->>'provider',
|
|
51
54
|
model.provider_type
|
|
52
55
|
) AS model_provider,
|
|
53
|
-
|
|
56
|
+
version.model_id,
|
|
57
|
+
COUNT(rr.id)::int AS run_result_count,
|
|
54
58
|
COUNT(rr.id) FILTER (WHERE event.lane_type = 'canary')::int AS canary_count,
|
|
55
|
-
COUNT(rr.id) FILTER (WHERE event.lane_type = 'production')::int AS online_count
|
|
59
|
+
COUNT(rr.id) FILTER (WHERE event.lane_type = 'production')::int AS online_count,
|
|
60
|
+
(
|
|
61
|
+
SELECT COALESCE(jsonb_object_agg(category_counts.category, category_counts.total), '{}'::jsonb)
|
|
62
|
+
FROM (
|
|
63
|
+
SELECT category_rr.decision_output AS category, COUNT(*)::int AS total
|
|
64
|
+
FROM ph_runs.run_results category_rr
|
|
65
|
+
INNER JOIN ph_releases.release_line_events category_event
|
|
66
|
+
ON category_event.id = category_rr.source_id
|
|
67
|
+
AND category_event.project_id = category_rr.project_id
|
|
68
|
+
INNER JOIN ph_releases.release_versions category_version
|
|
69
|
+
ON category_version.id = COALESCE(category_rr.release_version_id, category_event.release_version_id)
|
|
70
|
+
WHERE category_rr.project_id = line.project_id
|
|
71
|
+
AND category_rr.source = 'release'
|
|
72
|
+
AND category_event.release_line_id = line.id
|
|
73
|
+
AND category_version.id = version.id
|
|
74
|
+
AND category_rr.decision_output IS NOT NULL
|
|
75
|
+
GROUP BY category_rr.decision_output
|
|
76
|
+
) category_counts
|
|
77
|
+
) AS category_counts,
|
|
78
|
+
(
|
|
79
|
+
SELECT COUNT(*)::int
|
|
80
|
+
FROM ph_runs.run_results journey_rr
|
|
81
|
+
INNER JOIN ph_releases.release_line_events journey_event
|
|
82
|
+
ON journey_event.id = journey_rr.source_id
|
|
83
|
+
AND journey_event.project_id = journey_rr.project_id
|
|
84
|
+
INNER JOIN ph_releases.release_versions journey_version
|
|
85
|
+
ON journey_version.id = COALESCE(journey_rr.release_version_id, journey_event.release_version_id)
|
|
86
|
+
WHERE journey_rr.project_id = line.project_id
|
|
87
|
+
AND journey_rr.source = 'release'
|
|
88
|
+
AND journey_event.release_line_id = line.id
|
|
89
|
+
AND journey_event.lane_type = 'canary'
|
|
90
|
+
AND journey_version.target_production_version_number = version.target_production_version_number
|
|
91
|
+
) AS journey_canary_count,
|
|
92
|
+
(
|
|
93
|
+
SELECT COUNT(*)::int
|
|
94
|
+
FROM ph_runs.run_results journey_rr
|
|
95
|
+
INNER JOIN ph_releases.release_line_events journey_event
|
|
96
|
+
ON journey_event.id = journey_rr.source_id
|
|
97
|
+
AND journey_event.project_id = journey_rr.project_id
|
|
98
|
+
INNER JOIN ph_releases.release_versions journey_version
|
|
99
|
+
ON journey_version.id = COALESCE(journey_rr.release_version_id, journey_event.release_version_id)
|
|
100
|
+
WHERE journey_rr.project_id = line.project_id
|
|
101
|
+
AND journey_rr.source = 'release'
|
|
102
|
+
AND journey_event.release_line_id = line.id
|
|
103
|
+
AND journey_event.lane_type = 'production'
|
|
104
|
+
AND journey_version.target_production_version_number = version.target_production_version_number
|
|
105
|
+
) AS journey_online_count
|
|
56
106
|
FROM ph_releases.release_lines line
|
|
57
|
-
INNER JOIN ph_releases.
|
|
58
|
-
ON
|
|
59
|
-
AND
|
|
60
|
-
LEFT JOIN ph_assets.models model ON model.id =
|
|
107
|
+
INNER JOIN ph_releases.release_versions version
|
|
108
|
+
ON version.release_line_id = line.id
|
|
109
|
+
AND version.project_id = line.project_id
|
|
110
|
+
LEFT JOIN ph_assets.models model ON model.id = version.model_id
|
|
61
111
|
LEFT JOIN ph_releases.release_line_events event
|
|
62
112
|
ON event.release_line_id = line.id
|
|
63
|
-
AND event.
|
|
113
|
+
AND event.release_version_id = version.id
|
|
64
114
|
AND event.project_id = line.project_id
|
|
65
115
|
LEFT JOIN ph_runs.run_results rr
|
|
66
116
|
ON rr.source = 'release'
|
|
@@ -74,16 +124,19 @@ let AnnotationRepository = class AnnotationRepository {
|
|
|
74
124
|
line.status,
|
|
75
125
|
line.prompt_name,
|
|
76
126
|
line.input_connector_name,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
127
|
+
version.id,
|
|
128
|
+
version.kind,
|
|
129
|
+
version.production_version_number,
|
|
130
|
+
version.target_production_version_number,
|
|
131
|
+
version.candidate_number,
|
|
132
|
+
version.prompt_version_id,
|
|
133
|
+
version.prompt_version_number,
|
|
134
|
+
version.prompt_version_snapshot,
|
|
135
|
+
version.model_id,
|
|
136
|
+
version.model_snapshot,
|
|
84
137
|
model.name,
|
|
85
138
|
model.provider_type
|
|
86
|
-
ORDER BY line.updated_at DESC,
|
|
139
|
+
ORDER BY line.updated_at DESC, version.target_production_version_number ASC, version.kind DESC, version.candidate_number ASC
|
|
87
140
|
`);
|
|
88
141
|
const byLine = new Map();
|
|
89
142
|
for (const row of unwrapRows(rows)) {
|
|
@@ -94,64 +147,125 @@ let AnnotationRepository = class AnnotationRepository {
|
|
|
94
147
|
status: String(row['release_line_status'] ?? ''),
|
|
95
148
|
promptName: String(row['prompt_name'] ?? ''),
|
|
96
149
|
inputConnectorName: row['input_connector_name'] ?? null,
|
|
97
|
-
|
|
150
|
+
versions: [],
|
|
98
151
|
};
|
|
99
|
-
const
|
|
100
|
-
line.
|
|
101
|
-
id: row['
|
|
152
|
+
const categoryOptions = (0, shared_1.deriveClassificationOptionsFromPromptVersionSnapshot)(row['prompt_version_snapshot']);
|
|
153
|
+
line.versions.push({
|
|
154
|
+
id: row['release_version_id'],
|
|
102
155
|
releaseLineId: lineId,
|
|
103
|
-
label:
|
|
156
|
+
label: formatReleaseVersionLabel(row),
|
|
157
|
+
kind: row['release_version_kind'],
|
|
158
|
+
productionVersionNumber: toNumberOrNull(row['production_version_number']),
|
|
159
|
+
targetProductionVersionNumber: Number(row['target_production_version_number'] ?? 1),
|
|
160
|
+
candidateNumber: toNumberOrNull(row['candidate_number']),
|
|
104
161
|
promptVersionId: row['prompt_version_id'],
|
|
105
162
|
promptVersionNumber: toNumberOrNull(row['prompt_version_number']),
|
|
106
163
|
promptVersionLabel: formatPromptVersionLabel(row['prompt_version_number']),
|
|
107
|
-
categoryOptions
|
|
164
|
+
categoryOptions,
|
|
108
165
|
modelId: row['model_id'],
|
|
109
166
|
modelName: row['model_name'] ?? null,
|
|
110
167
|
modelProvider: row['model_provider'] ?? null,
|
|
168
|
+
runResultCount: Number(row['run_result_count'] ?? 0),
|
|
111
169
|
canaryCount: Number(row['canary_count'] ?? 0),
|
|
112
170
|
onlineCount: Number(row['online_count'] ?? 0),
|
|
171
|
+
journeyCanaryCount: Number(row['journey_canary_count'] ?? 0),
|
|
172
|
+
journeyOnlineCount: Number(row['journey_online_count'] ?? 0),
|
|
173
|
+
journeyCompatible: true,
|
|
174
|
+
categoryCounts: parseCategoryCounts(row['category_counts'], categoryOptions),
|
|
113
175
|
});
|
|
114
176
|
byLine.set(lineId, line);
|
|
115
177
|
}
|
|
178
|
+
for (const line of byLine.values()) {
|
|
179
|
+
const targetCategoryKeys = new Map();
|
|
180
|
+
for (const version of line.versions) {
|
|
181
|
+
const currentKey = categoryOptionsKey(version.categoryOptions);
|
|
182
|
+
const previousKey = targetCategoryKeys.get(version.targetProductionVersionNumber);
|
|
183
|
+
targetCategoryKeys.set(version.targetProductionVersionNumber, previousKey === undefined || previousKey === currentKey ? currentKey : null);
|
|
184
|
+
}
|
|
185
|
+
for (const version of line.versions) {
|
|
186
|
+
version.journeyCompatible =
|
|
187
|
+
targetCategoryKeys.get(version.targetProductionVersionNumber) === categoryOptionsKey(version.categoryOptions);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
116
190
|
return Array.from(byLine.values());
|
|
117
191
|
}
|
|
118
|
-
async countMatchingRunResults(projectId, releaseLineId,
|
|
192
|
+
async countMatchingRunResults(projectId, releaseLineId, releaseVersionId, releaseVersionScope, scope) {
|
|
193
|
+
const versionFilter = releaseVersionFilterSql(releaseVersionId, releaseVersionScope);
|
|
194
|
+
const scopeFilter = scopeFilterSql(scope);
|
|
119
195
|
const rows = await this.db.execute((0, drizzle_orm_1.sql) `
|
|
120
196
|
SELECT COUNT(*)::int AS total
|
|
121
197
|
FROM ph_runs.run_results rr
|
|
122
198
|
INNER JOIN ph_releases.release_line_events event
|
|
123
199
|
ON event.id = rr.source_id
|
|
124
200
|
AND event.project_id = rr.project_id
|
|
201
|
+
LEFT JOIN ph_releases.release_versions version
|
|
202
|
+
ON version.id = COALESCE(rr.release_version_id, event.release_version_id)
|
|
125
203
|
WHERE rr.project_id = ${projectId}::uuid
|
|
126
204
|
AND rr.source = 'release'
|
|
127
205
|
AND event.release_line_id = ${releaseLineId}::uuid
|
|
128
|
-
AND
|
|
129
|
-
AND
|
|
206
|
+
AND ${scopeFilter}
|
|
207
|
+
AND ${versionFilter}
|
|
130
208
|
`);
|
|
131
209
|
return Number(unwrapRows(rows)[0]?.['total'] ?? 0);
|
|
132
210
|
}
|
|
133
|
-
async
|
|
211
|
+
async countMatchingRunResultsByCategory(projectId, releaseLineId, releaseVersionId, releaseVersionScope, scope) {
|
|
212
|
+
const versionFilter = releaseVersionFilterSql(releaseVersionId, releaseVersionScope);
|
|
213
|
+
const scopeFilter = scopeFilterSql(scope);
|
|
214
|
+
const rows = await this.db.execute((0, drizzle_orm_1.sql) `
|
|
215
|
+
SELECT rr.decision_output AS category, COUNT(*)::int AS total
|
|
216
|
+
FROM ph_runs.run_results rr
|
|
217
|
+
INNER JOIN ph_releases.release_line_events event
|
|
218
|
+
ON event.id = rr.source_id
|
|
219
|
+
AND event.project_id = rr.project_id
|
|
220
|
+
LEFT JOIN ph_releases.release_versions version
|
|
221
|
+
ON version.id = COALESCE(rr.release_version_id, event.release_version_id)
|
|
222
|
+
WHERE rr.project_id = ${projectId}::uuid
|
|
223
|
+
AND rr.source = 'release'
|
|
224
|
+
AND event.release_line_id = ${releaseLineId}::uuid
|
|
225
|
+
AND ${scopeFilter}
|
|
226
|
+
AND ${versionFilter}
|
|
227
|
+
AND rr.decision_output IS NOT NULL
|
|
228
|
+
GROUP BY rr.decision_output
|
|
229
|
+
`);
|
|
230
|
+
return new Map(unwrapRows(rows).map((row) => [String(row['category']), Number(row['total'] ?? 0)]));
|
|
231
|
+
}
|
|
232
|
+
async findReleaseVersionCategoryOptions(projectId, releaseLineId, releaseVersionId, releaseVersionScope) {
|
|
233
|
+
const versionFilter = releaseVersionScope === 'journey'
|
|
234
|
+
? (0, drizzle_orm_1.sql) `version.target_production_version_number = selected.target_production_version_number`
|
|
235
|
+
: (0, drizzle_orm_1.sql) `version.id = selected.id`;
|
|
134
236
|
const rows = await this.db.execute((0, drizzle_orm_1.sql) `
|
|
135
|
-
SELECT
|
|
136
|
-
FROM ph_releases.
|
|
237
|
+
SELECT version.prompt_version_snapshot
|
|
238
|
+
FROM ph_releases.release_versions selected
|
|
239
|
+
INNER JOIN ph_releases.release_versions version
|
|
240
|
+
ON version.project_id = selected.project_id
|
|
241
|
+
AND version.release_line_id = selected.release_line_id
|
|
137
242
|
INNER JOIN ph_releases.release_lines line
|
|
138
|
-
ON line.id =
|
|
139
|
-
AND line.project_id =
|
|
140
|
-
WHERE
|
|
141
|
-
AND
|
|
142
|
-
AND
|
|
243
|
+
ON line.id = version.release_line_id
|
|
244
|
+
AND line.project_id = version.project_id
|
|
245
|
+
WHERE selected.project_id = ${projectId}::uuid
|
|
246
|
+
AND selected.id = ${releaseVersionId}::uuid
|
|
247
|
+
AND selected.release_line_id = ${releaseLineId}::uuid
|
|
248
|
+
AND ${versionFilter}
|
|
143
249
|
AND line.status <> 'archived'
|
|
144
|
-
|
|
250
|
+
ORDER BY version.kind, version.candidate_number NULLS FIRST, version.production_version_number NULLS LAST
|
|
145
251
|
`);
|
|
146
|
-
|
|
252
|
+
const optionSets = unwrapRows(rows).map((row) => (0, shared_1.deriveClassificationOptionsFromPromptVersionSnapshot)(row['prompt_version_snapshot']));
|
|
253
|
+
if (optionSets.length === 0)
|
|
254
|
+
return { options: [], compatible: true };
|
|
255
|
+
const firstKey = categoryOptionsKey(optionSets[0] ?? []);
|
|
256
|
+
const compatible = optionSets.every((options) => categoryOptionsKey(options) === firstKey);
|
|
257
|
+
return { options: compatible ? (optionSets[0] ?? []) : [], compatible };
|
|
147
258
|
}
|
|
148
259
|
async createTask(projectId, input, actorUserId, availableCount, categoryOptions) {
|
|
149
260
|
const annotationSchema = JSON.stringify(buildAnnotationSchema(categoryOptions));
|
|
261
|
+
const requestedSampleSize = getRequestedSampleSize(input);
|
|
262
|
+
const categorySampleCounts = getPositiveCategorySampleCounts(input);
|
|
150
263
|
return this.db.transaction(async (tx) => {
|
|
151
264
|
const taskRows = await tx.execute((0, drizzle_orm_1.sql) `
|
|
152
265
|
INSERT INTO ph_releases.annotation_tasks (
|
|
153
266
|
scope,
|
|
154
|
-
|
|
267
|
+
release_version_id,
|
|
268
|
+
release_version_scope,
|
|
155
269
|
name,
|
|
156
270
|
annotation_schema,
|
|
157
271
|
sampling_config,
|
|
@@ -164,15 +278,19 @@ let AnnotationRepository = class AnnotationRepository {
|
|
|
164
278
|
)
|
|
165
279
|
SELECT
|
|
166
280
|
${input.scope},
|
|
167
|
-
|
|
281
|
+
version.id,
|
|
282
|
+
${input.releaseVersionScope},
|
|
168
283
|
${input.name},
|
|
169
284
|
${annotationSchema}::jsonb,
|
|
170
285
|
${JSON.stringify({
|
|
171
286
|
releaseLineId: input.releaseLineId,
|
|
172
|
-
|
|
287
|
+
releaseVersionId: input.releaseVersionId,
|
|
288
|
+
releaseVersionScope: input.releaseVersionScope,
|
|
173
289
|
scope: input.scope,
|
|
290
|
+
samplingMode: input.samplingMode,
|
|
174
291
|
availableCount,
|
|
175
|
-
sampleSize:
|
|
292
|
+
sampleSize: requestedSampleSize,
|
|
293
|
+
categorySampleCounts,
|
|
176
294
|
})}::jsonb,
|
|
177
295
|
0,
|
|
178
296
|
0,
|
|
@@ -180,13 +298,13 @@ let AnnotationRepository = class AnnotationRepository {
|
|
|
180
298
|
${actorUserId}::uuid,
|
|
181
299
|
NOW(),
|
|
182
300
|
NOW()
|
|
183
|
-
FROM ph_releases.
|
|
301
|
+
FROM ph_releases.release_versions version
|
|
184
302
|
INNER JOIN ph_releases.release_lines line
|
|
185
|
-
ON line.id =
|
|
186
|
-
AND line.project_id =
|
|
187
|
-
WHERE
|
|
188
|
-
AND
|
|
189
|
-
AND
|
|
303
|
+
ON line.id = version.release_line_id
|
|
304
|
+
AND line.project_id = version.project_id
|
|
305
|
+
WHERE version.project_id = ${projectId}::uuid
|
|
306
|
+
AND version.id = ${input.releaseVersionId}::uuid
|
|
307
|
+
AND version.release_line_id = ${input.releaseLineId}::uuid
|
|
190
308
|
AND line.status <> 'archived'
|
|
191
309
|
RETURNING id
|
|
192
310
|
`);
|
|
@@ -194,20 +312,7 @@ let AnnotationRepository = class AnnotationRepository {
|
|
|
194
312
|
if (!taskId)
|
|
195
313
|
throw new Error('annotation_task_source_not_found');
|
|
196
314
|
const insertedRows = await tx.execute((0, drizzle_orm_1.sql) `
|
|
197
|
-
WITH
|
|
198
|
-
SELECT rr.id, rr.created_at
|
|
199
|
-
FROM ph_runs.run_results rr
|
|
200
|
-
INNER JOIN ph_releases.release_line_events event
|
|
201
|
-
ON event.id = rr.source_id
|
|
202
|
-
AND event.project_id = rr.project_id
|
|
203
|
-
WHERE rr.project_id = ${projectId}::uuid
|
|
204
|
-
AND rr.source = 'release'
|
|
205
|
-
AND event.release_line_id = ${input.releaseLineId}::uuid
|
|
206
|
-
AND event.lane_type = ${scopeToLane(input.scope)}
|
|
207
|
-
AND COALESCE(rr.release_variant_id, event.release_variant_id) = ${input.releaseVariantId}::uuid
|
|
208
|
-
ORDER BY rr.created_at DESC
|
|
209
|
-
LIMIT ${input.sampleSize}
|
|
210
|
-
),
|
|
315
|
+
WITH ${sampleCandidateCtesSql(projectId, input, requestedSampleSize, categorySampleCounts)},
|
|
211
316
|
inserted AS (
|
|
212
317
|
INSERT INTO ph_runs.annotations (
|
|
213
318
|
run_result_id,
|
|
@@ -243,11 +348,11 @@ let AnnotationRepository = class AnnotationRepository {
|
|
|
243
348
|
});
|
|
244
349
|
}
|
|
245
350
|
async listTasks(projectId) {
|
|
246
|
-
const rows = await this.db.execute(taskSelectSql((0, drizzle_orm_1.sql) `
|
|
351
|
+
const rows = await this.db.execute(taskSelectSql((0, drizzle_orm_1.sql) `version.project_id = ${projectId}::uuid`));
|
|
247
352
|
return unwrapRows(rows).map(mapTaskRow);
|
|
248
353
|
}
|
|
249
354
|
async findTask(projectId, taskId) {
|
|
250
|
-
const rows = await this.db.execute(taskSelectSql((0, drizzle_orm_1.sql) `
|
|
355
|
+
const rows = await this.db.execute(taskSelectSql((0, drizzle_orm_1.sql) `version.project_id = ${projectId}::uuid AND task.id = ${taskId}::uuid`));
|
|
251
356
|
return unwrapRows(rows).map(mapTaskRow)[0] ?? null;
|
|
252
357
|
}
|
|
253
358
|
async listSamples(taskId, filter) {
|
|
@@ -383,9 +488,10 @@ function taskSelectSql(whereSql) {
|
|
|
383
488
|
return (0, drizzle_orm_1.sql) `
|
|
384
489
|
SELECT
|
|
385
490
|
task.id,
|
|
386
|
-
|
|
491
|
+
version.project_id,
|
|
387
492
|
task.name,
|
|
388
493
|
task.scope,
|
|
494
|
+
task.release_version_scope,
|
|
389
495
|
task.status,
|
|
390
496
|
task.annotation_schema,
|
|
391
497
|
task.created_by,
|
|
@@ -393,17 +499,20 @@ function taskSelectSql(whereSql) {
|
|
|
393
499
|
task.updated_at,
|
|
394
500
|
line.id AS release_line_id,
|
|
395
501
|
line.name AS release_line_name,
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
502
|
+
version.id AS release_version_id,
|
|
503
|
+
version.kind AS release_version_kind,
|
|
504
|
+
version.production_version_number,
|
|
505
|
+
version.target_production_version_number,
|
|
506
|
+
version.candidate_number,
|
|
507
|
+
version.prompt_name,
|
|
508
|
+
version.prompt_version_id,
|
|
509
|
+
version.prompt_version_number,
|
|
510
|
+
version.prompt_version_snapshot,
|
|
511
|
+
version.model_id,
|
|
512
|
+
COALESCE(version.model_snapshot->>'name', model.name) AS model_name,
|
|
404
513
|
COALESCE(
|
|
405
|
-
|
|
406
|
-
|
|
514
|
+
version.model_snapshot->>'providerType',
|
|
515
|
+
version.model_snapshot->>'provider',
|
|
407
516
|
model.provider_type
|
|
408
517
|
) AS model_provider,
|
|
409
518
|
COUNT(annotation.id)::int AS total,
|
|
@@ -425,17 +534,18 @@ function taskSelectSql(whereSql) {
|
|
|
425
534
|
AND (rr.decision_output IS NULL OR annotation.fields->>'expected_output' <> rr.decision_output)
|
|
426
535
|
)::int AS mismatched
|
|
427
536
|
FROM ph_releases.annotation_tasks task
|
|
428
|
-
INNER JOIN ph_releases.
|
|
429
|
-
INNER JOIN ph_releases.release_lines line ON line.id =
|
|
430
|
-
LEFT JOIN ph_assets.models model ON model.id =
|
|
537
|
+
INNER JOIN ph_releases.release_versions version ON version.id = task.release_version_id
|
|
538
|
+
INNER JOIN ph_releases.release_lines line ON line.id = version.release_line_id
|
|
539
|
+
LEFT JOIN ph_assets.models model ON model.id = version.model_id
|
|
431
540
|
LEFT JOIN ph_runs.annotations annotation ON annotation.task_id = task.id
|
|
432
541
|
LEFT JOIN ph_runs.run_results rr ON rr.id = annotation.run_result_id
|
|
433
542
|
WHERE ${whereSql}
|
|
434
543
|
GROUP BY
|
|
435
544
|
task.id,
|
|
436
|
-
|
|
545
|
+
version.project_id,
|
|
437
546
|
task.name,
|
|
438
547
|
task.scope,
|
|
548
|
+
task.release_version_scope,
|
|
439
549
|
task.status,
|
|
440
550
|
task.annotation_schema,
|
|
441
551
|
task.created_by,
|
|
@@ -443,14 +553,17 @@ function taskSelectSql(whereSql) {
|
|
|
443
553
|
task.updated_at,
|
|
444
554
|
line.id,
|
|
445
555
|
line.name,
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
556
|
+
version.id,
|
|
557
|
+
version.kind,
|
|
558
|
+
version.production_version_number,
|
|
559
|
+
version.target_production_version_number,
|
|
560
|
+
version.candidate_number,
|
|
561
|
+
version.prompt_name,
|
|
562
|
+
version.prompt_version_id,
|
|
563
|
+
version.prompt_version_number,
|
|
564
|
+
version.prompt_version_snapshot,
|
|
565
|
+
version.model_id,
|
|
566
|
+
version.model_snapshot,
|
|
454
567
|
model.name,
|
|
455
568
|
model.provider_type
|
|
456
569
|
ORDER BY task.created_at DESC
|
|
@@ -510,7 +623,6 @@ function mapTaskRow(row) {
|
|
|
510
623
|
const matched = Number(row['matched'] ?? 0);
|
|
511
624
|
const mismatched = Number(row['mismatched'] ?? 0);
|
|
512
625
|
const judged = matched + mismatched;
|
|
513
|
-
const variantNumber = Number(row['variant_number'] ?? 0);
|
|
514
626
|
return {
|
|
515
627
|
id: row['id'],
|
|
516
628
|
projectId: row['project_id'],
|
|
@@ -518,8 +630,9 @@ function mapTaskRow(row) {
|
|
|
518
630
|
scope: row['scope'],
|
|
519
631
|
releaseLineId: row['release_line_id'],
|
|
520
632
|
releaseLineName: String(row['release_line_name'] ?? ''),
|
|
521
|
-
|
|
522
|
-
|
|
633
|
+
releaseVersionId: row['release_version_id'],
|
|
634
|
+
releaseVersionLabel: formatReleaseVersionLabel(row),
|
|
635
|
+
releaseVersionScope: row['release_version_scope'] ?? 'exact',
|
|
523
636
|
promptName: String(row['prompt_name'] ?? ''),
|
|
524
637
|
promptVersionId: row['prompt_version_id'],
|
|
525
638
|
promptVersionNumber: toNumberOrNull(row['prompt_version_number']),
|
|
@@ -583,15 +696,140 @@ function buildAnnotationSchema(categoryOptions) {
|
|
|
583
696
|
},
|
|
584
697
|
];
|
|
585
698
|
}
|
|
699
|
+
function sampleCandidateCtesSql(projectId, input, sampleSize, categorySampleCounts) {
|
|
700
|
+
const scopeFilter = scopeFilterSql(input.scope);
|
|
701
|
+
const versionFilter = releaseVersionFilterSql(input.releaseVersionId, input.releaseVersionScope);
|
|
702
|
+
if (input.samplingMode === 'per_category') {
|
|
703
|
+
const categoryRequestsJson = JSON.stringify(categorySampleCounts.map((item) => ({ category: item.category, sample_size: item.sampleSize })));
|
|
704
|
+
return (0, drizzle_orm_1.sql) `
|
|
705
|
+
requested_categories AS (
|
|
706
|
+
SELECT category, sample_size
|
|
707
|
+
FROM jsonb_to_recordset(${categoryRequestsJson}::jsonb) AS requested(category text, sample_size int)
|
|
708
|
+
WHERE sample_size > 0
|
|
709
|
+
),
|
|
710
|
+
ranked_candidates AS (
|
|
711
|
+
SELECT
|
|
712
|
+
rr.id,
|
|
713
|
+
rr.created_at,
|
|
714
|
+
rr.decision_output,
|
|
715
|
+
ROW_NUMBER() OVER (PARTITION BY rr.decision_output ORDER BY random()) AS category_rank
|
|
716
|
+
FROM ph_runs.run_results rr
|
|
717
|
+
INNER JOIN ph_releases.release_line_events event
|
|
718
|
+
ON event.id = rr.source_id
|
|
719
|
+
AND event.project_id = rr.project_id
|
|
720
|
+
LEFT JOIN ph_releases.release_versions version
|
|
721
|
+
ON version.id = COALESCE(rr.release_version_id, event.release_version_id)
|
|
722
|
+
INNER JOIN requested_categories requested
|
|
723
|
+
ON requested.category = rr.decision_output
|
|
724
|
+
WHERE rr.project_id = ${projectId}::uuid
|
|
725
|
+
AND rr.source = 'release'
|
|
726
|
+
AND event.release_line_id = ${input.releaseLineId}::uuid
|
|
727
|
+
AND ${scopeFilter}
|
|
728
|
+
AND ${versionFilter}
|
|
729
|
+
),
|
|
730
|
+
candidates AS (
|
|
731
|
+
SELECT ranked_candidates.id, ranked_candidates.created_at
|
|
732
|
+
FROM ranked_candidates
|
|
733
|
+
INNER JOIN requested_categories requested
|
|
734
|
+
ON requested.category = ranked_candidates.decision_output
|
|
735
|
+
WHERE ranked_candidates.category_rank <= requested.sample_size
|
|
736
|
+
)
|
|
737
|
+
`;
|
|
738
|
+
}
|
|
739
|
+
return (0, drizzle_orm_1.sql) `
|
|
740
|
+
candidates AS (
|
|
741
|
+
SELECT rr.id, rr.created_at
|
|
742
|
+
FROM ph_runs.run_results rr
|
|
743
|
+
INNER JOIN ph_releases.release_line_events event
|
|
744
|
+
ON event.id = rr.source_id
|
|
745
|
+
AND event.project_id = rr.project_id
|
|
746
|
+
LEFT JOIN ph_releases.release_versions version
|
|
747
|
+
ON version.id = COALESCE(rr.release_version_id, event.release_version_id)
|
|
748
|
+
WHERE rr.project_id = ${projectId}::uuid
|
|
749
|
+
AND rr.source = 'release'
|
|
750
|
+
AND event.release_line_id = ${input.releaseLineId}::uuid
|
|
751
|
+
AND ${scopeFilter}
|
|
752
|
+
AND ${versionFilter}
|
|
753
|
+
ORDER BY random()
|
|
754
|
+
LIMIT ${sampleSize}
|
|
755
|
+
)
|
|
756
|
+
`;
|
|
757
|
+
}
|
|
758
|
+
function getRequestedSampleSize(input) {
|
|
759
|
+
if (input.samplingMode === 'per_category') {
|
|
760
|
+
return getPositiveCategorySampleCounts(input).reduce((sum, item) => sum + item.sampleSize, 0);
|
|
761
|
+
}
|
|
762
|
+
return input.sampleSize ?? 0;
|
|
763
|
+
}
|
|
764
|
+
function getPositiveCategorySampleCounts(input) {
|
|
765
|
+
return (input.categorySampleCounts ?? []).filter((item) => item.sampleSize > 0);
|
|
766
|
+
}
|
|
586
767
|
function getTaskCategoryOptions(row) {
|
|
587
768
|
const fromSchema = (0, shared_1.deriveClassificationOptionsFromAnnotationSchema)(row['annotation_schema']);
|
|
588
769
|
if (fromSchema.length > 0)
|
|
589
770
|
return fromSchema;
|
|
590
771
|
return (0, shared_1.deriveClassificationOptionsFromPromptVersionSnapshot)(row['prompt_version_snapshot']);
|
|
591
772
|
}
|
|
773
|
+
function scopeFilterSql(scope) {
|
|
774
|
+
if (scope === 'all')
|
|
775
|
+
return (0, drizzle_orm_1.sql) `TRUE`;
|
|
776
|
+
return (0, drizzle_orm_1.sql) `event.lane_type = ${scopeToLane(scope)}`;
|
|
777
|
+
}
|
|
592
778
|
function scopeToLane(scope) {
|
|
593
779
|
return scope === 'online' ? 'production' : 'canary';
|
|
594
780
|
}
|
|
781
|
+
function parseCategoryCounts(value, categoryOptions) {
|
|
782
|
+
const counts = parseCountMap(value);
|
|
783
|
+
const categories = categoryOptions.length > 0 ? categoryOptions : Array.from(counts.keys()).sort();
|
|
784
|
+
return categories.map((category) => ({ category, count: counts.get(category) ?? 0 }));
|
|
785
|
+
}
|
|
786
|
+
function parseCountMap(value) {
|
|
787
|
+
const raw = parseRecord(value);
|
|
788
|
+
return new Map(Object.entries(raw)
|
|
789
|
+
.map(([category, count]) => [category, Number(count)])
|
|
790
|
+
.filter((entry) => entry[0].length > 0 && Number.isFinite(entry[1])));
|
|
791
|
+
}
|
|
792
|
+
function parseRecord(value) {
|
|
793
|
+
if (isRecord(value))
|
|
794
|
+
return value;
|
|
795
|
+
if (typeof value !== 'string')
|
|
796
|
+
return {};
|
|
797
|
+
try {
|
|
798
|
+
const parsed = JSON.parse(value);
|
|
799
|
+
return isRecord(parsed) ? parsed : {};
|
|
800
|
+
}
|
|
801
|
+
catch {
|
|
802
|
+
return {};
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
function releaseVersionFilterSql(releaseVersionId, releaseVersionScope) {
|
|
806
|
+
// The version table is LEFT JOINed so that release-line traffic whose
|
|
807
|
+
// release_version_id was nulled out by a run-config / route change (both
|
|
808
|
+
// rr.release_version_id and event.release_version_id NULL) is still reachable.
|
|
809
|
+
// For the version-scoped ('exact') path we restrict to the specific version,
|
|
810
|
+
// and a NULL-version row legitimately does not match it. For the
|
|
811
|
+
// non-version-scoped ('journey') path the task spans the whole release-line
|
|
812
|
+
// journey, so those detached NULL-version rows must be included.
|
|
813
|
+
if (releaseVersionScope === 'exact')
|
|
814
|
+
return (0, drizzle_orm_1.sql) `version.id = ${releaseVersionId}::uuid`;
|
|
815
|
+
return (0, drizzle_orm_1.sql) `(
|
|
816
|
+
version.id IS NULL
|
|
817
|
+
OR version.target_production_version_number = (
|
|
818
|
+
SELECT selected.target_production_version_number
|
|
819
|
+
FROM ph_releases.release_versions selected
|
|
820
|
+
WHERE selected.id = ${releaseVersionId}::uuid
|
|
821
|
+
AND selected.release_line_id = version.release_line_id
|
|
822
|
+
LIMIT 1
|
|
823
|
+
)
|
|
824
|
+
)`;
|
|
825
|
+
}
|
|
826
|
+
function categoryOptionsKey(options) {
|
|
827
|
+
// Canonicalize for set-equality comparison only: identical category sets in
|
|
828
|
+
// different declaration order must produce the same key. This key is used
|
|
829
|
+
// solely for compatibility checks, never to derive the displayed/stored
|
|
830
|
+
// option order (which is preserved from the source snapshot).
|
|
831
|
+
return JSON.stringify([...options].sort());
|
|
832
|
+
}
|
|
595
833
|
function unwrapRows(result) {
|
|
596
834
|
if (Array.isArray(result))
|
|
597
835
|
return result;
|
|
@@ -624,8 +862,14 @@ function toNumberOrNull(value) {
|
|
|
624
862
|
const n = typeof value === 'number' ? value : Number(value);
|
|
625
863
|
return Number.isFinite(n) ? n : null;
|
|
626
864
|
}
|
|
627
|
-
function
|
|
628
|
-
|
|
865
|
+
function formatReleaseVersionLabel(row) {
|
|
866
|
+
const kind = row['release_version_kind'] ?? row['kind'];
|
|
867
|
+
const productionVersionNumber = toNumberOrNull(row['production_version_number']);
|
|
868
|
+
const targetProductionVersionNumber = toNumberOrNull(row['target_production_version_number']) ?? 1;
|
|
869
|
+
const candidateNumber = toNumberOrNull(row['candidate_number']) ?? 0;
|
|
870
|
+
if (kind === 'production')
|
|
871
|
+
return `v${productionVersionNumber ?? targetProductionVersionNumber}`;
|
|
872
|
+
return `v${Math.max(0, targetProductionVersionNumber - 1)}.${candidateNumber}`;
|
|
629
873
|
}
|
|
630
874
|
function formatPromptVersionLabel(value) {
|
|
631
875
|
const num = toNumberOrNull(value);
|