diff --git a/static/app/components/events/autofix/useExplorerAutofix.tsx b/static/app/components/events/autofix/useExplorerAutofix.tsx index 6b5322cf060e..112513824acd 100644 --- a/static/app/components/events/autofix/useExplorerAutofix.tsx +++ b/static/app/components/events/autofix/useExplorerAutofix.tsx @@ -161,6 +161,7 @@ export interface ExplorerAutofixState { */ interface ExplorerAutofixResponse { autofix: ExplorerAutofixState | null; + formatted?: {content: string; format: string}; } const POLL_INTERVAL = 1000; @@ -170,7 +171,7 @@ function explorerAutofixApiOptions(orgSlug: string, groupId: string) { '/organizations/$organizationIdOrSlug/issues/$issueId/autofix/', { path: {organizationIdOrSlug: orgSlug, issueId: groupId}, - query: {mode: 'explorer'}, + query: {mode: 'explorer', llmFormat: 'markdown'}, staleTime: 0, } ); @@ -810,6 +811,10 @@ export function useExplorerAutofix( * Current autofix run state, or null if no run exists. */ runState, + /** + * Formatted markdown for LLM prompts + */ + autofixFormatted: apiData?.formatted?.content ?? null, /** * Whether the initial data fetch is pending. */ diff --git a/static/app/components/events/autofix/v3/autofixCards.spec.tsx b/static/app/components/events/autofix/v3/autofixCards.spec.tsx index d4472579b7c3..93512a243d23 100644 --- a/static/app/components/events/autofix/v3/autofixCards.spec.tsx +++ b/static/app/components/events/autofix/v3/autofixCards.spec.tsx @@ -102,6 +102,7 @@ function makePR(overrides: Partial = {}): RepoPRState { const mockAutofix: ReturnType = { runState: null, + autofixFormatted: null, isLoading: false, isPolling: false, startStep: jest.fn(), diff --git a/static/app/components/events/autofix/v3/nextStep.spec.tsx b/static/app/components/events/autofix/v3/nextStep.spec.tsx index 6b653d1f4360..79dd7eb6a6b9 100644 --- a/static/app/components/events/autofix/v3/nextStep.spec.tsx +++ b/static/app/components/events/autofix/v3/nextStep.spec.tsx @@ -20,6 +20,7 @@ function makeAutofix( ): ReturnType { const base: ReturnType = { runState: {run_id: 1} as any, + autofixFormatted: null, startStep: jest.fn(), createPR: jest.fn(), reset: jest.fn(), diff --git a/static/app/components/events/autofix/v3/prIterationFeedbackForm.spec.tsx b/static/app/components/events/autofix/v3/prIterationFeedbackForm.spec.tsx index 17f5812e6147..fa0dfdc8dc75 100644 --- a/static/app/components/events/autofix/v3/prIterationFeedbackForm.spec.tsx +++ b/static/app/components/events/autofix/v3/prIterationFeedbackForm.spec.tsx @@ -13,6 +13,7 @@ function makeAutofix( ): ReturnType { return { runState: {run_id: 1, blocks: []} as any, + autofixFormatted: null, startStep: jest.fn().mockResolvedValue(undefined), createPR: jest.fn(), reset: jest.fn(), diff --git a/static/app/components/events/autofix/v3/useAutoTriggerAutofix.spec.tsx b/static/app/components/events/autofix/v3/useAutoTriggerAutofix.spec.tsx index e5d28f2c4643..c79a33c0ed2a 100644 --- a/static/app/components/events/autofix/v3/useAutoTriggerAutofix.spec.tsx +++ b/static/app/components/events/autofix/v3/useAutoTriggerAutofix.spec.tsx @@ -10,6 +10,7 @@ function makeAutofix( ): ReturnType { const base: ReturnType = { runState: null, + autofixFormatted: null, startStep: jest.fn(), createPR: jest.fn(), reset: jest.fn(), diff --git a/static/app/components/events/autofix/v3/useResetAutofixStep.spec.tsx b/static/app/components/events/autofix/v3/useResetAutofixStep.spec.tsx index ed587513f702..341b0ad23034 100644 --- a/static/app/components/events/autofix/v3/useResetAutofixStep.spec.tsx +++ b/static/app/components/events/autofix/v3/useResetAutofixStep.spec.tsx @@ -11,6 +11,7 @@ function makeAutofix( ): ReturnType { const base: ReturnType = { runState: null, + autofixFormatted: null, startStep: jest.fn(), createPR: jest.fn(), reset: jest.fn(), diff --git a/static/app/types/event.tsx b/static/app/types/event.tsx index a50826dc0f10..2c5c3572a4f1 100644 --- a/static/app/types/event.tsx +++ b/static/app/types/event.tsx @@ -781,6 +781,7 @@ interface EventBase { dateCreated?: string; device?: Record; endTimestamp?: number; + formatted?: {content: string; format: string}; groupID?: string; groupingConfig?: { enhancements: string; diff --git a/static/app/views/issueDetails/eventNavigation/index.tsx b/static/app/views/issueDetails/eventNavigation/index.tsx index 92fcf914f7db..601c7be92b09 100644 --- a/static/app/views/issueDetails/eventNavigation/index.tsx +++ b/static/app/views/issueDetails/eventNavigation/index.tsx @@ -115,7 +115,9 @@ export function IssueEventNavigation({event, group}: IssueEventNavigationProps) const activeThreadId = useActiveThreadId(); // Get data for markdown copy functionality - const {runState: autofixData} = useExplorerAutofix(group.id, {enabled: false}); + const {runState: autofixData, autofixFormatted} = useExplorerAutofix(group.id, { + enabled: false, + }); const handleCopyMarkdown = useCallback(() => { const markdownText = issueAndEventToMarkdown({ @@ -124,6 +126,7 @@ export function IssueEventNavigation({event, group}: IssueEventNavigationProps) autofixData, activeThreadId, organization, + autofixFormatted, }); trackAnalytics('issue_details.copy_issue_details_as_markdown', { @@ -134,7 +137,7 @@ export function IssueEventNavigation({event, group}: IssueEventNavigationProps) }); return markdownText; - }, [activeThreadId, event, group, autofixData, organization]); + }, [activeThreadId, event, group, autofixData, organization, autofixFormatted]); return ( diff --git a/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx b/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx index 908d559aa7a3..f8ca3c13fef7 100644 --- a/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx +++ b/static/app/views/issueDetails/hooks/useCopyIssueDetails.tsx @@ -256,6 +256,7 @@ interface IssueAndEventToMarkdownOptions { organization: Organization; activeThreadId?: number; autofixData?: ExplorerAutofixState | null; + autofixFormatted?: string | null; event?: Event | null; } @@ -265,7 +266,24 @@ export const issueAndEventToMarkdown = ({ autofixData, activeThreadId, organization, + autofixFormatted, }: IssueAndEventToMarkdownOptions): string => { + const formatted = event?.formatted?.content; + if (formatted) { + let llmMarkdown = `**Issue ID:** ${group.id}\n`; + if (group.project?.slug) { + llmMarkdown += `**Project:** ${group.project.slug}\n`; + } + if (typeof event?.dateCreated === 'string') { + llmMarkdown += `**Date:** ${new Date(event.dateCreated).toLocaleString()}\n`; + } + llmMarkdown += `\n${formatted}`; + if (autofixFormatted) { + llmMarkdown += `\n\n${autofixFormatted}`; + } + return llmMarkdown; + } + // Format the basic issue information let markdownText = `# ${group.title}\n\n`; markdownText += `**Issue ID:** ${group.id}\n`; @@ -323,7 +341,9 @@ export const issueAndEventToMarkdown = ({ export const useCopyIssueDetails = (group: Group, event?: Event) => { const organization = useOrganization(); - const {runState: autofixData} = useExplorerAutofix(group.id, {enabled: false}); + const {runState: autofixData, autofixFormatted} = useExplorerAutofix(group.id, { + enabled: false, + }); const activeThreadId = useActiveThreadId(); const text = useMemo(() => { @@ -333,8 +353,9 @@ export const useCopyIssueDetails = (group: Group, event?: Event) => { autofixData, activeThreadId, organization, + autofixFormatted, }); - }, [group, event, autofixData, activeThreadId, organization]); + }, [group, event, autofixData, activeThreadId, organization, autofixFormatted]); const {copy} = useCopyToClipboard(); diff --git a/static/app/views/issueDetails/utils.tsx b/static/app/views/issueDetails/utils.tsx index 6241a5b867b4..d19fa4637c34 100644 --- a/static/app/views/issueDetails/utils.tsx +++ b/static/app/views/issueDetails/utils.tsx @@ -212,6 +212,7 @@ function getGroupEventDetailsQueryData({ }): Record { const params: Record = { collapse: ['fullRelease'], + llmFormat: 'markdown', }; if (query) {