Files
jusfutura/app/app.py
T
2026-08-19 20:38:56 -03:00

589 lines
17 KiB
Python

import os, json, uuid
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 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/<cid>")
@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/<cid>", 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/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/<cid>")
@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)