feat(#71): implement graph data API
Implemented three new API endpoints for knowledge graph visualization: 1. GET /api/knowledge/graph - Full knowledge graph - Returns all entries and links with optional filtering - Supports filtering by tags, status, and node count limit - Includes orphan detection (entries with no links) 2. GET /api/knowledge/graph/stats - Graph statistics - Total entries and links counts - Orphan entries detection - Average links per entry - Top 10 most connected entries - Tag distribution across entries 3. GET /api/knowledge/graph/:slug - Entry-centered subgraph - Returns graph centered on specific entry - Supports depth parameter (1-5) for traversal distance - Includes all connected nodes up to specified depth New Files: - apps/api/src/knowledge/graph.controller.ts - apps/api/src/knowledge/graph.controller.spec.ts Modified Files: - apps/api/src/knowledge/dto/graph-query.dto.ts (added GraphFilterDto) - apps/api/src/knowledge/entities/graph.entity.ts (extended with new types) - apps/api/src/knowledge/services/graph.service.ts (added new methods) - apps/api/src/knowledge/services/graph.service.spec.ts (added tests) - apps/api/src/knowledge/knowledge.module.ts (registered controller) - apps/api/src/knowledge/dto/index.ts (exported new DTOs) - docs/scratchpads/71-graph-data-api.md (implementation notes) Test Coverage: 21 tests (all passing) - 14 service tests including orphan detection, filtering, statistics - 7 controller tests for all three endpoints Follows TDD principles with tests written before implementation. All code quality gates passed (lint, typecheck, tests). Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
132
apps/orchestrator/src/valkey/valkey.service.ts
Normal file
132
apps/orchestrator/src/valkey/valkey.service.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import { Injectable, OnModuleDestroy } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { ValkeyClient, ValkeyClientConfig } from './valkey.client';
|
||||
import type {
|
||||
TaskState,
|
||||
AgentState,
|
||||
TaskStatus,
|
||||
AgentStatus,
|
||||
OrchestratorEvent,
|
||||
EventHandler,
|
||||
TaskContext,
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
* NestJS service for Valkey state management and pub/sub
|
||||
*/
|
||||
@Injectable()
|
||||
export class ValkeyService implements OnModuleDestroy {
|
||||
private readonly client: ValkeyClient;
|
||||
|
||||
constructor(private readonly configService: ConfigService) {
|
||||
const config: ValkeyClientConfig = {
|
||||
host: this.configService.get<string>('orchestrator.valkey.host', 'localhost'),
|
||||
port: this.configService.get<number>('orchestrator.valkey.port', 6379),
|
||||
};
|
||||
|
||||
const password = this.configService.get<string>('orchestrator.valkey.password');
|
||||
if (password) {
|
||||
config.password = password;
|
||||
}
|
||||
|
||||
this.client = new ValkeyClient(config);
|
||||
}
|
||||
|
||||
async onModuleDestroy(): Promise<void> {
|
||||
await this.client.disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Task State Management
|
||||
*/
|
||||
|
||||
async getTaskState(taskId: string): Promise<TaskState | null> {
|
||||
return this.client.getTaskState(taskId);
|
||||
}
|
||||
|
||||
async setTaskState(state: TaskState): Promise<void> {
|
||||
return this.client.setTaskState(state);
|
||||
}
|
||||
|
||||
async deleteTaskState(taskId: string): Promise<void> {
|
||||
return this.client.deleteTaskState(taskId);
|
||||
}
|
||||
|
||||
async updateTaskStatus(
|
||||
taskId: string,
|
||||
status: TaskStatus,
|
||||
agentId?: string,
|
||||
error?: string
|
||||
): Promise<TaskState> {
|
||||
return this.client.updateTaskStatus(taskId, status, agentId, error);
|
||||
}
|
||||
|
||||
async listTasks(): Promise<TaskState[]> {
|
||||
return this.client.listTasks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Agent State Management
|
||||
*/
|
||||
|
||||
async getAgentState(agentId: string): Promise<AgentState | null> {
|
||||
return this.client.getAgentState(agentId);
|
||||
}
|
||||
|
||||
async setAgentState(state: AgentState): Promise<void> {
|
||||
return this.client.setAgentState(state);
|
||||
}
|
||||
|
||||
async deleteAgentState(agentId: string): Promise<void> {
|
||||
return this.client.deleteAgentState(agentId);
|
||||
}
|
||||
|
||||
async updateAgentStatus(
|
||||
agentId: string,
|
||||
status: AgentStatus,
|
||||
error?: string
|
||||
): Promise<AgentState> {
|
||||
return this.client.updateAgentStatus(agentId, status, error);
|
||||
}
|
||||
|
||||
async listAgents(): Promise<AgentState[]> {
|
||||
return this.client.listAgents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Event Pub/Sub
|
||||
*/
|
||||
|
||||
async publishEvent(event: OrchestratorEvent): Promise<void> {
|
||||
return this.client.publishEvent(event);
|
||||
}
|
||||
|
||||
async subscribeToEvents(handler: EventHandler): Promise<void> {
|
||||
return this.client.subscribeToEvents(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience methods
|
||||
*/
|
||||
|
||||
async createTask(taskId: string, context: TaskContext): Promise<void> {
|
||||
const now = new Date().toISOString();
|
||||
const state: TaskState = {
|
||||
taskId,
|
||||
status: 'pending',
|
||||
context,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
await this.setTaskState(state);
|
||||
}
|
||||
|
||||
async createAgent(agentId: string, taskId: string): Promise<void> {
|
||||
const state: AgentState = {
|
||||
agentId,
|
||||
status: 'spawning',
|
||||
taskId,
|
||||
};
|
||||
await this.setAgentState(state);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user