AI Asistent Učitelj Vasa: Praktična obuka za početnike (32 dana)

Dan 4/28: Prvi poziv ka AI-ju - Učitelj Vasa progovara!

Vreme potrebno: 90 – 180 minuta

Gde je Učitelj Vasa danas?

Učitelj Vasa ima sve potrebno za pravu inteligenciju – bezbedno sačuvan API ključ (OpenAI ili Gemini), univerzalni Config modul koji upravlja postavkama za oba servisa, i razumevanje kako da elegantno prebacuje između njih. Danas je istorijski trenutak – povezaćemo Vasu sa pravim AI modelom koji si izabrao i omogućiti mu da samostalno razmišlja i odgovara!

Cilj današnje lekcije

Danas ćeš instalirati potrebne biblioteke za tvoj izabrani AI servis, napisati univerzalni sistem koji radi sa oba (OpenAI i Gemini), kreirati prvi pravi API poziv i omogućiti Učitelju Vasi da odgovara na pitanja koristeći pravu veštačku inteligenciju. Nakon ove lekcije, Vasa će moći da vodi inteligentne razgovore o programiranju, bez obzira koji AI servis koristiš!

Predznanja

  • API ključ bezbedno sačuvan u .env fajlu (Dan 3)
  • Config klasa koja učitava postavke za oba servisa (Dan 3)
  • Razumevanje AI_PROVIDER prekidača (Dan 3)
  • Postojeći ai_simulator.py kao referenca (Dan 2)

Glavni sadržaj

Instalacija AI biblioteka

Prvi korak ka pravoj AI inteligenciji je instalacija biblioteka. Pošto naš sistem podržava oba servisa, instalaćemo obe biblioteke – ne brini, one se neće sukobljavati!

📚 NIVO 1 – Osnovno objašnjenje

Biblioteke su kao adapteri između tvog koda i AI servera. Svaki AI servis ima svoju biblioteku koja “govori njihovim jezikom”. Mi ćemo instalirati obe, a naš kod će automatski koristiti pravu na osnovu tvog izbora u .env fajlu.

Instalacija je jednostavna:

  1. Otvori terminal u PyCharm-u (dole na ekranu)
  2. Prvo ažuriraj pip na najnoviju verziju:
pip install --upgrade pip
  1. Zatim instaliraj najnovije verzije biblioteka:
pip install openai google-generativeai
  1. Pritisni Enter i sačekaj da se instaliraju

🚀 NIVO 2 – Dublje razumevanje

Instaliramo najnovije verzije biblioteka jer želimo da koristimo najnovije funkcionalnosti i sigurnosne zakrpe. Ovo je važna lekcija – u stvarnom svetu, biblioteke se konstantno ažuriraju sa poboljšanjima i ispravkama grešaka.

# Instalacija najnovijih verzija
pip install --upgrade openai google-generativeai

# Proveri koje verzije su instalirane
pip show openai google-generativeai

# Trebalo bi da vidiš nešto ovako (jun 2025):
# openai: Version: 1.86.0 ili novija
# google-generativeai: Version: 0.8.5 ili novija

Cross-platform programiranje – Važna lekcija!

Sada trebamo da sačuvamo instalirane biblioteke u requirements.txt fajl. Ali ovde nailazimo na našu prvu cross-platform razliku! Različiti operacioni sistemi koriste različite komande za filtriranje teksta.

📚 NIVO 1 – Osnovno objašnjenje

Kada programiraš, važno je znati da Windows, Mac i Linux imaju različite komande za iste zadatke. To je kao kada putujemo – u različitim zemljama se koriste različiti utikači za struju. Mi programeri moramo znati kako da prilagodimo naše komande različitim “zemljama” (operacionim sistemima)!

Za Windows korisnike (PowerShell):

# Windows način 1 - koristi Select-String
pip freeze | Select-String -Pattern "openai|google-generativeai" | Out-File requirements.txt -Encoding UTF8

# Windows način 2 - koristi findstr (jednostavnije)
pip freeze | findstr /i "openai google-generativeai" > requirements.txt

Za Mac/Linux korisnike:

# Mac/Linux koriste grep komandu
pip freeze | grep -E "openai|google-generativeai" > requirements.txt

Univerzalno rešenje (radi na svim OS-ovima):

# Najjednostavnije - sačuvaj sve
pip freeze > requirements.txt

# Ili Python pristup koji radi svuda
python -c "import pkg_resources; installed = [f'{p.key}=={p.version}' for p in pkg_resources.working_set if 'openai' in p.key or 'google-generativeai' in p.key]; print('\n'.join(installed))" > requirements.txt

🚀 NIVO 2 – Dublje razumevanje

Ova razlika između operacionih sistema nije greška – to je rezultat različitih filozofija dizajna:

  • Unix/Linux (i Mac koji je baziran na Unix-u) imaju filozofiju “jedan alat, jedan zadatak”. grep je specijalizovan za pretraživanje teksta.
  • Windows ima PowerShell koji koristi objektno-orijentisan pristup sa cmdletima kao što je Select-String.
  • Python nudi cross-platform rešenja koja rade svuda isto.

🔍 UVID: U profesionalnom razvoju, često ćeš videti skripte koje počinju sa proverom operacionog sistema:

import platform
import subprocess

if platform.system() == "Windows":
    # Windows komande
    subprocess.run(["powershell", "-Command", "..."])
else:
    # Unix/Linux/Mac komande
    subprocess.run(["grep", "..."])

💡 PRO TIP: Kada pišeš dokumentaciju ili tutorijale, uvek navedi opcije za različite platforme. Još bolje, koristi Python ili druge cross-platform alate kada god je to moguće.

🎈 ZABAVNA ČINJENICA: Naziv “grep” dolazi od Unix editora ed komande “g/re/p” (globally search for regular expression and print). PowerShell-ov Select-String je inspirisan grep-om ali prilagođen Windows objektnom modelu!

Važna lekcija o SSL certifikatima i environment varijablama

Pre nego što nastavimo sa kreiranjem AI servisa, moramo razumeti jedan čest problem koji može da se javi, posebno na Windows sistemima. Ovo je odlična prilika da naučimo o environment varijablama i kako različiti programi mogu da utiču jedni na druge!

📚 NIVO 1 – Osnovno objašnjenje

Environment varijable su kao javne oglasne table na tvom računaru. Svaki program može da postavi poruku (varijablu) koju svi drugi programi mogu da vide. Problem nastaje kada jedan program ostavi poruku koja kvari rad drugih programa.

Zamisli ovo ovako:

  • Nekad davno si instalirao program (npr. Anaconda, Git, ili neki Python alat)
  • Taj program je ostavio poruku: “SSL certifikati su OVDE”
  • Ali ta lokacija više ne postoji ili nikad nije bila ispravna
  • Sada kada OpenAI pokušava da pronađe certifikate, gleda u pogrešno mesto!

🚀 NIVO 2 – Dublje razumevanje

SSL (Secure Sockets Layer) certifikati su digitalni dokumenti koji omogućavaju sigurnu komunikaciju preko interneta. Kada OpenAI biblioteka pokušava da se poveže sa serverom, mora da proveri SSL certifikat da bi bila sigurna da razgovara sa pravim serverom.

Problem koji se često javlja:

  1. Environment pollution – različiti programi ostavljaju globalne varijable
  2. httpx biblioteka (koju koristi OpenAI) poštuje SSL_CERT_FILE varijablu
  3. Ako ta varijabla pokazuje na nepostojeći fajl, dobićeš grešku: [Errno 2] No such file or directory

🔍 UVID: Ova greška je posebno frustrirajuća jer:

  • Javlja se u biblioteci koju nisi pisao
  • Uzrok je u sistemskoj varijabli koju nisi postavio
  • Error poruka ne objašnjava pravi problem

Kreiranje SSL fix sistema

Sada ćemo kreirati sistem koji automatski rešava SSL probleme. Ovo je odličan primer defensive programming – predviđamo probleme pre nego što se jave!

Kreiraj novi fajl src/ssl_fix.py:

"""
SSL Fix za Windows i druge sisteme
Rešava česte probleme sa SSL certifikatima i environment varijablama

Ovaj modul automatski čisti problematične environment varijable
koje mogu da interferiraju sa HTTPS konekcijama.
"""

import os
import sys
import platform


def clean_ssl_environment():
    """
    Čisti problematične SSL environment varijable.
    
    Mnogi programi (Anaconda, Git, razni Python alati) postavljaju
    SSL environment varijable koje mogu da kvare rad drugih biblioteka.
    Ova funkcija ih uklanja da bi omogućila normalnu komunikaciju.
    """
    # Lista svih SSL varijabli koje mogu praviti probleme
    problematic_vars = [
        'SSL_CERT_FILE',
        'SSL_CERT_DIR',
        'REQUESTS_CA_BUNDLE',
        'CURL_CA_BUNDLE',
        'HTTPLIB2_CA_CERTS',
        'GRPC_DEFAULT_SSL_ROOTS_FILE_PATH'
    ]
    
    cleaned_vars = []
    
    for var in problematic_vars:
        if var in os.environ:
            # Sačuvaj vrednost za debug
            old_value = os.environ[var]
            del os.environ[var]
            cleaned_vars.append((var, old_value))
    
    # Prikaži šta je očišćeno (samo ako je bilo problema)
    if cleaned_vars:
        print("🧹 SSL Fix: Očišćene problematične environment varijable:")
        for var, value in cleaned_vars:
            print(f"   - {var} = {value[:50]}..." if len(value) > 50 else f"   - {var} = {value}")
        print("✅ SSL environment je sada čist!")
    
    return cleaned_vars


def setup_ssl_certificates():
    """
    Postavlja ispravnu putanju do SSL certifikata koristeći certifi.
    
    Certifi je Python paket koji sadrži pouzdan skup SSL certifikata.
    Ova funkcija osigurava da Python može da pronađe te certifikate.
    """
    try:
        import certifi
        
        # Postavi ispravnu putanju
        cert_path = certifi.where()
        os.environ['SSL_CERT_FILE'] = cert_path
        os.environ['REQUESTS_CA_BUNDLE'] = cert_path
        
        if os.path.exists(cert_path):
            print(f"✅ SSL certifikati postavljeni na: {cert_path}")
            return True
        else:
            print(f"⚠️ Certifi putanja postoji ali fajl nije pronađen: {cert_path}")
            return False
            
    except ImportError:
        print("⚠️ Certifi paket nije instaliran. Instaliraj sa: pip install certifi")
        return False


def diagnose_ssl_issues():
    """
    Dijagnostikuje potencijalne SSL probleme na sistemu.
    Korisno za debugging kada stvari ne rade.
    """
    print("\n🔍 SSL DIJAGNOZA")
    print("=" * 60)
    
    # Informacije o sistemu
    print(f"OS: {platform.system()} {platform.version()}")
    print(f"Python: {sys.version}")
    
    # Proveri SSL modul
    try:
        import ssl
        print(f"SSL verzija: {ssl.OPENSSL_VERSION}")
    except Exception as e:
        print(f"❌ SSL modul problem: {e}")
    
    # Proveri certifi
    try:
        import certifi
        print(f"Certifi lokacija: {certifi.where()}")
        print(f"Certifi verzija: {certifi.__version__}")
    except Exception as e:
        print(f"❌ Certifi problem: {e}")
    
    # Prikaži trenutne SSL environment varijable
    print("\nTrenutne SSL environment varijable:")
    ssl_vars = ['SSL_CERT_FILE', 'SSL_CERT_DIR', 'REQUESTS_CA_BUNDLE', 'CURL_CA_BUNDLE']
    for var in ssl_vars:
        value = os.environ.get(var)
        if value:
            print(f"  {var} = {value}")
        else:
            print(f"  {var} = Nije postavljena")
    
    print("=" * 60)


# Automatski počisti environment kada se modul učita
_cleaned = clean_ssl_environment()


# Test funkcionalnost
if __name__ == "__main__":
    print("🧪 Test SSL Fix modula")
    print("=" * 50)
    
    # Pokreni dijagnozu
    diagnose_ssl_issues()
    
    # Pokušaj da postaviš certifikate
    print("\n🔧 Postavljanje SSL certifikata...")
    if setup_ssl_certificates():
        print("✅ SSL certifikati uspešno postavljeni!")
    else:
        print("❌ Problem sa postavljanjem SSL certifikata")
    
    # Test sa OpenAI
    print("\n🧪 Test OpenAI konekcije...")
    try:
        from openai import OpenAI
        client = OpenAI(api_key="test-key")
        print("✅ OpenAI klijent kreiran bez SSL grešaka!")
    except Exception as e:
        print(f"❌ OpenAI greška: {type(e).__name__}: {e}")

💡 PRO TIP: Ovaj modul automatski čisti environment varijable čim se učita. To znači da samo trebamo da ga importujemo na početku našeg OpenAI servisa!

Kreiranje univerzalnog AI service sistema

Sada ćemo kreirati elegantan sistem koji automatski bira pravi servis na osnovu tvoje konfiguracije. Ovo je profesionalni pristup koji omogućava lako dodavanje novih AI servisa u budućnosti!

📚 NIVO 1 – Osnovno objašnjenje

Umesto da svuda po kodu proveravamo koji servis koristimo, napravićemo “fabriku” koja automatski kreira pravi servis. To je kao da imaš univerzalni daljinski koji radi sa bilo kojim TV-om – pritisneš dugme i on zna kako da komunicira sa tvojim modelom.

🚀 NIVO 2 – Dublje razumevanje

Factory pattern je dizajn obrazac koji kreira objekte bez specificiranja njihove tačne klase. Naš AIServiceFactory će čitati AI_PROVIDER iz konfiguracije i vratiti odgovarajući servis sa istim interfejsom.

📊 DIJAGRAM: Arhitektura univerzalnog AI sistema

                    [main.py]
                        |
                        v
                [AIServiceFactory]
                   /         \
                  /           \
    [AI_PROVIDER=openai]  [AI_PROVIDER=gemini]
                /                    \
               v                      v
        [OpenAIService]         [GeminiService]
               |                      |
               v                      v
        [OpenAI API]         [Gemini API]

Prvo, kreiraj baznu klasu u src/ai_services/base_service.py:

"""
Bazna klasa za sve AI servise
Definiše interfejs koji svi servisi moraju implementirati
"""

from abc import ABC, abstractmethod
from typing import Optional, List, Dict


class BaseAIService(ABC):
    """Apstraktna bazna klasa za AI servise."""
    
    @abstractmethod
    def pozovi_ai(self, poruka: str, system_prompt: Optional[str] = None) -> str:
        """
        Šalje poruku AI-ju i vraća odgovor.
        
        Args:
            poruka: Korisnikova poruka/pitanje
            system_prompt: Opcioni system prompt za definisanje ponašanja
            
        Returns:
            AI odgovor kao string
        """
        pass
    
    @abstractmethod
    def pozovi_sa_istorijom(self, messages: List[Dict[str, str]]) -> str:
        """
        Šalje celu istoriju razgovora AI-ju.
        
        Args:
            messages: Lista poruka sa 'role' i 'content' ključevima
            
        Returns:
            AI odgovor kao string
        """
        pass
    
    def test_konekcija(self) -> bool:
        """
        Testira da li servis može da se poveže sa API-jem.
        
        Returns:
            True ako je konekcija uspešna, False inače
        """
        try:
            response = self.pozovi_ai("Reci 'zdravo' na srpskom.")
            return len(response) > 0
        except Exception as e:
            print(f"❌ Test konekcije neuspešan: {e}")
            return False

🎈 ZABAVNA ČINJENICA: Abstract Base Classes (ABC) u Python-u su inspirisane Java interfejsima. Guido van Rossum ih je dodao u Python 2.6 nakon što su programeri godinama tražili način da formalno definišu “ugovore” između klasa!

Sada kreiraj src/ai_services/openai_service.py sa SSL fix-om:

"""
OpenAI servis za Učitelja Vasu
Implementira BaseAIService interfejs

NAPOMENA: Ovaj modul automatski rešava česte SSL probleme
koji se javljaju na Windows sistemima sa OpenAI bibliotekom.
"""

# KRITIČNO: Učitaj SSL fix PRE bilo čega drugog!
# Ovo mora biti prva linija da bi počistilo environment varijable
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

# Importuj i primeni SSL fix
try:
    import ssl_fix
except ImportError:
    print("⚠️ SSL fix modul nije pronađen, nastavljam bez njega...")

# Sada možemo bezbedno da importujemo ostale module
from typing import Optional, List, Dict
from openai import OpenAI
from utils.config import Config
from .base_service import BaseAIService


class OpenAIService(BaseAIService):
    """Servis za komunikaciju sa OpenAI API-jem."""
    
    def __init__(self):
        """Inicijalizuje OpenAI klijenta sa API ključem iz Config-a."""
        if not Config.OPENAI_API_KEY:
            raise ValueError("OpenAI API ključ nije postavljen!")
        
        try:
            # Kreiraj klijent
            self.client = OpenAI(api_key=Config.OPENAI_API_KEY)
            self.model = Config.OPENAI_MODEL or "gpt-3.5-turbo"
            self.max_tokens = Config.OPENAI_MAX_TOKENS
            self.temperature = Config.OPENAI_TEMPERATURE
            
            print(f"✅ OpenAI servis inicijalizovan (model: {self.model})")
            
        except Exception as e:
            # Dodatna dijagnoza ako i dalje ima problema
            if "SSL" in str(e) or "certificate" in str(e).lower() or "[Errno 2]" in str(e):
                print("❌ SSL problem detektovan!")
                print("   Pokušaj ove korake:")
                print("   1. Restartuj PyCharm/terminal")
                print("   2. Proveri sistemske environment varijable")
                print("   3. Koristi Gemini kao alternativu")
            raise
        
    def pozovi_ai(self, poruka: str, system_prompt: Optional[str] = None) -> str:
        """
        Šalje poruku AI-ju i vraća odgovor.
        
        Args:
            poruka: Korisnikova poruka/pitanje
            system_prompt: Opcioni system prompt za definisanje ponašanja
            
        Returns:
            AI odgovor kao string
        """
        try:
            # Pripremi poruke
            messages = []
            
            if system_prompt:
                messages.append({
                    "role": "system",
                    "content": system_prompt
                })
            
            messages.append({
                "role": "user",
                "content": poruka
            })
            
            # Pozovi API
            response = self.client.chat.completions.create(
                model=self.model,
                messages=messages,
                max_tokens=self.max_tokens,
                temperature=self.temperature
            )
            
            return response.choices[0].message.content.strip()
            
        except Exception as e:
            # Detaljno rukovanje greškama
            error_msg = f"Greška pri komunikaciji sa OpenAI: {str(e)}"
            print(f"❌ {error_msg}")
            
            # Specifične poruke za različite greške
            if "api_key" in str(e).lower():
                return "Izgleda da OpenAI API ključ nije valjan. Proveri podešavanja."
            elif "rate_limit" in str(e).lower():
                return "Previše zahteva ka OpenAI. Sačekaj malo pa pokušaj ponovo."
            elif "insufficient_quota" in str(e).lower():
                return "Nemaš dovoljno OpenAI kredita. Proveri svoj balans ili prebaci se na Gemini (AI_PROVIDER=gemini)."
            elif "connection" in str(e).lower():
                return "Problem sa internet konekcijom. Proveri da li si povezan."
            elif "SSL" in str(e) or "certificate" in str(e).lower():
                return "SSL problem - restartuj program ili koristi Gemini servis."
            else:
                return "Ups! Nešto je pošlo po zlu sa OpenAI. Pokušaj ponovo za koji trenutak."
    
    def pozovi_sa_istorijom(self, messages: List[Dict[str, str]]) -> str:
        """
        Šalje celu istoriju razgovora AI-ju.
        
        Args:
            messages: Lista poruka sa 'role' i 'content' ključevima
            
        Returns:
            AI odgovor kao string
        """
        try:
            response = self.client.chat.completions.create(
                model=self.model,
                messages=messages,
                max_tokens=self.max_tokens,
                temperature=self.temperature
            )
            
            return response.choices[0].message.content.strip()
            
        except Exception as e:
            print(f"❌ OpenAI greška: {str(e)}")
            return "Izvini, trenutno ne mogu da odgovorim preko OpenAI. Pokušaj ponovo."


# Test funkcionalnosti
if __name__ == "__main__":
    print("🧪 Test OpenAI servisa")
    print("=" * 50)
    
    # Prvo pokreni SSL dijagnozu
    print("\n🔍 SSL dijagnoza:")
    import ssl_fix
    ssl_fix.diagnose_ssl_issues()
    
    # Proveri da li je OpenAI konfigurisan
    if Config.AI_PROVIDER != 'openai':
        print(f"\n⚠️  AI_PROVIDER je postavljen na '{Config.AI_PROVIDER}'")
        print("   Promeni na 'openai' u .env fajlu za ovaj test")
    elif not Config.OPENAI_API_KEY:
        print("\n❌ OpenAI API ključ nije postavljen!")
    else:
        try:
            # Kreiraj servis
            print("\n🏗️ Kreiram OpenAI servis...")
            service = OpenAIService()
            
            # Test jednostavnog poziva
            print("\n📤 Test pitanje: 'Šta je Python?'")
            odgovor = service.pozovi_ai("Šta je Python u jednoj rečenici?")
            print(f"📥 Odgovor: {odgovor}")
            
            # Test konekcije
            print("\n🔌 Test konekcije...")
            if service.test_konekcija():
                print("✅ Konekcija sa OpenAI API-jem uspešna!")
            
        except Exception as e:
            print(f"\n❌ Test neuspešan: {type(e).__name__}: {e}")
            
            if "[Errno 2]" in str(e):
                print("\n💡 REŠENJE:")
                print("1. SSL environment varijable su problematične")
                print("2. Restartuj PyCharm/terminal")
                print("3. Ili koristi Gemini umesto OpenAI")

🔍 UVID: Primetićeš da importujemo ssl_fix na samom početku fajla, pre svega ostalog. Ovo je kritično jer mora da očisti environment varijable pre nego što OpenAI biblioteka pokuša da ih koristi!

💡 PRO TIP: Koristimo try/except blok oko SSL fix importa tako da kod radi čak i ako neko zaboravi da kreira ssl_fix.py fajl.

Sada kreiraj src/ai_services/gemini_service.py:

"""
Google Gemini servis za Učitelja Vasu
Implementira BaseAIService interfejs

NAPOMENA: Gemini obično ne zahteva SSL fix kao OpenAI,
ali importujemo ga za svaki slučaj.
"""

import time
from typing import Optional, List, Dict
import sys
import os

# Dodaj parent folder u path
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

# SSL fix (iako Gemini obično ne zahteva)
try:
    import ssl_fix
except ImportError:
    pass  # Gemini obično radi bez SSL fix-a

import google.generativeai as genai
from utils.config import Config
from .base_service import BaseAIService


class GeminiService(BaseAIService):
    """Servis za komunikaciju sa Google Gemini API-jem."""
    
    def __init__(self):
        """Inicijalizuje Gemini klijenta sa API ključem iz Config-a."""
        if not Config.GEMINI_API_KEY:
            raise ValueError("Gemini API ključ nije postavljen!")
        
        # Konfiguriši Gemini
        genai.configure(api_key=Config.GEMINI_API_KEY)
        
        # Kreiraj model
        self.model = genai.GenerativeModel(Config.GEMINI_MODEL)
        self.max_tokens = Config.GEMINI_MAX_TOKENS
        self.temperature = Config.GEMINI_TEMPERATURE
        
        # Gemini koristi drugačije nazive za parametre
        self.generation_config = genai.GenerationConfig(
            max_output_tokens=self.max_tokens,
            temperature=self.temperature
        )
        
        print(f"✅ Gemini servis inicijalizovan (model: {Config.GEMINI_MODEL})")
        
    def pozovi_ai(self, poruka: str, system_prompt: Optional[str] = None) -> str:
        """
        Šalje poruku AI-ju i vraća odgovor.
        
        Args:
            poruka: Korisnikova poruka/pitanje
            system_prompt: Opcioni system prompt za definisanje ponašanja
            
        Returns:
            AI odgovor kao string
        """
        try:
            # Gemini kombinuje system prompt i korisničku poruku
            full_prompt = poruka
            if system_prompt:
                full_prompt = f"{system_prompt}\n\nKorisnik: {poruka}\nAsistent:"
            
            # Generiši odgovor
            response = self.model.generate_content(
                full_prompt,
                generation_config=self.generation_config
            )
            
            # Vrati odgovor
            return response.text.strip()
            
        except Exception as e:
            # Rukovanje greškama
            error_msg = f"Greška pri komunikaciji sa Gemini: {str(e)}"
            print(f"❌ {error_msg}")
            
            # Vraćamo user-friendly poruku
            if "api_key" in str(e).lower():
                return "Izgleda da Gemini API ključ nije valjan. Proveri podešavanja."
            elif "rate_limit" in str(e).lower() or "429" in str(e):
                return "Previše zahteva ka Gemini. Sačekaj malo pa pokušaj ponovo."
            elif "safety" in str(e).lower():
                return "Gemini je blokirao odgovor iz sigurnosnih razloga. Pokušaj sa drugim pitanjem."
            elif "connection" in str(e).lower():
                return "Problem sa internet konekcijom. Proveri da li si povezan."
            else:
                return "Ups! Nešto je pošlo po zlu sa Gemini. Pokušaj ponovo za koji trenutak."
    
    def pozovi_sa_istorijom(self, messages: List[Dict[str, str]]) -> str:
        """
        Šalje celu istoriju razgovora AI-ju.
        
        Args:
            messages: Lista poruka sa 'role' i 'content' ključevima
            
        Returns:
            AI odgovor kao string
        """
        try:
            # Konvertuj poruke u Gemini format
            chat = self.model.start_chat(history=[])
            
            # Rekonstruiši razgovor
            full_conversation = ""
            system_prompt = ""
            
            for msg in messages:
                if msg['role'] == 'system':
                    system_prompt = msg['content']
                elif msg['role'] == 'user':
                    if full_conversation:
                        full_conversation += "\n\n"
                    full_conversation += f"Korisnik: {msg['content']}"
                elif msg['role'] == 'assistant':
                    full_conversation += f"\nAsistent: {msg['content']}"
            
            # Dodaj system prompt na početak ako postoji
            if system_prompt:
                full_prompt = f"{system_prompt}\n\n{full_conversation}\nAsistent:"
            else:
                full_prompt = f"{full_conversation}\nAsistent:"
            
            # Generiši odgovor
            response = self.model.generate_content(
                full_prompt,
                generation_config=self.generation_config
            )
            
            return response.text.strip()
            
        except Exception as e:
            print(f"❌ Gemini greška: {str(e)}")
            return "Izvini, trenutno ne mogu da odgovorim preko Gemini. Pokušaj ponovo."


# Test funkcionalnosti
if __name__ == "__main__":
    print("🧪 Test Gemini servisa")
    print("=" * 50)
    
    # Proveri da li je Gemini konfigurisan
    if Config.AI_PROVIDER != 'gemini':
        print(f"⚠️  AI_PROVIDER je postavljen na '{Config.AI_PROVIDER}'")
        print("   Promeni na 'gemini' u .env fajlu za ovaj test")
    elif not Config.GEMINI_API_KEY:
        print("❌ Gemini API ključ nije postavljen!")
    else:
        try:
            # Kreiraj servis
            service = GeminiService()
            
            # Test jednostavnog poziva
            print("\n📤 Test pitanje: 'Šta je Python?'")
            odgovor = service.pozovi_ai("Šta je Python u jednoj rečenici?")
            print(f"📥 Odgovor: {odgovor}")
            
            # Test konekcije
            print("\n🔌 Test konekcije...")
            if service.test_konekcija():
                print("✅ Konekcija sa Gemini API-jem uspešna!")
            
            # Malo pauze zbog rate limit-a
            time.sleep(1)
            
        except Exception as e:
            print(f"❌ Test neuspešan: {e}")

🔍 UVID: Primetićeš da Gemini API ima drugačiji pristup istoriji razgovora. Dok OpenAI prima listu poruka sa ulogama, Gemini preferira kontinuirani tekst. Ovo je česta situacija u multi-provider sistemima – svaki API ima svoje quirks koje moramo handle-ovati.

Kreiranje AI Service Factory

Sada dolazi najelegantniji deo – factory koji automatski bira pravi servis!

📚 NIVO 1 – Osnovno objašnjenje

Factory je kao pametni prekidač. Gleda u .env fajl, vidi koji si servis izabrao (AI_PROVIDER), i automatski kreira pravi objekat. Ti samo kažeš “daj mi AI servis” i on se pobrine za sve ostalo!

🚀 NIVO 2 – Dublje razumevanje

Factory pattern omogućava kreiranje objekata bez eksplicitnog navođenja klase. Ovo čini kod fleksibilnijim i lakšim za održavanje. Kada sutra dodamo Claude ili neki drugi servis, samo ćemo dodati novu klasu i jedan red u factory.

Kreiraj src/ai_services/ai_factory.py:

"""
AI Service Factory
Automatski kreira pravi AI servis na osnovu konfiguracije
"""

import sys
import os
from typing import Optional

# Dodaj parent folder u path
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from utils.config import Config
from .base_service import BaseAIService
from .openai_service import OpenAIService
from .gemini_service import GeminiService


class AIServiceFactory:
    """Factory klasa za kreiranje AI servisa."""
    
    _instance: Optional[BaseAIService] = None
    
    @classmethod
    def get_service(cls, force_new: bool = False) -> BaseAIService:
        """
        Vraća instancu AI servisa na osnovu konfiguracije.
        Koristi Singleton pattern za efikasnost.
        
        Args:
            force_new: Ako je True, kreira novu instancu
            
        Returns:
            Instanca AI servisa (OpenAI ili Gemini)
            
        Raises:
            ValueError: Ako je AI_PROVIDER nepoznat
        """
        # Ako već imamo instancu i ne tražimo novu, vrati postojeću
        if cls._instance is not None and not force_new:
            return cls._instance
        
        # Kreiraj novu instancu na osnovu providera
        provider = Config.AI_PROVIDER.lower()
        
        print(f"\n🏭 AI Factory: Kreiram {provider.upper()} servis...")
        
        if provider == 'openai':
            cls._instance = OpenAIService()
        elif provider == 'gemini':
            cls._instance = GeminiService()
        else:
            raise ValueError(
                f"Nepoznat AI provider: {provider}. "
                f"Dozvoljeni: 'openai', 'gemini'"
            )
        
        print(f"✅ {provider.upper()} servis uspešno kreiran!\n")
        return cls._instance
    
    @classmethod
    def reset(cls):
        """Resetuje factory (korisno za testiranje)."""
        cls._instance = None
        print("🔄 AI Factory resetovan")
    
    @classmethod
    def switch_provider(cls, new_provider: str) -> BaseAIService:
        """
        Prebacuje na drugi provider i vraća novi servis.
        
        Args:
            new_provider: 'openai' ili 'gemini'
            
        Returns:
            Nova instanca AI servisa
        """
        # Promeni provider u konfiguraciji
        Config.AI_PROVIDER = new_provider
        
        # Resetuj postojeću instancu
        cls.reset()
        
        # Kreiraj i vrati novu
        return cls.get_service()


# Test funkcionalnosti
if __name__ == "__main__":
    print("🧪 Test AI Factory")
    print("=" * 50)
    
    try:
        # Test 1: Kreiraj servis na osnovu trenutne konfiguracije
        print(f"Trenutni provider: {Config.AI_PROVIDER}")
        service1 = AIServiceFactory.get_service()
        
        # Test 2: Proveri Singleton
        service2 = AIServiceFactory.get_service()
        print(f"\n🔍 Singleton test: service1 == service2? {service1 is service2}")
        
        # Test 3: Test poziva
        print("\n📤 Test poziv...")
        response = service1.pozovi_ai("Reci 'Zdravo' na srpskom")
        print(f"📥 Odgovor: {response}")
        
        # Test 4: Prebacivanje providera (samo ako imaš oba ključa)
        if Config.OPENAI_API_KEY and Config.GEMINI_API_KEY:
            drugi_provider = 'gemini' if Config.AI_PROVIDER == 'openai' else 'openai'
            print(f"\n🔄 Prebacujem na {drugi_provider}...")
            
            service3 = AIServiceFactory.switch_provider(drugi_provider)
            response2 = service3.pozovi_ai("Reci 'Cao' na srpskom")
            print(f"📥 Odgovor od {drugi_provider}: {response2}")
            
            # Vrati na originalni
            AIServiceFactory.switch_provider(Config.AI_PROVIDER)
        
    except Exception as e:
        print(f"❌ Factory test neuspešan: {e}")

💡 PRO TIP: Factory koristi Singleton pattern – kreira samo jednu instancu servisa i ponovo je koristi. Ovo štedi resurse jer ne moramo svaki put da uspostavljamo novu konekciju. Ako eksplicitno trebaš novu instancu, koristi get_service(force_new=True).

📊 DIJAGRAM: Tok kreiranja AI servisa

[main.py poziva]
       |
       v
[AIServiceFactory.get_service()]
       |
       v
[Čita Config.AI_PROVIDER]
       |
    /     \
   v       v
openai?  gemini?
   |       |
   v       v
[OpenAI] [Gemini]
Service  Service

🌐 GOOGLE CONNECTION: Ovaj factory pattern je identičan onome što Google koristi interno za svoje servise. Google Cloud SDK koristi sličan pristup gde gcloud config set project menja aktivni projekat, a svi naredni pozivi automatski koriste taj projekat.

Integracija sa Učiteljem Vasom

Sada integrišemo naš univerzalni sistem sa glavnim programom!

📚 NIVO 1 – Osnovno objašnjenje

Učitelj Vasa više ne mora da zna koji AI servis koristi. On samo traži “AI servis” od factory-ja, i dobija pravi. To je kao kada tražiš “kafu” u kafiću – ne moraš da specificiraš mašinu, barista zna koju da koristi.

🚀 NIVO 2 – Dublje razumevanje

Ova abstrakcija čini kod održivijim. Glavni program ne zavisi od konkretnog AI servisa, već od interfejsa koji svi implementiraju. Sutra možemo dodati Claude, GPT-5, ili bilo koji drugi servis bez menjanja main.py!

Ažuriraj src/vasa_core.py (ostaje isti kao u originalnoj lekciji):

"""
Učitelj Vasa - Osnovni modul
Ovaj modul sadrži osnovne funkcionalnosti AI asistenta Vase.
"""

# Osnovne informacije o Vasi
VASA_INFO = {
    "ime": "Učitelj Vasa",
    "verzija": "0.1.0",
    "opis": "AI asistent za pomoć u učenju programiranja",
    "autor": "Student AISYNTHESIS akademije"
}

# Vasina ličnost - ovo će biti system prompt
VASA_LICNOST = """Ti si Učitelj Vasa, ljubazni i strpljivi AI asistent koji pomaže ljudima da nauče programiranje.

Tvoje karakteristike:
- Uvek si pozitivan i ohrabrujući
- Objašnjavaš koncepte jednostavno, korak po korak
- Koristiš analogije iz svakodnevnog života
- Daješ praktične primere koda
- Nikad ne omalovažavaš pitanja, ma koliko jednostavna bila
- Govoriš na srpskom jeziku, ali programske termine ostavljaš na engleskom

Kada objašnjavaš:
1. Prvo daj kratak, jasan odgovor
2. Zatim objasni detaljnije ako je potrebno
3. Uvek ponudi primer ako je relevantan
4. Završi sa ohrabrenjem ili predlogom šta dalje učiti

Zapamti: Tvoj cilj je da učenje programiranja učiniš pristupačnim i zabavnim!"""


def pozdrav():
    """Vraća pozdravnu poruku od Učitelja Vase."""
    return "Zdravo! Ja sam Učitelj Vasa, tvoj AI asistent za učenje programiranja! 🎓"


def predstavi_se():
    """Vraća detaljne informacije o Učitelju Vasi."""
    poruka = f"""
{VASA_INFO['ime']} - Verzija {VASA_INFO['verzija']}
{'=' * 50}
{VASA_INFO['opis']}

Stvorio: {VASA_INFO['autor']}
    """
    return poruka.strip()


def glavni_meni():
    """Prikazuje glavni meni sa opcijama."""
    meni = """
Šta želiš da uradiš?
1. Pozdravi me
2. Predstavi se
3. Postavi pitanje Učitelju Vasi
4. Razgovaraj sa Vasom (kontinuirani mod)
5. Proveri AI status
6. Promeni AI servis (samo ako imaš oba ključa)
7. Izađi

Tvoj izbor: """
    return meni


# Test funkcionalnost ostaje ista
if __name__ == "__main__":
    print("=" * 50)
    print("Dobrodošao u test mode za vasa_core.py!")
    print("=" * 50)
    
    print("\nTest funkcije pozdrav():")
    print(pozdrav())
    
    print("\nTest funkcije predstavi_se():")
    print(predstavi_se())
    
    print("\nTest funkcije glavni_meni():")
    print(glavni_meni())

Praktična implementacija

Ažuriranje glavnog programa

Sada ažurirajmo main.py da koristi naš univerzalni sistem:

"""
Glavni program za Učitelja Vasu
Univerzalna podrška za OpenAI i Gemini
"""

# Dodaj src folder u Python path
import sys
import os
sys.path.append(os.path.dirname(os.path.abspath(__file__)))

from vasa_core import pozdrav, predstavi_se, glavni_meni, VASA_LICNOST
from ai_simulator import simuliraj_ai_odgovor
from utils.config import Config
from ai_services.ai_factory import AIServiceFactory
from ai_services.base_service import BaseAIService
from typing import Optional


# Globalna varijabla za AI servis
ai_service: Optional[BaseAIService] = None


def inicijalizuj_ai_servis():
    """Pokušava da kreira AI servis koristeći factory."""
    global ai_service
    
    try:
        if Config.validate():
            ai_service = AIServiceFactory.get_service()
            print(f"✅ {Config.AI_PROVIDER.upper()} servis uspešno pokrenut!")
            return True
    except Exception as e:
        print(f"⚠️ AI servis nije dostupan: {e}")
        print("Koristićemo simulaciju umesto pravog AI-ja.")
    
    return False


def postavi_pitanje_vasi(pitanje: str) -> str:
    """Postavlja pitanje Vasi koristeći AI ili simulaciju."""
    if ai_service:
        # Koristi pravi AI
        print(f"🤖 [Koristim {Config.AI_PROVIDER.upper()} AI model...]")
        return ai_service.pozovi_ai(pitanje, system_prompt=VASA_LICNOST)
    else:
        # Fallback na simulaciju
        print("🎭 [Koristim simulaciju...]")
        return simuliraj_ai_odgovor(pitanje)


def kontinuirani_razgovor():
    """Omogućava kontinuirani razgovor sa Vasom."""
    print("\n💬 KONTINUIRANI RAZGOVOR SA UČITELJEM VASOM")
    print("=" * 50)
    print("Možeš postavljati pitanja jedno za drugim.")
    print("Ukucaj 'kraj' ili 'exit' za povratak u glavni meni.\n")
    
    # Istorija razgovora za kontekst
    istorija = []
    
    # Dodaj system prompt u istoriju
    if ai_service:
        istorija.append({
            "role": "system",
            "content": VASA_LICNOST
        })
    
    while True:
        # Korisnikov unos
        pitanje = input("\n👤 Ti: ").strip()
        
        # Proveri da li korisnik želi da izađe
        if pitanje.lower() in ['kraj', 'exit', 'izlaz', 'nazad']:
            print("\n👋 Vraćam te u glavni meni...")
            break
        
        if not pitanje:
            print("❌ Molim te ukucaj pitanje.")
            continue
        
        # Dodaj pitanje u istoriju
        istorija.append({
            "role": "user",
            "content": pitanje
        })
        
        # Dobij odgovor
        if ai_service and len(istorija) > 1:  # Ima bar system + user poruku
            print("\n🤖 Učitelj Vasa: ", end="", flush=True)
            odgovor = ai_service.pozovi_sa_istorijom(istorija)
        else:
            print("\n🎭 Učitelj Vasa: ", end="", flush=True)
            odgovor = simuliraj_ai_odgovor(pitanje)
        
        print(odgovor)
        
        # Dodaj odgovor u istoriju
        istorija.append({
            "role": "assistant",
            "content": odgovor
        })
        
        # Ograniči istoriju na poslednjih 10 poruka + system prompt
        if len(istorija) > 11:
            istorija = [istorija[0]] + istorija[-10:]


def promeni_ai_servis():
    """Omogućava promenu AI servisa tokom rada."""
    print("\n🔄 PROMENA AI SERVISA")
    print("=" * 50)
    
    # Proveri da li su oba ključa dostupna
    if not (Config.OPENAI_API_KEY and Config.GEMINI_API_KEY):
        print("❌ Ne možeš menjati servis jer nemaš oba API ključa.")
        if not Config.OPENAI_API_KEY:
            print("   - OpenAI ključ nedostaje")
        if not Config.GEMINI_API_KEY:
            print("   - Gemini ključ nedostaje")
        return
    
    # Prikaži trenutni i dostupne
    print(f"Trenutno koristiš: {Config.AI_PROVIDER.upper()}")
    drugi = 'gemini' if Config.AI_PROVIDER == 'openai' else 'openai'
    
    print(f"\nDa li želiš da pređeš na {drugi.upper()}? (da/ne): ", end="")
    odgovor = input().strip().lower()
    
    if odgovor in ['da', 'd', 'yes', 'y']:
        global ai_service
        try:
            ai_service = AIServiceFactory.switch_provider(drugi)
            print(f"✅ Uspešno prebačeno na {drugi.upper()}!")
        except Exception as e:
            print(f"❌ Greška pri prebacivanju: {e}")


def pokreni_vasu():
    """Pokreće glavnu petlju programa Učitelj Vasa."""
    # Inicijalizuj AI servis
    ai_dostupan = inicijalizuj_ai_servis()
    
    print("\n" + "🎓" * 25)
    print(pozdrav())
    if ai_dostupan:
        provider_info = {
            'openai': "✨ Povezan sa OpenAI GPT - najpoznatiji AI model!",
            'gemini': "✨ Povezan sa Google Gemini - moćan i besplatan!"
        }
        print(provider_info.get(Config.AI_PROVIDER.lower(), "✨ AI je spreman!"))
    else:
        print("📚 Radim u offline modu sa simulacijom.")
    print("🎓" * 25 + "\n")
    
    # Glavna petlja programa
    while True:
        print(glavni_meni())
        izbor = input().strip()
        
        if izbor == "1":
            print("\n" + pozdrav() + "\n")
            
        elif izbor == "2":
            print("\n" + predstavi_se() + "\n")
            
        elif izbor == "3":
            print("\n💭 Postavi mi bilo koje pitanje o programiranju:")
            pitanje = input("👤 Ti: ").strip()
            if pitanje:
                print("\n🤖 Učitelj Vasa: ", end="", flush=True)
                odgovor = postavi_pitanje_vasi(pitanje)
                print(odgovor)
            else:
                print("\n❌ Nisi uneo pitanje.")
                
        elif izbor == "4":
            kontinuirani_razgovor()
            
        elif izbor == "5":
            print("\n" + "=" * 50)
            print("📊 STATUS AI SERVISA")
            print("=" * 50)
            if ai_service:
                print(f"✅ Aktivan servis: {Config.AI_PROVIDER.upper()}")
                print(f"🤖 Model: {Config.get_model()}")
                print(f"🔥 Temperature: {Config.OPENAI_TEMPERATURE if Config.AI_PROVIDER == 'openai' else Config.GEMINI_TEMPERATURE}")
                print(f"📝 Max tokena: {Config.OPENAI_MAX_TOKENS if Config.AI_PROVIDER == 'openai' else Config.GEMINI_MAX_TOKENS}")
                
                # Troškovi
                if Config.AI_PROVIDER == 'openai':
                    print("\n💰 Troškovi: ~$0.002 po 1K tokena")
                else:
                    print("\n💰 Troškovi: BESPLATNO! (do 15M tokena dnevno)")
            else:
                print("❌ AI servis nije aktivan")
                print("📚 Koristi se simulacija")
            print("=" * 50)
            
        elif izbor == "6":
            promeni_ai_servis()
            
        elif izbor == "7":
            print("\nHvala što si koristio Učitelja Vasu! ")
            print("Nastavi sa učenjem i ne zaboravi - svaki ekspert je nekad bio početnik! 🌟")
            break
            
        else:
            print("\n❌ Nepoznata opcija. Pokušaj ponovo.\n")
    
    print("\nProgram završen. Srećno sa programiranjem! 👋")


if __name__ == "__main__":
    pokreni_vasu()

🔍 UVID: Kontinuirani razgovor koristi istu istorija listu bez obzira na AI servis. Ovo je moć apstrakcije – oba servisa implementiraju isti interfejs, pa glavni kod ne mora da brine o detaljima implementacije.

SAVET ZA OPTIMIZACIJU: Funkcija inicijalizuj_ai_servis() se poziva samo jednom. Factory interno koristi Singleton pattern, što znači da će svaki sledeći poziv get_service() vratiti istu instancu. Ovo značajno ubrzava program jer ne mora svaki put da kreira novu konekciju.

Konfiguracija .env fajla

Pre nego što testirate, ažurirajte .env fajl:

Za OpenAI korisnike:

# AI Provider
AI_PROVIDER=openai

# OpenAI Configuration
OPENAI_API_KEY=sk-proj-tvoj-pravi-kljuc-ovde
OPENAI_MODEL=gpt-3.5-turbo
OPENAI_MAX_TOKENS=150
OPENAI_TEMPERATURE=0.7

Za Gemini korisnike:

# AI Provider
AI_PROVIDER=gemini

# Gemini Configuration
GEMINI_API_KEY=AIzaSy-tvoj-pravi-kljuc-ovde
GEMINI_MODEL=gemini-1.5-flash
GEMINI_MAX_TOKENS=150
GEMINI_TEMPERATURE=0.7

📊 MODELI I KARAKTERISTIKE:

OpenAI modeli (jun 2025):

  • gpt-3.5-turbo: Brz i efikasan za većinu zadataka
  • gpt-4: Najnapredniji model (skuplji)
  • gpt-4-turbo: Balans između brzine i kvaliteta

Gemini modeli (jun 2025):

  • gemini-1.5-flash: Brz i efikasan, odličan za većinu zadataka
  • gemini-1.5-pro: Moćniji model za zahtevnije zadatke
  • gemini-2.0-flash: Najnoviji eksperimentalni model

Testiranje univerzalnog sistema

Hajde da testiramo da li sve radi sa oba servisa! Pokreni program:

cd src
python main.py

Kada se program pokrene, trebalo bi da vidiš:

  • ✅ [OPENAI ili GEMINI] servis uspešno pokrenut!
  • ✨ Povezan sa [odgovarajućim opisom]

Testiraj sa različitim pitanjima:

  1. “Šta je promenljiva u Python-u?”
  2. “Kako da napravim petlju koja broji od 1 do 10?”
  3. “Objasni mi razliku između liste i tuple-a”

🔄 VEŽBA:

  1. Postavi isto pitanje sa OpenAI
  2. Promeni na Gemini (opcija 6 u meniju)
  3. Postavi isto pitanje ponovo
  4. Uporedi odgovore – svaki AI ima svoj stil!

Česte greške i rešenja

Univerzalne greške:

GREŠKA: No module named 'openai' ili No module named 'google'
💡 REŠENJE: Instaliraj obe biblioteke:

pip install --upgrade openai google-generativeai

GREŠKA: ValueError: Nepoznat AI provider
💡 REŠENJE: Proveri AI_PROVIDER u .env fajlu. Mora biti tačno ‘openai’ ili ‘gemini’ (bez navodnika)

GREŠKA: Program se ruši pri promeni servisa
💡 REŠENJE: Factory možda kešira staru instancu. Restartuj program nakon promene .env fajla

OpenAI specifične:

GREŠKA: [Errno 2] No such file or directory
💡 REŠENJE: Ovo je SSL problem! Naš ssl_fix.py automatski rešava ovo, ali ako i dalje imaš problema:

  1. Restartuj PyCharm/terminal
  2. Proveri da li je ssl_fix.py u src folderu
  3. Proveri sistemske environment varijable
  4. Kao poslednje rešenje, koristi Gemini

GREŠKA: openai.AuthenticationError: Error code: 401
💡 REŠENJE: OpenAI API ključ nije valjan. Proveri:

  • Da li si dodao kredit ($5) na svoj nalog
  • Da li si pravilno kopirao ključ
  • Da li ključ počinje sa sk-

🔬 DETALJNIJE: Greška 401 – AuthenticationError
Kada pozoveš OpenAI, biblioteka šalje HTTP zahtev sa tvojim ključem u header-u. Server proverava ključ u svojoj bazi i ako ne pronađe valjan ključ, vraća 401. Naš error handler prepoznaje ovu grešku i predlaže prebacivanje na Gemini.

GREŠKA: insufficient_quota
💡 REŠENJE: Nemaš više OpenAI kredita. Opcije:

  • Dodaj još $5 kredita
  • Prebaci se na Gemini (besplatno!)

Gemini specifične:

GREŠKA: google.api_core.exceptions.InvalidArgument
💡 REŠENJE: Gemini model možda nije dostupan. Proveri da koristiš podržani model

GREŠKA: Rate limit exceeded
💡 REŠENJE: Gemini ima limit od 60 zahteva po minuti. Naš Config već ima RETRY_DELAY, ali možeš ga povećati

GREŠKA: Safety ratings caused content to be blocked
💡 REŠENJE: Gemini ima strože sigurnosne filtre. Preformuliši pitanje ili prebaci na OpenAI

SSL/Environment specifične greške:

GREŠKA: SSL greške uprkos ssl_fix.py
💡 REŠENJE:

  1. Pokreni python ssl_fix.py direktno da vidiš dijagnozu
  2. Proveri sistemske environment varijable u Control Panel
  3. Instaliraj/reinstaliraj certifi: pip install --upgrade certifi
  4. Kao krajnje rešenje, koristi samo Gemini

Proveri svoje razumevanje

[NIVO 1]:

  1. Šta se dešava kada pozoveš AIServiceFactory.get_service()?
  2. Zašto oba servisa implementiraju istu BaseAIService klasu?
  3. Kako program zna koji AI servis da koristi?
  4. Šta su environment varijable i zašto mogu da prave probleme?

[NIVO 2]:

  1. Šta je prednost Singleton pattern-a u našem factory-ju?
  2. Kako bi dodao podršku za Claude AI kao treći servis?
  3. Zašto SSL problemi utiču na OpenAI ali ne na Gemini?
  4. Kako bi napisao dijagnostički alat za pronalaženje environment problema?

🤔 MINI-KVIZ:

  1. (Nivo 1) Ako promeniš AI_PROVIDER u .env fajlu dok program radi, da li će se promeniti servis?
  2. (Nivo 1) Zašto Windows ne prepoznaje grep komandu?
  3. (Nivo 1) Šta radi ssl_fix.py modul?
  4. (Nivo 2) Kako bi implementirao automatski fallback – ako OpenAI ne radi, prebaci na Gemini?
  5. (Nivo 2) Koji pattern koristimo kada svi servisi implementiraju isti interfejs?
  6. (Nivo 2) Kako bi napisao Python funkciju koja automatski detektuje OS i koristi pravu komandu za filtriranje?

Ažuriranje dokumentacije

Ažuriraj README.md:

## 🚀 Trenutni Status

- ✅ Dan -3: Python 3.13+ instaliran
- ✅ Dan -2: PyCharm unified edition podešen
- ✅ Dan -1: GitHub repository kreiran
- ✅ Dan 0: Profesionalna struktura projekta
- ✅ Dan 1: Prvi Python moduli - Vasa može da pozdravi!
- ✅ Dan 2: Razumevanje AI API-ja - simulacija komunikacije
- ✅ Dan 3: Multi-provider podrška - OpenAI i Gemini
- ✅ Dan 4: Prvi AI poziv - Vasa govori preko OpenAI ili Gemini! 🤖
- ⏳ Dan 5: Dodavanje ličnosti i prilagođavanje odgovora (sutra)

## 🎯 Trenutne mogućnosti

Učitelj Vasa sada može:
- ✨ Odgovarati na pitanja koristeći OpenAI GPT ili Google Gemini
- 🔄 Prebacivati između AI servisa tokom rada
- 💬 Voditi kontinuirane razgovore sa kontekstom
- 🎓 Objašnjavati programske koncepte na srpskom jeziku
- 🔄 Raditi u offline modu sa simulacijom ako AI nije dostupan
- 🛡️ Automatski rešavati SSL probleme na Windows sistemima

## 🤖 Arhitektura

main.py
└── AIServiceFactory
├── OpenAIService (sa SSL fix-om)
└── GeminiService

## ⚠️ Poznati problemi i rešenja

### SSL problemi na Windows-u
Ako dobiješ `[Errno 2] No such file or directory` grešku sa OpenAI:
1. ssl_fix.py automatski rešava većinu problema
2. Restartuj PyCharm/terminal ako problemi potraju
3. Koristi Gemini kao alternativu

Dodaj u docs/development_log.md:

## Dan 4: Prvi AI poziv - univerzalna podrška! (16.06.2025)

### Šta je urađeno:
- ✅ Instalirane obe AI biblioteke (openai i google-generativeai)
- ✅ Kreiran ssl_fix.py modul za rešavanje SSL problema
- ✅ Kreiran BaseAIService interfejs
- ✅ Implementiran OpenAIService sa automatskim SSL fix-om
- ✅ Implementiran GeminiService sa istim interfejsom
- ✅ Kreiran AIServiceFactory sa Singleton pattern-om
- ✅ main.py koristi factory za automatski izbor servisa
- ✅ Dodata opcija promene servisa tokom rada
- ✅ Kontinuirani razgovor radi sa oba servisa

### Naučene lekcije:
- Environment varijable mogu da interferiraju sa bibliotekama
- SSL problemi su česti na Windows sistemima
- Factory pattern omogućava elegantno upravljanje više servisa
- Isti interfejs (BaseAIService) čini servise zamenljivim
- Singleton pattern štedi resurse
- Error handling mora biti specifičan za svaki servis
- Defensive programming predviđa i rešava probleme pre nego što se jave

### Problemi i rešenja:
- **Problem**: [Errno 2] No such file or directory sa OpenAI
- **Rešenje**: Kreiran ssl_fix.py koji čisti problematične environment varijable
- **Lekcija**: Environment pollution je realan problem u produkciji

### Testiranje:
- OpenAI: Brži odgovori, kraći, precizniji
- Gemini: Detaljniji odgovori, ponekad previše opširni
- SSL fix: Rešava probleme na većini Windows sistema

### Za sutra (Dan 5):
- Proširivanje Vasine ličnosti
- Fine-tuning parametara za oba servisa
- Dodavanje "modova" ponašanja

Git commit za danas

git add .
git commit -m "Dan 4: Univerzalna AI podrška sa SSL fix-om za Windows!"
git push

💡 NAPOMENA O SSL PROBLEMU: Ova lekcija o SSL problemima i environment varijablama je možda najvrednija lekcija do sada. U stvarnom svetu programiranja, čak 50% vremena možeš provesti debugujući ovakve “sistemske” probleme koji nisu greške u tvom kodu.

Naučio si:

  1. Kako sistemske varijable mogu da utiču na Python biblioteke
  2. Kako da sistematski pristupiš debugovanju
  3. Kako da napišeš defensive kod koji predviđa probleme
  4. Važnost fallback opcija (Gemini kada OpenAI ne radi)

Ovo su veštine koje te čine pravim programerom!

🎈 ZABAVNA ČINJENICA: Procenjuje se da programeri provedu 20-30% vremena debugujući environment i dependency probleme. Ti si danas naučio kako da taj procenat smanjiš pisanjem koda koji automatski rešava česte probleme!

ČESTITAM! 🎉 Učitelj Vasa sada ima pravu veštačku inteligenciju koja radi sa oba popularna AI servisa! Može da odgovara na pitanja, objašnjava koncepte i vodi prirodne razgovore. Još bolje – sistem automatski rešava česte probleme koji frustriraju mnoge developere!

Sutra Učitelj Vasa uči

Sutra ćemo se fokusirati na Vasinu ličnost i ponašanje. Naučićeš kako da prilagodiš AI odgovore kroz naprednije system prompte koji rade sa oba servisa, kako da dodaš različite “modove” ponašanja (strpljiv učitelj, brzi savetnik, debug pomocnik) i kako da optimizuješ parametre za najbolje rezultate sa svakim servisom!

📚 REČNIK DANAŠNJE LEKCIJE:

  • Multi-provider arhitektura: Sistem koji podržava više različitih servisa
  • Factory pattern: Dizajn obrazac za kreiranje objekata bez specificiranja klase
  • Abstract Base Class (ABC): Python način definisanja interfejsa
  • Singleton pattern: Obrazac koji osigurava samo jednu instancu klase
  • Provider-agnostic: Kod koji radi nezavisno od konkretnog servisa
  • Interface: Skup metoda koje klasa mora implementirati
  • Error handling: Proces rukovanja greškama specifičnim za svaki servis
  • Rate limiting: Ograničenje broja API poziva u određenom periodu
  • Cross-platform: Softver koji radi na različitim operacionim sistemima
  • grep: Unix/Linux komanda za pretraživanje teksta
  • Select-String: PowerShell ekvivalent grep komande
  • SSL (Secure Sockets Layer): Protokol za sigurnu komunikaciju preko interneta
  • Environment varijable: Globalne sistemske postavke koje programi mogu da čitaju
  • Environment pollution: Kada različiti programi ostavljaju konfliktne sistemske varijable
  • Defensive programming: Pisanje koda koji predviđa i rukuje potencijalnim problemima
  • Dependency hell: Situacija kada različite biblioteke imaju konfliktne zahteve

Dodatni primeri i zabavne vežbe

  1. Uporedi stilove: Postavi isto pitanje oba servisa i analiziraj razlike
  2. Meri brzinu: Dodaj merenje vremena odgovora za svaki servis
  3. Kreiraj “battle” mod: Postavi pitanje oba servisa istovremeno i prikaži oba odgovora
  4. SSL dijagnoza: Pokreni python ssl_fix.py i analiziraj output
  5. Environment explorer: Napiši skriptu koja lista sve environment varijable na tvom sistemu