import os, json, uuid import requests from flask import Flask, Response, request, jsonify, session, redirect, url_for, send_file from authlib.integrations.flask_client import OAuth from functools import wraps from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from markdown import markdown from config import Config from models import Base, Conversation, Message, Attachment app = Flask(__name__) app.config.from_object(Config) app.secret_key = app.config["SECRET_KEY"] engine = create_engine(os.getenv("DATABASE_URL")) SessionLocal = sessionmaker(bind=engine) Base.metadata.create_all(engine) oauth = OAuth(app) google = oauth.register( name="google", client_id=app.config["GOOGLE_CLIENT_ID"], client_secret=app.config["GOOGLE_CLIENT_SECRET"], access_token_url="https://oauth2.googleapis.com/token", authorize_url="https://accounts.google.com/o/oauth2/v2/auth", api_base_url="https://www.googleapis.com/oauth2/v2/", client_kwargs={"scope": "openid email profile"}, ) def login_required(f): @wraps(f) def wrapper(*args, **kwargs): if "user" not in session: return redirect(url_for("login")) return f(*args, **kwargs) return wrapper @app.route("/login") def login(): return google.authorize_redirect(app.config["GOOGLE_REDIRECT_URI"]) @app.route("/oauth/callback") def oauth_callback(): token = google.authorize_access_token() user_info = google.get("userinfo").json() session["user"] = user_info return redirect("/") @app.route("/api/new_conversation", methods=["POST"]) @login_required def new_conversation(): db = SessionLocal() cid = str(uuid.uuid4()) title = request.json.get("title", "New conversation") conv = Conversation(id=cid, title=title) db.add(conv) db.commit() db.close() return jsonify({"conversation_id": cid, "title": title}) @app.route("/api/conversations") @login_required def list_conversations(): db = SessionLocal() convs = db.query(Conversation).all() data = [{"id": c.id, "title": c.title} for c in convs] db.close() return jsonify(data) @app.route("/api/history/") @login_required def history(cid): db = SessionLocal() msgs = db.query(Message).filter_by(conversation_id=cid).order_by(Message.created_at).all() data = [{"role": m.role, "content": markdown(m.content)} for m in msgs] db.close() return jsonify(data) @app.route("/api/attachment/", methods=["POST"]) @login_required def upload_attachment(cid): db = SessionLocal() file = request.files.get("file") if not file: return jsonify({"error": "No file"}), 400 content = file.read().decode("latin1") att = Attachment( id=str(uuid.uuid4()), conversation_id=cid, filename=file.filename, mime_type=file.mimetype, data=content, ) db.add(att) db.commit() db.close() return jsonify({"status": "ok"}) @app.route("/api/attachments/") @login_required def list_attachments(cid): db = SessionLocal() atts = db.query(Attachment).filter_by(conversation_id=cid).all() data = [{"id": a.id, "filename": a.filename} for a in atts] db.close() return jsonify(data) @app.route("/api/download/") @login_required def download(att_id): db = SessionLocal() att = db.query(Attachment).filter_by(id=att_id).first() db.close() if not att: return "Not found", 404 return send_file(att.path, as_attachment=True, download_name=att.filename) @app.route("/api/stream") @login_required def stream(): cid = request.args.get("conversation_id") model = request.args.get("model", "llama3.2") prompt = request.args.get("prompt") db = SessionLocal() db.add(Message(id=str(uuid.uuid4()), conversation_id=cid, role="user", content=prompt)) db.commit() def generate(): url = f"{app.config['OLLAMA_BASE_URL']}/api/generate" payload = {"model": model, "prompt": prompt, "stream": True} with requests.post(url, json=payload, stream=True) as r: buffer = "" for line in r.iter_lines(): if not line: continue try: data = json.loads(line.decode()) token = data.get("response", "") buffer += token # streaming animation: send partial buffer yield f"data: {json.dumps({'token': token})}\n\n" except: continue db.add(Message(id=str(uuid.uuid4()), conversation_id=cid, role="assistant", content=buffer)) db.commit() db.close() return Response(generate(), mimetype="text/event-stream") # *** import json import requests from flask import Flask, Response, request, jsonify, session, redirect, url_for from authlib.integrations.flask_client import OAuth from functools import wraps from config import Config from chat_store import ChatStore app = Flask(__name__) app.config.from_object(Config) app.secret_key = app.config["SECRET_KEY"] store = ChatStore() oauth = OAuth(app) google = oauth.register( name="google", client_id=app.config["GOOGLE_CLIENT_ID"], client_secret=app.config["GOOGLE_CLIENT_SECRET"], access_token_url="https://oauth2.googleapis.com/token", authorize_url="https://accounts.google.com/o/oauth2/v2/auth", api_base_url="https://www.googleapis.com/oauth2/v2/", client_kwargs={"scope": "openid email profile"}, ) def login_required(f): @wraps(f) def wrapper(*args, **kwargs): if "user" not in session: return redirect(url_for("login")) return f(*args, **kwargs) return wrapper @app.route("/login") def login(): return google.authorize_redirect(app.config["GOOGLE_REDIRECT_URI"]) @app.route("/oauth/callback") def oauth_callback(): token = google.authorize_access_token() user_info = google.get("userinfo").json() session["user"] = user_info return redirect("/") @app.route("/api/new_conversation") @login_required def new_conversation(): cid = store.new_conversation() return jsonify({"conversation_id": cid}) @app.route("/api/history/") @login_required def history(cid): return jsonify(store.get_messages(cid)) @app.route("/api/stream") @login_required def stream(): cid = request.args.get("conversation_id") model = request.args.get("model", "llama3.2") prompt = request.args.get("prompt") store.add_message(cid, "user", prompt) def generate(): url = f"{app.config['OLLAMA_BASE_URL']}/api/generate" payload = {"model": model, "prompt": prompt, "stream": True} with requests.post(url, json=payload, stream=True) as r: for line in r.iter_lines(): if not line: continue try: data = json.loads(line.decode()) token = data.get("response", "") store.add_message(cid, "assistant", token) yield f"data: {json.dumps({'token': token})}\n\n" except: continue return Response(generate(), mimetype="text/event-stream") # *** import os import json import requests from functools import wraps from flask import Flask, request, jsonify, redirect, session, url_for from authlib.integrations.flask_client import OAuth from config import Config # All comments are in English. app = Flask(__name__) app.config.from_object(Config) app.secret_key = app.config["SECRET_KEY"] oauth = OAuth(app) google = oauth.register( name="google", client_id=app.config["GOOGLE_CLIENT_ID"], client_secret=app.config["GOOGLE_CLIENT_SECRET"], access_token_url="https://oauth2.googleapis.com/token", authorize_url="https://accounts.google.com/o/oauth2/v2/auth", authorize_params={"access_type": "offline", "prompt": "consent"}, api_base_url="https://www.googleapis.com/oauth2/v2/", client_kwargs={"scope": "openid email profile"}, ) def login_required(f): """Decorator to ensure the user is authenticated via Google OAuth.""" @wraps(f) def wrapper(*args, **kwargs): if "user" not in session: return redirect(url_for("login")) return f(*args, **kwargs) return wrapper @app.route("/login") def login(): """Start Google OAuth login flow.""" return google.authorize_redirect(app.config["Google_REDIRECT_URI"]) @app.route("/oauth/callback") def oauth_callback(): """Handle Google OAuth callback and store user session.""" token = google.authorize_access_token() user_info = google.get("userinfo").json() session["user"] = { "email": user_info.get("email"), "name": user_info.get("name"), } return redirect(url_for("home")) @app.route("/") def home(): return jsonify({ "message": "Ollama Gateway is running.", "domain": "https://safira.renatorosa.com", "auth": "Google OAuth required for /api/* endpoints.", "endpoints": { "health": "/health", "generate": "/api/generate" } }) @app.route("/health") def health(): return jsonify({"status": "ok"}) @app.route("/api/generate", methods=["POST"]) @login_required def generate(): data = request.get_json(silent=True) or {} model_name = data.get("model", "llama3.2") prompt = data.get("prompt") if not prompt: return jsonify({"error": "Missing 'prompt' field."}), 400 model_id = app.config["MODEL_MAP"].get(model_name) if not model_id: return jsonify({"error": f"Unsupported model '{model_name}'."}), 400 ollama_url = f"{app.config['OLLAMA_BASE_URL']}/api/generate" payload = {"model": model_id, "prompt": prompt} try: response = requests.post(ollama_url, json=payload, timeout=60) response.raise_for_status() except requests.RequestException as e: return jsonify({"error": "Failed to call Ollama API.", "details": str(e)}), 502 try: ollama_data = response.json() except json.JSONDecodeError: return jsonify({"error": "Invalid JSON response from Ollama."}), 502 output_text = ollama_data.get("response") or ollama_data.get("output") or "" return jsonify({"model": model_name, "output": output_text}) if __name__ == "__main__": app.run(host="0.0.0.0", port=8000) # *** import os import json import requests from functools import wraps from flask import Flask, request, jsonify, redirect, session, url_for from authlib.integrations.flask_client import OAuth from config import Config # All comments are in English. app = Flask(__name__) app.config.from_object(Config) # Configure session secret key. app.secret_key = app.config["SECRET_KEY"] # Configure OAuth with Google. oauth = OAuth(app) google = oauth.register( name="google", client_id=app.config["GOOGLE_CLIENT_ID"], client_secret=app.config["GOOGLE_CLIENT_SECRET"], access_token_url="https://oauth2.googleapis.com/token", access_token_params=None, authorize_url="https://accounts.google.com/o/oauth2/v2/auth", authorize_params={ "access_type": "offline", "prompt": "consent", }, api_base_url="https://www.googleapis.com/oauth2/v2/", client_kwargs={"scope": "openid email profile"}, ) def login_required(f): """Decorator to ensure the user is authenticated via Google OAuth.""" @wraps(f) def wrapper(*args, **kwargs): # Check if user info is stored in session. if "user" not in session: # Redirect to login if not authenticated. return redirect(url_for("login")) return f(*args, **kwargs) return wrapper @app.route("/login") def login(): """Start Google OAuth login flow.""" redirect_uri = app.config["GOOGLE_REDIRECT_URI"] # Use the configured redirect URI for OAuth callback. return google.authorize_redirect(redirect_uri) @app.route("/oauth/callback") def oauth_callback(): """Handle Google OAuth callback and store user session.""" # Exchange authorization code for tokens. token = google.authorize_access_token() # Fetch user info from Google. resp = google.get("userinfo") user_info = resp.json() # Store minimal user info in session. session["user"] = { "email": user_info.get("email"), "name": user_info.get("name"), } # Redirect to a simple home or API docs page. return redirect(url_for("home")) @app.route("/") def home(): """Simple home endpoint explaining the service.""" # This endpoint is intentionally simple and easy to understand. return jsonify( { "message": "Ollama Gateway is running.", "auth": "Google OAuth required for /api/* endpoints.", "endpoints": { "health": "/health", "generate": "/api/generate", }, } ) @app.route("/health") def health(): """Health check endpoint.""" return jsonify({"status": "ok"}) @app.route("/api/generate", methods=["POST"]) @login_required def generate(): """Generate text using a selected Ollama model. Expected JSON body: { "model": "llama3.2", "prompt": "Your prompt here" } """ data = request.get_json(silent=True) or {} # Extract model and prompt from request. model_name = data.get("model", "llama3.2") prompt = data.get("prompt") if not prompt: return jsonify({"error": "Missing 'prompt' field."}), 400 # Map user-facing model name to Ollama model identifier. model_id = app.config["MODEL_MAP"].get(model_name) if not model_id: return jsonify({"error": f"Unsupported model '{model_name}'."}), 400 # Prepare request to Ollama API. ollama_url = f"{app.config['OLLAMA_BASE_URL']}/api/generate" payload = { "model": model_id, "prompt": prompt, # You can add more Ollama-specific parameters here if needed. } try: # Call Ollama HTTP API. response = requests.post(ollama_url, json=payload, timeout=60) response.raise_for_status() except requests.RequestException as e: # Handle network or API errors gracefully. return jsonify({"error": "Failed to call Ollama API.", "details": str(e)}), 502 # Parse Ollama response. try: ollama_data = response.json() except json.JSONDecodeError: return jsonify({"error": "Invalid JSON response from Ollama."}), 502 # Extract output text (this depends on Ollama's response format). output_text = ollama_data.get("response") or ollama_data.get("output") or "" return jsonify( { "model": model_name, "output": output_text, } ) if __name__ == "__main__": # This block is mainly for local debugging. # In production, Gunicorn will run the app. app.run(host="0.0.0.0", port=8000) # *** from flask import Flask, request, jsonify from flask_cors import CORS from db import Base, engine from models import User from services.intent_service import IntentService from services.mode_service import ModeService from services.preference_service import PreferenceService from services.suggestion_service import SuggestionService from services.document_service import DocumentService from services.search_service import SearchService from services.monitoring_service import MonitoringService from services.export_service import ExportService from services.audit_service import AuditService from services.security_service import SecurityService from services.ollama_service import OllamaService ollama_service = OllamaService() from services.model_router import ModelRouter router = ModelRouter() generated = router.generate(intent, mode, prompt) app = Flask(__name__) CORS(app) Base.metadata.create_all(bind=engine) intent_service = IntentService() mode_service = ModeService() preference_service = PreferenceService() suggestion_service = SuggestionService() document_service = DocumentService() search_service = SearchService() monitoring_service = MonitoringService() export_service = ExportService() audit_service = AuditService() security_service = SecurityService() USER_ID = 1 @app.route("/api/conversation", methods=["POST"]) def conversation(): data = request.json message = data.get("message", "") mode = mode_service.get_current_mode(USER_ID) if not mode: reply = ( "{user.name}, como vocĂȘ quer trabalhar hoje?\n\n" "- Modo Advogado\n- Modo Corporativo\n- Apenas Conversar\n\n" "Me diga qual modo vocĂȘ prefere agora." ) return jsonify({"reply": reply, "suggestions": [], "quick_actions": []}) intent = intent_service.detect_intent(message) prefs = preference_service.get_preferences(USER_ID) if intent == "set_mode": mode_service.set_mode(USER_ID, intent_service.extract_mode(message)) reply = f"Perfeito, vou atuar no modo {mode_service.get_current_mode(USER_ID)}. O que vamos fazer agora?" return jsonify({"reply": reply, "suggestions": [], "quick_actions": []}) if intent == "document": response = document_service.handle(message, mode, prefs) elif intent == "search": response = search_service.handle(message, mode, prefs) elif intent == "monitoring": response = monitoring_service.handle(message, mode, prefs) else: response = document_service.chat_like(message, mode, prefs) suggestions = suggestion_service.generate_suggestions(response, intent, mode, prefs) quick_actions = suggestion_service.generate_quick_actions(response, intent, mode, prefs) return jsonify({ "reply": response["text"], "suggestions": suggestions, "quick_actions": quick_actions }) if __name__ == "__main__": app.run(debug=True)