Nattapong Tapachoom commited on
Commit
18f1382
·
1 Parent(s): 861a5b2
Files changed (3) hide show
  1. app.py +747 -3
  2. requirements.txt +7 -0
  3. sample_data.csv +6 -0
app.py CHANGED
@@ -1,7 +1,751 @@
1
  import gradio as gr
 
 
 
 
 
 
 
 
 
 
 
2
 
3
- def greet(name):
4
- return "Hello " + name + "!!"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- demo = gr.Interface(fn=greet, inputs="text", outputs="text")
7
  demo.launch()
 
1
  import gradio as gr
2
+ import os
3
+ import json
4
+ import uuid
5
+ import re
6
+ from typing import List, Optional, Dict, Any
7
+ from pydantic import BaseModel, ValidationError
8
+ from datasets import load_dataset, Dataset, DatasetDict
9
+ import pandas as pd
10
+ import requests
11
+ from datetime import datetime
12
+ import hashlib
13
 
14
+ # 1. Dataset Schema
15
+ class DataSample(BaseModel):
16
+ id: str
17
+ context: str
18
+ question: str
19
+ options: Optional[List[str]] = None
20
+ answer: str
21
+ rationale: str
22
+ category: str
23
+ difficulty: str
24
+ source: str
25
+ language: str
26
+
27
+ # 2. Load dataset (local file หรือ Hugging Face)
28
+ def load_data(source_type, path_or_name):
29
+ try:
30
+ if source_type == "local":
31
+ if not os.path.exists(path_or_name):
32
+ raise FileNotFoundError(f"ไฟล์ {path_or_name} ไม่พบ")
33
+
34
+ ext = os.path.splitext(path_or_name)[-1].lower()
35
+ if ext == ".jsonl":
36
+ data = []
37
+ with open(path_or_name, 'r', encoding="utf-8") as f:
38
+ for line_num, line in enumerate(f, 1):
39
+ try:
40
+ data.append(json.loads(line.strip()))
41
+ except json.JSONDecodeError as e:
42
+ print(f"Warning: บรรทัด {line_num} มีข้อผิดพลาด JSON: {e}")
43
+ continue
44
+ elif ext == ".csv":
45
+ df = pd.read_csv(path_or_name, encoding="utf-8")
46
+ data = df.to_dict(orient="records")
47
+ elif ext == ".json":
48
+ with open(path_or_name, 'r', encoding="utf-8") as f:
49
+ raw_data = json.load(f)
50
+ data = raw_data if isinstance(raw_data, list) else [raw_data]
51
+ else:
52
+ raise ValueError(f"ไม่รองรับไฟล์ประเภท {ext}")
53
+
54
+ # แปลงเป็น DataSample objects
55
+ samples = []
56
+ for i, item in enumerate(data):
57
+ try:
58
+ # เติมค่า default ถ้าไม่มี
59
+ if 'id' not in item:
60
+ item['id'] = str(uuid.uuid4())
61
+ if 'source' not in item:
62
+ item['source'] = f"local_{os.path.basename(path_or_name)}"
63
+ if 'difficulty' not in item:
64
+ item['difficulty'] = "medium"
65
+ if 'language' not in item:
66
+ item['language'] = "th"
67
+
68
+ samples.append(DataSample(**item))
69
+ except ValidationError as e:
70
+ print(f"Warning: รายการที่ {i+1} ข้อมูลไม่ถูกต้อง: {e}")
71
+ continue
72
+
73
+ return samples
74
+
75
+ elif source_type == "hf":
76
+ try:
77
+ ds = load_dataset(path_or_name)
78
+ # หา split ที่มีข้อมูล
79
+ available_splits = list(ds.keys())
80
+ if not available_splits:
81
+ raise ValueError("ไม่พบข้อมูลใน dataset")
82
+
83
+ # ใช้ split แรกที่มีข้อมูล
84
+ split_name = available_splits[0]
85
+ data = ds[split_name]
86
+
87
+ samples = []
88
+ for i, item in enumerate(data):
89
+ try:
90
+ # แปลง HF format เป็น DataSample
91
+ sample_dict = dict(item)
92
+
93
+ # เติมค่า default
94
+ if 'id' not in sample_dict:
95
+ sample_dict['id'] = f"hf_{i}"
96
+ if 'source' not in sample_dict:
97
+ sample_dict['source'] = f"hf_{path_or_name}"
98
+ if 'difficulty' not in sample_dict:
99
+ sample_dict['difficulty'] = "medium"
100
+ if 'language' not in sample_dict:
101
+ sample_dict['language'] = "en"
102
+
103
+ samples.append(DataSample(**sample_dict))
104
+ except ValidationError as e:
105
+ print(f"Warning: รายการที่ {i+1} จาก HF ข้อมูลไม่ถูกต้อง: {e}")
106
+ continue
107
+
108
+ return samples
109
+
110
+ except Exception as e:
111
+ raise ValueError(f"ไม่สามารถโหลด HF dataset '{path_or_name}': {e}")
112
+ else:
113
+ raise ValueError("source_type ต้องเป็น 'local' หรือ 'hf'")
114
+
115
+ except Exception as e:
116
+ raise Exception(f"ข้อผิดพลาดในการโหลดข้อมูล: {e}")
117
+
118
+ # 3. LLM API Integration (รองรับหลาย provider)
119
+ class LLMProvider:
120
+ def __init__(self, provider="ollama", api_key=None, base_url="http://localhost:11434"):
121
+ self.provider = provider
122
+ self.api_key = api_key
123
+ self.base_url = base_url
124
+
125
+ def generate(self, prompt, model="llama3.2", temperature=0.7, max_tokens=1000):
126
+ try:
127
+ if self.provider == "ollama":
128
+ return self._generate_ollama(prompt, model, temperature, max_tokens)
129
+ elif self.provider == "openai":
130
+ return self._generate_openai(prompt, model, temperature, max_tokens)
131
+ elif self.provider == "huggingface":
132
+ return self._generate_huggingface(prompt, model, temperature, max_tokens)
133
+ else:
134
+ raise ValueError(f"ไม่รองรับ provider: {self.provider}")
135
+ except Exception as e:
136
+ return f"Error generating response: {e}"
137
+
138
+ def _generate_ollama(self, prompt, model, temperature, max_tokens):
139
+ response = requests.post(
140
+ f"{self.base_url}/api/generate",
141
+ json={
142
+ "model": model,
143
+ "prompt": prompt,
144
+ "stream": False,
145
+ "options": {
146
+ "temperature": temperature,
147
+ "num_predict": max_tokens
148
+ }
149
+ }
150
+ )
151
+ response.raise_for_status()
152
+ return response.json()["response"]
153
+
154
+ def _generate_openai(self, prompt, model, temperature, max_tokens):
155
+ import openai
156
+ if self.api_key:
157
+ openai.api_key = self.api_key
158
+
159
+ response = openai.ChatCompletion.create(
160
+ model=model,
161
+ messages=[{"role": "user", "content": prompt}],
162
+ temperature=temperature,
163
+ max_tokens=max_tokens
164
+ )
165
+ return response.choices[0].message.content
166
+
167
+ def _generate_huggingface(self, prompt, model, temperature, max_tokens):
168
+ headers = {"Authorization": f"Bearer {self.api_key}"}
169
+ response = requests.post(
170
+ f"https://api-inference.huggingface.co/models/{model}",
171
+ headers=headers,
172
+ json={
173
+ "inputs": prompt,
174
+ "parameters": {
175
+ "temperature": temperature,
176
+ "max_new_tokens": max_tokens
177
+ }
178
+ }
179
+ )
180
+ response.raise_for_status()
181
+ result = response.json()
182
+ if isinstance(result, list) and len(result) > 0:
183
+ return result[0].get("generated_text", "").replace(prompt, "").strip()
184
+ return str(result)
185
+
186
+ # 4. Dataset Generation & Augmentation
187
+ def generate_new_samples(samples: List[DataSample], llm_provider: LLMProvider,
188
+ generation_type="augment", n_generate=1, custom_prompt=""):
189
+ """
190
+ generation_type: 'augment', 'roleplay', 'topic_conditioning', 'self_critique'
191
+ """
192
+ generated_samples = []
193
+
194
+ for sample in samples[:5]: # จำกัดแค่ 5 samples แรกเพื่อทดสอบ
195
+ for _ in range(n_generate):
196
+ try:
197
+ if generation_type == "augment":
198
+ prompt = f"""
199
+ Based on this context and question, create a similar but different scenario:
200
+
201
+ Context: {sample.context}
202
+ Question: {sample.question}
203
+ Answer: {sample.answer}
204
+ Rationale: {sample.rationale}
205
+
206
+ Generate a new scenario in the same category ({sample.category}) with:
207
+ - Different context but similar moral/logical challenge
208
+ - Appropriate question
209
+ - Clear answer
210
+ - Detailed rationale
211
+
212
+ Format as JSON:
213
+ {{
214
+ "context": "new context here",
215
+ "question": "new question here",
216
+ "answer": "new answer here",
217
+ "rationale": "detailed reasoning here"
218
+ }}"""
219
+
220
+ elif generation_type == "roleplay":
221
+ roles = ["ครูใหญ่", "หมอ", "นักบวช", "นักจิตวิทยา", "ผู้ปกครอง"]
222
+ role = roles[len(generated_samples) % len(roles)]
223
+ prompt = f"""
224
+ คุณคือ{role} กำลังให้คำแนะนำเกี่ยวกับสถานการณ์นี้:
225
+
226
+ Context: {sample.context}
227
+ Question: {sample.question}
228
+
229
+ ในฐานะ{role} จงสร้างคำตอบและเหตุผลที่เหมาะสมจากมุมมองของบทบาทนี้
230
+
231
+ Format as JSON:
232
+ {{
233
+ "context": "{sample.context}",
234
+ "question": "{sample.question}",
235
+ "answer": "คำตอบในฐานะ{role}",
236
+ "rationale": "เหตุผลจากมุมมอง{role}"
237
+ }}"""
238
+
239
+ elif generation_type == "topic_conditioning":
240
+ topics = ["ปัญหาวัยรุ่น", "ความยากจน", "เทคโนโลยี", "สิ่งแวดล้อม", "คร���บครัว"]
241
+ topic = topics[len(generated_samples) % len(topics)]
242
+ prompt = f"""
243
+ สร้างสถานการณ์ใหม่ในหัวข้อ "{topic}" ที่มีความซับซ้อนทางจริยธรรมคล้ายกับ:
244
+
245
+ Original context: {sample.context}
246
+ Category: {sample.category}
247
+
248
+ สร้างสถานการณ์ใหม่ที่เกี่ยวข้องกับ{topic}:
249
+
250
+ Format as JSON:
251
+ {{
252
+ "context": "สถานการณ์เกี่ยวกับ{topic}",
253
+ "question": "คำถามที่เหมาะสม",
254
+ "answer": "คำตอบที่ดีที่สุด",
255
+ "rationale": "เหตุผลโดยละเอียด"
256
+ }}"""
257
+
258
+ elif generation_type == "self_critique":
259
+ prompt = f"""
260
+ Analyze and improve this moral reasoning scenario:
261
+
262
+ Context: {sample.context}
263
+ Question: {sample.question}
264
+ Answer: {sample.answer}
265
+ Rationale: {sample.rationale}
266
+
267
+ 1. First, critique the reasoning - what could be improved?
268
+ 2. Then provide an enhanced version with better rationale
269
+
270
+ Format as JSON:
271
+ {{
272
+ "context": "{sample.context}",
273
+ "question": "{sample.question}",
274
+ "answer": "improved answer",
275
+ "rationale": "enhanced rationale with deeper analysis"
276
+ }}"""
277
+
278
+ else: # custom prompt
279
+ prompt = custom_prompt.format(**sample.dict())
280
+
281
+ # Generate ด้วย LLM
282
+ response = llm_provider.generate(prompt)
283
+
284
+ # Parse JSON response
285
+ try:
286
+ # ลองหา JSON ใน response
287
+ json_match = re.search(r'\{.*\}', response, re.DOTALL)
288
+ if json_match:
289
+ json_str = json_match.group()
290
+ parsed_data = json.loads(json_str)
291
+
292
+ # สร้าง DataSample ใหม่
293
+ new_sample = DataSample(
294
+ id=str(uuid.uuid4()),
295
+ context=parsed_data.get("context", sample.context),
296
+ question=parsed_data.get("question", sample.question),
297
+ answer=parsed_data.get("answer", sample.answer),
298
+ rationale=parsed_data.get("rationale", sample.rationale),
299
+ category=sample.category,
300
+ difficulty=sample.difficulty,
301
+ source=f"generated_{generation_type}",
302
+ language=sample.language,
303
+ options=sample.options
304
+ )
305
+ generated_samples.append(new_sample)
306
+
307
+ except (json.JSONDecodeError, KeyError) as e:
308
+ print(f"Warning: ไม่สามารถ parse JSON response: {e}")
309
+ continue
310
+
311
+ except Exception as e:
312
+ print(f"Warning: ไม่สามารถ generate sample: {e}")
313
+ continue
314
+
315
+ return generated_samples
316
+
317
+ # 5. Post-processing & Filtering
318
+ def remove_duplicates(samples: List[DataSample]) -> List[DataSample]:
319
+ """Remove duplicate samples based on context and question"""
320
+ seen = set()
321
+ unique = []
322
+ for s in samples:
323
+ # สร้าง hash จาก context + question
324
+ content_hash = hashlib.md5(f"{s.context.lower().strip()}{s.question.lower().strip()}".encode()).hexdigest()
325
+ if content_hash not in seen:
326
+ unique.append(s)
327
+ seen.add(content_hash)
328
+ return unique
329
+
330
+ def syntax_check(samples: List[DataSample]) -> List[DataSample]:
331
+ """Check for basic syntax issues and filter out problematic samples"""
332
+ valid_samples = []
333
+ for s in samples:
334
+ # Check ว่ามีเนื้อหาครบถ้วน
335
+ if (len(s.context.strip()) < 10 or
336
+ len(s.question.strip()) < 5 or
337
+ len(s.answer.strip()) < 3 or
338
+ len(s.rationale.strip()) < 10):
339
+ continue
340
+
341
+ # Check ว่าไม่มี placeholder text
342
+ placeholder_texts = ["[ใส่ข้อความ]", "TODO", "xxx", "example", "sample"]
343
+ has_placeholder = any(placeholder in s.context.lower() or
344
+ placeholder in s.question.lower() or
345
+ placeholder in s.answer.lower() or
346
+ placeholder in s.rationale.lower()
347
+ for placeholder in placeholder_texts)
348
+ if has_placeholder:
349
+ continue
350
+
351
+ valid_samples.append(s)
352
+
353
+ return valid_samples
354
+
355
+ def difficulty_assessment(samples: List[DataSample]) -> List[DataSample]:
356
+ """Assess and update difficulty based on heuristics"""
357
+ for sample in samples:
358
+ # Heuristic based on token count and complexity
359
+ total_tokens = len(sample.context.split()) + len(sample.question.split()) + len(sample.rationale.split())
360
+
361
+ # Count complexity indicators
362
+ complexity_indicators = [
363
+ "ถ้า", "แต่", "อย่างไรก็ตาม", "ในขณะที่", "แม้ว่า",
364
+ "เนื่องจาก", "ดังนั้น", "เพราะว่า", "หากว่า", "เว้นแต่"
365
+ ]
366
+ complexity_count = sum(1 for indicator in complexity_indicators
367
+ if indicator in sample.context or indicator in sample.rationale)
368
+
369
+ # Assess difficulty
370
+ if total_tokens < 50 and complexity_count < 2:
371
+ sample.difficulty = "easy"
372
+ elif total_tokens > 150 or complexity_count > 4:
373
+ sample.difficulty = "hard"
374
+ else:
375
+ sample.difficulty = "medium"
376
+
377
+ return samples
378
+
379
+ def translate_to_multilingual(samples: List[DataSample], llm_provider: LLMProvider, target_lang="en") -> List[DataSample]:
380
+ """Translate samples to target language"""
381
+ translated = []
382
+
383
+ for sample in samples[:3]: # จำกัดเพื่อทดสอบ
384
+ if sample.language == target_lang:
385
+ continue
386
+
387
+ try:
388
+ prompt = f"""
389
+ Translate this moral reasoning scenario to {target_lang}:
390
+
391
+ Context: {sample.context}
392
+ Question: {sample.question}
393
+ Answer: {sample.answer}
394
+ Rationale: {sample.rationale}
395
+
396
+ Maintain the moral and cultural context appropriately.
397
+
398
+ Format as JSON:
399
+ {{
400
+ "context": "translated context",
401
+ "question": "translated question",
402
+ "answer": "translated answer",
403
+ "rationale": "translated rationale"
404
+ }}"""
405
+
406
+ response = llm_provider.generate(prompt)
407
+
408
+ # Parse JSON
409
+ json_match = re.search(r'\{.*\}', response, re.DOTALL)
410
+ if json_match:
411
+ parsed_data = json.loads(json_match.group())
412
+
413
+ translated_sample = DataSample(
414
+ id=f"{sample.id}_{target_lang}",
415
+ context=parsed_data["context"],
416
+ question=parsed_data["question"],
417
+ answer=parsed_data["answer"],
418
+ rationale=parsed_data["rationale"],
419
+ category=sample.category,
420
+ difficulty=sample.difficulty,
421
+ source=f"{sample.source}_translated",
422
+ language=target_lang,
423
+ options=sample.options
424
+ )
425
+ translated.append(translated_sample)
426
+
427
+ except Exception as e:
428
+ print(f"Warning: ไม่สามารถแปลภาษา sample {sample.id}: {e}")
429
+ continue
430
+
431
+ return translated
432
+
433
+ def add_multiple_choice_options(samples: List[DataSample], llm_provider: LLMProvider) -> List[DataSample]:
434
+ """Add multiple choice options to samples"""
435
+ for sample in samples[:3]: # จำกัดเพื่อทดสอบ
436
+ if sample.options: # มี options อยู่แล้ว
437
+ continue
438
+
439
+ try:
440
+ prompt = f"""
441
+ Create 4 multiple choice options for this scenario, with one correct answer:
442
+
443
+ Context: {sample.context}
444
+ Question: {sample.question}
445
+ Correct Answer: {sample.answer}
446
+
447
+ Generate 3 plausible but incorrect options and include the correct answer.
448
+
449
+ Format as JSON array:
450
+ ["option A", "option B", "option C", "option D"]
451
+
452
+ Make sure the correct answer ({sample.answer}) is included as one of the options.
453
+ """
454
+
455
+ response = llm_provider.generate(prompt)
456
+
457
+ # Parse JSON array
458
+ json_match = re.search(r'\[.*\]', response, re.DOTALL)
459
+ if json_match:
460
+ options = json.loads(json_match.group())
461
+ if len(options) == 4:
462
+ sample.options = options
463
+
464
+ except Exception as e:
465
+ print(f"Warning: ไม่สามารถสร้าง multiple choice สำหรับ {sample.id}: {e}")
466
+ continue
467
+
468
+ return samples
469
+
470
+ # 6. Export & Visualization
471
+ def export_dataset(samples: List[DataSample], format_type="csv", output_path="output"):
472
+ """Export dataset ในรูปแบบต่างๆ"""
473
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
474
+
475
+ if format_type == "csv":
476
+ df = pd.DataFrame([s.dict() for s in samples])
477
+ filename = f"{output_path}_{timestamp}.csv"
478
+ df.to_csv(filename, index=False, encoding="utf-8-sig")
479
+ return filename
480
+
481
+ elif format_type == "jsonl":
482
+ filename = f"{output_path}_{timestamp}.jsonl"
483
+ with open(filename, 'w', encoding="utf-8") as f:
484
+ for sample in samples:
485
+ f.write(json.dumps(sample.dict(), ensure_ascii=False) + "\n")
486
+ return filename
487
+
488
+ elif format_type == "hf_dataset":
489
+ # Create Hugging Face Dataset
490
+ data_dict = {key: [] for key in samples[0].dict().keys()}
491
+ for sample in samples:
492
+ sample_dict = sample.dict()
493
+ for key, value in sample_dict.items():
494
+ data_dict[key].append(value)
495
+
496
+ dataset = Dataset.from_dict(data_dict)
497
+ dirname = f"{output_path}_hf_{timestamp}"
498
+ dataset.save_to_disk(dirname)
499
+ return dirname
500
+
501
+ else:
502
+ raise ValueError(f"ไม่รองรับรูปแบบ: {format_type}")
503
+
504
+ def get_dataset_stats(samples: List[DataSample]) -> Dict[str, Any]:
505
+ """สถิติของ dataset"""
506
+ if not samples:
507
+ return {"total": 0}
508
+
509
+ df = pd.DataFrame([s.dict() for s in samples])
510
+
511
+ stats = {
512
+ "total": len(samples),
513
+ "categories": df["category"].value_counts().to_dict(),
514
+ "difficulties": df["difficulty"].value_counts().to_dict(),
515
+ "languages": df["language"].value_counts().to_dict(),
516
+ "sources": df["source"].value_counts().to_dict(),
517
+ "avg_context_length": df["context"].str.len().mean(),
518
+ "avg_question_length": df["question"].str.len().mean(),
519
+ "avg_answer_length": df["answer"].str.len().mean(),
520
+ "avg_rationale_length": df["rationale"].str.len().mean(),
521
+ "with_options": sum(1 for s in samples if s.options is not None)
522
+ }
523
+
524
+ return stats
525
+
526
+ # 7. Main Workflow Function
527
+ def main_workflow(source_type, path_or_name, llm_provider_type, api_key, base_url,
528
+ generation_type, n_generate, custom_prompt, target_language,
529
+ add_multiple_choice, export_format):
530
+ try:
531
+ progress_text = "เริ่มต้น workflow...\n"
532
+
533
+ # 1. Load dataset
534
+ progress_text += "📂 กำลังโหลด dataset...\n"
535
+ samples = load_data(source_type, path_or_name)
536
+ progress_text += f"✅ โหลดสำเร็จ {len(samples)} samples\n"
537
+
538
+ # 2. Setup LLM
539
+ progress_text += f"🤖 กำลังตั้งค่า LLM ({llm_provider_type})...\n"
540
+ llm_provider = LLMProvider(
541
+ provider=llm_provider_type,
542
+ api_key=api_key if api_key else None,
543
+ base_url=base_url if base_url else "http://localhost:11434"
544
+ )
545
+
546
+ # 3. Generate new samples
547
+ if n_generate > 0:
548
+ progress_text += f"✨ กำลัง generate {n_generate} samples ใหม่ ({generation_type})...\n"
549
+ new_samples = generate_new_samples(samples, llm_provider, generation_type, n_generate, custom_prompt)
550
+ samples.extend(new_samples)
551
+ progress_text += f"✅ Generate สำเร็จ {len(new_samples)} samples ใหม่\n"
552
+
553
+ # 4. Post-processing
554
+ progress_text += "🔧 กำลัง post-process...\n"
555
+ original_count = len(samples)
556
+ samples = remove_duplicates(samples)
557
+ progress_text += f" - ลบ duplicate: {original_count} -> {len(samples)}\n"
558
+
559
+ samples = syntax_check(samples)
560
+ progress_text += f" - syntax check: {len(samples)} samples ผ่าน\n"
561
+
562
+ samples = difficulty_assessment(samples)
563
+ progress_text += f" - ประเมิน difficulty เสร็จสิ้น\n"
564
+
565
+ # 5. Translation
566
+ if target_language and target_language != "none":
567
+ progress_text += f"🌐 กำลังแปลเป็น {target_language}...\n"
568
+ translated = translate_to_multilingual(samples, llm_provider, target_language)
569
+ samples.extend(translated)
570
+ progress_text += f"✅ แปลภาษาสำเร็จ {len(translated)} samples\n"
571
+
572
+ # 6. Add multiple choice
573
+ if add_multiple_choice:
574
+ progress_text += "📝 กำลังเพิ่ม multiple choice options...\n"
575
+ samples = add_multiple_choice_options(samples, llm_provider)
576
+ progress_text += "✅ เพิ่ม multiple choice เสร็จสิ้น\n"
577
+
578
+ # 7. Export
579
+ progress_text += f"💾 กำลัง export เป็น {export_format}...\n"
580
+ output_file = export_dataset(samples, export_format)
581
+ progress_text += f"✅ Export สำเร็จ: {output_file}\n"
582
+
583
+ # 8. Stats
584
+ stats = get_dataset_stats(samples)
585
+ progress_text += "\n📊 สถิติ Dataset:\n"
586
+ progress_text += f" - จำนวนทั้งหมด: {stats['total']}\n"
587
+ progress_text += f" - Categories: {stats['categories']}\n"
588
+ progress_text += f" - Difficulties: {stats['difficulties']}\n"
589
+ progress_text += f" - Languages: {stats['languages']}\n"
590
+ progress_text += f" - มี Multiple Choice: {stats['with_options']}\n"
591
+
592
+ return progress_text, pd.DataFrame([s.dict() for s in samples]).head(10).to_html()
593
+
594
+ except Exception as e:
595
+ error_text = f"❌ เกิดข้อผิดพลาด: {str(e)}"
596
+ return error_text, ""
597
+
598
+ # 8. Gradio Interface
599
+ with gr.Blocks(title="Dataset Generator System", theme=gr.themes.Soft()) as demo:
600
+ gr.Markdown("# 🤖 ระบบ Generate Dataset จากโมเดล AI")
601
+ gr.Markdown("ระบบสำหรับสร้าง, ขยาย, และประมวลผล dataset ด้วย AI models")
602
+
603
+ with gr.Tab("📂 Dataset Input"):
604
+ with gr.Row():
605
+ source_type = gr.Radio(
606
+ ["local", "hf"],
607
+ label="ประเภทแหล่งข้อมูล",
608
+ info="local = ไฟล์ในเครื่อง, hf = Hugging Face dataset",
609
+ value="local"
610
+ )
611
+ path_or_name = gr.Textbox(
612
+ label="Path หรือ Dataset Name",
613
+ placeholder="เช่น data.csv หรือ microsoft/DialoGPT-medium",
614
+ info="สำหรับ local: ใส่ path ไฟล์ (.csv, .jsonl, .json) / สำหรับ HF: ใส่ชื่อ dataset"
615
+ )
616
+
617
+ with gr.Tab("🤖 LLM Settings"):
618
+ with gr.Row():
619
+ llm_provider_type = gr.Dropdown(
620
+ ["ollama", "openai", "huggingface"],
621
+ label="LLM Provider",
622
+ value="ollama",
623
+ info="เลือกผู้ให้บริการ LLM"
624
+ )
625
+ api_key = gr.Textbox(
626
+ label="API Key (ถ้าจำเป็น)",
627
+ type="password",
628
+ placeholder="สำหรับ OpenAI หรือ HuggingFace"
629
+ )
630
+ base_url = gr.Textbox(
631
+ label="Base URL",
632
+ value="http://localhost:11434",
633
+ info="สำหรับ Ollama หรือ local LLM server"
634
+ )
635
+
636
+ with gr.Tab("✨ Generation Settings"):
637
+ with gr.Row():
638
+ generation_type = gr.Dropdown(
639
+ ["augment", "roleplay", "topic_conditioning", "self_critique", "custom"],
640
+ label="ประเภทการ Generate",
641
+ value="augment",
642
+ info="วิธีการสร้างข้อมูลใหม่"
643
+ )
644
+ n_generate = gr.Slider(
645
+ 1, 5, value=1, step=1,
646
+ label="จำนวนรอบ Generate",
647
+ info="จำนวน samples ใหม่ที่จะสร้างต่อ original sample"
648
+ )
649
+
650
+ custom_prompt = gr.Textbox(
651
+ label="Custom Prompt (ถ้าเลือก custom)",
652
+ placeholder="ใช้ {context}, {question}, {answer} เป็น placeholder",
653
+ lines=3,
654
+ visible=False
655
+ )
656
+
657
+ def update_custom_prompt_visibility(gen_type):
658
+ return gr.update(visible=(gen_type == "custom"))
659
+
660
+ generation_type.change(
661
+ update_custom_prompt_visibility,
662
+ inputs=[generation_type],
663
+ outputs=[custom_prompt]
664
+ )
665
+
666
+ with gr.Tab("🔧 Post-processing"):
667
+ with gr.Row():
668
+ target_language = gr.Dropdown(
669
+ ["none", "en", "th", "zh", "ja"],
670
+ label="แปลภาษา",
671
+ value="none",
672
+ info="แปลเป็นภาษาเป้าหมาย (none = ไม่แปล)"
673
+ )
674
+ add_multiple_choice = gr.Checkbox(
675
+ label="เพิ่ม Multiple Choice Options",
676
+ value=False,
677
+ info="สร้างตัวเลือกผิดสำหรับทำ multiple choice"
678
+ )
679
+
680
+ with gr.Tab("💾 Export Settings"):
681
+ export_format = gr.Dropdown(
682
+ ["csv", "jsonl", "hf_dataset"],
683
+ label="รูปแบบ Export",
684
+ value="csv",
685
+ info="รูปแบบไฟล์ที่ต้องการ export"
686
+ )
687
+
688
+ with gr.Row():
689
+ run_btn = gr.Button("🚀 เริ่มต้น Workflow", variant="primary", size="lg")
690
+ clear_btn = gr.Button("🗑️ ล้างข้อมูล", variant="secondary")
691
+
692
+ with gr.Tab("📊 ผลลัพธ์"):
693
+ progress_output = gr.Textbox(
694
+ label="สถานะ",
695
+ lines=15,
696
+ max_lines=20,
697
+ interactive=False,
698
+ show_copy_button=True
699
+ )
700
+
701
+ preview_output = gr.HTML(
702
+ label="ตัวอย่างข้อมูล (10 รายการแรก)"
703
+ )
704
+
705
+ # Event handlers
706
+ run_btn.click(
707
+ fn=main_workflow,
708
+ inputs=[
709
+ source_type, path_or_name, llm_provider_type, api_key, base_url,
710
+ generation_type, n_generate, custom_prompt, target_language,
711
+ add_multiple_choice, export_format
712
+ ],
713
+ outputs=[progress_output, preview_output]
714
+ )
715
+
716
+ clear_btn.click(
717
+ lambda: ("", ""),
718
+ outputs=[progress_output, preview_output]
719
+ )
720
+
721
+ # ตัวอย่าง dataset schema
722
+ with gr.Tab("📋 ตัวอย่าง Dataset Schema"):
723
+ gr.Markdown("""
724
+ ## Schema ของ Dataset
725
+
726
+ | Field | ประเภท | อธิบาย |
727
+ |-------|--------|--------|
728
+ | id | string | รหัสเฉพาะของ sample |
729
+ | context | string | บริบท/สถานการณ์ |
730
+ | question | string | คำถาม |
731
+ | options | list | ตัวเลือก (สำหรับ multiple choice) |
732
+ | answer | string | คำตอบที่ถูกต้อง |
733
+ | rationale | string | เหตุผล/คำอธิบาย |
734
+ | category | string | หมวดหมู่ |
735
+ | difficulty | string | ระดับความยาก (easy/medium/hard) |
736
+ | source | string | แหล่งที่มาของข้อมูล |
737
+ | language | string | ภาษา (th/en/zh/ja) |
738
+
739
+ ## ตัวอย่างไฟล์ CSV:
740
+ ```csv
741
+ id,context,question,answer,rationale,category,difficulty,source,language
742
+ 1,"นักเรียนคนหนึ่งเห็นเพื่อนทำโกง","ควรรายงานครูหรือไม่","ควรรายงาน","เพื่อความยุติธรรม","การศึกษา","medium","manual","th"
743
+ ```
744
+
745
+ ## ตัวอย่างไฟล์ JSONL:
746
+ ```json
747
+ {"id": "1", "context": "นักเรียนคนหนึ่งเห็นเพื่อนทำโกง", "question": "ควรรายงานครูหรือไม่", "answer": "ควรรายงาน", "rationale": "เพื่อความยุติธรรม", "category": "การศึกษา", "difficulty": "medium", "source": "manual", "language": "th"}
748
+ ```
749
+ """)
750
 
 
751
  demo.launch()
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ gradio>=4.0.0
2
+ pandas>=1.5.0
3
+ datasets>=2.0.0
4
+ pydantic>=2.0.0
5
+ requests>=2.28.0
6
+ openai>=1.0.0
7
+ huggingface-hub>=0.16.0
sample_data.csv ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ id,context,question,answer,rationale,category,difficulty,source,language
2
+ 1,"นักเรียนมัธยมคนหนึ่งเห็นเพื่อนสนิทกำลังลอกการบ้านจากเพื่อนคนอื่น ก่อนที่จะส่งครู","ควรแจ้งครูเรื่องการลอกการบ้านหรือไม่","ควรคุยกับเพื่อนก่อน แล้วค่อยพิจารณาแจ้งครูถ้าไม่หยุด","การคุยกับเพื่อนก่อนจะช่วยให้เขามีโอกาสแก้ไขตัวเอง และรักษาความสัมพันธ์มิตรภาพไว้ได้","การศึกษา","medium","manual","th"
3
+ 2,"พนักงานคนหนึ่งพบว่าหัวหน้างานมีการทุจริตโดยการเบิกเงินเท็จ","ควรรายงานการทุจริตนี้หรือไม่","ควรรายงานผ่านช่องทางที่เหมาะสม","การทุจริตส่งผลเสียต่อองค์กรและสังคม การรายงานเป็นหน้าที่ของพลเมืองดี","การทำงาน","hard","manual","th"
4
+ 3,"ครอบครัวหนึ่งมีปัญหาทางการเงิน ลูกคิดจะหยุดเรียนเพื่อไปทำงานช่วยครอบครัว","ลูกควรหยุดเรียนเพื่อทำงานหรือไม่","ไม่ควร ควรหาทางออกอื่น เช่น ขอทุนการศึกษา","การศึกษาเป็นรากฐานสำคัญของอนาคต ควรหาวิธีแก้ปัญหาการเงินโดยไม่ต้องเสียโอกาสทางการศึกษา","ครอบครัว","medium","manual","th"
5
+ 4,"นักท่องเที่ยวเห็นคนท้องถิ่นทิ้งขยะลงในแม่น้ำ","ควรไปตักเตือนหรือไม่","ควรตักเตือนอย่างสุภาพและให้ความรู้","การรักษาสิ่งแวดล้อมเป็นหน้าที่ของทุกคน การให้ความรู้อย่างสุภาพจะมีประสิทธิภาพมากกว่าการตำหนิ","สิ่งแวดล้อม","easy","manual","th"
6
+ 5,"หมอพบว่าผู้ป่วยมีโรคร้ายแรง แต่ผู้ป่วยไม่อยากให้ครอบครัวรู้","ควรบอกความจริงกับครอบครัวหรือไม่","ควรเคารพความประสงค์ของผู้ป่วย แต่แนะนำให้เล่าเอง","การเคารพสิทธิส่วนบุคคลของผู้ป่วยเป็นสิ่งสำคัญ แต่ควรให้คำปรึกษาเพื่อประโยชน์ในการรักษา","การแพทย์","hard","manual","th"