feat(P4-003): @mosaic/log — log ingest, parsing, tiered storage
Implement AgentLogsRepo with structured log ingest (single + batch), flexible query builder (filter by session, level, category, tier, date range), and tiered storage management (hot→warm→cold→purge). Add getLogsForSummarization() for the summarization pipeline. Wire LogModule into gateway with REST endpoints at /api/logs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
"@mosaic/brain": "workspace:^",
|
||||
"@mosaic/coord": "workspace:^",
|
||||
"@mosaic/db": "workspace:^",
|
||||
"@mosaic/log": "workspace:^",
|
||||
"@mosaic/memory": "workspace:^",
|
||||
"@mosaic/types": "workspace:^",
|
||||
"@nestjs/common": "^11.0.0",
|
||||
|
||||
@@ -11,6 +11,7 @@ import { MissionsModule } from './missions/missions.module.js';
|
||||
import { TasksModule } from './tasks/tasks.module.js';
|
||||
import { CoordModule } from './coord/coord.module.js';
|
||||
import { MemoryModule } from './memory/memory.module.js';
|
||||
import { LogModule } from './log/log.module.js';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -25,6 +26,7 @@ import { MemoryModule } from './memory/memory.module.js';
|
||||
TasksModule,
|
||||
CoordModule,
|
||||
MemoryModule,
|
||||
LogModule,
|
||||
],
|
||||
controllers: [HealthController],
|
||||
})
|
||||
|
||||
62
apps/gateway/src/log/log.controller.ts
Normal file
62
apps/gateway/src/log/log.controller.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { Body, Controller, Get, Inject, Param, Post, Query, UseGuards } from '@nestjs/common';
|
||||
import type { LogService } from '@mosaic/log';
|
||||
import { LOG_SERVICE } from './log.tokens.js';
|
||||
import { AuthGuard } from '../auth/auth.guard.js';
|
||||
import type { IngestLogDto, QueryLogsDto } from './log.dto.js';
|
||||
|
||||
@Controller('api/logs')
|
||||
@UseGuards(AuthGuard)
|
||||
export class LogController {
|
||||
constructor(@Inject(LOG_SERVICE) private readonly logService: LogService) {}
|
||||
|
||||
@Post()
|
||||
async ingest(@Query('userId') userId: string, @Body() dto: IngestLogDto) {
|
||||
return this.logService.logs.ingest({
|
||||
sessionId: dto.sessionId,
|
||||
userId,
|
||||
level: dto.level,
|
||||
category: dto.category,
|
||||
content: dto.content,
|
||||
metadata: dto.metadata,
|
||||
});
|
||||
}
|
||||
|
||||
@Post('batch')
|
||||
async ingestBatch(@Query('userId') userId: string, @Body() dtos: IngestLogDto[]) {
|
||||
const entries = dtos.map((dto) => ({
|
||||
sessionId: dto.sessionId,
|
||||
userId,
|
||||
level: dto.level as 'debug' | 'info' | 'warn' | 'error' | undefined,
|
||||
category: dto.category as
|
||||
| 'decision'
|
||||
| 'tool_use'
|
||||
| 'learning'
|
||||
| 'error'
|
||||
| 'general'
|
||||
| undefined,
|
||||
content: dto.content,
|
||||
metadata: dto.metadata,
|
||||
}));
|
||||
return this.logService.logs.ingestBatch(entries);
|
||||
}
|
||||
|
||||
@Get()
|
||||
async query(@Query('userId') userId: string, @Query() params: QueryLogsDto) {
|
||||
return this.logService.logs.query({
|
||||
userId,
|
||||
sessionId: params.sessionId,
|
||||
level: params.level,
|
||||
category: params.category,
|
||||
tier: params.tier,
|
||||
since: params.since ? new Date(params.since) : undefined,
|
||||
until: params.until ? new Date(params.until) : undefined,
|
||||
limit: params.limit ? Number(params.limit) : undefined,
|
||||
offset: params.offset ? Number(params.offset) : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
async findOne(@Param('id') id: string) {
|
||||
return this.logService.logs.findById(id);
|
||||
}
|
||||
}
|
||||
18
apps/gateway/src/log/log.dto.ts
Normal file
18
apps/gateway/src/log/log.dto.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
export interface IngestLogDto {
|
||||
sessionId: string;
|
||||
level?: 'debug' | 'info' | 'warn' | 'error';
|
||||
category?: 'decision' | 'tool_use' | 'learning' | 'error' | 'general';
|
||||
content: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface QueryLogsDto {
|
||||
sessionId?: string;
|
||||
level?: 'debug' | 'info' | 'warn' | 'error';
|
||||
category?: 'decision' | 'tool_use' | 'learning' | 'error' | 'general';
|
||||
tier?: 'hot' | 'warm' | 'cold';
|
||||
since?: string;
|
||||
until?: string;
|
||||
limit?: string;
|
||||
offset?: string;
|
||||
}
|
||||
20
apps/gateway/src/log/log.module.ts
Normal file
20
apps/gateway/src/log/log.module.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
import { createLogService, type LogService } from '@mosaic/log';
|
||||
import type { Db } from '@mosaic/db';
|
||||
import { DB } from '../database/database.module.js';
|
||||
import { LOG_SERVICE } from './log.tokens.js';
|
||||
import { LogController } from './log.controller.js';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [
|
||||
{
|
||||
provide: LOG_SERVICE,
|
||||
useFactory: (db: Db): LogService => createLogService(db),
|
||||
inject: [DB],
|
||||
},
|
||||
],
|
||||
controllers: [LogController],
|
||||
exports: [LOG_SERVICE],
|
||||
})
|
||||
export class LogModule {}
|
||||
1
apps/gateway/src/log/log.tokens.ts
Normal file
1
apps/gateway/src/log/log.tokens.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const LOG_SERVICE = 'LOG_SERVICE';
|
||||
Reference in New Issue
Block a user