Spaces:
Sleeping
Sleeping
File size: 7,584 Bytes
8176754 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | """
Concept Graph Generator
Generates canonical concept dependency graphs for given concepts
"""
from typing import Dict, List
import os
import requests
import json
class ConceptGraphGenerator:
def __init__(self):
self.hf_api_key = os.getenv('HUGGINGFACE_API_KEY')
self.llm_endpoint = "https://api-inference.huggingface.co/models/mistralai/Mistral-7B-Instruct-v0.2"
self._ready = True
def is_ready(self) -> bool:
return self._ready
async def generate_graph(self, concept: str) -> Dict:
"""
Generate canonical concept dependency graph
Returns:
{
'nodes': [{'id': str, 'label': str, 'level': int}],
'edges': [{'source': str, 'target': str, 'relationship': str}]
}
"""
# Use LLM to generate concept structure
graph_structure = await self._llm_generate_structure(concept)
# Validate and format
return self._format_graph(graph_structure, concept)
async def _llm_generate_structure(self, concept: str) -> Dict:
"""Use LLM to generate concept prerequisite structure"""
prompt = f"""<s>[INST] You are a concept structure expert. For the concept "{concept}", identify the core prerequisite concepts that must be understood first, and their relationships.
Output a JSON structure with:
1. "prerequisites": list of prerequisite concepts needed to understand {concept}
2. "core_components": main parts/aspects of {concept} itself
3. "relationships": how concepts connect (prerequisite, causal, etc.)
Be precise and pedagogical. Focus on understanding order.
Output only valid JSON, no other text. [/INST]"""
try:
headers = {"Authorization": f"Bearer {self.hf_api_key}"}
payload = {
"inputs": prompt,
"parameters": {
"max_new_tokens": 800,
"temperature": 0.4,
"return_full_text": False
}
}
response = requests.post(self.llm_endpoint, headers=headers, json=payload, timeout=30)
if response.status_code == 200:
result = response.json()
text = result[0]['generated_text'] if isinstance(result, list) else result.get('generated_text', '')
# Try to parse JSON from response
try:
# Extract JSON if wrapped in other text
start = text.find('{')
end = text.rfind('}') + 1
if start != -1 and end > start:
json_str = text[start:end]
return json.loads(json_str)
except:
pass
return self._fallback_graph(concept)
else:
return self._fallback_graph(concept)
except Exception as e:
print(f"Graph generation error: {e}")
return self._fallback_graph(concept)
def _fallback_graph(self, concept: str) -> Dict:
"""Fallback: create a basic graph structure"""
# Predefined templates for common concepts
templates = {
'entropy': {
'prerequisites': ['energy', 'system states', 'probability'],
'core_components': ['disorder measure', 'thermodynamic entropy', 'information entropy'],
'relationships': [
('energy', 'entropy', 'prerequisite'),
('system states', 'entropy', 'prerequisite'),
('probability', 'entropy', 'prerequisite')
]
},
'neural networks': {
'prerequisites': ['linear algebra', 'calculus', 'probability'],
'core_components': ['neurons', 'layers', 'weights', 'activation functions', 'backpropagation'],
'relationships': [
('linear algebra', 'neural networks', 'prerequisite'),
('neurons', 'layers', 'component'),
('weights', 'neurons', 'component'),
('backpropagation', 'weights', 'causal')
]
},
'photosynthesis': {
'prerequisites': ['energy', 'chemical reactions', 'cells'],
'core_components': ['light reactions', 'dark reactions', 'chlorophyll', 'glucose production'],
'relationships': [
('energy', 'light reactions', 'prerequisite'),
('light reactions', 'dark reactions', 'causal'),
('dark reactions', 'glucose production', 'causal')
]
}
}
# Check if concept matches template
concept_lower = concept.lower()
for key, template in templates.items():
if key in concept_lower:
return template
# Generic fallback
return {
'prerequisites': ['foundational knowledge'],
'core_components': [concept, f'{concept} principles', f'{concept} applications'],
'relationships': [
('foundational knowledge', concept, 'prerequisite')
]
}
def _format_graph(self, structure: Dict, concept: str) -> Dict:
"""Format graph structure for frontend"""
nodes = []
edges = []
node_id = 0
node_map = {}
# Add prerequisite nodes
for prereq in structure.get('prerequisites', []):
node_map[prereq] = f'node_{node_id}'
nodes.append({
'id': f'node_{node_id}',
'label': prereq,
'level': 0,
'type': 'prerequisite'
})
node_id += 1
# Add main concept node
node_map[concept] = f'node_{node_id}'
nodes.append({
'id': f'node_{node_id}',
'label': concept,
'level': 1,
'type': 'main'
})
concept_node_id = f'node_{node_id}'
node_id += 1
# Add core component nodes
for component in structure.get('core_components', []):
node_map[component] = f'node_{node_id}'
nodes.append({
'id': f'node_{node_id}',
'label': component,
'level': 2,
'type': 'component'
})
node_id += 1
# Add edges from relationships
for rel in structure.get('relationships', []):
if len(rel) >= 3:
source_key, target_key, rel_type = rel[0], rel[1], rel[2]
source_id = node_map.get(source_key, node_map.get(concept))
target_id = node_map.get(target_key, concept_node_id)
edges.append({
'source': source_id,
'target': target_id,
'relationship': rel_type
})
# Add default prerequisite edges if none exist
if not edges:
for prereq in structure.get('prerequisites', []):
edges.append({
'source': node_map[prereq],
'target': concept_node_id,
'relationship': 'prerequisite'
})
return {
'nodes': nodes,
'edges': edges,
'concept': concept
}
|