Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ spec:
storageClassName: local-path
resources:
requests:
storage: 1Gi
storage: 30Gi
---
apiVersion: v1
kind: Service
Expand Down
1 change: 1 addition & 0 deletions studio-backend/app/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ fastapi==0.115.4
uvicorn==0.30.6
kubernetes==30.1.0
requests==2.32.3
urllib3==2.0.0
pydantic==1.10.18
starlette==0.41.2
websockets==10.3
Expand Down
2 changes: 2 additions & 0 deletions studio-backend/app/routers/sandbox_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ async def deploy_sandbox(request: PipelineFlow):
try:
response = deploy_manifest_in_namespace(core_v1_api, apps_v1_api, json.loads(workflow_info.export_to_json()))
except Exception as e:
import traceback
traceback.print_exc()
raise HTTPException(status_code=500, detail=str(e))

return response
Expand Down
3 changes: 2 additions & 1 deletion studio-backend/tests/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ pytest==8.3.3
fastapi==0.115.0
httpx==0.27.2
kubernetes==30.1.0
pydantic==1.10.18
pydantic==1.10.18
urllib3==2.0.0
6 changes: 4 additions & 2 deletions studio-frontend/packages/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@
"typeorm": "^0.3.6",
"uuid": "^9.0.1",
"winston": "^3.9.0",
"https-proxy-agent": "^7.0.4"
"https-proxy-agent": "^7.0.4",
"archiver": "^6.0.1"
},
"devDependencies": {
"@types/content-disposition": "0.5.8",
Expand All @@ -105,6 +106,7 @@
"start-server-and-test": "^2.0.3",
"ts-node": "^10.7.0",
"tsc-watch": "^6.0.4",
"typescript": "^5.4.5"
"typescript": "^5.4.5",
"@types/archiver": "^6.0.2"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { StatusCodes } from 'http-status-codes'
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
import finetuningService from '../../services/finetuning'

// Declare timer globals for Node.js
declare function setTimeout(cb: (...args: any[]) => void, ms?: number): any

/**
* Upload a training file
* POST /api/v1/finetuning/files
Expand Down Expand Up @@ -154,6 +157,7 @@ const getFineTuningJobLogs = async (req: Request, res: Response, next: NextFunct
/**
* Download fine-tuning job output as a zip file
* GET /api/v1/finetuning/download-ft/:jobId
* Creates zip, streams it to client, then deletes the zip file after download completes
*/
const downloadFineTuningOutput = async (req: Request, res: Response, next: NextFunction) => {
try {
Expand All @@ -166,31 +170,63 @@ const downloadFineTuningOutput = async (req: Request, res: Response, next: NextF
)
}

// Get the zip file path (creates if needed, but returns immediately if already exists)
// Get the zip file path from service
const filePath = await finetuningService.downloadFineTuningOutput(jobId)

if (!filePath) {
throw new InternalFlowiseError(
StatusCodes.NOT_FOUND,
`Error: finetuningController.downloadFineTuningOutput - output not found for job: ${jobId}`
`Error: finetuningController.downloadFineTuningOutput - zip file not found for job: ${jobId}. Please request download via WebSocket first.`
)
}

const fs = require('fs')

// Set response headers for file download
const fileName = `${jobId}-output.zip`
res.setHeader('Content-Type', 'application/zip')
res.setHeader('Content-Disposition', `attachment; filename="${fileName}"`)

// Stream the file
const fs = require('fs')
const fileStream = fs.createReadStream(filePath)

// Log when stream opens
fileStream.on('open', () => {
console.debug(`finetuningController.downloadFineTuningOutput - starting to stream: ${filePath}`)
})

// Delete zip file after response fully finishes (browser completes download)
res.on('finish', () => {
setTimeout(() => {
try {
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath)
console.debug(`finetuningController.downloadFineTuningOutput - deleted zip after download complete: ${filePath}`)
}
} catch (deleteErr: any) {
console.warn(`finetuningController.downloadFineTuningOutput - failed to delete zip: ${deleteErr?.message || deleteErr}`)
}
}, 100)
})

fileStream.on('error', (err: any) => {
console.error('Error streaming fine-tuning output file:', err)
console.error('finetuningController.downloadFineTuningOutput - error streaming file:', err)
// Try to delete zip on error too
try {
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath)
console.debug(`finetuningController.downloadFineTuningOutput - deleted zip after error: ${filePath}`)
}
} catch (deleteErr: any) {
console.warn(`finetuningController.downloadFineTuningOutput - failed to delete zip on error: ${deleteErr?.message || deleteErr}`)
}
if (!res.headersSent) {
res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
error: 'Error streaming fine-tuning output file'
})
}
})

fileStream.pipe(res)
} catch (error) {
next(error)
Expand Down
Loading