@@ -248,6 +248,13 @@ export class PythonBridge extends EventEmitter {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available quantum targets
|
||||
*/
|
||||
async getAvailableTargets(): Promise<PythonResponse> {
|
||||
return this.sendCommand('get_available_targets');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sample measurement results
|
||||
*/
|
||||
|
||||
990
src/http-server.ts
Archivo normal
990
src/http-server.ts
Archivo normal
@@ -0,0 +1,990 @@
|
||||
/**
|
||||
* HTTP Server for CUDA Quantum MCP
|
||||
* Provides REST API endpoints and Server-Sent Events for quantum computing operations
|
||||
*/
|
||||
|
||||
import express, { Request, Response } from 'express';
|
||||
import cors from 'cors';
|
||||
import helmet from 'helmet';
|
||||
import swaggerJsdoc from 'swagger-jsdoc';
|
||||
import swaggerUiExpress from 'swagger-ui-express';
|
||||
import { WebSocketServer, WebSocket } from 'ws';
|
||||
import { createServer } from 'http';
|
||||
import { initializePythonBridge, getPythonBridge } from './bridge/python-bridge.js';
|
||||
import { Logger, LogLevel } from './utils/logger.js';
|
||||
|
||||
/**
|
||||
* HTTP Server configuration
|
||||
*/
|
||||
interface HttpServerConfig {
|
||||
port: number;
|
||||
host: string;
|
||||
corsOrigins: string[];
|
||||
logLevel: LogLevel;
|
||||
pythonPath?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* SSE Client interface
|
||||
*/
|
||||
interface SSEClient {
|
||||
id: string;
|
||||
response: Response;
|
||||
subscriptions: Set<string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* WebSocket Client interface
|
||||
*/
|
||||
interface WSClient {
|
||||
id: string;
|
||||
ws: WebSocket;
|
||||
subscriptions: Set<string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swagger API Documentation Configuration
|
||||
*/
|
||||
const swaggerOptions = {
|
||||
definition: {
|
||||
openapi: '3.0.0',
|
||||
info: {
|
||||
title: 'CUDA Quantum MCP API',
|
||||
version: '1.0.0',
|
||||
description: 'RESTful API for CUDA Quantum operations with GPU acceleration',
|
||||
contact: {
|
||||
name: 'MCP Quantum Team',
|
||||
email: 'quantum@mcp.dev'
|
||||
},
|
||||
license: {
|
||||
name: 'MIT',
|
||||
url: 'https://opensource.org/licenses/MIT'
|
||||
}
|
||||
},
|
||||
servers: [
|
||||
{
|
||||
url: 'http://localhost:3000',
|
||||
description: 'Development server'
|
||||
}
|
||||
],
|
||||
components: {
|
||||
schemas: {
|
||||
QuantumKernel: {
|
||||
type: 'object',
|
||||
required: ['name', 'num_qubits'],
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Unique kernel identifier'
|
||||
},
|
||||
num_qubits: {
|
||||
type: 'integer',
|
||||
minimum: 1,
|
||||
maximum: 64,
|
||||
description: 'Number of qubits in the circuit'
|
||||
},
|
||||
parameters: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string' },
|
||||
type: { type: 'string', enum: ['float', 'int', 'complex', 'angle'] },
|
||||
description: { type: 'string' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
QuantumGate: {
|
||||
type: 'object',
|
||||
required: ['kernel_name', 'gate_name', 'target_qubits'],
|
||||
properties: {
|
||||
kernel_name: { type: 'string' },
|
||||
gate_name: { type: 'string', enum: ['h', 'x', 'y', 'z', 'cnot', 'rx', 'ry', 'rz', 's', 't'] },
|
||||
target_qubits: {
|
||||
type: 'array',
|
||||
items: { type: 'integer', minimum: 0 }
|
||||
},
|
||||
control_qubits: {
|
||||
type: 'array',
|
||||
items: { type: 'integer', minimum: 0 }
|
||||
},
|
||||
parameters: {
|
||||
type: 'array',
|
||||
items: { type: 'number' }
|
||||
},
|
||||
adjoint: { type: 'boolean', default: false }
|
||||
}
|
||||
},
|
||||
SampleRequest: {
|
||||
type: 'object',
|
||||
required: ['kernel_name'],
|
||||
properties: {
|
||||
kernel_name: { type: 'string' },
|
||||
shots: { type: 'integer', minimum: 1, default: 1000 },
|
||||
parameters: { type: 'object' }
|
||||
}
|
||||
},
|
||||
ObserveRequest: {
|
||||
type: 'object',
|
||||
required: ['kernel_name', 'hamiltonian_terms'],
|
||||
properties: {
|
||||
kernel_name: { type: 'string' },
|
||||
hamiltonian_terms: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
coefficient: { type: 'number' },
|
||||
pauli_string: { type: 'string' }
|
||||
}
|
||||
}
|
||||
},
|
||||
shots: { type: 'integer', minimum: 1, default: 1000 },
|
||||
parameters: { type: 'object' }
|
||||
}
|
||||
},
|
||||
TargetRequest: {
|
||||
type: 'object',
|
||||
required: ['target'],
|
||||
properties: {
|
||||
target: { type: 'string' },
|
||||
configuration: { type: 'object' }
|
||||
}
|
||||
},
|
||||
ApiResponse: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
success: { type: 'boolean' },
|
||||
data: {},
|
||||
error: { type: 'string' },
|
||||
timestamp: { type: 'string', format: 'date-time' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
apis: ['./src/http-server.ts']
|
||||
};
|
||||
|
||||
export class CudaQuantumHttpServer {
|
||||
private app: express.Application;
|
||||
private server!: any;
|
||||
private wsServer!: WebSocketServer;
|
||||
private logger: Logger;
|
||||
private sseClients: Map<string, SSEClient>;
|
||||
private wsClients: Map<string, WSClient>;
|
||||
private config: HttpServerConfig;
|
||||
|
||||
constructor(config: HttpServerConfig) {
|
||||
this.config = config;
|
||||
this.app = express();
|
||||
this.logger = new Logger('HttpServer', { level: config.logLevel });
|
||||
this.sseClients = new Map();
|
||||
this.wsClients = new Map();
|
||||
|
||||
this.setupMiddleware();
|
||||
this.setupRoutes();
|
||||
this.setupSwagger();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Express middleware
|
||||
*/
|
||||
private setupMiddleware(): void {
|
||||
// Security middleware
|
||||
this.app.use(helmet({
|
||||
contentSecurityPolicy: false // Allow Swagger UI
|
||||
}));
|
||||
|
||||
// CORS configuration
|
||||
this.app.use(cors({
|
||||
origin: this.config.corsOrigins,
|
||||
methods: ['GET', 'POST', 'PUT', 'DELETE'],
|
||||
allowedHeaders: ['Content-Type', 'Authorization'],
|
||||
credentials: true
|
||||
}));
|
||||
|
||||
// Body parsing
|
||||
this.app.use(express.json({ limit: '10mb' }));
|
||||
this.app.use(express.urlencoded({ extended: true }));
|
||||
|
||||
// Request logging
|
||||
this.app.use((req, res, next) => {
|
||||
this.logger.debug(`${req.method} ${req.path}`, {
|
||||
ip: req.ip,
|
||||
userAgent: req.get('User-Agent')
|
||||
});
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup API routes with Swagger documentation
|
||||
*/
|
||||
private setupRoutes(): void {
|
||||
// Health check
|
||||
/**
|
||||
* @swagger
|
||||
* /health:
|
||||
* get:
|
||||
* summary: Health check endpoint
|
||||
* tags: [System]
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Server is healthy
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/ApiResponse'
|
||||
*/
|
||||
this.app.get('/health', (req, res) => {
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
status: 'healthy',
|
||||
timestamp: new Date().toISOString(),
|
||||
uptime: process.uptime()
|
||||
},
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
});
|
||||
|
||||
// Quantum Kernels
|
||||
/**
|
||||
* @swagger
|
||||
* /api/kernels:
|
||||
* post:
|
||||
* summary: Create a new quantum kernel
|
||||
* tags: [Quantum Kernels]
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/QuantumKernel'
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Kernel created successfully
|
||||
* 400:
|
||||
* description: Invalid request parameters
|
||||
*/
|
||||
this.app.post('/api/kernels', this.handleCreateKernel.bind(this));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/kernels:
|
||||
* get:
|
||||
* summary: List all quantum kernels
|
||||
* tags: [Quantum Kernels]
|
||||
* responses:
|
||||
* 200:
|
||||
* description: List of kernels retrieved successfully
|
||||
*/
|
||||
this.app.get('/api/kernels', this.handleListKernels.bind(this));
|
||||
|
||||
// Quantum Gates
|
||||
/**
|
||||
* @swagger
|
||||
* /api/gates:
|
||||
* post:
|
||||
* summary: Apply a quantum gate to a kernel
|
||||
* tags: [Quantum Gates]
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/QuantumGate'
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Gate applied successfully
|
||||
*/
|
||||
this.app.post('/api/gates', this.handleApplyGate.bind(this));
|
||||
|
||||
// Quantum Execution
|
||||
/**
|
||||
* @swagger
|
||||
* /api/sample:
|
||||
* post:
|
||||
* summary: Sample quantum circuit measurements
|
||||
* tags: [Quantum Execution]
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/SampleRequest'
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Sampling completed successfully
|
||||
*/
|
||||
this.app.post('/api/sample', this.handleSample.bind(this));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/observe:
|
||||
* post:
|
||||
* summary: Calculate Hamiltonian expectation value
|
||||
* tags: [Quantum Execution]
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/ObserveRequest'
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Expectation value calculated successfully
|
||||
*/
|
||||
this.app.post('/api/observe', this.handleObserve.bind(this));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/state/{kernelName}:
|
||||
* get:
|
||||
* summary: Get quantum state vector
|
||||
* tags: [Quantum Execution]
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: kernelName
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: State vector retrieved successfully
|
||||
*/
|
||||
this.app.get('/api/state/:kernelName', this.handleGetState.bind(this));
|
||||
|
||||
// Quantum Backends
|
||||
/**
|
||||
* @swagger
|
||||
* /api/targets:
|
||||
* get:
|
||||
* summary: List available quantum targets
|
||||
* tags: [Quantum Backends]
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Available targets retrieved successfully
|
||||
*/
|
||||
this.app.get('/api/targets', this.handleGetTargets.bind(this));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/targets:
|
||||
* post:
|
||||
* summary: Set quantum computing target
|
||||
* tags: [Quantum Backends]
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/TargetRequest'
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Target set successfully
|
||||
*/
|
||||
this.app.post('/api/targets', this.handleSetTarget.bind(this));
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/platform:
|
||||
* get:
|
||||
* summary: Get platform information
|
||||
* tags: [System]
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Platform info retrieved successfully
|
||||
*/
|
||||
this.app.get('/api/platform', this.handleGetPlatform.bind(this));
|
||||
|
||||
// Server-Sent Events
|
||||
/**
|
||||
* @swagger
|
||||
* /api/events:
|
||||
* get:
|
||||
* summary: Server-Sent Events stream
|
||||
* tags: [Real-time]
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: topics
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Comma-separated list of topics to subscribe to
|
||||
* responses:
|
||||
* 200:
|
||||
* description: SSE stream established
|
||||
* content:
|
||||
* text/event-stream:
|
||||
* schema:
|
||||
* type: string
|
||||
*/
|
||||
this.app.get('/api/events', this.handleSSE.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Swagger documentation
|
||||
*/
|
||||
private setupSwagger(): void {
|
||||
const specs = swaggerJsdoc(swaggerOptions);
|
||||
this.app.use('/api-docs', swaggerUiExpress.serve, swaggerUiExpress.setup(specs, {
|
||||
explorer: true,
|
||||
customSiteTitle: 'CUDA Quantum MCP API Documentation',
|
||||
customfavIcon: '/favicon.ico'
|
||||
}));
|
||||
|
||||
// Serve raw OpenAPI spec
|
||||
this.app.get('/api-docs.json', (req, res) => {
|
||||
res.json(specs);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle kernel creation
|
||||
*/
|
||||
private async handleCreateKernel(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { name, num_qubits, parameters } = req.body;
|
||||
|
||||
if (!name || !num_qubits) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: 'Missing required parameters: name, num_qubits',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const bridge = getPythonBridge();
|
||||
const result = await bridge.createKernel(name, num_qubits, parameters);
|
||||
|
||||
// Broadcast to SSE clients
|
||||
this.broadcastSSE('kernel_created', { name, num_qubits, result });
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: result,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.error('Error creating kernel:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle kernel listing
|
||||
*/
|
||||
private async handleListKernels(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const bridge = getPythonBridge();
|
||||
const result = await bridge.listKernels();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: result,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.error('Error listing kernels:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle gate application
|
||||
*/
|
||||
private async handleApplyGate(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { kernel_name, gate_name, target_qubits, control_qubits, parameters, adjoint } = req.body;
|
||||
|
||||
if (!kernel_name || !gate_name || !target_qubits) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: 'Missing required parameters: kernel_name, gate_name, target_qubits',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const bridge = getPythonBridge();
|
||||
const result = await bridge.applyGate(
|
||||
kernel_name, gate_name, target_qubits, control_qubits, parameters, adjoint
|
||||
);
|
||||
|
||||
// Broadcast to SSE clients
|
||||
this.broadcastSSE('gate_applied', { kernel_name, gate_name, result });
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: result,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.error('Error applying gate:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle quantum sampling
|
||||
*/
|
||||
private async handleSample(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { kernel_name, shots = 1000, parameters } = req.body;
|
||||
|
||||
if (!kernel_name) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: 'Missing required parameter: kernel_name',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const bridge = getPythonBridge();
|
||||
const result = await bridge.sample(kernel_name, shots, parameters);
|
||||
|
||||
// Broadcast to SSE clients
|
||||
this.broadcastSSE('sampling_completed', { kernel_name, shots, result });
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: result,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.error('Error sampling:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Hamiltonian observation
|
||||
*/
|
||||
private async handleObserve(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { kernel_name, hamiltonian_terms, shots = 1000, parameters } = req.body;
|
||||
|
||||
if (!kernel_name || !hamiltonian_terms) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: 'Missing required parameters: kernel_name, hamiltonian_terms',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const bridge = getPythonBridge();
|
||||
const result = await bridge.observe(kernel_name, hamiltonian_terms, shots, parameters);
|
||||
|
||||
// Broadcast to SSE clients
|
||||
this.broadcastSSE('observation_completed', { kernel_name, result });
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: result,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.error('Error observing:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle state vector retrieval
|
||||
*/
|
||||
private async handleGetState(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { kernelName } = req.params;
|
||||
const { parameters } = req.query;
|
||||
|
||||
if (!kernelName) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: 'Missing required parameter: kernelName',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const bridge = getPythonBridge();
|
||||
const result = await bridge.getState(
|
||||
kernelName,
|
||||
parameters ? JSON.parse(parameters as string) : undefined
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: result,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.error('Error getting state:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle target listing
|
||||
*/
|
||||
private async handleGetTargets(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const bridge = getPythonBridge();
|
||||
const result = await bridge.getAvailableTargets();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: result,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.error('Error getting targets:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle target setting
|
||||
*/
|
||||
private async handleSetTarget(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { target, configuration } = req.body;
|
||||
|
||||
if (!target) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: 'Missing required parameter: target',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const bridge = getPythonBridge();
|
||||
const result = await bridge.setTarget(target, configuration);
|
||||
|
||||
// Broadcast to SSE clients
|
||||
this.broadcastSSE('target_changed', { target, result });
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: result,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.error('Error setting target:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle platform info
|
||||
*/
|
||||
private async handleGetPlatform(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const bridge = getPythonBridge();
|
||||
const result = await bridge.getPlatformInfo();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: result,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.error('Error getting platform info:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Server-Sent Events
|
||||
*/
|
||||
private handleSSE(req: Request, res: Response): void {
|
||||
const clientId = Math.random().toString(36).substring(2, 15);
|
||||
const topics = req.query.topics ? (req.query.topics as string).split(',') : ['all'];
|
||||
|
||||
// Setup SSE headers
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'text/event-stream',
|
||||
'Cache-Control': 'no-cache',
|
||||
'Connection': 'keep-alive',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Headers': 'Cache-Control'
|
||||
});
|
||||
|
||||
// Store client
|
||||
const client: SSEClient = {
|
||||
id: clientId,
|
||||
response: res,
|
||||
subscriptions: new Set(topics)
|
||||
};
|
||||
|
||||
this.sseClients.set(clientId, client);
|
||||
|
||||
// Send initial connection event
|
||||
res.write(`data: ${JSON.stringify({
|
||||
type: 'connection',
|
||||
clientId: clientId,
|
||||
timestamp: new Date().toISOString()
|
||||
})}\\n\\n`);
|
||||
|
||||
// Handle client disconnect
|
||||
req.on('close', () => {
|
||||
this.sseClients.delete(clientId);
|
||||
this.logger.debug(`SSE client ${clientId} disconnected`);
|
||||
});
|
||||
|
||||
this.logger.debug(`SSE client ${clientId} connected with topics: ${topics.join(', ')}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast event to SSE clients
|
||||
*/
|
||||
private broadcastSSE(eventType: string, data: any): void {
|
||||
const message = {
|
||||
type: eventType,
|
||||
data: data,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
for (const [clientId, client] of this.sseClients) {
|
||||
if (client.subscriptions.has('all') || client.subscriptions.has(eventType)) {
|
||||
try {
|
||||
client.response.write(`data: ${JSON.stringify(message)}\\n\\n`);
|
||||
} catch (error) {
|
||||
this.logger.warn(`Failed to send SSE to client ${clientId}:`, error);
|
||||
this.sseClients.delete(clientId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup WebSocket server
|
||||
*/
|
||||
private setupWebSocket(): void {
|
||||
this.wsServer.on('connection', (ws: WebSocket, req: any) => {
|
||||
const clientId = Math.random().toString(36).substring(2, 15);
|
||||
const client: WSClient = {
|
||||
id: clientId,
|
||||
ws: ws,
|
||||
subscriptions: new Set(['all'])
|
||||
};
|
||||
|
||||
this.wsClients.set(clientId, client);
|
||||
this.logger.debug(`WebSocket client ${clientId} connected`);
|
||||
|
||||
// Send welcome message
|
||||
ws.send(JSON.stringify({
|
||||
type: 'connection',
|
||||
clientId: clientId,
|
||||
timestamp: new Date().toISOString()
|
||||
}));
|
||||
|
||||
// Handle messages
|
||||
ws.on('message', (message: Buffer) => {
|
||||
try {
|
||||
const data = JSON.parse(message.toString());
|
||||
this.handleWebSocketMessage(clientId, data);
|
||||
} catch (error) {
|
||||
this.logger.warn(`Invalid WebSocket message from ${clientId}:`, error);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle disconnect
|
||||
ws.on('close', () => {
|
||||
this.wsClients.delete(clientId);
|
||||
this.logger.debug(`WebSocket client ${clientId} disconnected`);
|
||||
});
|
||||
|
||||
ws.on('error', (error: any) => {
|
||||
this.logger.error(`WebSocket error for client ${clientId}:`, error);
|
||||
this.wsClients.delete(clientId);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle WebSocket messages
|
||||
*/
|
||||
private handleWebSocketMessage(clientId: string, message: any): void {
|
||||
const client = this.wsClients.get(clientId);
|
||||
if (!client) return;
|
||||
|
||||
switch (message.type) {
|
||||
case 'subscribe':
|
||||
if (message.topics && Array.isArray(message.topics)) {
|
||||
message.topics.forEach((topic: string) => {
|
||||
client.subscriptions.add(topic);
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 'unsubscribe':
|
||||
if (message.topics && Array.isArray(message.topics)) {
|
||||
message.topics.forEach((topic: string) => {
|
||||
client.subscriptions.delete(topic);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the HTTP server
|
||||
*/
|
||||
async start(): Promise<void> {
|
||||
try {
|
||||
// Initialize Python bridge
|
||||
this.logger.info('Initializing Python bridge...');
|
||||
await initializePythonBridge({ pythonPath: this.config.pythonPath });
|
||||
this.logger.info('Python bridge initialized successfully');
|
||||
|
||||
// Create HTTP server
|
||||
this.server = createServer(this.app);
|
||||
|
||||
// Setup WebSocket server
|
||||
this.wsServer = new WebSocketServer({ server: this.server });
|
||||
this.setupWebSocket();
|
||||
|
||||
// Start listening
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
this.server.listen(this.config.port, this.config.host, () => {
|
||||
resolve();
|
||||
}).on('error', reject);
|
||||
});
|
||||
|
||||
this.logger.info(`CUDA Quantum HTTP Server started on ${this.config.host}:${this.config.port}`);
|
||||
this.logger.info(`API Documentation: http://${this.config.host}:${this.config.port}/api-docs`);
|
||||
this.logger.info(`Health Check: http://${this.config.host}:${this.config.port}/health`);
|
||||
this.logger.info(`SSE Endpoint: http://${this.config.host}:${this.config.port}/api/events`);
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error('Failed to start HTTP server:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the HTTP server
|
||||
*/
|
||||
async stop(): Promise<void> {
|
||||
this.logger.info('Shutting down HTTP server...');
|
||||
|
||||
// Close WebSocket connections
|
||||
for (const [clientId, client] of this.wsClients) {
|
||||
client.ws.close();
|
||||
}
|
||||
this.wsClients.clear();
|
||||
|
||||
// Close SSE connections
|
||||
for (const [clientId, client] of this.sseClients) {
|
||||
client.response.end();
|
||||
}
|
||||
this.sseClients.clear();
|
||||
|
||||
// Close WebSocket server
|
||||
if (this.wsServer) {
|
||||
this.wsServer.close();
|
||||
}
|
||||
|
||||
// Close HTTP server
|
||||
if (this.server) {
|
||||
await new Promise<void>((resolve) => {
|
||||
this.server.close(() => resolve());
|
||||
});
|
||||
}
|
||||
|
||||
// Close Python bridge
|
||||
const bridge = getPythonBridge();
|
||||
if (bridge) {
|
||||
await bridge.close();
|
||||
}
|
||||
|
||||
this.logger.info('HTTP server shutdown complete');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entry point for HTTP server
|
||||
*/
|
||||
async function main(): Promise<void> {
|
||||
const config: HttpServerConfig = {
|
||||
port: parseInt(process.env.HTTP_PORT || '3000'),
|
||||
host: process.env.HTTP_HOST || 'localhost',
|
||||
corsOrigins: process.env.CORS_ORIGINS ? process.env.CORS_ORIGINS.split(',') : ['*'],
|
||||
pythonPath: process.env.CUDAQ_PYTHON_PATH,
|
||||
logLevel: process.env.LOG_LEVEL === 'debug' ? LogLevel.DEBUG :
|
||||
process.env.LOG_LEVEL === 'warn' ? LogLevel.WARN :
|
||||
process.env.LOG_LEVEL === 'error' ? LogLevel.ERROR :
|
||||
LogLevel.INFO
|
||||
};
|
||||
|
||||
const server = new CudaQuantumHttpServer(config);
|
||||
|
||||
// Handle graceful shutdown
|
||||
process.on('SIGTERM', async () => {
|
||||
await server.stop();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
process.on('SIGINT', async () => {
|
||||
await server.stop();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
try {
|
||||
await server.start();
|
||||
} catch (error) {
|
||||
console.error('Failed to start CUDA Quantum HTTP Server:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Start the server if this file is run directly
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
main().catch((error) => {
|
||||
console.error('Fatal error:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
Referencia en una nueva incidencia
Block a user