-
-
Notifications
You must be signed in to change notification settings - Fork 6
feat: Integrate ONNX model via API endpoint #311
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
384b2b3
ebc1982
30ac4aa
6d849f6
0d86125
fe63b3f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,90 @@ | ||||||||||||||||||||||||||||||||||
| import { NextResponse } from 'next/server'; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // Simulate sending an error to a monitoring service (e.g., Sentry, DataDog) | ||||||||||||||||||||||||||||||||||
| const reportErrorToMonitoringService = (error: Error, context: object) => { | ||||||||||||||||||||||||||||||||||
| // In a real application, this would send the error to a service like Sentry. | ||||||||||||||||||||||||||||||||||
| console.log("Reporting error to monitoring service:", { | ||||||||||||||||||||||||||||||||||
| errorMessage: error.message, | ||||||||||||||||||||||||||||||||||
| stack: error.stack, | ||||||||||||||||||||||||||||||||||
| context, | ||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| export async function POST(request: Request) { | ||||||||||||||||||||||||||||||||||
| // 1. Input Validation for FormData | ||||||||||||||||||||||||||||||||||
| let formData; | ||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||
| formData = await request.formData(); | ||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||
| return NextResponse.json({ error: 'Invalid FormData body' }, { status: 400 }); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| const query = formData.get('query') as string | null; | ||||||||||||||||||||||||||||||||||
| const file = formData.get('file') as File | null; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| if (!query && !file) { | ||||||||||||||||||||||||||||||||||
| return NextResponse.json({ error: 'Request must contain a non-empty "query" string or a "file"' }, { status: 400 }); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| if (query && (typeof query !== 'string' || query.trim() === '')) { | ||||||||||||||||||||||||||||||||||
| return NextResponse.json({ error: 'Field "query" must be a non-empty string if provided' }, { status: 400 }); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // 2. Use Environment Variable for Endpoint and Provide Mock Fallback | ||||||||||||||||||||||||||||||||||
| const azureVmEndpoint = process.env.AZURE_ONNX_ENDPOINT; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| if (!azureVmEndpoint) { | ||||||||||||||||||||||||||||||||||
| console.warn('AZURE_ONNX_ENDPOINT is not configured. Falling back to mock response.'); | ||||||||||||||||||||||||||||||||||
| const mockResponse = { | ||||||||||||||||||||||||||||||||||
| prediction: "This is a simulated response because the Azure endpoint is not configured.", | ||||||||||||||||||||||||||||||||||
| confidence: 0.98, | ||||||||||||||||||||||||||||||||||
| input: { | ||||||||||||||||||||||||||||||||||
| query: query, | ||||||||||||||||||||||||||||||||||
| fileName: file?.name, | ||||||||||||||||||||||||||||||||||
| fileSize: file?.size, | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||
| return NextResponse.json(mockResponse); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // 3. Forward Request and Handle Errors | ||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||
| // Pass the FormData directly to the fetch call. | ||||||||||||||||||||||||||||||||||
| // The browser will automatically set the 'Content-Type' to 'multipart/form-data' with the correct boundary. | ||||||||||||||||||||||||||||||||||
| const response = await fetch(azureVmEndpoint, { | ||||||||||||||||||||||||||||||||||
| method: 'POST', | ||||||||||||||||||||||||||||||||||
| body: formData, | ||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+50
to
+57
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Correct the misleading comment about Content-Type. The comment states "The browser will automatically set the 'Content-Type' to 'multipart/form-data' with the correct boundary," but this code runs server-side in a Next.js API route, not in a browser. The Node.js Apply this diff to clarify: try {
- // Pass the FormData directly to the fetch call.
- // The browser will automatically set the 'Content-Type' to 'multipart/form-data' with the correct boundary.
+ // Pass the FormData directly to fetch.
+ // The fetch implementation will automatically set 'Content-Type: multipart/form-data' with the appropriate boundary.
const response = await fetch(azureVmEndpoint, {
method: 'POST',
body: formData,
});📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| if (!response.ok) { | ||||||||||||||||||||||||||||||||||
| // Create an error with status to be caught by the catch block | ||||||||||||||||||||||||||||||||||
| const error = new Error(`Azure endpoint returned an error: ${response.statusText}`); | ||||||||||||||||||||||||||||||||||
| (error as any).status = response.status; | ||||||||||||||||||||||||||||||||||
| throw error; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| const data = await response.json(); | ||||||||||||||||||||||||||||||||||
| return NextResponse.json(data); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| } catch (error: unknown) { | ||||||||||||||||||||||||||||||||||
| let errorMessage = 'An unknown error occurred'; | ||||||||||||||||||||||||||||||||||
| let errorStatus = 500; | ||||||||||||||||||||||||||||||||||
| let errorContext: { [key: string]: any } = { query, fileName: file?.name }; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| if (error instanceof Error) { | ||||||||||||||||||||||||||||||||||
| errorMessage = error.message; | ||||||||||||||||||||||||||||||||||
| // Use status from the error if it exists, otherwise default to 500 | ||||||||||||||||||||||||||||||||||
| errorStatus = (error as any).status || 500; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // In a production environment, send the error to a monitoring service | ||||||||||||||||||||||||||||||||||
| if (process.env.NODE_ENV === 'production') { | ||||||||||||||||||||||||||||||||||
| reportErrorToMonitoringService(error as Error, errorContext); | ||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||
| // Log detailed error in development | ||||||||||||||||||||||||||||||||||
| console.error('Error in ONNX proxy API:', error); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| return NextResponse.json({ error: 'Internal Server Error', details: errorMessage }, { status: errorStatus }); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,51 @@ | ||||||||||||||||||||||||||||
| 'use client'; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| import React, { Component, ErrorInfo, ReactNode } from 'react'; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| interface Props { | ||||||||||||||||||||||||||||
| children: ReactNode; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| interface State { | ||||||||||||||||||||||||||||
| hasError: boolean; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| class ErrorBoundary extends Component<Props, State> { | ||||||||||||||||||||||||||||
| public state: State = { | ||||||||||||||||||||||||||||
| hasError: false, | ||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| public static getDerivedStateFromError(_: Error): State { | ||||||||||||||||||||||||||||
| // Update state so the next render will show the fallback UI. | ||||||||||||||||||||||||||||
| return { hasError: true }; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| public componentDidCatch(error: Error, errorInfo: ErrorInfo) { | ||||||||||||||||||||||||||||
| // In a real application, you would log this to an error reporting service. | ||||||||||||||||||||||||||||
| console.error("ErrorBoundary caught an error:", error, errorInfo); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| public render() { | ||||||||||||||||||||||||||||
| if (this.state.hasError) { | ||||||||||||||||||||||||||||
| // You can render any custom fallback UI. | ||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||
| <div className="flex flex-col items-center justify-center h-screen bg-background text-foreground p-4 text-center"> | ||||||||||||||||||||||||||||
| <h1 className="text-2xl font-bold mb-4">Something went wrong.</h1> | ||||||||||||||||||||||||||||
| <p className="text-muted-foreground"> | ||||||||||||||||||||||||||||
| An unexpected error occurred. We've logged the issue and will look into it. | ||||||||||||||||||||||||||||
| </p> | ||||||||||||||||||||||||||||
| <button | ||||||||||||||||||||||||||||
| onClick={() => this.setState({ hasError: false })} | ||||||||||||||||||||||||||||
| className="mt-6 px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90" | ||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||
| Try again | ||||||||||||||||||||||||||||
| </button> | ||||||||||||||||||||||||||||
|
Comment on lines
+37
to
+42
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add explicit The button lacks an explicit Apply this diff: <button
+ type="button"
onClick={() => this.setState({ hasError: false })}
className="mt-6 px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90"
>
Try again
</button>📝 Committable suggestion
Suggested change
🧰 Tools🪛 Biome (2.1.2)[error] 37-40: Provide an explicit type prop for the button element. The default type of a button is submit, which causes the submission of a form when placed inside a (lint/a11y/useButtonType) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| return this.props.children; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| export default ErrorBoundary; | ||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Strengthen type safety for the monitoring helper.
The
reportErrorToMonitoringServicefunction acceptscontext: object, which is too permissive and loses type information. Consider defining a specific interface for the context to ensure consistent structure and improve maintainability.Apply this diff to add type safety:
📝 Committable suggestion
🤖 Prompt for AI Agents