feat: add zero-configuration Brainy service template with augmentation-first architecture
- Implement WebSocket augmentation for real-time communication - Implement WebRTC augmentation for peer-to-peer connections - Implement HTTP augmentation as minimal REST fallback - Add auto-discovery augmentation for data pattern analysis - Add adaptive storage augmentation for intelligent resource management - Add environment adapter augmentation for universal compatibility - Template auto-detects environment (browser, Node.js, serverless, containers) - Intelligent transport selection (WebRTC → WebSocket → HTTP) - Automatic storage optimization (memory → filesystem → S3) - Zero configuration required - just npm start - Includes intelligent verb scoring by default - Works in any environment without configuration - Full documentation and examples included
This commit is contained in:
parent
880b8f74e3
commit
ee003a9473
30 changed files with 3548 additions and 0 deletions
174
examples/brainy-service-template/src/controllers/entities.js
Normal file
174
examples/brainy-service-template/src/controllers/entities.js
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
import { logger } from '../utils/logger.js'
|
||||
import { ApiError } from '../utils/errors.js'
|
||||
|
||||
export const entitiesController = {
|
||||
async create(req, res, next) {
|
||||
try {
|
||||
const { data, metadata = {}, options = {} } = req.body
|
||||
const { brainyService } = req.app.locals
|
||||
|
||||
if (!data) {
|
||||
throw new ApiError(400, 'Data is required')
|
||||
}
|
||||
|
||||
// Add timestamp and source info to metadata
|
||||
const enhancedMetadata = {
|
||||
...metadata,
|
||||
createdAt: new Date().toISOString(),
|
||||
source: 'api'
|
||||
}
|
||||
|
||||
const entityId = await brainyService.addEntity(data, enhancedMetadata, options)
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
id: entityId,
|
||||
data: {
|
||||
id: entityId,
|
||||
data,
|
||||
metadata: enhancedMetadata
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
},
|
||||
|
||||
async getById(req, res, next) {
|
||||
try {
|
||||
const { id } = req.params
|
||||
const { brainyService } = req.app.locals
|
||||
|
||||
const entity = await brainyService.getEntity(id)
|
||||
|
||||
if (!entity) {
|
||||
throw new ApiError(404, `Entity with ID ${id} not found`)
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: entity
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
},
|
||||
|
||||
async update(req, res, next) {
|
||||
try {
|
||||
const { id } = req.params
|
||||
const { data, metadata = {} } = req.body
|
||||
const { brainyService } = req.app.locals
|
||||
|
||||
// Check if entity exists
|
||||
const existingEntity = await brainyService.getEntity(id)
|
||||
if (!existingEntity) {
|
||||
throw new ApiError(404, `Entity with ID ${id} not found`)
|
||||
}
|
||||
|
||||
// Add update timestamp
|
||||
const enhancedMetadata = {
|
||||
...metadata,
|
||||
updatedAt: new Date().toISOString()
|
||||
}
|
||||
|
||||
await brainyService.updateEntity(id, data, enhancedMetadata)
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Entity updated successfully',
|
||||
id
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
},
|
||||
|
||||
async delete(req, res, next) {
|
||||
try {
|
||||
const { id } = req.params
|
||||
const { brainyService } = req.app.locals
|
||||
|
||||
// Check if entity exists
|
||||
const existingEntity = await brainyService.getEntity(id)
|
||||
if (!existingEntity) {
|
||||
throw new ApiError(404, `Entity with ID ${id} not found`)
|
||||
}
|
||||
|
||||
await brainyService.deleteEntity(id)
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Entity deleted successfully',
|
||||
id
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
},
|
||||
|
||||
async search(req, res, next) {
|
||||
try {
|
||||
const { query, limit = 10, threshold = 0.7, includeMetadata = true } = req.body
|
||||
const { brainyService } = req.app.locals
|
||||
|
||||
if (!query) {
|
||||
throw new ApiError(400, 'Query is required')
|
||||
}
|
||||
|
||||
const options = {
|
||||
limit: Math.min(limit, 100), // Cap at 100 results
|
||||
threshold,
|
||||
includeMetadata
|
||||
}
|
||||
|
||||
const results = await brainyService.searchEntities(query, options)
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
query,
|
||||
results: results.map(result => ({
|
||||
id: result.id,
|
||||
similarity: result.similarity,
|
||||
data: result.data,
|
||||
metadata: result.metadata
|
||||
})),
|
||||
count: results.length,
|
||||
options
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
},
|
||||
|
||||
async list(req, res, next) {
|
||||
try {
|
||||
const { page = 1, limit = 50, type } = req.query
|
||||
const { brainyService } = req.app.locals
|
||||
|
||||
const options = {
|
||||
offset: (page - 1) * limit,
|
||||
limit: Math.min(limit, 100)
|
||||
}
|
||||
|
||||
// Add type filter if specified
|
||||
if (type) {
|
||||
options.filter = { type }
|
||||
}
|
||||
|
||||
const entities = await brainyService.listEntities(options)
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: entities,
|
||||
pagination: {
|
||||
page: parseInt(page),
|
||||
limit: parseInt(limit),
|
||||
count: entities.length
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
130
examples/brainy-service-template/src/controllers/health.js
Normal file
130
examples/brainy-service-template/src/controllers/health.js
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
import { logger } from '../utils/logger.js'
|
||||
|
||||
export const healthController = {
|
||||
async check(req, res, next) {
|
||||
try {
|
||||
const { brainyService } = req.app.locals
|
||||
|
||||
// Get Brainy health status
|
||||
const brainyHealth = await brainyService.getHealth()
|
||||
|
||||
// Overall system health
|
||||
const health = {
|
||||
status: brainyHealth.status === 'healthy' ? 'healthy' : 'unhealthy',
|
||||
timestamp: new Date().toISOString(),
|
||||
version: process.env.npm_package_version || '1.0.0',
|
||||
environment: process.env.NODE_ENV || 'development',
|
||||
uptime: Math.floor(process.uptime()),
|
||||
services: {
|
||||
brainy: brainyHealth
|
||||
},
|
||||
system: {
|
||||
memory: {
|
||||
used: Math.round(process.memoryUsage().heapUsed / 1024 / 1024),
|
||||
total: Math.round(process.memoryUsage().heapTotal / 1024 / 1024),
|
||||
external: Math.round(process.memoryUsage().external / 1024 / 1024)
|
||||
},
|
||||
cpu: process.cpuUsage()
|
||||
}
|
||||
}
|
||||
|
||||
const statusCode = health.status === 'healthy' ? 200 : 503
|
||||
|
||||
res.status(statusCode).json(health)
|
||||
} catch (error) {
|
||||
logger.error('Health check failed:', error)
|
||||
|
||||
res.status(503).json({
|
||||
status: 'unhealthy',
|
||||
timestamp: new Date().toISOString(),
|
||||
error: error.message,
|
||||
services: {
|
||||
brainy: { status: 'error', error: error.message }
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async readiness(req, res, next) {
|
||||
try {
|
||||
const { brainyService } = req.app.locals
|
||||
|
||||
// Check if services are ready
|
||||
const brainyHealth = await brainyService.getHealth()
|
||||
const isReady = brainyHealth.status === 'healthy'
|
||||
|
||||
const readiness = {
|
||||
ready: isReady,
|
||||
timestamp: new Date().toISOString(),
|
||||
checks: {
|
||||
brainy: {
|
||||
ready: brainyHealth.status === 'healthy',
|
||||
details: brainyHealth
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const statusCode = readiness.ready ? 200 : 503
|
||||
res.status(statusCode).json(readiness)
|
||||
} catch (error) {
|
||||
logger.error('Readiness check failed:', error)
|
||||
|
||||
res.status(503).json({
|
||||
ready: false,
|
||||
timestamp: new Date().toISOString(),
|
||||
error: error.message
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async liveness(req, res, next) {
|
||||
try {
|
||||
// Simple liveness check - just verify the process is running
|
||||
res.status(200).json({
|
||||
alive: true,
|
||||
timestamp: new Date().toISOString(),
|
||||
pid: process.pid,
|
||||
uptime: Math.floor(process.uptime())
|
||||
})
|
||||
} catch (error) {
|
||||
logger.error('Liveness check failed:', error)
|
||||
|
||||
res.status(503).json({
|
||||
alive: false,
|
||||
timestamp: new Date().toISOString(),
|
||||
error: error.message
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async metrics(req, res, next) {
|
||||
try {
|
||||
const { brainyService } = req.app.locals
|
||||
|
||||
// Get detailed metrics
|
||||
const brainyMetrics = await brainyService.getMetrics()
|
||||
|
||||
const metrics = {
|
||||
timestamp: new Date().toISOString(),
|
||||
brainy: brainyMetrics.database,
|
||||
system: {
|
||||
...brainyMetrics.performance,
|
||||
process: {
|
||||
pid: process.pid,
|
||||
version: process.version,
|
||||
platform: process.platform,
|
||||
arch: process.arch,
|
||||
env: process.env.NODE_ENV || 'development'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: metrics
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
import { logger } from '../utils/logger.js'
|
||||
import { ApiError } from '../utils/errors.js'
|
||||
|
||||
export const relationshipsController = {
|
||||
async create(req, res, next) {
|
||||
try {
|
||||
const { sourceId, targetId, type, weight, metadata = {}, autoCreateMissingNouns = false } = req.body
|
||||
const { brainyService } = req.app.locals
|
||||
|
||||
if (!sourceId || !targetId || !type) {
|
||||
throw new ApiError(400, 'sourceId, targetId, and type are required')
|
||||
}
|
||||
|
||||
// Add timestamp and source info to metadata
|
||||
const enhancedMetadata = {
|
||||
...metadata,
|
||||
createdAt: new Date().toISOString(),
|
||||
source: 'api'
|
||||
}
|
||||
|
||||
const options = {
|
||||
weight,
|
||||
metadata: enhancedMetadata,
|
||||
autoCreateMissingNouns
|
||||
}
|
||||
|
||||
const relationshipId = await brainyService.addRelationship(sourceId, targetId, type, options)
|
||||
|
||||
// Get the created relationship to return full details
|
||||
const relationship = await brainyService.getRelationship(relationshipId)
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
id: relationshipId,
|
||||
data: {
|
||||
id: relationshipId,
|
||||
sourceId,
|
||||
targetId,
|
||||
type,
|
||||
weight: relationship?.metadata?.weight,
|
||||
confidence: relationship?.metadata?.confidence,
|
||||
intelligentScoring: relationship?.metadata?.intelligentScoring,
|
||||
metadata: enhancedMetadata
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
},
|
||||
|
||||
async getById(req, res, next) {
|
||||
try {
|
||||
const { id } = req.params
|
||||
const { brainyService } = req.app.locals
|
||||
|
||||
const relationship = await brainyService.getRelationship(id)
|
||||
|
||||
if (!relationship) {
|
||||
throw new ApiError(404, `Relationship with ID ${id} not found`)
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: relationship
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
},
|
||||
|
||||
async update(req, res, next) {
|
||||
try {
|
||||
const { id } = req.params
|
||||
const { weight, metadata = {} } = req.body
|
||||
const { brainyService } = req.app.locals
|
||||
|
||||
// Check if relationship exists
|
||||
const existingRelationship = await brainyService.getRelationship(id)
|
||||
if (!existingRelationship) {
|
||||
throw new ApiError(404, `Relationship with ID ${id} not found`)
|
||||
}
|
||||
|
||||
// Prepare updates
|
||||
const updates = {}
|
||||
if (weight !== undefined) updates.weight = weight
|
||||
if (Object.keys(metadata).length > 0) {
|
||||
updates.metadata = {
|
||||
...metadata,
|
||||
updatedAt: new Date().toISOString()
|
||||
}
|
||||
}
|
||||
|
||||
await brainyService.updateRelationship(id, updates)
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Relationship updated successfully',
|
||||
id
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
},
|
||||
|
||||
async delete(req, res, next) {
|
||||
try {
|
||||
const { id } = req.params
|
||||
const { brainyService } = req.app.locals
|
||||
|
||||
// Check if relationship exists
|
||||
const existingRelationship = await brainyService.getRelationship(id)
|
||||
if (!existingRelationship) {
|
||||
throw new ApiError(404, `Relationship with ID ${id} not found`)
|
||||
}
|
||||
|
||||
await brainyService.deleteRelationship(id)
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Relationship deleted successfully',
|
||||
id
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
},
|
||||
|
||||
async list(req, res, next) {
|
||||
try {
|
||||
const { page = 1, limit = 50, sourceId, targetId, type } = req.query
|
||||
const { brainyService } = req.app.locals
|
||||
|
||||
const options = {
|
||||
pagination: {
|
||||
offset: (page - 1) * limit,
|
||||
limit: Math.min(limit, 100)
|
||||
}
|
||||
}
|
||||
|
||||
// Add filters if specified
|
||||
const filter = {}
|
||||
if (sourceId) filter.sourceId = sourceId
|
||||
if (targetId) filter.targetId = targetId
|
||||
if (type) filter.verbType = type
|
||||
|
||||
if (Object.keys(filter).length > 0) {
|
||||
options.filter = filter
|
||||
}
|
||||
|
||||
const result = await brainyService.listRelationships(options)
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: result.items || result,
|
||||
pagination: {
|
||||
page: parseInt(page),
|
||||
limit: parseInt(limit),
|
||||
count: result.items ? result.items.length : result.length,
|
||||
hasMore: result.hasMore || false,
|
||||
totalCount: result.totalCount
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
195
examples/brainy-service-template/src/controllers/scoring.js
Normal file
195
examples/brainy-service-template/src/controllers/scoring.js
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
import { logger } from '../utils/logger.js'
|
||||
import { ApiError } from '../utils/errors.js'
|
||||
|
||||
export const scoringController = {
|
||||
async provideFeedback(req, res, next) {
|
||||
try {
|
||||
const { id } = req.params
|
||||
const { weight, confidence, type = 'correction' } = req.body
|
||||
const { scoringService } = req.app.locals
|
||||
|
||||
if (!scoringService) {
|
||||
throw new ApiError(400, 'Intelligent verb scoring is not enabled')
|
||||
}
|
||||
|
||||
if (weight === undefined) {
|
||||
throw new ApiError(400, 'Weight is required for feedback')
|
||||
}
|
||||
|
||||
if (weight < 0 || weight > 1) {
|
||||
throw new ApiError(400, 'Weight must be between 0 and 1')
|
||||
}
|
||||
|
||||
if (confidence !== undefined && (confidence < 0 || confidence > 1)) {
|
||||
throw new ApiError(400, 'Confidence must be between 0 and 1')
|
||||
}
|
||||
|
||||
const feedbackData = {
|
||||
weight,
|
||||
confidence,
|
||||
type
|
||||
}
|
||||
|
||||
const result = await scoringService.provideFeedback(id, feedbackData)
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: result.message,
|
||||
feedback: {
|
||||
relationshipId: id,
|
||||
weight,
|
||||
confidence,
|
||||
type,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
},
|
||||
|
||||
async getStats(req, res, next) {
|
||||
try {
|
||||
const { scoringService } = req.app.locals
|
||||
|
||||
if (!scoringService) {
|
||||
throw new ApiError(400, 'Intelligent verb scoring is not enabled')
|
||||
}
|
||||
|
||||
const stats = await scoringService.getStats()
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: stats
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
},
|
||||
|
||||
async exportLearningData(req, res, next) {
|
||||
try {
|
||||
const { scoringService } = req.app.locals
|
||||
|
||||
if (!scoringService) {
|
||||
throw new ApiError(400, 'Intelligent verb scoring is not enabled')
|
||||
}
|
||||
|
||||
const result = await scoringService.exportLearningData()
|
||||
|
||||
if (!result.data) {
|
||||
return res.json({
|
||||
success: true,
|
||||
message: result.message
|
||||
})
|
||||
}
|
||||
|
||||
// Set appropriate headers for download
|
||||
res.setHeader('Content-Type', 'application/json')
|
||||
res.setHeader('Content-Disposition', 'attachment; filename="brainy-learning-data.json"')
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: result.data,
|
||||
timestamp: result.timestamp,
|
||||
format: result.format
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
},
|
||||
|
||||
async importLearningData(req, res, next) {
|
||||
try {
|
||||
const { data } = req.body
|
||||
const { scoringService } = req.app.locals
|
||||
|
||||
if (!scoringService) {
|
||||
throw new ApiError(400, 'Intelligent verb scoring is not enabled')
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
throw new ApiError(400, 'Learning data is required')
|
||||
}
|
||||
|
||||
const result = await scoringService.importLearningData(data)
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: result.message,
|
||||
timestamp: new Date().toISOString()
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
},
|
||||
|
||||
async clearStats(req, res, next) {
|
||||
try {
|
||||
const { scoringService } = req.app.locals
|
||||
|
||||
if (!scoringService) {
|
||||
throw new ApiError(400, 'Intelligent verb scoring is not enabled')
|
||||
}
|
||||
|
||||
const result = await scoringService.clearStats()
|
||||
|
||||
res.json({
|
||||
success: result.success,
|
||||
message: result.message,
|
||||
timestamp: new Date().toISOString()
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
},
|
||||
|
||||
async analyzePattern(req, res, next) {
|
||||
try {
|
||||
const { sourceType, targetType, relationshipType } = req.body
|
||||
const { scoringService } = req.app.locals
|
||||
|
||||
if (!scoringService) {
|
||||
throw new ApiError(400, 'Intelligent verb scoring is not enabled')
|
||||
}
|
||||
|
||||
if (!sourceType || !targetType || !relationshipType) {
|
||||
throw new ApiError(400, 'sourceType, targetType, and relationshipType are required')
|
||||
}
|
||||
|
||||
const analysis = await scoringService.analyzeRelationshipPattern(
|
||||
sourceType,
|
||||
targetType,
|
||||
relationshipType
|
||||
)
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: analysis
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
},
|
||||
|
||||
async getRecommendations(req, res, next) {
|
||||
try {
|
||||
const { entityId } = req.params
|
||||
const { limit = 10 } = req.query
|
||||
const { scoringService } = req.app.locals
|
||||
|
||||
if (!scoringService) {
|
||||
throw new ApiError(400, 'Intelligent verb scoring is not enabled')
|
||||
}
|
||||
|
||||
const recommendations = await scoringService.getRecommendations(entityId, parseInt(limit))
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: recommendations
|
||||
})
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in a new issue