Skip to content

Commit 9d75032

Browse files
committed
feat(web): add knowledge graph API client functions (SSC-11)
Add frontend API functions for knowledge graph: - Concept and relationship types - CRUD operations: getConcepts, createConcept, etc. - Relationship CRUD: createRelationship, etc. - AI extraction: extractConcepts - Search: semanticSearch, findSimilarConcepts - Visualization: getKnowledgeGraph, getConceptStrengths - Statistics: getKnowledgeGraphStats
1 parent 57e99c7 commit 9d75032

1 file changed

Lines changed: 345 additions & 0 deletions

File tree

apps/web/src/lib/api.ts

Lines changed: 345 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,3 +498,348 @@ export async function getUploads(params: { page?: number; limit?: number; status
498498
const query = searchParams.toString();
499499
return fetchApi(`/uploads${query ? `?${query}` : ''}`);
500500
}
501+
502+
// ============================================
503+
// KNOWLEDGE GRAPH API FUNCTIONS
504+
// ============================================
505+
506+
// Entity types for concepts
507+
export type ConceptEntityType =
508+
| 'PERSON'
509+
| 'THEORY'
510+
| 'FORMULA'
511+
| 'EVENT'
512+
| 'TERM'
513+
| 'PROCESS'
514+
| 'PRINCIPLE'
515+
| 'CONCEPT'
516+
| 'EXAMPLE'
517+
| 'DATE';
518+
519+
// Relationship types between concepts
520+
export type RelationshipType =
521+
| 'PREREQUISITE'
522+
| 'RELATED'
523+
| 'OPPOSITE'
524+
| 'EXAMPLE_OF'
525+
| 'PART_OF'
526+
| 'CAUSES'
527+
| 'DERIVED_FROM'
528+
| 'SIMILAR_TO'
529+
| 'APPLIED_IN'
530+
| 'SUPPORTS';
531+
532+
export interface Concept {
533+
id: string;
534+
userId: string;
535+
uploadId?: string;
536+
name: string;
537+
description?: string;
538+
entityType: ConceptEntityType;
539+
importance: number;
540+
lectureOrder?: number;
541+
metadata?: Record<string, unknown>;
542+
createdAt: string;
543+
updatedAt: string;
544+
upload?: {
545+
id: string;
546+
originalName: string;
547+
};
548+
_count?: {
549+
outgoingRelations: number;
550+
incomingRelations: number;
551+
};
552+
}
553+
554+
export interface ConceptRelationship {
555+
id: string;
556+
fromConceptId: string;
557+
toConceptId: string;
558+
relationshipType: RelationshipType;
559+
strength: number;
560+
description?: string;
561+
bidirectional: boolean;
562+
fromConcept?: { id: string; name: string; entityType: ConceptEntityType };
563+
toConcept?: { id: string; name: string; entityType: ConceptEntityType };
564+
}
565+
566+
// Concept CRUD
567+
568+
export interface GetConceptsParams {
569+
page?: number;
570+
limit?: number;
571+
search?: string;
572+
entityType?: ConceptEntityType;
573+
uploadId?: string;
574+
}
575+
576+
export interface GetConceptsResponse {
577+
concepts: Concept[];
578+
pagination: PaginationInfo;
579+
}
580+
581+
export async function getConcepts(params: GetConceptsParams = {}): Promise<GetConceptsResponse> {
582+
const searchParams = new URLSearchParams();
583+
if (params.page) searchParams.set('page', params.page.toString());
584+
if (params.limit) searchParams.set('limit', params.limit.toString());
585+
if (params.search) searchParams.set('search', params.search);
586+
if (params.entityType) searchParams.set('entityType', params.entityType);
587+
if (params.uploadId) searchParams.set('uploadId', params.uploadId);
588+
589+
const query = searchParams.toString();
590+
return fetchApi<GetConceptsResponse>(`/knowledge-graph/concepts${query ? `?${query}` : ''}`);
591+
}
592+
593+
export interface CreateConceptParams {
594+
name: string;
595+
description?: string;
596+
entityType: ConceptEntityType;
597+
uploadId?: string;
598+
importance?: number;
599+
lectureOrder?: number;
600+
metadata?: Record<string, unknown>;
601+
}
602+
603+
export async function createConcept(params: CreateConceptParams): Promise<{ message: string; concept: Concept }> {
604+
return fetchApi('/knowledge-graph/concepts', {
605+
method: 'POST',
606+
body: JSON.stringify(params),
607+
});
608+
}
609+
610+
export interface GetConceptResponse {
611+
concept: Concept & {
612+
outgoingRelations: ConceptRelationship[];
613+
incomingRelations: ConceptRelationship[];
614+
};
615+
}
616+
617+
export async function getConcept(id: string): Promise<GetConceptResponse> {
618+
return fetchApi<GetConceptResponse>(`/knowledge-graph/concepts/${id}`);
619+
}
620+
621+
export async function updateConcept(
622+
id: string,
623+
params: Partial<Omit<CreateConceptParams, 'uploadId'>>
624+
): Promise<{ message: string; concept: Concept }> {
625+
return fetchApi(`/knowledge-graph/concepts/${id}`, {
626+
method: 'PUT',
627+
body: JSON.stringify(params),
628+
});
629+
}
630+
631+
export async function deleteConcept(id: string): Promise<{ message: string }> {
632+
return fetchApi(`/knowledge-graph/concepts/${id}`, {
633+
method: 'DELETE',
634+
});
635+
}
636+
637+
// Relationship CRUD
638+
639+
export interface CreateRelationshipParams {
640+
fromConceptId: string;
641+
toConceptId: string;
642+
relationshipType: RelationshipType;
643+
strength?: number;
644+
description?: string;
645+
bidirectional?: boolean;
646+
}
647+
648+
export async function createRelationship(
649+
params: CreateRelationshipParams
650+
): Promise<{ message: string; relationship: ConceptRelationship }> {
651+
return fetchApi('/knowledge-graph/relationships', {
652+
method: 'POST',
653+
body: JSON.stringify(params),
654+
});
655+
}
656+
657+
export async function updateRelationship(
658+
id: string,
659+
params: Partial<Omit<CreateRelationshipParams, 'fromConceptId' | 'toConceptId'>>
660+
): Promise<{ message: string; relationship: ConceptRelationship }> {
661+
return fetchApi(`/knowledge-graph/relationships/${id}`, {
662+
method: 'PUT',
663+
body: JSON.stringify(params),
664+
});
665+
}
666+
667+
export async function deleteRelationship(id: string): Promise<{ message: string }> {
668+
return fetchApi(`/knowledge-graph/relationships/${id}`, {
669+
method: 'DELETE',
670+
});
671+
}
672+
673+
// AI Extraction
674+
675+
export interface ExtractConceptsParams {
676+
uploadId: string;
677+
options?: {
678+
maxConcepts?: number;
679+
minImportance?: number;
680+
focusEntityTypes?: ConceptEntityType[];
681+
extractRelationships?: boolean;
682+
includeContext?: boolean;
683+
};
684+
}
685+
686+
export interface ExtractionResult {
687+
message: string;
688+
extraction: {
689+
conceptsCreated: number;
690+
relationshipsCreated: number;
691+
topicSummary: string;
692+
processingTimeMs: number;
693+
entityTypeDistribution: Record<ConceptEntityType, number>;
694+
};
695+
}
696+
697+
export async function extractConcepts(params: ExtractConceptsParams): Promise<ExtractionResult> {
698+
return fetchApi('/knowledge-graph/extract', {
699+
method: 'POST',
700+
body: JSON.stringify(params),
701+
});
702+
}
703+
704+
// Semantic Search
705+
706+
export interface SemanticSearchParams {
707+
query: string;
708+
options?: {
709+
limit?: number;
710+
minSimilarity?: number;
711+
entityTypes?: ConceptEntityType[];
712+
uploadId?: string;
713+
};
714+
}
715+
716+
export interface SemanticSearchResult {
717+
conceptId: string;
718+
name: string;
719+
description: string | null;
720+
entityType: ConceptEntityType;
721+
similarity: number;
722+
uploadName?: string;
723+
}
724+
725+
export interface SemanticSearchResponse {
726+
query: string;
727+
results: SemanticSearchResult[];
728+
total: number;
729+
}
730+
731+
export async function semanticSearch(params: SemanticSearchParams): Promise<SemanticSearchResponse> {
732+
return fetchApi('/knowledge-graph/search', {
733+
method: 'POST',
734+
body: JSON.stringify(params),
735+
});
736+
}
737+
738+
export interface SimilarConceptsResponse {
739+
concept: string;
740+
similar: SemanticSearchResult[];
741+
total: number;
742+
}
743+
744+
export async function findSimilarConcepts(id: string, limit?: number): Promise<SimilarConceptsResponse> {
745+
const query = limit ? `?limit=${limit}` : '';
746+
return fetchApi(`/knowledge-graph/concepts/${id}/similar${query}`);
747+
}
748+
749+
// Knowledge Graph Visualization
750+
751+
export interface GraphNode {
752+
id: string;
753+
name: string;
754+
entityType: ConceptEntityType;
755+
importance: number;
756+
description?: string;
757+
uploadId?: string;
758+
uploadName?: string;
759+
}
760+
761+
export interface GraphEdge {
762+
id: string;
763+
source: string;
764+
target: string;
765+
relationshipType: RelationshipType;
766+
strength: number;
767+
bidirectional: boolean;
768+
}
769+
770+
export interface KnowledgeGraphData {
771+
nodes: GraphNode[];
772+
edges: GraphEdge[];
773+
metadata: {
774+
totalNodes: number;
775+
totalEdges: number;
776+
entityTypeDistribution: Record<string, number>;
777+
relationshipTypeDistribution: Record<string, number>;
778+
};
779+
}
780+
781+
export interface GetGraphParams {
782+
uploadId?: string;
783+
entityTypes?: ConceptEntityType[];
784+
minImportance?: number;
785+
}
786+
787+
export async function getKnowledgeGraph(params: GetGraphParams = {}): Promise<KnowledgeGraphData> {
788+
const searchParams = new URLSearchParams();
789+
if (params.uploadId) searchParams.set('uploadId', params.uploadId);
790+
if (params.entityTypes) searchParams.set('entityTypes', params.entityTypes.join(','));
791+
if (params.minImportance) searchParams.set('minImportance', params.minImportance.toString());
792+
793+
const query = searchParams.toString();
794+
return fetchApi<KnowledgeGraphData>(`/knowledge-graph/graph${query ? `?${query}` : ''}`);
795+
}
796+
797+
// Concept Strength Scores
798+
799+
export interface ConceptStrength {
800+
conceptId: string;
801+
name: string;
802+
overallStrength: number;
803+
metrics: {
804+
connectionCount: number;
805+
averageRelationshipStrength: number;
806+
importance: number;
807+
isPrerequisiteFor: number;
808+
hasPrerequisites: number;
809+
};
810+
}
811+
812+
export interface GetConceptStrengthsParams {
813+
uploadId?: string;
814+
limit?: number;
815+
}
816+
817+
export interface ConceptStrengthsResponse {
818+
strengths: ConceptStrength[];
819+
total: number;
820+
}
821+
822+
export async function getConceptStrengths(params: GetConceptStrengthsParams = {}): Promise<ConceptStrengthsResponse> {
823+
const searchParams = new URLSearchParams();
824+
if (params.uploadId) searchParams.set('uploadId', params.uploadId);
825+
if (params.limit) searchParams.set('limit', params.limit.toString());
826+
827+
const query = searchParams.toString();
828+
return fetchApi<ConceptStrengthsResponse>(`/knowledge-graph/strengths${query ? `?${query}` : ''}`);
829+
}
830+
831+
// Knowledge Graph Statistics
832+
833+
export interface KnowledgeGraphStats {
834+
stats: {
835+
totalConcepts: number;
836+
totalRelationships: number;
837+
averageImportance: number;
838+
uploadsWithConcepts: number;
839+
};
840+
entityTypeDistribution: Record<ConceptEntityType, number>;
841+
}
842+
843+
export async function getKnowledgeGraphStats(): Promise<KnowledgeGraphStats> {
844+
return fetchApi('/knowledge-graph/stats');
845+
}

0 commit comments

Comments
 (0)