emylton's picture
Upload folder using huggingface_hub
9338a41 verified
Raw
History Blame Contribute Delete
10.6 kB
"""
templat.py — Template kalimat bahasa Indonesia untuk PewujudKalimat.
ANTI-TRANSFORMER:
Transformer: urutan kata diputuskan via distribusi probabilitas.
AKSARA: urutan kata mengikuti pola S-P-O-K bahasa Indonesia (TBBBI)
yang dipilih berdasarkan slot yang tersedia di Proposisi.
Template adalah pola deterministik — bukan learned, bukan probabilistik.
Setiap template punya justifikasi gramatikal dari TBBBI.
Hierarki pemilihan template:
1. Cek slot yang tersedia di Proposisi
2. Pilih template yang paling lengkap yang bisa diisi
3. Terapkan preposisi yang benar untuk setiap keterangan
4. Gabungkan menjadi kalimat
Konvensi:
{agen} — pengisi peran AGEN (subjek kalimat aktif)
{verba} — verba dalam bentuk yang sudah direalisasikan
{pasien} — pengisi peran PASIEN (objek kalimat aktif)
{penerima} — pengisi peran PENERIMA (prepositional object: "kepada X")
{lokasi} — keterangan tempat (prepositional: "di X")
{waktu} — keterangan waktu (prepositional: "pada X" / bare)
{cara} — keterangan cara ("dengan X")
{tujuan} — keterangan tujuan ("untuk X" / "ke X")
{sebab} — keterangan sebab ("karena X")
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Dict, List, Optional, Tuple
from aksara.primitives.krl.proposition import Proposisi, TipeSlot
# ── Preposisi per tipe slot ───────────────────────────────────────────────
# Kunci: TipeSlot → preposisi default yang digunakan dalam realisasi
PREPOSISI_SLOT: Dict[TipeSlot, str] = {
TipeSlot.LOKASI: "di",
TipeSlot.TUJUAN: "ke",
TipeSlot.ASAL: "dari",
TipeSlot.PENERIMA: "kepada",
TipeSlot.CARA: "dengan",
TipeSlot.SEBAB: "karena",
TipeSlot.WAKTU: "pada",
TipeSlot.SYARAT: "jika",
}
@dataclass
class TemplatKalimat:
"""
Satu pola kalimat beserta metadata gramatikalnya.
nama: identifikasi pola
pola: string template dengan placeholder {slot}
slot_wajib: slot yang HARUS terisi agar template valid
slot_opsional: slot yang memperkaya kalimat jika tersedia
prioritas: lebih tinggi = dipilih lebih dahulu jika sama-sama valid
"""
nama: str
pola: str
slot_wajib: List[TipeSlot]
slot_opsional: List[TipeSlot]
prioritas: int = 0
# ── Daftar template kalimat bahasa Indonesia ─────────────────────────────
# Diurutkan dari paling lengkap ke paling minimal.
# Template pertama yang semua slot_wajib-nya terpenuhi akan dipakai.
TEMPLAT_AKTIF: List[TemplatKalimat] = [
# ── Kalimat lengkap S-P-O-K ──────────────────────────────────────────
TemplatKalimat(
nama="AKTIF_TRANS_PENERIMA_LOKASI",
pola="{agen} {verba} {pasien} {prep_penerima} {penerima} {prep_lokasi} {lokasi}",
slot_wajib=[TipeSlot.AGEN, TipeSlot.PASIEN, TipeSlot.PENERIMA, TipeSlot.LOKASI],
slot_opsional=[TipeSlot.WAKTU],
prioritas=10,
),
TemplatKalimat(
nama="AKTIF_TRANS_PENERIMA",
pola="{agen} {verba} {pasien} {prep_penerima} {penerima}",
slot_wajib=[TipeSlot.AGEN, TipeSlot.PASIEN, TipeSlot.PENERIMA],
slot_opsional=[TipeSlot.LOKASI, TipeSlot.WAKTU],
prioritas=9,
),
TemplatKalimat(
nama="AKTIF_TRANS_LOKASI",
pola="{agen} {verba} {pasien} {prep_lokasi} {lokasi}",
slot_wajib=[TipeSlot.AGEN, TipeSlot.PASIEN, TipeSlot.LOKASI],
slot_opsional=[TipeSlot.WAKTU, TipeSlot.CARA],
prioritas=8,
),
TemplatKalimat(
nama="AKTIF_TRANS_TUJUAN",
pola="{agen} {verba} {pasien} {prep_tujuan} {tujuan}",
slot_wajib=[TipeSlot.AGEN, TipeSlot.PASIEN, TipeSlot.TUJUAN],
slot_opsional=[TipeSlot.WAKTU],
prioritas=8,
),
TemplatKalimat(
nama="AKTIF_TRANS_CARA",
pola="{agen} {verba} {pasien} {prep_cara} {cara}",
slot_wajib=[TipeSlot.AGEN, TipeSlot.PASIEN, TipeSlot.CARA],
slot_opsional=[TipeSlot.WAKTU],
prioritas=7,
),
# ── Kalimat dasar S-P-O ───────────────────────────────────────────────
TemplatKalimat(
nama="AKTIF_TRANS",
pola="{agen} {verba} {pasien}",
slot_wajib=[TipeSlot.AGEN, TipeSlot.PASIEN],
slot_opsional=[TipeSlot.LOKASI, TipeSlot.WAKTU],
prioritas=6,
),
# ── Kalimat intransitif S-P-K ──────────────────────────────────────────
TemplatKalimat(
nama="INTR_LOKASI",
pola="{agen} {verba} {prep_lokasi} {lokasi}",
slot_wajib=[TipeSlot.AGEN, TipeSlot.LOKASI],
slot_opsional=[TipeSlot.WAKTU],
prioritas=5,
),
TemplatKalimat(
nama="INTR_TUJUAN",
pola="{agen} {verba} {prep_tujuan} {tujuan}",
slot_wajib=[TipeSlot.AGEN, TipeSlot.TUJUAN],
slot_opsional=[TipeSlot.CARA],
prioritas=5,
),
TemplatKalimat(
nama="INTR",
pola="{agen} {verba}",
slot_wajib=[TipeSlot.AGEN],
slot_opsional=[TipeSlot.LOKASI, TipeSlot.WAKTU],
prioritas=4,
),
]
TEMPLAT_PASIF: List[TemplatKalimat] = [
TemplatKalimat(
nama="PASIF_OLEH_LOKASI",
pola="{pasien} {verba} oleh {agen} {prep_lokasi} {lokasi}",
slot_wajib=[TipeSlot.PASIEN, TipeSlot.AGEN, TipeSlot.LOKASI],
slot_opsional=[TipeSlot.WAKTU],
prioritas=8,
),
TemplatKalimat(
nama="PASIF_OLEH",
pola="{pasien} {verba} oleh {agen}",
slot_wajib=[TipeSlot.PASIEN, TipeSlot.AGEN],
slot_opsional=[TipeSlot.LOKASI, TipeSlot.WAKTU],
prioritas=7,
),
TemplatKalimat(
nama="PASIF",
pola="{pasien} {verba}",
slot_wajib=[TipeSlot.PASIEN],
slot_opsional=[TipeSlot.AGEN, TipeSlot.LOKASI],
prioritas=5,
),
]
TEMPLAT_ATRIBUSI: List[TemplatKalimat] = [
TemplatKalimat(
nama="ATRIBUSI_ADALAH",
pola="{atribut} adalah {tema}",
slot_wajib=[TipeSlot.ATRIBUT, TipeSlot.TEMA],
slot_opsional=[],
prioritas=6,
),
TemplatKalimat(
nama="ATRIBUSI_MENJADI",
pola="{atribut} menjadi {tema}",
slot_wajib=[TipeSlot.ATRIBUT, TipeSlot.TEMA],
slot_opsional=[TipeSlot.SEBAB],
prioritas=7,
),
]
# Template untuk merealisasikan inferensi KRL
TEMPLAT_INFERENSI: Dict[str, str] = {
"STATUS_MENJADI": "{subjek} menjadi {objek}",
"WAJIB": "{subjek} wajib {objek}",
"MEMILIKI": "{subjek} memiliki {objek}",
"BERADA_DI": "{subjek} berada di {objek}",
"TERLIBAT_DALAM": "{subjek} terlibat dalam {objek}",
"MENYEBABKAN": "{subjek} menyebabkan {objek}",
"KAUSAL": "{subjek} mengakibatkan {objek}",
"TELAH_MEMUTUS": "{subjek} telah memutus {objek}",
"BERTANGGUNG_JAWAB": "{subjek} bertanggung jawab atas {objek}",
"BERHAK_ATAS": "{subjek} berhak atas {objek}",
"DIPINDAHKAN": "{subjek} dipindahkan ke {objek}",
}
def pilih_templat(
proposisi: Proposisi,
pasif: bool = False,
) -> Optional[TemplatKalimat]:
"""
Pilih template yang paling sesuai untuk proposisi ini.
Algoritma:
1. Tentukan set template yang relevan (aktif/pasif/atribusi)
2. Filter template yang semua slot_wajib-nya tersedia
3. Kembalikan template dengan prioritas tertinggi
Returns:
TemplatKalimat atau None jika tidak ada template yang cocok.
"""
slot_tersedia = set(proposisi.slot.keys())
# Kalimat atributif (aksi = "adalah" atau tidak ada verba)
if proposisi.aksi in ("adalah", "merupakan", "ialah"):
kandidat = TEMPLAT_ATRIBUSI
elif pasif:
kandidat = TEMPLAT_PASIF
else:
kandidat = TEMPLAT_AKTIF
valid = [
t for t in kandidat
if all(s in slot_tersedia for s in t.slot_wajib)
]
if not valid:
return None
return max(valid, key=lambda t: t.prioritas)
def isi_templat(
templat: TemplatKalimat,
proposisi: Proposisi,
verba_bentuk: str,
) -> str:
"""
Isi template dengan nilai slot dari proposisi.
Setiap placeholder {xxx} digantikan dengan nilai dari slot proposisi
atau preposisi yang sesuai.
Args:
templat: template yang dipilih
proposisi: proposisi sumber
verba_bentuk: verba yang sudah direalisasikan oleh MorfologiRealizer
Returns:
Kalimat lengkap sebagai string.
"""
def nilai_slot(tipe: TipeSlot) -> str:
s = proposisi.slot.get(tipe)
return s.nilai if s else ""
def prep(tipe: TipeSlot) -> str:
return PREPOSISI_SLOT.get(tipe, "")
mapping: Dict[str, str] = {
"agen": nilai_slot(TipeSlot.AGEN),
"verba": verba_bentuk,
"pasien": nilai_slot(TipeSlot.PASIEN),
"tema": nilai_slot(TipeSlot.TEMA) or nilai_slot(TipeSlot.PASIEN),
"atribut": nilai_slot(TipeSlot.ATRIBUT) or nilai_slot(TipeSlot.AGEN),
"penerima": nilai_slot(TipeSlot.PENERIMA),
"lokasi": nilai_slot(TipeSlot.LOKASI),
"tujuan": nilai_slot(TipeSlot.TUJUAN),
"asal": nilai_slot(TipeSlot.ASAL),
"cara": nilai_slot(TipeSlot.CARA),
"sebab": nilai_slot(TipeSlot.SEBAB),
"waktu": nilai_slot(TipeSlot.WAKTU),
# Preposisi
"prep_penerima": prep(TipeSlot.PENERIMA),
"prep_lokasi": prep(TipeSlot.LOKASI),
"prep_tujuan": prep(TipeSlot.TUJUAN),
"prep_asal": prep(TipeSlot.ASAL),
"prep_cara": prep(TipeSlot.CARA),
"prep_sebab": prep(TipeSlot.SEBAB),
"prep_waktu": prep(TipeSlot.WAKTU),
}
kalimat = templat.pola
for kunci, nilai in mapping.items():
kalimat = kalimat.replace("{" + kunci + "}", nilai)
# Bersihkan placeholder yang tidak terisi dan spasi ganda
import re
kalimat = re.sub(r"\{[^}]+\}", "", kalimat)
kalimat = re.sub(r"\s{2,}", " ", kalimat).strip()
# Kapitalisasi awal kalimat
if kalimat:
kalimat = kalimat[0].upper() + kalimat[1:]
return kalimat