Initial commit
This commit is contained in:
112
api/modules/ai/routes.ts
Normal file
112
api/modules/ai/routes.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import express, { type Request, type Response } from 'express'
|
||||
import { spawn } from 'child_process'
|
||||
import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
import fs from 'fs/promises'
|
||||
import fsSync from 'fs'
|
||||
import { asyncHandler } from '../../utils/asyncHandler.js'
|
||||
import { successResponse } from '../../utils/response.js'
|
||||
import { resolveNotebookPath } from '../../utils/pathSafety.js'
|
||||
import { ValidationError, NotFoundError, InternalError } from '../../../shared/errors/index.js'
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = path.dirname(__filename)
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
const PYTHON_TIMEOUT_MS = 30000
|
||||
|
||||
const spawnPythonWithTimeout = (
|
||||
scriptPath: string,
|
||||
args: string[],
|
||||
stdinContent: string,
|
||||
timeoutMs: number = PYTHON_TIMEOUT_MS
|
||||
): Promise<string> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const pythonProcess = spawn('python', args, {
|
||||
env: { ...process.env },
|
||||
})
|
||||
|
||||
let stdout = ''
|
||||
let stderr = ''
|
||||
let timeoutId: NodeJS.Timeout | null = null
|
||||
|
||||
const cleanup = () => {
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId)
|
||||
timeoutId = null
|
||||
}
|
||||
}
|
||||
|
||||
timeoutId = setTimeout(() => {
|
||||
cleanup()
|
||||
pythonProcess.kill()
|
||||
reject(new Error(`Python script timed out after ${timeoutMs}ms`))
|
||||
}, timeoutMs)
|
||||
|
||||
pythonProcess.stdout.on('data', (data) => {
|
||||
stdout += data.toString()
|
||||
})
|
||||
|
||||
pythonProcess.stderr.on('data', (data) => {
|
||||
stderr += data.toString()
|
||||
})
|
||||
|
||||
pythonProcess.on('close', (code) => {
|
||||
cleanup()
|
||||
if (code !== 0) {
|
||||
reject(new Error(`Python script exited with code ${code}. Stderr: ${stderr}`))
|
||||
} else {
|
||||
resolve(stdout)
|
||||
}
|
||||
})
|
||||
|
||||
pythonProcess.on('error', (err) => {
|
||||
cleanup()
|
||||
reject(new Error(`Failed to start python process: ${err.message}`))
|
||||
})
|
||||
|
||||
pythonProcess.stdin.write(stdinContent)
|
||||
pythonProcess.stdin.end()
|
||||
})
|
||||
}
|
||||
|
||||
router.post(
|
||||
'/doubao',
|
||||
asyncHandler(async (req: Request, res: Response) => {
|
||||
const { task, path: relPath } = req.body as { task?: string; path?: string }
|
||||
|
||||
if (!task) throw new ValidationError('Task is required')
|
||||
if (!relPath) throw new ValidationError('Path is required')
|
||||
|
||||
const { fullPath } = resolveNotebookPath(relPath)
|
||||
|
||||
try {
|
||||
await fs.access(fullPath)
|
||||
} catch {
|
||||
throw new NotFoundError('File not found')
|
||||
}
|
||||
|
||||
const content = await fs.readFile(fullPath, 'utf-8')
|
||||
|
||||
const projectRoot = path.resolve(__dirname, '..', '..', '..')
|
||||
const scriptPath = path.join(projectRoot, 'tools', 'doubao', 'main.py')
|
||||
|
||||
if (!fsSync.existsSync(scriptPath)) {
|
||||
throw new InternalError(`Python script not found: ${scriptPath}`)
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await spawnPythonWithTimeout(scriptPath, ['--task', task], content)
|
||||
await fs.writeFile(fullPath, result, 'utf-8')
|
||||
successResponse(res, { message: 'Task completed successfully' })
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : 'Unknown error'
|
||||
throw new InternalError(`AI task failed: ${message}`)
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
export const createAiRoutes = (): express.Router => router
|
||||
|
||||
export default createAiRoutes
|
||||
Reference in New Issue
Block a user