diff --git a/README.md b/README.md index 3310137..c2de768 100644 --- a/README.md +++ b/README.md @@ -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`. diff --git a/idriz/config.py b/idriz/config.py index 31899a2..619eb18 100644 --- a/idriz/config.py +++ b/idriz/config.py @@ -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")) diff --git a/idriz/textgen.py b/idriz/textgen.py index 2904234..28e1456 100644 --- a/idriz/textgen.py +++ b/idriz/textgen.py @@ -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": "
uvod
...
...", @@ -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 "Nažalost, nisam uspio napisati sažetak.
",