@allma/core-cdk
Version:
Core AWS CDK constructs for deploying the Allma serverless AI orchestration platform.
109 lines • 5.5 kB
JavaScript
import { LambdaClient, InvokeCommand } from '@aws-sdk/client-lambda';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { ENV_VAR_NAMES, } from '@allma/core-types';
import { log_error, log_debug } from '@allma/core-sdk';
const lambdaClient = new LambdaClient({});
const s3Client = new S3Client({});
const EXECUTION_LOGGER_LAMBDA_ARN = process.env.EXECUTION_LOGGER_LAMBDA_ARN;
const EXECUTION_TRACES_BUCKET_NAME = process.env[ENV_VAR_NAMES.ALLMA_EXECUTION_TRACES_BUCKET_NAME];
const LAMBDA_PAYLOAD_LIMIT_BYTES = 240 * 1024;
class ExecutionLoggerClient {
async invokeLogger(payload, correlationId) {
if (!EXECUTION_LOGGER_LAMBDA_ARN) {
log_error('EXECUTION_LOGGER_LAMBDA_ARN is not configured. Cannot log execution event.', { action: payload.action }, correlationId);
return;
}
try {
const finalPayloadString = JSON.stringify(payload);
const finalPayloadSize = Buffer.byteLength(finalPayloadString, 'utf-8');
if (finalPayloadSize > LAMBDA_PAYLOAD_LIMIT_BYTES) {
log_error('Minimal ExecutionLogger payload exceeds size limit. This should not happen. Event will be dropped.', {
error: `${finalPayloadSize} byte payload is too large for the Event invocation type (limit ${LAMBDA_PAYLOAD_LIMIT_BYTES} bytes)`,
action: payload.action,
}, correlationId);
return;
}
await lambdaClient.send(new InvokeCommand({
FunctionName: EXECUTION_LOGGER_LAMBDA_ARN,
Payload: finalPayloadString,
InvocationType: 'Event', // Fire-and-forget
}));
}
catch (e) {
log_error('Failed to invoke ExecutionLogger Lambda.', { error: e.message, action: payload.action }, correlationId);
}
}
async createMetadataRecord(record) {
const payload = {
action: 'CREATE_METADATA',
record,
};
await this.invokeLogger(payload, record.flowExecutionId);
}
async updateFinalStatus(updateData) {
const payload = {
action: 'UPDATE_FINAL_STATUS',
...updateData,
};
await this.invokeLogger(payload, updateData.flowExecutionId);
}
/**
* Logs a step execution.
* Uploads the full record to S3 and sends a minimal record to DynamoDB via Lambda.
* @returns The S3Pointer to the full record if successful, otherwise void.
*/
async logStepExecution(fullRecordData) {
const correlationId = fullRecordData.flowExecutionId;
const s3Identifier = `${fullRecordData.stepInstanceId}_${fullRecordData.attemptNumber || 1}`;
log_debug(`[execution-logger-client] Logging step execution for '${fullRecordData.stepInstanceId}' using hybrid storage model.`, {}, correlationId);
try {
// Step 1: ALWAYS upload the full, detailed record to S3.
const fullRecordString = JSON.stringify(fullRecordData);
const s3Key = `full_step_records/${correlationId}/${s3Identifier}_${new Date().toISOString()}.json`;
await s3Client.send(new PutObjectCommand({
Bucket: EXECUTION_TRACES_BUCKET_NAME,
Key: s3Key,
Body: fullRecordString,
ContentType: 'application/json',
}));
const s3Pointer = { bucket: EXECUTION_TRACES_BUCKET_NAME, key: s3Key };
// Step 2: Construct the minimal record for DynamoDB.
const minimalRecordForDb = {
flowExecutionId: fullRecordData.flowExecutionId,
branchId: fullRecordData.branchId,
branchExecutionId: fullRecordData.branchExecutionId,
eventTimestamp: fullRecordData.eventTimestamp,
stepInstanceId: fullRecordData.stepInstanceId,
stepDefinitionId: fullRecordData.stepDefinitionId,
stepDefinitionVersion: fullRecordData.stepDefinitionVersion,
stepType: fullRecordData.stepType,
status: fullRecordData.status,
startTime: fullRecordData.startTime,
endTime: fullRecordData.endTime,
durationMs: fullRecordData.durationMs,
attemptNumber: fullRecordData.attemptNumber,
...(fullRecordData.errorInfo && {
errorInfoSummary: {
errorName: fullRecordData.errorInfo.errorName,
errorMessage: fullRecordData.errorInfo.errorMessage,
}
}),
fullRecordS3Pointer: s3Pointer,
};
// Step 3: Create the payload for the logger Lambda. This payload is now always small.
const payload = {
action: 'LOG_STEP_EXECUTION',
record: minimalRecordForDb,
};
// Step 4: Invoke the logger.
await this.invokeLogger(payload, correlationId);
return s3Pointer;
}
catch (e) {
log_error(`[execution-logger-client] Failed during S3 upload or payload construction for step '${s3Identifier}'`, { error: e.message }, correlationId);
// We do not throw here to avoid failing the main execution flow due to a logging error.
}
}
}
export const executionLoggerClient = new ExecutionLoggerClient();
//# sourceMappingURL=execution-logger-client.js.map