Summaries on gpt-5.5 with strict Bosnian-only rules and a proofreading pass

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LzKpYaEsuWDevuNnvSjcFW
This commit is contained in:
idriz
2026-09-02 19:27:29 +02:00
parent 92e6254888
commit c7add89c6e
3 changed files with 33 additions and 3 deletions

View File

@@ -28,7 +28,7 @@ journalctl --user -u idriz -f
```
## Konfiguracija (okolina)
`OPENAI_API_KEY`, `IDRIZ_REALTIME_MODEL` (gpt-realtime-2.1), `IDRIZ_TEXT_MODEL` (gpt-5.4-mini), `IDRIZ_IMAGE_MODEL` (gpt-image-2),
`OPENAI_API_KEY`, `IDRIZ_REALTIME_MODEL` (gpt-realtime-2.1), `IDRIZ_TEXT_MODEL` (gpt-5.5), `IDRIZ_IMAGE_MODEL` (gpt-image-2),
`IDRIZ_VOICE` (cedar), `IDRIZ_PRINTER` (idriz-printer), `IDRIZ_DRY_RUN=1` (ne štampa), `IDRIZ_MAX_PAGES` (2),
`IDRIZ_VAD_SILENCE_MS` (900), `IDRIZ_MIC_DEVICE`, `IDRIZ_SPEAKER_DEVICE`.

View File

@@ -12,7 +12,7 @@ def _env(name: str, default: str) -> str:
class Config:
openai_api_key: str = field(default_factory=lambda: _env("OPENAI_API_KEY", ""))
realtime_model: str = field(default_factory=lambda: _env("IDRIZ_REALTIME_MODEL", "gpt-realtime-2.1"))
text_model: str = field(default_factory=lambda: _env("IDRIZ_TEXT_MODEL", "gpt-5.4-mini"))
text_model: str = field(default_factory=lambda: _env("IDRIZ_TEXT_MODEL", "gpt-5.5"))
image_model: str = field(default_factory=lambda: _env("IDRIZ_IMAGE_MODEL", "gpt-image-2"))
transcription_model: str = field(default_factory=lambda: _env("IDRIZ_TRANSCRIPTION_MODEL", "gpt-4o-mini-transcribe"))
voice: str = field(default_factory=lambda: _env("IDRIZ_VOICE", "cedar"))

View File

@@ -17,7 +17,12 @@ Pravila:
- Dužina: između 280 i 420 riječi. Nikako više. Ovo ide na papir sa slikom, mora biti prozračno.
- Struktura: naslov (kratak, može i sa uzvikom), uvod od 2-3 rečenice koji odmah privuče pažnju, zatim 3 kratka poglavlja sa podnaslovima u obliku pitanja ("Kako nastaje?", "Gdje ih ima?"), po 2-4 rečenice, i na kraju "Da li si znao?" sa tačno 3 kratke crtice.
- Piši tačno, prema članku. Ne izmišljaj podatke. Brojeve i godine zadrži.
- Bez engleskih riječi osim imena koja se ne prevode.
- JEZIK JE NAJVAŽNIJI: svaka riječ mora biti na bosanskom. Nikakve engleske riječi ni polu-prevedeni izrazi.
Stručne pojmove prevedi domaćom riječju koju djeca u BiH koriste u školi (npr. "tectonic plates" → "tektonske ploče",
"hotspot" → "vruća tačka", "mantle" → "plašt", "crust" → "kora", "species" → "vrsta", "habitat" → "stanište").
Ako je pojam neophodno zadržati, daj ga u zagradi iza prevoda, ali samo za lična imena, nazive mjesta i naučna latinska imena.
Nazive mjesta koristi u domaćem obliku (Island, Havaji, Sjedinjene Američke Države, Tihi okean).
- Pazi na padeže, rod i ijekavicu (mlijeko, rijeka, cvijet, dijete). Bez srpskih i hrvatskih posebnosti kad postoji bosanska riječ.
Odgovori ISKLJUČIVO JSON objektom oblika:
{"naslov": "...",
"html": "<p>uvod</p><h2>Pitanje?</h2><p>...</p>...",
@@ -36,6 +41,27 @@ class Summary:
image_prompt: str
LEKTOR = """Ti si lektor za bosanski jezik u dječijoj izdavačkoj kući. Dobit ćeš JSON sa sažetkom za dijete.
Vrati ISTI JSON (ista polja, ista struktura, isti HTML tagovi) ali ispravljen:
- svaku englesku ili stranu riječ zamijeni bosanskom (osim ličnih imena, naziva mjesta i latinskih naučnih naziva u zagradi),
- ispravi gramatiku, padeže, rod i ijekavicu,
- rečenice zadrži kratke i razumljive djetetu, ne dodaj novi sadržaj i ne mijenjaj činjenice.
Odgovori isključivo JSON objektom."""
async def proofread(client: AsyncOpenAI, model: str, data: dict) -> dict:
resp = await client.chat.completions.create(
model=model,
messages=[{"role": "system", "content": LEKTOR}, {"role": "user", "content": json.dumps(data, ensure_ascii=False)}],
response_format={"type": "json_object"},
)
try:
fixed = json.loads(resp.choices[0].message.content or "{}")
except json.JSONDecodeError:
return data
return fixed if fixed.get("html") and fixed.get("naslov") else data
async def summarize(client: AsyncOpenAI, model: str, article: Article, topic: str, age: str) -> Summary:
user = (
f"Tema koju je dijete tražilo: {topic}\n"
@@ -48,6 +74,10 @@ async def summarize(client: AsyncOpenAI, model: str, article: Article, topic: st
response_format={"type": "json_object"},
)
data = json.loads(resp.choices[0].message.content or "{}")
try:
data = await proofread(client, model, data)
except Exception: # noqa: BLE001
log.exception("lektorski prolaz nije uspio, koristim prvu verziju")
s = Summary(
title=data.get("naslov") or article.title,
html=data.get("html") or "<p>Nažalost, nisam uspio napisati sažetak.</p>",