Skip to content
This repository was archived by the owner on Jan 29, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all 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
40 changes: 39 additions & 1 deletion backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@
},
"dependencies": {
"@google/generative-ai": "^0.24.1",
"express": "^4.18.2",
"cors": "^2.8.5",
"dotenv": "^16.3.1"
"dotenv": "^16.3.1",
"express": "^4.18.2",
"prom-client": "^15.1.3"
},
"devDependencies": {
"nodemon": "^3.0.1"
}
}
}
30 changes: 30 additions & 0 deletions backend/src/api/gemini/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@

import express from 'express';
import { GoogleGenerativeAI } from '@google/generative-ai';
import {
geminiApiDuration,
geminiApiTotal,
flowNodesProcessed,
flowEdgesProcessed,
errorsTotal
} from '../../monitoring/metrics.js';

const router = express.Router();

Expand Down Expand Up @@ -100,6 +107,8 @@
* Execute a visual flow via Gemini API
*/
router.post('/execute', async (req, res) => {
const apiStart = Date.now();

try {
const { nodes, edges } = req.body;

Expand All @@ -118,6 +127,10 @@

console.log(`πŸ”„ Executing flow with ${nodes.length} nodes and ${edges.length} edges`);

// Record flow statistics
flowNodesProcessed.observe(nodes.length);
flowEdgesProcessed.observe(edges.length);

// Build prompt from graph
const prompt = buildPromptFromGraph(nodes, edges);
console.log('πŸ“ Built prompt:', prompt);
Expand All @@ -134,6 +147,11 @@

console.log('βœ… Received response from Gemini API');

// Record successful API call
const apiDuration = (Date.now() - apiStart) / 1000;
geminiApiDuration.observe({ status: 'success' }, apiDuration);
geminiApiTotal.inc({ status: 'success' });

// Return successful response
res.json({
success: true,
Expand All @@ -149,6 +167,18 @@
} catch (error) {
console.error('❌ Gemini API request failed:', error);

// Record failed API call
const apiDuration = (Date.now() - apiStart) / 1000;
apiStatus = 'error';

Check warning on line 172 in backend/src/api/gemini/index.js

View check run for this annotation

codefactor.io / CodeFactor

backend/src/api/gemini/index.js#L172

'apiStatus' is not defined. (no-undef)
geminiApiDuration.observe({ status: 'error' }, apiDuration);
geminiApiTotal.inc({ status: 'error' });

// Track error metrics
errorsTotal.inc({
type: error.name || 'GeminiAPIError',
path: '/api/gemini/execute'
Comment thread
clduab11 marked this conversation as resolved.
});

// Handle specific error types
if (error.message.includes('API key')) {
return res.status(401).json({
Expand Down
25 changes: 25 additions & 0 deletions backend/src/api/middleware/errorHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Error Handler Middleware
*
* Centralized error handling with metrics tracking.
*/

import { errorsTotal } from '../../monitoring/metrics.js';

/**
* Error handling middleware
*/
export function errorHandler(err, req, res, next) {

Check warning on line 12 in backend/src/api/middleware/errorHandler.js

View check run for this annotation

codefactor.io / CodeFactor

backend/src/api/middleware/errorHandler.js#L12

'next' is defined but never used. Allowed unused args must match /^_/u. (@typescript-eslint/no-unused-vars)
// Increment error counter
errorsTotal.inc({
type: err.name || 'UnknownError',
path: req.path
});

console.error('Error:', err);

res.status(500).json({
error: 'Internal server error',
message: err.message
Comment thread
clduab11 marked this conversation as resolved.
});
}
29 changes: 29 additions & 0 deletions backend/src/api/middleware/metricsMiddleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Metrics Middleware
*
* Tracks HTTP request duration and counts for all routes.
*/

import { httpRequestDuration, httpRequestTotal } from '../../monitoring/metrics.js';

/**
* Middleware to collect HTTP request metrics
*/
export function metricsMiddleware(req, res, next) {
const start = Date.now();

res.on('finish', () => {
const duration = (Date.now() - start) / 1000; // Convert to seconds
const route = req.route?.path || req.path;
const labels = {
method: req.method,
Comment thread
clduab11 marked this conversation as resolved.
route,
status_code: res.statusCode
};

httpRequestDuration.observe(labels, duration);
httpRequestTotal.inc(labels);
});

next();
}
67 changes: 67 additions & 0 deletions backend/src/monitoring/metrics.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* Prometheus Metrics Configuration
*
* Defines and exports all metrics for the Gemini Flow backend.
* Metrics are collected and exposed for Prometheus scraping.
*/

import client from 'prom-client';

// Enable default metrics (CPU, memory, event loop lag)
client.collectDefaultMetrics({
prefix: 'gemini_flow_',
gcDurationBuckets: [0.001, 0.01, 0.1, 1, 2, 5]
});

// HTTP Request Duration Histogram
export const httpRequestDuration = new client.Histogram({
name: 'gemini_flow_http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
labelNames: ['method', 'route', 'status_code'],
buckets: [0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 2, 5]
});

// HTTP Request Counter
export const httpRequestTotal = new client.Counter({
name: 'gemini_flow_http_requests_total',
help: 'Total number of HTTP requests',
labelNames: ['method', 'route', 'status_code']
});

// Error Counter
export const errorsTotal = new client.Counter({
name: 'gemini_flow_errors_total',
help: 'Total number of errors',
labelNames: ['type', 'path']
});

// Gemini API Request Duration (specific to our use case)
export const geminiApiDuration = new client.Histogram({
name: 'gemini_flow_gemini_api_duration_seconds',
help: 'Duration of Gemini API requests in seconds',
labelNames: ['status'],
buckets: [0.1, 0.5, 1, 2, 5, 10, 30]
});

// Gemini API Request Counter
export const geminiApiTotal = new client.Counter({
name: 'gemini_flow_gemini_api_requests_total',
help: 'Total number of Gemini API requests',
labelNames: ['status']
});

// Flow Execution Metrics
export const flowNodesProcessed = new client.Histogram({
name: 'gemini_flow_nodes_processed',
help: 'Distribution of node counts in executed flows',
buckets: [0, 5, 10, 25, 50, 100, 250, 500]
});

export const flowEdgesProcessed = new client.Histogram({
name: 'gemini_flow_edges_processed',
help: 'Distribution of edge counts in executed flows',
buckets: [0, 5, 10, 25, 50, 100, 250, 500]
});

// Registry for all metrics
export const register = client.register;
29 changes: 22 additions & 7 deletions backend/src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@
// Import API routes
import geminiRoutes from './api/gemini/index.js';

// Import middleware
import { metricsMiddleware } from './api/middleware/metricsMiddleware.js';
import { errorHandler } from './api/middleware/errorHandler.js';

// Import metrics registry
import { register } from './monitoring/metrics.js';

// Load environment variables
dotenv.config();

Expand All @@ -31,6 +38,9 @@
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Metrics middleware (track all requests)
app.use(metricsMiddleware);

// Health check endpoint
app.get('/health', (req, res) => {
res.json({
Expand All @@ -40,17 +50,21 @@
});
});

// Metrics endpoint (for Prometheus scraping)
app.get('/metrics', async (req, res) => {
try {
res.set('Content-Type', register.contentType);
res.end(await register.metrics());
} catch (error) {
res.status(500).end(error.message);
Comment thread
clduab11 marked this conversation as resolved.
}
});

// API routes
app.use('/api/gemini', geminiRoutes);

// Error handling middleware
app.use((err, req, res, next) => {
console.error('Error:', err);
res.status(500).json({
error: 'Internal server error',
message: err.message
});
});
app.use(errorHandler);

// 404 handler
app.use('*', (req, res) => {
Expand All @@ -64,5 +78,6 @@
app.listen(PORT, () => {
console.log(`πŸš€ Gemini Flow Backend Server running on port ${PORT}`);
console.log(`πŸ“‹ Health check: http://localhost:${PORT}/health`);
console.log(`πŸ“Š Metrics: http://localhost:${PORT}/metrics`);

Check notice on line 81 in backend/src/server.js

View check run for this annotation

codefactor.io / CodeFactor

backend/src/server.js#L81

Unexpected console statement. (no-console)
console.log(`πŸ”§ API Base URL: http://localhost:${PORT}/api`);
});
Loading