legalmind-ai 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of legalmind-ai might be problematic. Click here for more details.
- legalmind/__init__.py +1 -0
- legalmind/ai/__init__.py +7 -0
- legalmind/ai/legal_ai.py +232 -0
- legalmind/analyzers/__init__.py +0 -0
- legalmind/api/__init__.py +0 -0
- legalmind/api/server.py +288 -0
- legalmind/config.py +41 -0
- legalmind/core.py +92 -0
- legalmind/core_enhanced.py +206 -0
- legalmind/enhanced_search.py +148 -0
- legalmind/prompt_templates.py +284 -0
- legalmind/providers/__init__.py +0 -0
- legalmind/providers/fallback/__init__.py +11 -0
- legalmind/providers/fallback/config.py +66 -0
- legalmind/providers/fallback/data_loader.py +308 -0
- legalmind/providers/fallback/enhanced_system.py +151 -0
- legalmind/providers/fallback/system.py +456 -0
- legalmind/providers/fallback/versalaw2_core/__init__.py +11 -0
- legalmind/providers/fallback/versalaw2_core/config.py +66 -0
- legalmind/providers/fallback/versalaw2_core/data_loader.py +308 -0
- legalmind/providers/fallback/versalaw2_core/enhanced_system.py +151 -0
- legalmind/providers/fallback/versalaw2_core/system.py +456 -0
- legalmind/providers/qodo.py +139 -0
- legalmind/providers/qodo_ai.py +85 -0
- legalmind/study_cases/CROSS_PROJECT_INTEGRATION_ANALYSIS.md +411 -0
- legalmind/study_cases/DAFTAR_KASUS_PRIORITAS_ANALISIS.md +779 -0
- legalmind/study_cases/JAWABAN_ANALISIS_3_KASUS_MENANTANG.md +393 -0
- legalmind/study_cases/JAWABAN_TERBAIK_KONTRAK_REAL.md +854 -0
- legalmind/study_cases/LEGAL_PROJECTS_ANALYSIS_REPORT.md +442 -0
- legalmind/study_cases/PORTFOLIO_11_KASUS_LENGKAP.md +458 -0
- legalmind/study_cases/RINGKASAN_3_KASUS_TECH_INTERNASIONAL.md +565 -0
- legalmind/study_cases/RINGKASAN_HASIL_PENGUJIAN.md +112 -0
- legalmind/study_cases/RINGKASAN_IDE_MONETISASI.md +464 -0
- legalmind/study_cases/RINGKASAN_LENGKAP.md +419 -0
- legalmind/study_cases/RINGKASAN_VISUAL_HASIL_ANALISIS.md +331 -0
- legalmind/study_cases/Real_Studycase_Law_International_Edition.md +434 -0
- legalmind/study_cases/analyze_5_additional_cases.py +905 -0
- legalmind/study_cases/analyze_5_additional_cases_part2.py +461 -0
- legalmind/study_cases/analyze_challenging_cases.py +963 -0
- legalmind/study_cases/analyze_international_tech_cases.py +1706 -0
- legalmind/study_cases/analyze_real_problematic_contracts.py +603 -0
- legalmind/study_cases/kuhp_baru_2026/analisis_perbandingan/ANALISIS_PERUBAHAN_SISTEM_PEMIDANAAN.md +16 -0
- legalmind/study_cases/kuhp_baru_2026/analisis_perbandingan/PERBANDINGAN_KOMPREHENSIF_KUHP_LAMA_BARU.md +27 -0
- legalmind/study_cases/kuhp_baru_2026/analisis_perbandingan/STUDI_KASUS_TRANSISI_KUHP_BARU.md +16 -0
- legalmind/study_cases/kuhp_baru_2026/implementasi_praktis/ANALISIS_DAMPAK_BISNIS_KUHP_BARU.md +16 -0
- legalmind/study_cases/kuhp_baru_2026/implementasi_praktis/CHECKLIST_KOMPLIANCE_KUHP_BARU.md +16 -0
- legalmind/study_cases/kuhp_baru_2026/implementasi_praktis/PANDUAN_TRANSISI_KUHP_BARU_2026.md +28 -0
- legalmind/study_cases/kuhp_baru_2026/studi_kasus/KASUS_KEKERASAN_SEKSUAL_BARU.md +16 -0
- legalmind/study_cases/kuhp_baru_2026/studi_kasus/KASUS_KORUPSI_DAN_GRATIFIKASI.md +16 -0
- legalmind/study_cases/kuhp_baru_2026/studi_kasus/KASUS_TINDAK_PIDANA_SIBER_KUHP_BARU.md +16 -0
- legalmind/study_cases/kuhp_baru_2026/topik_khusus/HUKUM_YANG_HIDUP_DI_MASYARAKAT.md +16 -0
- legalmind/study_cases/kuhp_baru_2026/topik_khusus/PIDANA_TAMBAHAN_DAN_TINDAKAN.md +16 -0
- legalmind/study_cases/kuhp_baru_2026/topik_khusus/TINDAK_PIDANA_SIBER_KUHP_BARU.md +16 -0
- legalmind_ai-1.1.0.dist-info/METADATA +93 -0
- legalmind_ai-1.1.0.dist-info/RECORD +58 -0
- legalmind_ai-1.1.0.dist-info/WHEEL +5 -0
- legalmind_ai-1.1.0.dist-info/entry_points.txt +4 -0
- legalmind_ai-1.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Configuration Management for VersaLaw2
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Optional
|
|
9
|
+
import json
|
|
10
|
+
|
|
11
|
+
class Config:
|
|
12
|
+
"""Configuration manager"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, config_file: Optional[str] = None):
|
|
15
|
+
self.config_file = config_file
|
|
16
|
+
self.config = self.load_config()
|
|
17
|
+
|
|
18
|
+
def load_config(self) -> dict:
|
|
19
|
+
"""Load configuration"""
|
|
20
|
+
default_config = {
|
|
21
|
+
'mayalaw_path': '/root/dragon/global/mayalaw',
|
|
22
|
+
'ai_provider': 'mock',
|
|
23
|
+
'ai_api_key': os.getenv('AI_API_KEY', ''),
|
|
24
|
+
'openai_api_key': os.getenv('OPENAI_API_KEY', ''),
|
|
25
|
+
'deepseek_api_key': os.getenv('DEEPSEEK_API_KEY', ''),
|
|
26
|
+
'qodo_api_key': os.getenv('QODO_API_KEY', ''), # NEW: Qodo.ai support
|
|
27
|
+
'qodo_base_url': os.getenv('QODO_BASE_URL', 'https://api.qodo.ai/v1'), # NEW
|
|
28
|
+
'cache_enabled': True,
|
|
29
|
+
'cache_dir': '/root/dragon/global/lab/.cache',
|
|
30
|
+
'log_level': 'INFO',
|
|
31
|
+
'log_file': '/root/dragon/global/lab/versalaw2.log',
|
|
32
|
+
'max_search_results': 3,
|
|
33
|
+
'ai_temperature': 0.3,
|
|
34
|
+
'ai_max_tokens': 2000,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if self.config_file and Path(self.config_file).exists():
|
|
38
|
+
try:
|
|
39
|
+
with open(self.config_file, 'r') as f:
|
|
40
|
+
user_config = json.load(f)
|
|
41
|
+
default_config.update(user_config)
|
|
42
|
+
except Exception as e:
|
|
43
|
+
print(f"Warning: Could not load config file: {e}")
|
|
44
|
+
|
|
45
|
+
return default_config
|
|
46
|
+
|
|
47
|
+
def get(self, key: str, default=None):
|
|
48
|
+
"""Get configuration value"""
|
|
49
|
+
return self.config.get(key, default)
|
|
50
|
+
|
|
51
|
+
def set(self, key: str, value):
|
|
52
|
+
"""Set configuration value"""
|
|
53
|
+
self.config[key] = value
|
|
54
|
+
|
|
55
|
+
def save(self, filepath: Optional[str] = None):
|
|
56
|
+
"""Save configuration to file"""
|
|
57
|
+
save_path = filepath or self.config_file
|
|
58
|
+
if save_path:
|
|
59
|
+
with open(save_path, 'w') as f:
|
|
60
|
+
json.dump(self.config, f, indent=2)
|
|
61
|
+
|
|
62
|
+
def __getitem__(self, key):
|
|
63
|
+
return self.config[key]
|
|
64
|
+
|
|
65
|
+
def __setitem__(self, key, value):
|
|
66
|
+
self.config[key] = value
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Enhanced MayaLaw Data Loader
|
|
4
|
+
Supports multiple markdown formats
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import List, Dict, Optional
|
|
10
|
+
import logging
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
class MayaLawDataLoader:
|
|
15
|
+
"""Enhanced data loader with multiple parsers"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, mayalaw_path="/root/dragon/global/mayalaw"):
|
|
18
|
+
self.mayalaw_path = Path(mayalaw_path)
|
|
19
|
+
self.cases = []
|
|
20
|
+
self.stats = {
|
|
21
|
+
'files_loaded': 0,
|
|
22
|
+
'cases_loaded': 0,
|
|
23
|
+
'errors': []
|
|
24
|
+
}
|
|
25
|
+
self.load_all_cases()
|
|
26
|
+
|
|
27
|
+
def load_all_cases(self):
|
|
28
|
+
"""Load all study cases from MayaLaw"""
|
|
29
|
+
files_to_load = [
|
|
30
|
+
("JAWABAN_LENGKAP_20_PERTANYAAN_HUKUM.md", "pertanyaan_format"),
|
|
31
|
+
("LAW_LIBRARY_BATCH_IV_ANSWERS.md", "qa_format"),
|
|
32
|
+
("LAW_LIBRARY_BATCH_V_FINAL.md", "qa_format"),
|
|
33
|
+
("LAW_LIBRARY_BATCH_IV_PART2.md", "qa_format"),
|
|
34
|
+
("ADVANCED_LEGAL_QUESTIONS_20_PART2.md", "pertanyaan_simple"),
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
for filename, parser_type in files_to_load:
|
|
38
|
+
filepath = self.mayalaw_path / filename
|
|
39
|
+
if filepath.exists():
|
|
40
|
+
try:
|
|
41
|
+
cases = self.parse_file(filepath, parser_type)
|
|
42
|
+
self.cases.extend(cases)
|
|
43
|
+
self.stats['files_loaded'] += 1
|
|
44
|
+
self.stats['cases_loaded'] += len(cases)
|
|
45
|
+
logger.info(f"✅ Loaded {len(cases)} cases from {filename}")
|
|
46
|
+
print(f"✅ Loaded {len(cases)} cases from {filename}")
|
|
47
|
+
except Exception as e:
|
|
48
|
+
error_msg = f"Error loading {filename}: {e}"
|
|
49
|
+
self.stats['errors'].append(error_msg)
|
|
50
|
+
logger.error(error_msg)
|
|
51
|
+
print(f"⚠️ {error_msg}")
|
|
52
|
+
else:
|
|
53
|
+
logger.warning(f"File not found: {filename}")
|
|
54
|
+
|
|
55
|
+
print(f"\n📚 Total: {len(self.cases)} study cases loaded from {self.stats['files_loaded']} files\n")
|
|
56
|
+
logger.info(f"Total cases loaded: {len(self.cases)}")
|
|
57
|
+
|
|
58
|
+
def parse_file(self, filepath: Path, parser_type: str) -> List[Dict]:
|
|
59
|
+
"""Parse file based on type"""
|
|
60
|
+
with open(filepath, 'r', encoding='utf-8') as f:
|
|
61
|
+
content = f.read()
|
|
62
|
+
|
|
63
|
+
if parser_type == "pertanyaan_format":
|
|
64
|
+
return self.parse_pertanyaan_format(content, filepath.name)
|
|
65
|
+
elif parser_type == "qa_format":
|
|
66
|
+
return self.parse_qa_format(content, filepath.name)
|
|
67
|
+
elif parser_type == "pertanyaan_simple":
|
|
68
|
+
return self.parse_pertanyaan_simple(content, filepath.name)
|
|
69
|
+
else:
|
|
70
|
+
logger.warning(f"Unknown parser type: {parser_type}")
|
|
71
|
+
return []
|
|
72
|
+
|
|
73
|
+
def parse_pertanyaan_format(self, content: str, filename: str) -> List[Dict]:
|
|
74
|
+
"""Parse '## 📋 PERTANYAAN #N' format"""
|
|
75
|
+
cases = []
|
|
76
|
+
pattern = r'##\s+📋\s+PERTANYAAN\s+#(\d+)'
|
|
77
|
+
parts = re.split(pattern, content)
|
|
78
|
+
|
|
79
|
+
for i in range(1, len(parts), 2):
|
|
80
|
+
if i+1 < len(parts):
|
|
81
|
+
case_num = parts[i]
|
|
82
|
+
case_content = parts[i+1]
|
|
83
|
+
case = self.parse_case_content(case_num, case_content, filename)
|
|
84
|
+
if case:
|
|
85
|
+
cases.append(case)
|
|
86
|
+
|
|
87
|
+
return cases
|
|
88
|
+
|
|
89
|
+
def parse_qa_format(self, content: str, filename: str) -> List[Dict]:
|
|
90
|
+
"""Parse Q&A format like '**Q1:**'"""
|
|
91
|
+
cases = []
|
|
92
|
+
|
|
93
|
+
# Try multiple Q&A patterns
|
|
94
|
+
patterns = [
|
|
95
|
+
r'\*\*Q(\d+):\s*(.*?)\*\*',
|
|
96
|
+
r'##\s+\*\*Q(\d+):',
|
|
97
|
+
r'Q(\d+):\s+(.+?)(?=Q\d+:|$)',
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
for pattern in patterns:
|
|
101
|
+
matches = re.finditer(pattern, content, re.DOTALL)
|
|
102
|
+
for match in matches:
|
|
103
|
+
case_num = match.group(1)
|
|
104
|
+
# Get content after this Q until next Q or end
|
|
105
|
+
start_pos = match.end()
|
|
106
|
+
next_q = re.search(r'\*\*Q\d+:', content[start_pos:])
|
|
107
|
+
end_pos = start_pos + next_q.start() if next_q else len(content)
|
|
108
|
+
case_content = content[start_pos:end_pos]
|
|
109
|
+
|
|
110
|
+
case = self.parse_qa_content(case_num, case_content, filename)
|
|
111
|
+
if case:
|
|
112
|
+
cases.append(case)
|
|
113
|
+
|
|
114
|
+
if cases: # If we found cases with this pattern, stop trying others
|
|
115
|
+
break
|
|
116
|
+
|
|
117
|
+
return cases
|
|
118
|
+
|
|
119
|
+
def parse_pertanyaan_simple(self, content: str, filename: str) -> List[Dict]:
|
|
120
|
+
"""Parse simple '## PERTANYAAN N' format"""
|
|
121
|
+
cases = []
|
|
122
|
+
pattern = r'##\s+PERTANYAAN\s+(\d+)'
|
|
123
|
+
parts = re.split(pattern, content)
|
|
124
|
+
|
|
125
|
+
for i in range(1, len(parts), 2):
|
|
126
|
+
if i+1 < len(parts):
|
|
127
|
+
case_num = parts[i]
|
|
128
|
+
case_content = parts[i+1]
|
|
129
|
+
case = self.parse_case_content(case_num, case_content, filename)
|
|
130
|
+
if case:
|
|
131
|
+
cases.append(case)
|
|
132
|
+
|
|
133
|
+
return cases
|
|
134
|
+
|
|
135
|
+
def parse_case_content(self, num: str, content: str, filename: str) -> Optional[Dict]:
|
|
136
|
+
"""Parse detailed case content with sections"""
|
|
137
|
+
|
|
138
|
+
# Extract KASUS
|
|
139
|
+
kasus_match = re.search(r'\*\*KASUS:\*\*\s*(.*?)(?=\*\*PERTANYAAN:|\*\*JAWABAN:|###|\Z)', content, re.DOTALL)
|
|
140
|
+
kasus = kasus_match.group(1).strip() if kasus_match else ""
|
|
141
|
+
|
|
142
|
+
# Extract PERTANYAAN
|
|
143
|
+
pertanyaan_match = re.search(r'\*\*PERTANYAAN:\*\*\s*(.*?)(?=---|###|\*\*JAWABAN:|\Z)', content, re.DOTALL)
|
|
144
|
+
pertanyaan = pertanyaan_match.group(1).strip() if pertanyaan_match else ""
|
|
145
|
+
|
|
146
|
+
# Extract JAWABAN
|
|
147
|
+
jawaban_match = re.search(r'###\s+⚖️\s+JAWABAN\s*(.*?)(?=###|\Z)', content, re.DOTALL)
|
|
148
|
+
if not jawaban_match:
|
|
149
|
+
jawaban_match = re.search(r'\*\*JAWABAN:\*\*\s*(.*?)(?=###|\Z)', content, re.DOTALL)
|
|
150
|
+
jawaban = jawaban_match.group(1).strip() if jawaban_match else ""
|
|
151
|
+
|
|
152
|
+
# Extract DASAR HUKUM
|
|
153
|
+
dasar_hukum_match = re.search(r'###\s+📖\s+DASAR HUKUM\s*(.*?)(?=###|\Z)', content, re.DOTALL)
|
|
154
|
+
dasar_hukum = dasar_hukum_match.group(1).strip() if dasar_hukum_match else ""
|
|
155
|
+
|
|
156
|
+
# Extract ANALISIS
|
|
157
|
+
analisis_match = re.search(r'###\s+🔍\s+ANALISIS\s*(.*?)(?=##|\Z)', content, re.DOTALL)
|
|
158
|
+
analisis = analisis_match.group(1).strip() if analisis_match else ""
|
|
159
|
+
|
|
160
|
+
# Extract references
|
|
161
|
+
pasal = self.extract_pasal(content)
|
|
162
|
+
uu = self.extract_uu(content)
|
|
163
|
+
|
|
164
|
+
# Build case
|
|
165
|
+
case = {
|
|
166
|
+
'id': f'case_{num}',
|
|
167
|
+
'number': num,
|
|
168
|
+
'file': filename,
|
|
169
|
+
'kasus': kasus[:500] if kasus else "",
|
|
170
|
+
'pertanyaan': pertanyaan[:500] if pertanyaan else "",
|
|
171
|
+
'jawaban': jawaban[:800] if jawaban else "",
|
|
172
|
+
'dasar_hukum': dasar_hukum[:500] if dasar_hukum else "",
|
|
173
|
+
'analisis': analisis[:1000] if analisis else "",
|
|
174
|
+
'pasal': pasal,
|
|
175
|
+
'uu': uu,
|
|
176
|
+
'full_content': content[:2000]
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
# Only return if we have meaningful content
|
|
180
|
+
if kasus or pertanyaan or jawaban:
|
|
181
|
+
return case
|
|
182
|
+
return None
|
|
183
|
+
|
|
184
|
+
def parse_qa_content(self, num: str, content: str, filename: str) -> Optional[Dict]:
|
|
185
|
+
"""Parse Q&A style content"""
|
|
186
|
+
|
|
187
|
+
# For Q&A format, the question is usually at the start
|
|
188
|
+
lines = content.strip().split('\n')
|
|
189
|
+
question = lines[0].strip() if lines else ""
|
|
190
|
+
|
|
191
|
+
# Answer is usually after "JAWABAN:" or "A:"
|
|
192
|
+
answer_match = re.search(r'(?:JAWABAN:|A:)\s*(.*?)(?=\*\*Q\d+:|\Z)', content, re.DOTALL)
|
|
193
|
+
answer = answer_match.group(1).strip() if answer_match else content[:500]
|
|
194
|
+
|
|
195
|
+
# Extract references
|
|
196
|
+
pasal = self.extract_pasal(content)
|
|
197
|
+
uu = self.extract_uu(content)
|
|
198
|
+
|
|
199
|
+
case = {
|
|
200
|
+
'id': f'case_{num}',
|
|
201
|
+
'number': num,
|
|
202
|
+
'file': filename,
|
|
203
|
+
'kasus': "",
|
|
204
|
+
'pertanyaan': question[:500],
|
|
205
|
+
'jawaban': answer[:800],
|
|
206
|
+
'dasar_hukum': "",
|
|
207
|
+
'analisis': "",
|
|
208
|
+
'pasal': pasal,
|
|
209
|
+
'uu': uu,
|
|
210
|
+
'full_content': content[:2000]
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if question or answer:
|
|
214
|
+
return case
|
|
215
|
+
return None
|
|
216
|
+
|
|
217
|
+
def extract_pasal(self, content: str) -> List[str]:
|
|
218
|
+
"""Extract Pasal references"""
|
|
219
|
+
pasal_patterns = [
|
|
220
|
+
r'Pasal\s+(\d+(?:\s+ayat\s+\(\d+\))?)\s+([A-Z]+)',
|
|
221
|
+
r'Pasal\s+(\d+)',
|
|
222
|
+
]
|
|
223
|
+
|
|
224
|
+
pasal_list = []
|
|
225
|
+
for pattern in pasal_patterns:
|
|
226
|
+
matches = re.findall(pattern, content)
|
|
227
|
+
for match in matches:
|
|
228
|
+
if isinstance(match, tuple):
|
|
229
|
+
if len(match) == 2:
|
|
230
|
+
pasal_list.append(f"Pasal {match[0]} {match[1]}")
|
|
231
|
+
else:
|
|
232
|
+
pasal_list.append(f"Pasal {match[0]}")
|
|
233
|
+
else:
|
|
234
|
+
pasal_list.append(f"Pasal {match}")
|
|
235
|
+
|
|
236
|
+
# Remove duplicates
|
|
237
|
+
seen = set()
|
|
238
|
+
unique = []
|
|
239
|
+
for p in pasal_list:
|
|
240
|
+
if p not in seen:
|
|
241
|
+
seen.add(p)
|
|
242
|
+
unique.append(p)
|
|
243
|
+
|
|
244
|
+
return unique[:10]
|
|
245
|
+
|
|
246
|
+
def extract_uu(self, content: str) -> List[str]:
|
|
247
|
+
"""Extract UU references"""
|
|
248
|
+
uu_patterns = [
|
|
249
|
+
r'UU\s+No\.?\s*(\d+)\s+Tahun\s+(\d+)',
|
|
250
|
+
r'UU\s+No\.?\s*(\d+)/(\d+)',
|
|
251
|
+
]
|
|
252
|
+
|
|
253
|
+
uu_list = []
|
|
254
|
+
for pattern in uu_patterns:
|
|
255
|
+
matches = re.findall(pattern, content)
|
|
256
|
+
for match in matches:
|
|
257
|
+
uu_list.append(f"UU No. {match[0]} Tahun {match[1]}")
|
|
258
|
+
|
|
259
|
+
return list(set(uu_list))[:5]
|
|
260
|
+
|
|
261
|
+
def search(self, query: str, top_k: int = 3) -> List[Dict]:
|
|
262
|
+
"""Search for relevant cases"""
|
|
263
|
+
query_lower = query.lower()
|
|
264
|
+
|
|
265
|
+
scored_cases = []
|
|
266
|
+
for case in self.cases:
|
|
267
|
+
score = self.calculate_relevance(query_lower, case)
|
|
268
|
+
if score > 0:
|
|
269
|
+
scored_cases.append((score, case))
|
|
270
|
+
|
|
271
|
+
scored_cases.sort(reverse=True, key=lambda x: x[0])
|
|
272
|
+
return [case for score, case in scored_cases[:top_k]]
|
|
273
|
+
|
|
274
|
+
def calculate_relevance(self, query: str, case: Dict) -> float:
|
|
275
|
+
"""Calculate relevance score"""
|
|
276
|
+
score = 0.0
|
|
277
|
+
|
|
278
|
+
# High weight for pertanyaan
|
|
279
|
+
if query in case.get('pertanyaan', '').lower():
|
|
280
|
+
score += 20
|
|
281
|
+
|
|
282
|
+
# Medium weight for kasus
|
|
283
|
+
if query in case.get('kasus', '').lower():
|
|
284
|
+
score += 10
|
|
285
|
+
|
|
286
|
+
# Lower weight for jawaban/analisis
|
|
287
|
+
if query in case.get('jawaban', '').lower():
|
|
288
|
+
score += 5
|
|
289
|
+
if query in case.get('analisis', '').lower():
|
|
290
|
+
score += 3
|
|
291
|
+
|
|
292
|
+
# Keyword matching
|
|
293
|
+
keywords = [k for k in query.split() if len(k) > 3]
|
|
294
|
+
for keyword in keywords:
|
|
295
|
+
full_content = case.get('full_content', '').lower()
|
|
296
|
+
count = full_content.count(keyword)
|
|
297
|
+
score += count * 2
|
|
298
|
+
|
|
299
|
+
return score
|
|
300
|
+
|
|
301
|
+
def get_stats(self) -> Dict:
|
|
302
|
+
"""Get loading statistics"""
|
|
303
|
+
return {
|
|
304
|
+
'total_cases': len(self.cases),
|
|
305
|
+
'files_loaded': self.stats['files_loaded'],
|
|
306
|
+
'cases_by_file': {},
|
|
307
|
+
'errors': self.stats['errors']
|
|
308
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Enhanced VersaLaw2 System with Maya Wisdom Integration
|
|
4
|
+
Combines MayaLaw cases + Maya Wisdom + AI
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
import logging
|
|
10
|
+
|
|
11
|
+
# Add maya-legal-system to path
|
|
12
|
+
maya_system_path = Path("/root/dragon/global/mayalaw/maya-legal-system")
|
|
13
|
+
if maya_system_path.exists():
|
|
14
|
+
sys.path.insert(0, str(maya_system_path))
|
|
15
|
+
|
|
16
|
+
from .system import VersaLaw2System
|
|
17
|
+
from .config import Config
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
class EnhancedVersaLaw2System(VersaLaw2System):
|
|
22
|
+
"""
|
|
23
|
+
Enhanced system with Maya Wisdom integration
|
|
24
|
+
|
|
25
|
+
Features:
|
|
26
|
+
- MayaLaw cases (126 cases)
|
|
27
|
+
- Maya Wisdom knowledge base
|
|
28
|
+
- AI processing
|
|
29
|
+
- Combined context
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(self, config=None):
|
|
33
|
+
"""Initialize enhanced system"""
|
|
34
|
+
super().__init__(config)
|
|
35
|
+
|
|
36
|
+
# Try to load Maya Wisdom
|
|
37
|
+
self.wisdom = None
|
|
38
|
+
self.wisdom_available = False
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
from core.maya_wisdom_processor import MayaWisdomProcessor
|
|
42
|
+
self.wisdom = MayaWisdomProcessor()
|
|
43
|
+
self.wisdom_available = True
|
|
44
|
+
print("✅ Maya Wisdom Processor loaded")
|
|
45
|
+
logger.info("Maya Wisdom Processor loaded successfully")
|
|
46
|
+
except ImportError as e:
|
|
47
|
+
print("⚠️ Maya Wisdom not available (optional)")
|
|
48
|
+
logger.warning(f"Maya Wisdom not loaded: {e}")
|
|
49
|
+
except Exception as e:
|
|
50
|
+
print(f"⚠️ Error loading Maya Wisdom: {e}")
|
|
51
|
+
logger.error(f"Maya Wisdom error: {e}")
|
|
52
|
+
|
|
53
|
+
def ask(self, question, use_cache=True, include_wisdom=True):
|
|
54
|
+
"""
|
|
55
|
+
Enhanced ask with Maya Wisdom
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
question: Legal question
|
|
59
|
+
use_cache: Use caching
|
|
60
|
+
include_wisdom: Include Maya Wisdom in context
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Enhanced result with wisdom
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
# Get wisdom if available and requested
|
|
67
|
+
wisdom_response = None
|
|
68
|
+
if include_wisdom and self.wisdom_available:
|
|
69
|
+
try:
|
|
70
|
+
wisdom_response = self.wisdom.process_legal_question(question)
|
|
71
|
+
logger.info("Maya Wisdom response generated")
|
|
72
|
+
except Exception as e:
|
|
73
|
+
logger.warning(f"Maya Wisdom processing error: {e}")
|
|
74
|
+
|
|
75
|
+
# Get standard result
|
|
76
|
+
result = super().ask(question, use_cache)
|
|
77
|
+
|
|
78
|
+
# Enhance result with wisdom
|
|
79
|
+
if wisdom_response:
|
|
80
|
+
result['wisdom'] = wisdom_response
|
|
81
|
+
result['enhanced'] = True
|
|
82
|
+
|
|
83
|
+
# Add wisdom to metadata
|
|
84
|
+
result['metadata']['wisdom_type'] = wisdom_response.get('type', 'unknown')
|
|
85
|
+
result['metadata']['wisdom_confidence'] = wisdom_response.get('confidence', 0.0)
|
|
86
|
+
else:
|
|
87
|
+
result['enhanced'] = False
|
|
88
|
+
|
|
89
|
+
return result
|
|
90
|
+
|
|
91
|
+
def print_answer(self, result):
|
|
92
|
+
"""Enhanced print with wisdom"""
|
|
93
|
+
|
|
94
|
+
# Print wisdom if available
|
|
95
|
+
if result.get('enhanced') and result.get('wisdom'):
|
|
96
|
+
wisdom = result['wisdom']
|
|
97
|
+
|
|
98
|
+
print(f"{'='*60}")
|
|
99
|
+
print("🧠 MAYA WISDOM")
|
|
100
|
+
print(f"{'='*60}\n")
|
|
101
|
+
|
|
102
|
+
print(f"Type: {wisdom.get('type', 'N/A')}")
|
|
103
|
+
print(f"Confidence: {wisdom.get('confidence', 0):.0%}\n")
|
|
104
|
+
|
|
105
|
+
if 'answer' in wisdom:
|
|
106
|
+
print(f"Basic Knowledge:")
|
|
107
|
+
print(f"{wisdom['answer']}\n")
|
|
108
|
+
|
|
109
|
+
if 'details' in wisdom:
|
|
110
|
+
print(f"Details:")
|
|
111
|
+
for key, value in wisdom['details'].items():
|
|
112
|
+
print(f" • {key}: {value}")
|
|
113
|
+
print()
|
|
114
|
+
|
|
115
|
+
# Print standard answer
|
|
116
|
+
super().print_answer(result)
|
|
117
|
+
|
|
118
|
+
def get_stats(self):
|
|
119
|
+
"""Enhanced stats with wisdom info"""
|
|
120
|
+
stats = super().get_stats()
|
|
121
|
+
|
|
122
|
+
stats['wisdom'] = {
|
|
123
|
+
'available': self.wisdom_available,
|
|
124
|
+
'loaded': self.wisdom is not None
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return stats
|
|
128
|
+
|
|
129
|
+
def create_enhanced_system(ai_provider='mock', api_key=None):
|
|
130
|
+
"""
|
|
131
|
+
Helper function to create enhanced system
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
ai_provider: 'openai', 'deepseek', 'qodo', or 'mock'
|
|
135
|
+
api_key: API key for the provider
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
EnhancedVersaLaw2System instance
|
|
139
|
+
"""
|
|
140
|
+
config = Config()
|
|
141
|
+
config['ai_provider'] = ai_provider
|
|
142
|
+
|
|
143
|
+
if api_key:
|
|
144
|
+
if ai_provider == 'openai':
|
|
145
|
+
config['openai_api_key'] = api_key
|
|
146
|
+
elif ai_provider == 'deepseek':
|
|
147
|
+
config['deepseek_api_key'] = api_key
|
|
148
|
+
elif ai_provider == 'qodo':
|
|
149
|
+
config['qodo_api_key'] = api_key
|
|
150
|
+
|
|
151
|
+
return EnhancedVersaLaw2System(config)
|