mirror of
https://github.com/codeflash-ai/codeflash-internal.git
synced 2026-05-04 18:25:18 +00:00
## Summary - **Fix CI build failure**: Auth0Client crashes during Next.js prerendering when env vars aren't set. Returns a no-op stub (`getSession → null`) when domain is missing — semantically correct for static generation - **Lazy-load markdown libs (~260kb)**: ReactMarkdown, remarkGfm, and react-syntax-highlighter were eagerly imported in monaco-diff-viewer but only rendered when user expands "Generated Tests". Extracted into a dynamic component - **Parallelize repo detail query**: `getRepositoryById` ran the activity count sequentially after the repo lookup. Since `repoId` is already available, all three queries now run in parallel ## Test plan - [ ] CI `build` check passes (was failing since #2598) - [ ] Trace page still renders generated tests correctly when expanded - [ ] Repository detail page loads correctly with activity status
681 lines
21 KiB
TypeScript
681 lines
21 KiB
TypeScript
import { prisma } from "@codeflash-ai/common"
|
||
import { sendSlackMessage } from "./slack_util.js"
|
||
import {
|
||
requiresApproval,
|
||
hasQualityMonitoring,
|
||
getApprovalEmoji,
|
||
getRejectionEmoji,
|
||
} from "../config/approval-config.js"
|
||
import {
|
||
missingRequiredFields,
|
||
optimizationNotFound,
|
||
internalServerError,
|
||
} from "../exceptions/index.js"
|
||
const SLACK_CHANNEL = process.env.SLACK_APPROVAL_CHANNEL_ID || process.env.SLACK_CHANNEL_ID
|
||
const APPROVAL_EMOJI = getApprovalEmoji()
|
||
const REJECTION_EMOJI = getRejectionEmoji()
|
||
|
||
const CODEFLASH_APP_BASE_URL = (
|
||
process.env.CODEFLASH_APP_URL || "https://app.codeflash.ai"
|
||
).replace(/\/$/, "") // Removes trailing slash if present
|
||
|
||
// Check if the given organization and repository are for quality monitoring
|
||
export function isQualityMonitoringRepo(owner: string, repo: string): boolean {
|
||
return hasQualityMonitoring(owner, repo)
|
||
}
|
||
|
||
// Check if the given organization and repository require approval
|
||
export function requiresApprovalForRepo(owner: string, repo: string): boolean {
|
||
// Flag to override the approval system
|
||
if (process.env.DISABLE_APPROVAL_SYSTEM === "true") {
|
||
return false
|
||
}
|
||
|
||
// Quality monitoring repos don't require approval but get notifications
|
||
if (isQualityMonitoringRepo(owner, repo)) {
|
||
return false
|
||
}
|
||
|
||
return requiresApproval(owner, repo)
|
||
}
|
||
|
||
/**
|
||
* Send quality monitoring notification for an optimization (non-blocking)
|
||
*
|
||
* @param {string} traceId - The trace ID of the optimization
|
||
* @param {string} owner - Repository owner/organization
|
||
* @param {string} repo - Repository name
|
||
* @param {string} functionName - The function being optimized
|
||
* @param {string} userId - The requesting user's ID
|
||
* @param {object} requestData - Additional optimization details, including prCommentFields and diffContents
|
||
* @param {string} prUrl - The URL of the created PR
|
||
* @returns {Promise<boolean>} - True if successful
|
||
*/
|
||
export async function sendQualityMonitoringNotification(
|
||
traceId: string,
|
||
owner: string,
|
||
repo: string,
|
||
functionName: string,
|
||
userId: string,
|
||
requestData: any,
|
||
prUrl?: string,
|
||
): Promise<boolean> {
|
||
try {
|
||
const prType = requestData.type === "create-pr" ? "PR Creation" : "PR Change Suggestion"
|
||
const prCommentFields = requestData.prCommentFields || {}
|
||
const traceViewUrl = `${CODEFLASH_APP_BASE_URL}/trace/${traceId}`
|
||
|
||
// Store quality monitoring data in DB for trace link access in slack
|
||
await prisma.optimization_features.upsert({
|
||
where: { trace_id: traceId },
|
||
update: {
|
||
approval_required: false, // Not approval, just Quanlity monitoring
|
||
approval_status: "quality_monitoring", // Special status to distinguish from approval
|
||
organization: owner,
|
||
repository: repo,
|
||
experiment_metadata: requestData,
|
||
},
|
||
create: {
|
||
trace_id: traceId,
|
||
approval_required: false,
|
||
approval_status: "quality_monitoring",
|
||
organization: owner,
|
||
repository: repo,
|
||
user_id: userId,
|
||
experiment_metadata: requestData,
|
||
},
|
||
})
|
||
|
||
const blocks: any[] = [
|
||
{
|
||
type: "header",
|
||
text: {
|
||
type: "plain_text",
|
||
text: `🔍 Quality Monitoring: ${prType} Applied`,
|
||
emoji: true,
|
||
},
|
||
},
|
||
{
|
||
type: "section",
|
||
fields: [
|
||
{ type: "mrkdwn", text: `*Repository:*\n📁 ${owner}/${repo}` },
|
||
{ type: "mrkdwn", text: `*Function:*\n🔧 \`${functionName}\`` },
|
||
],
|
||
},
|
||
{
|
||
type: "section",
|
||
fields: [
|
||
{ type: "mrkdwn", text: `*Speedup:*\n🚀 ${prCommentFields.speedup_pct || "N/A"}` },
|
||
{
|
||
type: "mrkdwn",
|
||
text: `*Orig. Runtime:*\n⏱️ ${prCommentFields.original_runtime || "N/A"}`,
|
||
},
|
||
],
|
||
},
|
||
{
|
||
type: "section",
|
||
fields: [
|
||
{ type: "mrkdwn", text: `*User ID:*\n👤 ${userId}` },
|
||
{ type: "mrkdwn", text: `*Trace ID:*\n🏷️ \`${traceId}\`` },
|
||
...(requestData.optimizationReview
|
||
? [
|
||
{
|
||
type: "mrkdwn",
|
||
text: `*Optimization Review Rating:*\n🎯 \`${requestData.optimizationReview}\``,
|
||
},
|
||
]
|
||
: []),
|
||
],
|
||
},
|
||
]
|
||
|
||
// Add explanation snippet if available
|
||
const explanation = prCommentFields.optimization_explanation
|
||
if (explanation) {
|
||
const explanationSnippet = `${explanation.substring(0, 280)}${explanation.length > 280 ? "..." : ""}`
|
||
blocks.push({
|
||
type: "section",
|
||
text: {
|
||
type: "mrkdwn",
|
||
text: `*Explanation Snippet:*\n>>>${explanationSnippet}`,
|
||
},
|
||
})
|
||
}
|
||
|
||
// Add buttons for viewing details and PR
|
||
const actionElements: any[] = [
|
||
{
|
||
type: "button",
|
||
text: {
|
||
type: "plain_text",
|
||
text: "View Full Diff & Details 📄",
|
||
emoji: true,
|
||
},
|
||
url: traceViewUrl,
|
||
style: "primary",
|
||
action_id: `view_trace_details_${traceId.replace(/\./g, "_")}`,
|
||
},
|
||
]
|
||
|
||
if (prUrl) {
|
||
actionElements.push({
|
||
type: "button",
|
||
text: {
|
||
type: "plain_text",
|
||
text: "View PR 🔗",
|
||
emoji: true,
|
||
},
|
||
url: prUrl,
|
||
action_id: `view_pr_${traceId.replace(/\./g, "_")}`,
|
||
})
|
||
}
|
||
|
||
blocks.push({
|
||
type: "actions",
|
||
elements: actionElements,
|
||
})
|
||
|
||
blocks.push({ type: "divider" })
|
||
|
||
blocks.push({
|
||
type: "context",
|
||
elements: [
|
||
{
|
||
type: "mrkdwn",
|
||
text: "🔍 *Quality Monitoring Mode:* This optimization was automatically applied for quality review purposes. No approval required.",
|
||
},
|
||
],
|
||
})
|
||
|
||
const message = {
|
||
blocks,
|
||
text: `Quality Monitoring: ${prType} Applied for ${functionName} in ${owner}/${repo} (${traceId}). Speedup: ${prCommentFields.speedup_pct || "N/A"}. View details: ${traceViewUrl}${prUrl ? ` | PR: ${prUrl}` : ""}`,
|
||
}
|
||
|
||
// Send message to Slack (non-blocking)
|
||
await sendSlackMessage(message, SLACK_CHANNEL, false) // false = don't require reactions
|
||
|
||
console.log(`Quality monitoring notification sent for ${owner}/${repo} optimization ${traceId}`)
|
||
return true
|
||
} catch (error) {
|
||
console.error(`Error sending quality monitoring notification for trace ${traceId}: ${error}`)
|
||
return false
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Request approval for an optimization
|
||
*
|
||
* @param {string} traceId - The trace ID of the optimization
|
||
* @param {string} owner - Repository owner/organization
|
||
* @param {string} repo - Repository name
|
||
* @param {string} functionName - The function being optimized
|
||
* @param {string} userId - The requesting user's ID
|
||
* @param {object} requestData - Additional optimization details, including prCommentFields and diffContents
|
||
* @returns {Promise<boolean>} - True if successful
|
||
*/
|
||
export async function requestApproval(
|
||
traceId: string,
|
||
owner: string,
|
||
repo: string,
|
||
functionName: string,
|
||
userId: string,
|
||
requestData: any,
|
||
): Promise<boolean> {
|
||
try {
|
||
await prisma.optimization_features.upsert({
|
||
where: { trace_id: traceId },
|
||
update: {
|
||
approval_required: true,
|
||
approval_status: "pending",
|
||
organization: owner,
|
||
repository: repo,
|
||
experiment_metadata: requestData,
|
||
},
|
||
create: {
|
||
trace_id: traceId,
|
||
approval_required: true,
|
||
approval_status: "pending",
|
||
organization: owner,
|
||
repository: repo,
|
||
user_id: userId,
|
||
experiment_metadata: requestData,
|
||
},
|
||
})
|
||
|
||
const prType = requestData.type === "create-pr" ? "PR Creation" : "PR Change Suggestion"
|
||
const prCommentFields = requestData.prCommentFields || {}
|
||
const traceViewUrl = `${CODEFLASH_APP_BASE_URL}/trace/${traceId}`
|
||
|
||
const blocks: any[] = [
|
||
{
|
||
type: "header",
|
||
text: {
|
||
type: "plain_text",
|
||
text: `⚠️ ${prType} Optimization Approval Request`,
|
||
emoji: true,
|
||
},
|
||
},
|
||
{
|
||
type: "section",
|
||
fields: [
|
||
{ type: "mrkdwn", text: `*Repository:*\n📁 ${owner}/${repo}` },
|
||
{ type: "mrkdwn", text: `*Function:*\n🔧 \`${functionName}\`` },
|
||
],
|
||
},
|
||
{
|
||
type: "section",
|
||
fields: [
|
||
{ type: "mrkdwn", text: `*Speedup:*\n🚀 ${prCommentFields.speedup_pct || "N/A"}` },
|
||
{
|
||
type: "mrkdwn",
|
||
text: `*Orig. Runtime:*\n⏱️ ${prCommentFields.original_runtime || "N/A"}`,
|
||
},
|
||
],
|
||
},
|
||
{
|
||
type: "section",
|
||
fields: [
|
||
{ type: "mrkdwn", text: `*User ID:*\n👤 ${userId}` },
|
||
{ type: "mrkdwn", text: `*Trace ID:*\n🏷️ \`${traceId}\`` }, // Changed emoji
|
||
...(requestData.optimizationReview
|
||
? [
|
||
{
|
||
type: "mrkdwn",
|
||
text: `*Optimization Review Rating:*\n🎯 \`${requestData.optimizationReview}\``,
|
||
},
|
||
]
|
||
: []),
|
||
],
|
||
},
|
||
]
|
||
|
||
// Add explanation snippet if available
|
||
const explanation = prCommentFields.optimization_explanation
|
||
if (explanation) {
|
||
const explanationSnippet = `${explanation.substring(0, 280)}${explanation.length > 280 ? "..." : ""}`
|
||
blocks.push({
|
||
type: "section",
|
||
text: {
|
||
type: "mrkdwn",
|
||
text: `*Explanation Snippet:*\n>>>${explanationSnippet}`,
|
||
},
|
||
})
|
||
}
|
||
|
||
// Add "View Full Diff & Details" button
|
||
blocks.push({
|
||
type: "actions",
|
||
elements: [
|
||
{
|
||
type: "button",
|
||
text: {
|
||
type: "plain_text",
|
||
text: "View Full Diff & Details 📄",
|
||
emoji: true,
|
||
},
|
||
url: traceViewUrl,
|
||
style: "primary", // Makes the button blue and prominent
|
||
// Replace dots in traceId for action_id as they can be problematic
|
||
action_id: `view_trace_details_${traceId.replace(/\./g, "_")}`,
|
||
},
|
||
],
|
||
})
|
||
|
||
blocks.push({ type: "divider" })
|
||
|
||
blocks.push({
|
||
type: "context",
|
||
elements: [
|
||
{
|
||
type: "mrkdwn",
|
||
text: `:${APPROVAL_EMOJI}: to approve | :${REJECTION_EMOJI}: to reject`,
|
||
},
|
||
],
|
||
})
|
||
|
||
const message = {
|
||
blocks,
|
||
text: `${prType} Optimization Approval Request for ${functionName} in ${owner}/${repo} (${traceId}). Speedup: ${prCommentFields.speedup_pct || "N/A"}. View details: ${traceViewUrl}`,
|
||
}
|
||
|
||
// Send message to Slack
|
||
const response = await sendSlackMessage(message, SLACK_CHANNEL, true)
|
||
|
||
if (response && typeof (response as any).ts === "string") {
|
||
await prisma.optimization_features.update({
|
||
where: { trace_id: traceId },
|
||
data: { slack_message_ts: (response as any).ts },
|
||
})
|
||
}
|
||
|
||
return true
|
||
} catch (error) {
|
||
console.error(`Error requesting approval for trace ${traceId}: ${error}`)
|
||
return false
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Check the approval status of an optimization
|
||
*/
|
||
export async function checkApprovalStatus(req, res) {
|
||
try {
|
||
const { traceId } = req.query
|
||
|
||
if (!traceId) {
|
||
throw missingRequiredFields("traceId")
|
||
}
|
||
|
||
// Find the optimization record
|
||
const optimization = await prisma.optimization_features.findUnique({
|
||
where: { trace_id: traceId as string },
|
||
select: {
|
||
approval_required: true,
|
||
approval_status: true,
|
||
approval_user: true,
|
||
approval_timestamp: true,
|
||
organization: true,
|
||
repository: true,
|
||
},
|
||
})
|
||
|
||
if (!optimization) {
|
||
throw optimizationNotFound(traceId as string)
|
||
}
|
||
|
||
// If approval not required, consider it approved
|
||
if (!optimization.approval_required) {
|
||
return res.json({
|
||
status: "approved",
|
||
message: "Approval not required for this optimization",
|
||
details: optimization,
|
||
})
|
||
}
|
||
|
||
// For current status
|
||
const statusMessages = {
|
||
pending: "This optimization is pending approval",
|
||
approved: "This optimization has been approved",
|
||
rejected: "This optimization was rejected",
|
||
}
|
||
|
||
return res.json({
|
||
status: optimization.approval_status || "pending",
|
||
message:
|
||
statusMessages[optimization.approval_status as keyof typeof statusMessages] ||
|
||
"Unknown status",
|
||
details: optimization,
|
||
})
|
||
} catch (error) {
|
||
if (error && typeof error === "object" && "getHttpStatus" in error) {
|
||
throw error
|
||
}
|
||
console.error(`Error checking approval status: ${error}`)
|
||
throw internalServerError("Error checking approval status")
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Process approval or rejection based on emoji reactions
|
||
*
|
||
* @param {object} event - Slack reaction event
|
||
* @returns {Promise<boolean>} - True if processed successfully
|
||
*/
|
||
export async function processReaction(event: any): Promise<boolean> {
|
||
// Consider more specific type for event
|
||
try {
|
||
// Only handle reaction_added events on messages
|
||
if (event.type !== "reaction_added" || event.item.type !== "message") {
|
||
return false
|
||
}
|
||
|
||
const { reaction, user, item } = event
|
||
const { ts, channel } = item
|
||
|
||
// Find optimization with this message timestamp - ONLY for approval required messages
|
||
const optimization = await prisma.optimization_features.findFirst({
|
||
where: {
|
||
slack_message_ts: ts,
|
||
approval_required: true, // CRITICAL: Only process reactions for approval messages
|
||
approval_status: "pending", // Only process reactions for pending approvals
|
||
},
|
||
})
|
||
|
||
if (!optimization) {
|
||
return false // Not one of our approval messages or already processed
|
||
}
|
||
|
||
// Double-check: Ensure this is not a quality monitoring message
|
||
if (optimization.approval_status === "quality_monitoring") {
|
||
console.log(
|
||
`Ignoring reaction on quality monitoring message for trace ${optimization.trace_id}`,
|
||
)
|
||
return false
|
||
}
|
||
|
||
// Process approval
|
||
if (reaction === APPROVAL_EMOJI) {
|
||
await prisma.optimization_features.update({
|
||
where: { trace_id: String(optimization.trace_id) },
|
||
data: {
|
||
approval_status: "approved",
|
||
approval_user: user,
|
||
approval_timestamp: new Date(),
|
||
},
|
||
})
|
||
|
||
// Send confirmation
|
||
await sendSlackMessage(
|
||
{
|
||
blocks: [
|
||
{
|
||
type: "section",
|
||
text: {
|
||
type: "mrkdwn",
|
||
text: `:${APPROVAL_EMOJI}: Optimization \`${optimization.trace_id}\` has been *approved* by <@${user}>.`,
|
||
},
|
||
},
|
||
],
|
||
text: `Optimization ${optimization.trace_id} has been approved by <@${user}>.`,
|
||
},
|
||
channel,
|
||
)
|
||
|
||
// Get the stored request data
|
||
const requestData = optimization.experiment_metadata as Record<string, any>
|
||
if (requestData) {
|
||
try {
|
||
// Process based on request type
|
||
if (requestData.type === "create-pr") {
|
||
const { triggerCreatePr } = await import("../endpoints/create-pr.js")
|
||
|
||
// Get user's nickname - needed for PR
|
||
const userNickname = await getUserNickname(requestData.userId)
|
||
if (!userNickname) {
|
||
console.error(`Could not get nickname for user ${requestData.userId}`)
|
||
return false
|
||
}
|
||
|
||
// Get installation octokit
|
||
const installationOctokit = await getInstallationOctokit(
|
||
requestData.owner,
|
||
requestData.repo,
|
||
requestData.userId,
|
||
)
|
||
if (!installationOctokit || installationOctokit instanceof Error) {
|
||
console.error(
|
||
`Could not get installation octokit for ${requestData.owner}/${requestData.repo}: ${installationOctokit instanceof Error ? installationOctokit.message : ""}`,
|
||
)
|
||
return false
|
||
}
|
||
|
||
// Trigger PR creation
|
||
await triggerCreatePr(
|
||
requestData.owner,
|
||
requestData.repo,
|
||
requestData.baseBranch,
|
||
requestData.diffContents,
|
||
requestData.prCommentFields,
|
||
requestData.existingTests,
|
||
requestData.generatedTests,
|
||
requestData.coverage_message,
|
||
requestData.userId,
|
||
userNickname,
|
||
installationOctokit,
|
||
requestData.replayTests,
|
||
requestData.concolicTests,
|
||
String(optimization.trace_id),
|
||
requestData.optimizationReview,
|
||
)
|
||
} else if (requestData.type === "suggest-pr-changes") {
|
||
const { triggerSuggestPrChanges } = await import("../endpoints/suggest-pr-changes.js")
|
||
|
||
// Get user's nickname - needed for PR
|
||
const userNickname = await getUserNickname(requestData.userId)
|
||
if (!userNickname) {
|
||
console.error(`Could not get nickname for user ${requestData.userId}`)
|
||
return false
|
||
}
|
||
|
||
// Get installation octokit
|
||
const installationOctokit = await getInstallationOctokit(
|
||
requestData.owner,
|
||
requestData.repo,
|
||
requestData.userId,
|
||
)
|
||
if (!installationOctokit || installationOctokit instanceof Error) {
|
||
console.error(
|
||
`Could not get installation octokit for ${requestData.owner}/${requestData.repo}: ${installationOctokit instanceof Error ? installationOctokit.message : ""}`,
|
||
)
|
||
return false
|
||
}
|
||
|
||
// Trigger PR suggestion
|
||
await triggerSuggestPrChanges(
|
||
requestData.owner,
|
||
requestData.repo,
|
||
requestData.pullNumber,
|
||
requestData.diffContents,
|
||
requestData.prCommentFields,
|
||
requestData.existingTests,
|
||
requestData.generatedTests,
|
||
requestData.coverage_message,
|
||
requestData.userId,
|
||
userNickname,
|
||
installationOctokit,
|
||
requestData.replayTests,
|
||
requestData.concolicTests,
|
||
String(optimization.trace_id),
|
||
requestData.optimizationReview,
|
||
)
|
||
}
|
||
} catch (err: any) {
|
||
console.error(
|
||
`Error processing approved request for trace ${optimization.trace_id}: ${err}`,
|
||
)
|
||
|
||
// Extract helpful error details for Slack notification
|
||
const errorMessage = err.message || String(err)
|
||
const errorType = err.constructor?.name || "Error"
|
||
const isPrMergedOrClosed =
|
||
errorMessage.includes("merged") || errorMessage.includes("closed")
|
||
|
||
const errorBlocks: any[] = [
|
||
{
|
||
type: "section",
|
||
text: {
|
||
type: "mrkdwn",
|
||
text: `:warning: Error processing approved optimization \`${optimization.trace_id}\`:`,
|
||
},
|
||
},
|
||
{
|
||
type: "section",
|
||
text: {
|
||
type: "mrkdwn",
|
||
text: `\`\`\`${errorMessage}\`\`\``,
|
||
},
|
||
},
|
||
]
|
||
|
||
// Add helpful context if PR is merged/closed
|
||
if (isPrMergedOrClosed) {
|
||
errorBlocks.push({
|
||
type: "context",
|
||
elements: [
|
||
{
|
||
type: "mrkdwn",
|
||
text: `ℹ️ The target PR may have been merged or closed since the optimization was queued for approval.`,
|
||
},
|
||
],
|
||
})
|
||
}
|
||
|
||
await sendSlackMessage(
|
||
{
|
||
blocks: errorBlocks,
|
||
text: `Error processing optimization ${optimization.trace_id}: ${errorMessage}`,
|
||
},
|
||
channel,
|
||
)
|
||
|
||
// Return false to indicate the reaction processing failed
|
||
return false
|
||
}
|
||
}
|
||
|
||
return true
|
||
}
|
||
|
||
// Process rejection
|
||
if (reaction === REJECTION_EMOJI) {
|
||
await prisma.optimization_features.update({
|
||
where: { trace_id: String(optimization.trace_id) },
|
||
data: {
|
||
approval_status: "rejected",
|
||
approval_user: user,
|
||
approval_timestamp: new Date(),
|
||
},
|
||
})
|
||
|
||
await sendSlackMessage(
|
||
{
|
||
blocks: [
|
||
{
|
||
type: "section",
|
||
text: {
|
||
type: "mrkdwn",
|
||
text: `:${REJECTION_EMOJI}: Optimization \`${optimization.trace_id}\` has been *rejected* by <@${user}>.`,
|
||
},
|
||
},
|
||
],
|
||
text: `Optimization ${optimization.trace_id} has been rejected by <@${user}>.`,
|
||
},
|
||
channel,
|
||
)
|
||
|
||
return true
|
||
}
|
||
|
||
return false
|
||
} catch (error) {
|
||
console.error(`Error processing reaction: ${error}`)
|
||
return false
|
||
}
|
||
}
|
||
|
||
// Helper functions (ensure these are correctly typed and paths are valid)
|
||
async function getUserNickname(userId: string): Promise<string | null> {
|
||
const { userNickname } = await import("../auth0-mgmt.js")
|
||
return await userNickname(userId)
|
||
}
|
||
|
||
async function getInstallationOctokit(
|
||
owner: string,
|
||
repo: string,
|
||
userId?: string,
|
||
): Promise<any | Error> {
|
||
const { getInstallationOctokitByOwner } = await import("../github/github-utils.js")
|
||
const { githubApp } = await import("../github/github-app.js")
|
||
return await getInstallationOctokitByOwner(githubApp, owner, repo, userId)
|
||
}
|