feat(J): Phase J Self-Improvement Plugin — controlled improvement loop
Check Cross-Plugin Imports / check (push) Has been cancelled
Check Cross-Plugin Imports / check (push) Has been cancelled
- New self_improvement plugin: models, services, routes, plugin - 4 SQLAlchemy models: ImprovementSignal, ImprovementPattern, ImprovementProposal, ImpactMeasurement - Migration 0132: 4 tables with RLS - Services: collect_signals, detect_patterns, create_proposal, evaluate_proposal, request_approval, activate_proposal, rollback_proposal, measure_impact - 11 API routes under /api/v1/improvement/ - Frontend: improvement.ts API client, ImprovementPanel.tsx in AISidebar proactive tab - 24/24 integration tests pass - Fix: __init__.py imports Plugin class for discover_builtins() (self_improvement + knowledge) - Fix: automation plugin register_plugin_contributions skips when no tenant exists
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* React Query hooks for Self-Improvement API.
|
||||
* Follows the pattern from automation.ts.
|
||||
*/
|
||||
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { apiGet, apiPost } from './client';
|
||||
|
||||
// ── Types ──
|
||||
|
||||
export interface ImprovementSignal {
|
||||
id: string;
|
||||
source_type: string;
|
||||
signal_kind: string;
|
||||
severity: string;
|
||||
summary: string;
|
||||
confidence: number;
|
||||
pattern_id: string | null;
|
||||
created_at: string | null;
|
||||
}
|
||||
|
||||
export interface ImprovementPattern {
|
||||
id: string;
|
||||
pattern_kind: string;
|
||||
title: string;
|
||||
description: string;
|
||||
target_type: string;
|
||||
target_name: string;
|
||||
occurrence_count: number;
|
||||
confidence: number;
|
||||
status: string;
|
||||
proposed_action: string;
|
||||
created_at: string | null;
|
||||
}
|
||||
|
||||
export interface ImprovementProposal {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
target_type: string;
|
||||
target_name: string | null;
|
||||
status: string;
|
||||
version_number: number;
|
||||
rationale: string;
|
||||
expected_benefit: string;
|
||||
risk_assessment: string;
|
||||
evaluation_score: number;
|
||||
pattern_id: string | null;
|
||||
created_at: string | null;
|
||||
activated_at: string | null;
|
||||
}
|
||||
|
||||
export interface ProposalDetail extends ImprovementProposal {
|
||||
target_ref_id: string | null;
|
||||
proposed_config: Record<string, any>;
|
||||
previous_config: Record<string, any>;
|
||||
evidence_refs: any[];
|
||||
evaluation_result: Record<string, any>;
|
||||
evaluated_at: string | null;
|
||||
approval_request_id: string | null;
|
||||
approved_by: string | null;
|
||||
approved_at: string | null;
|
||||
rolled_back_at: string | null;
|
||||
rollback_reason: string | null;
|
||||
}
|
||||
|
||||
interface PaginatedResponse<T> {
|
||||
items: T[];
|
||||
total: number;
|
||||
page: number;
|
||||
page_size: number;
|
||||
}
|
||||
|
||||
// ── Signal Hooks ──
|
||||
|
||||
export function useSignals(page = 1, pageSize = 20, sourceType?: string) {
|
||||
const params = new URLSearchParams({ page: String(page), page_size: String(pageSize) });
|
||||
if (sourceType) params.set('source_type', sourceType);
|
||||
return useQuery({
|
||||
queryKey: ['improvement-signals', page, pageSize, sourceType],
|
||||
queryFn: () => apiGet<PaginatedResponse<ImprovementSignal>>(`/improvement/signals?${params}`),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCollectSignals() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (body: { since?: string; limit?: number }) => apiPost('/improvement/signals/collect', body),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['improvement-signals'] });
|
||||
qc.invalidateQueries({ queryKey: ['improvement-patterns'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// ── Pattern Hooks ──
|
||||
|
||||
export function usePatterns(page = 1, pageSize = 20) {
|
||||
return useQuery({
|
||||
queryKey: ['improvement-patterns', page, pageSize],
|
||||
queryFn: () => apiGet<PaginatedResponse<ImprovementPattern>>(`/improvement/patterns?page=${page}&page_size=${pageSize}`),
|
||||
});
|
||||
}
|
||||
|
||||
export function useDetectPatterns() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (body: { min_occurrences?: number }) => apiPost('/improvement/patterns/detect', body),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['improvement-patterns'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// ── Proposal Hooks ──
|
||||
|
||||
export function useProposals(page = 1, pageSize = 20, status?: string) {
|
||||
const params = new URLSearchParams({ page: String(page), page_size: String(pageSize) });
|
||||
if (status) params.set('status', status);
|
||||
return useQuery({
|
||||
queryKey: ['improvement-proposals', page, pageSize, status],
|
||||
queryFn: () => apiGet<PaginatedResponse<ImprovementProposal>>(`/improvement/proposals?${params}`),
|
||||
});
|
||||
}
|
||||
|
||||
export function useProposal(id: string) {
|
||||
return useQuery({
|
||||
queryKey: ['improvement-proposal', id],
|
||||
queryFn: () => apiGet<ProposalDetail>(`/improvement/proposals/${id}`),
|
||||
enabled: !!id,
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateProposal() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (body: Record<string, any>) => apiPost('/improvement/proposals', body),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['improvement-proposals'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useEvaluateProposal() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (proposalId: string) => apiPost(`/improvement/proposals/${proposalId}/evaluate`, {}),
|
||||
onSuccess: (_data, proposalId) => {
|
||||
qc.invalidateQueries({ queryKey: ['improvement-proposals'] });
|
||||
qc.invalidateQueries({ queryKey: ['improvement-proposal', proposalId] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useRequestApproval() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ proposalId, body }: { proposalId: string; body: { approver_id?: string } }) =>
|
||||
apiPost(`/improvement/proposals/${proposalId}/request-approval`, body),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['improvement-proposals'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useActivateProposal() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (proposalId: string) => apiPost(`/improvement/proposals/${proposalId}/activate`, {}),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['improvement-proposals'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useRollbackProposal() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ proposalId, reason }: { proposalId: string; reason?: string }) =>
|
||||
apiPost(`/improvement/proposals/${proposalId}/rollback`, { reason: reason || '' }),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['improvement-proposals'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useMeasureImpact() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (proposalId: string) => apiPost(`/improvement/proposals/${proposalId}/measure`, {}),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['improvement-proposals'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user