Skip to content

Commit dd273e3

Browse files
authored
Merge branch 'main' into feat/frontend-cicd-deployment
2 parents 8d79917 + 31b0489 commit dd273e3

File tree

28 files changed

+3665
-2940
lines changed

28 files changed

+3665
-2940
lines changed
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
name: Format Issue with OpenAI
2+
3+
on:
4+
issues:
5+
types: [opened]
6+
7+
permissions:
8+
issues: write
9+
10+
jobs:
11+
format-issue:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Rewrite issue body
15+
id: body
16+
uses: actions/github-script@v8
17+
env:
18+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
19+
with:
20+
script: |
21+
const issue = context.payload.issue;
22+
23+
const response = await fetch('https://api.openai.com/v1/responses', {
24+
method: 'POST',
25+
headers: {
26+
'Content-Type': 'application/json',
27+
'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`
28+
},
29+
body: JSON.stringify({
30+
model: 'gpt-4o-mini',
31+
input: `Rewrite this GitHub issue body using the exact format below. Keep it short, crisp and easy to understand. Do NOT add any information that wasn't in the original. Output ONLY the formatted markdown, nothing else.
32+
33+
Format:
34+
**Is your feature request related to a problem?**
35+
(Describe the problem in 1-2 sentences. Explain why it matters and what pain it causes.)
36+
37+
**Describe the solution you'd like**
38+
(What should be done. Use bullet points if there are multiple items.)
39+
40+
Here is an example of a well-formatted body:
41+
42+
**Is your feature request related to a problem?**
43+
After running an evaluation, the cost information is available in Langfuse but not visible in our UI. This is valuable information for users to understand the expense of their evaluation runs.
44+
45+
**Describe the solution you'd like**
46+
Display evaluation cost in the UI. Two approaches:
47+
1. Fetch from Langfuse - Retrieve cost data from Langfuse API when fetching results
48+
2. Calculate locally - Compute cost ourselves once results are returned, based on token usage and model pricing
49+
50+
Original issue title: ${issue.title}
51+
Original issue body:
52+
${issue.body || '(empty)'}`
53+
})
54+
});
55+
56+
const data = await response.json();
57+
58+
const text = data.output
59+
?.find(o => o.type === 'message')
60+
?.content?.find(c => c.type === 'output_text')
61+
?.text;
62+
63+
if (!text) {
64+
core.setFailed(`OpenAI API error: ${JSON.stringify(data)}`);
65+
return;
66+
}
67+
68+
return text;
69+
70+
- name: Rewrite issue title
71+
id: title
72+
uses: actions/github-script@v8
73+
env:
74+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
75+
with:
76+
script: |
77+
const formattedBody = ${{ steps.body.outputs.result }};
78+
79+
const response = await fetch('https://api.openai.com/v1/responses', {
80+
method: 'POST',
81+
headers: {
82+
'Content-Type': 'application/json',
83+
'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`
84+
},
85+
body: JSON.stringify({
86+
model: 'gpt-4o-mini',
87+
input: `Based on the issue body below, generate a short title in this exact format:
88+
"Module Name: 4-5 word summary"
89+
90+
Examples:
91+
- Evaluation: Automated metrics for STT
92+
- Evaluation: Refactoring CRON
93+
- Evaluation: Fix score format
94+
95+
Output ONLY the title, nothing else.
96+
97+
Issue body:
98+
${formattedBody}`
99+
})
100+
});
101+
102+
const data = await response.json();
103+
104+
const text = data.output
105+
?.find(o => o.type === 'message')
106+
?.content?.find(c => c.type === 'output_text')
107+
?.text;
108+
109+
if (!text) {
110+
core.setFailed(`OpenAI API error: ${JSON.stringify(data)}`);
111+
return;
112+
}
113+
114+
return text.replace(/^["']|["']$/g, '');
115+
116+
- name: Update issue
117+
uses: actions/github-script@v8
118+
with:
119+
script: |
120+
const formattedBody = ${{ steps.body.outputs.result }};
121+
const formattedTitle = ${{ steps.title.outputs.result }};
122+
const original = context.payload.issue.body || '(empty)';
123+
124+
await github.rest.issues.update({
125+
owner: context.repo.owner,
126+
repo: context.repo.repo,
127+
issue_number: context.payload.issue.number,
128+
title: formattedTitle,
129+
body: `${formattedBody}\n\n<details><summary>Original issue</summary>\n\n${original}\n\n</details>`
130+
});

app/api/evaluations/[id]/route.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ export async function GET(
3737
const fileContent = fs.readFileSync(filePath, 'utf-8');
3838
const mockData = JSON.parse(fileContent);
3939

40-
console.log(`[MOCK MODE] Returning mock data for ID ${id} from ${mockFileName}`);
4140
return NextResponse.json(mockData, { status: 200 });
4241
} catch (err) {
4342
console.error('Error reading mock data:', err);
@@ -61,8 +60,6 @@ export async function GET(
6160
url.searchParams.set('resync_score', resyncScore);
6261
url.searchParams.set('export_format', exportFormat);
6362

64-
console.log(`[REAL BACKEND] Fetching evaluation ${id} from ${url.toString()}`);
65-
6663
const response = await fetch(url.toString(), {
6764
method: 'GET',
6865
headers: {
@@ -90,7 +87,6 @@ export async function GET(
9087
);
9188
}
9289

93-
console.log(`[REAL BACKEND] Successfully fetched evaluation ${id}`);
9490
return NextResponse.json(data, { status: 200 });
9591
} catch (error: any) {
9692
console.error('Proxy error:', error);

app/api/evaluations/datasets/[dataset_id]/route.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,60 @@
11
import { NextRequest, NextResponse } from 'next/server';
22

3+
/**
4+
* GET /api/evaluations/datasets/:dataset_id
5+
*
6+
* Proxy endpoint to get dataset details (with optional signed URL).
7+
*/
8+
export async function GET(
9+
request: NextRequest,
10+
{ params }: { params: Promise<{ dataset_id: string }> }
11+
) {
12+
try {
13+
const apiKey = request.headers.get('X-API-KEY');
14+
if (!apiKey) {
15+
return NextResponse.json({ error: 'Missing X-API-KEY header' }, { status: 401 });
16+
}
17+
18+
const { dataset_id } = await params;
19+
const backendUrl = process.env.NEXT_PUBLIC_BACKEND_URL || 'http://localhost:8000';
20+
const searchParams = request.nextUrl.searchParams.toString();
21+
const queryString = searchParams ? `?${searchParams}` : '';
22+
23+
const response = await fetch(`${backendUrl}/api/v1/evaluations/datasets/${dataset_id}${queryString}`, {
24+
method: 'GET',
25+
headers: { 'X-API-KEY': apiKey },
26+
});
27+
28+
const data = await response.json();
29+
if (!response.ok) {
30+
return NextResponse.json(data, { status: response.status });
31+
}
32+
33+
// If fetch_content=true, download the CSV from the signed URL and return it
34+
const fetchContent = request.nextUrl.searchParams.get('fetch_content');
35+
if (fetchContent === 'true') {
36+
const signedUrl = data?.data?.signed_url || data?.signed_url;
37+
if (!signedUrl) {
38+
return NextResponse.json({ error: 'No signed URL available' }, { status: 404 });
39+
}
40+
const csvResponse = await fetch(signedUrl);
41+
if (!csvResponse.ok) {
42+
return NextResponse.json({ error: 'Failed to fetch CSV file' }, { status: 502 });
43+
}
44+
const csvText = await csvResponse.text();
45+
return NextResponse.json({ ...data, csv_content: csvText }, { status: 200 });
46+
}
47+
48+
return NextResponse.json(data, { status: 200 });
49+
} catch (error: any) {
50+
console.error('Proxy error:', error);
51+
return NextResponse.json(
52+
{ error: 'Failed to forward request to backend', details: error.message },
53+
{ status: 500 }
54+
);
55+
}
56+
}
57+
358
/**
459
* DELETE /api/evaluations/datasets/:dataset_id
560
*

app/api/evaluations/route.ts

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,30 +22,6 @@ export async function GET(request: NextRequest) {
2222

2323
const data = await response.json();
2424

25-
// Log the structure to help debug score visibility issues
26-
//TODO Fix it later
27-
if (data && Array.isArray(data)) {
28-
console.log('[GET /api/evaluations] Sample evaluation structure:', {
29-
firstItem: data[0] ? {
30-
id: data[0].id,
31-
hasScore: !!data[0].score,
32-
hasScores: !!data[0].scores,
33-
scoreKeys: data[0].score ? Object.keys(data[0].score) : [],
34-
scoresKeys: data[0].scores ? Object.keys(data[0].scores) : []
35-
} : 'No items'
36-
});
37-
} else if (data && data.data && Array.isArray(data.data)) {
38-
console.log('[GET /api/evaluations] Sample evaluation structure (nested):', {
39-
firstItem: data.data[0] ? {
40-
id: data.data[0].id,
41-
hasScore: !!data.data[0].score,
42-
hasScores: !!data.data[0].scores,
43-
scoreKeys: data.data[0].score ? Object.keys(data.data[0].score) : [],
44-
scoresKeys: data.data[0].scores ? Object.keys(data.data[0].scores) : []
45-
} : 'No items'
46-
});
47-
}
48-
4925
return NextResponse.json(data, { status: response.status });
5026
} catch (error) {
5127
console.error('Proxy error:', error);

app/api/evaluations/stt/datasets/[dataset_id]/route.ts

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,15 @@ export async function GET(
1616
}
1717

1818
try {
19-
// Get query parameters from the request
19+
// Forward all query parameters to the backend
2020
const { searchParams } = new URL(request.url);
21-
const includeSamples = searchParams.get('include_samples');
22-
const includeAudio = searchParams.get('include_audio');
23-
24-
// Build backend URL with query parameters
2521
const backendParams = new URLSearchParams();
26-
if (includeSamples) backendParams.append('include_samples', includeSamples);
27-
if (includeAudio) backendParams.append('include_audio', includeAudio);
22+
for (const [key, value] of searchParams.entries()) {
23+
backendParams.append(key, value);
24+
}
25+
const queryString = backendParams.toString() ? `?${backendParams.toString()}` : '';
2826

29-
const backendUrlWithParams = `${backendUrl}/api/v1/evaluations/stt/datasets/${dataset_id}${backendParams.toString() ? `?${backendParams.toString()}` : ''
30-
}`;
27+
const backendUrlWithParams = `${backendUrl}/api/v1/evaluations/stt/datasets/${dataset_id}${queryString}`;
3128

3229
const response = await fetch(backendUrlWithParams, {
3330
headers: {
@@ -43,4 +40,29 @@ export async function GET(
4340
{ status: 500 }
4441
);
4542
}
43+
}
44+
45+
export async function DELETE(
46+
request: Request,
47+
{ params }: { params: Promise<{ dataset_id: string }> }
48+
) {
49+
const { dataset_id } = await params;
50+
const backendUrl = process.env.NEXT_PUBLIC_BACKEND_URL || 'http://localhost:8000';
51+
const apiKey = request.headers.get('X-API-KEY');
52+
53+
if (!apiKey) {
54+
return NextResponse.json({ success: false, error: 'Unauthorized: Missing API key' }, { status: 401 });
55+
}
56+
57+
try {
58+
const response = await fetch(`${backendUrl}/api/v1/evaluations/stt/datasets/${dataset_id}`, {
59+
method: 'DELETE',
60+
headers: { 'X-API-KEY': apiKey },
61+
});
62+
let data;
63+
try { data = await response.json(); } catch { data = { success: true }; }
64+
return NextResponse.json(data, { status: response.ok ? 200 : response.status });
65+
} catch (error: any) {
66+
return NextResponse.json({ success: false, error: 'Failed to delete dataset', details: error.message }, { status: 500 });
67+
}
4668
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { NextResponse } from 'next/server';
2+
3+
export async function PATCH(
4+
request: Request,
5+
{ params }: { params: Promise<{ sample_id: string }> }
6+
) {
7+
const { sample_id } = await params;
8+
const backendUrl = process.env.NEXT_PUBLIC_BACKEND_URL || 'http://localhost:8000';
9+
const apiKey = request.headers.get('X-API-KEY');
10+
11+
if (!apiKey) {
12+
return NextResponse.json(
13+
{ success: false, error: 'Unauthorized: Missing API key' },
14+
{ status: 401 }
15+
);
16+
}
17+
18+
try {
19+
const body = await request.json();
20+
21+
const response = await fetch(
22+
`${backendUrl}/api/v1/evaluations/stt/samples/${sample_id}`,
23+
{
24+
method: 'PATCH',
25+
headers: {
26+
'X-API-KEY': apiKey,
27+
'Content-Type': 'application/json',
28+
},
29+
body: JSON.stringify(body),
30+
}
31+
);
32+
33+
let data;
34+
try {
35+
data = await response.json();
36+
} catch {
37+
data = { success: response.ok };
38+
}
39+
return NextResponse.json(data, { status: response.status });
40+
} catch (error: any) {
41+
console.error('Sample update error:', error);
42+
return NextResponse.json(
43+
{ success: false, error: 'Failed to update sample', details: error.message },
44+
{ status: 500 }
45+
);
46+
}
47+
}

0 commit comments

Comments
 (0)