Update app.py
Browse files
app.py
CHANGED
@@ -1,25 +1,27 @@
|
|
1 |
import os
|
2 |
import logging
|
3 |
-
|
|
|
|
|
|
|
|
|
4 |
from flask_sqlalchemy import SQLAlchemy
|
5 |
from flask_login import LoginManager, login_user, logout_user, login_required, current_user
|
6 |
from sqlalchemy.orm import DeclarativeBase
|
7 |
import cohere
|
8 |
-
import requests
|
9 |
-
import uuid
|
10 |
-
from datetime import datetime
|
11 |
|
12 |
-
# Setup
|
|
|
13 |
logging.basicConfig(level=logging.INFO)
|
14 |
|
15 |
class Base(DeclarativeBase):
|
16 |
pass
|
17 |
|
18 |
-
|
19 |
-
app = Flask(__name__, instance_path='/tmp/instance')
|
20 |
app.secret_key = os.environ.get("SESSION_SECRET", os.urandom(24).hex())
|
21 |
|
22 |
-
# Database
|
|
|
23 |
app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get("DATABASE_URL", "sqlite:///inkboard.db")
|
24 |
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
|
25 |
app.config["SQLALCHEMY_ENGINE_OPTIONS"] = {
|
@@ -29,32 +31,37 @@ app.config["SQLALCHEMY_ENGINE_OPTIONS"] = {
|
|
29 |
|
30 |
db = SQLAlchemy(app, model_class=Base)
|
31 |
|
32 |
-
# Flask-Login
|
|
|
33 |
login_manager = LoginManager()
|
34 |
login_manager.init_app(app)
|
35 |
login_manager.login_view = 'login'
|
36 |
login_manager.login_message = 'Please log in to access InkBoard'
|
37 |
|
38 |
-
# API
|
39 |
-
|
|
|
40 |
HUGGINGFACE_API_KEY = os.environ.get("HUGGINGFACE_API_KEY")
|
41 |
-
HF_IMAGE_MODEL = "stabilityai/stable-diffusion-2-1"
|
42 |
|
43 |
cohere_client = cohere.Client(API_KEY) if API_KEY else None
|
44 |
-
|
|
|
|
|
|
|
45 |
|
46 |
@login_manager.user_loader
|
47 |
def load_user(user_id):
|
48 |
-
from models import User
|
49 |
return User.query.get(int(user_id))
|
50 |
|
|
|
|
|
51 |
@app.route('/')
|
52 |
def index():
|
53 |
return render_template('dashboard.html', user=current_user) if current_user.is_authenticated else render_template('index.html')
|
54 |
|
|
|
55 |
@app.route('/register', methods=['GET', 'POST'])
|
56 |
def register():
|
57 |
-
from models import User
|
58 |
if request.method == 'POST':
|
59 |
data = request.get_json() if request.is_json else request.form
|
60 |
username, email, password = data.get('username', '').strip(), data.get('email', '').strip(), data.get('password', '').strip()
|
@@ -76,12 +83,13 @@ def register():
|
|
76 |
|
77 |
return render_template('register.html')
|
78 |
|
|
|
79 |
@app.route('/login', methods=['GET', 'POST'])
|
80 |
def login():
|
81 |
-
from models import User
|
82 |
if request.method == 'POST':
|
83 |
data = request.get_json() if request.is_json else request.form
|
84 |
-
username
|
|
|
85 |
|
86 |
user = User.query.filter((User.username == username) | (User.email == username)).first()
|
87 |
|
@@ -93,6 +101,7 @@ def login():
|
|
93 |
|
94 |
return render_template('login.html')
|
95 |
|
|
|
96 |
@app.route('/logout')
|
97 |
@login_required
|
98 |
def logout():
|
@@ -103,29 +112,20 @@ def logout():
|
|
103 |
@app.route('/generate', methods=['POST'])
|
104 |
@login_required
|
105 |
def generate_content():
|
106 |
-
from models import Creation
|
107 |
-
|
108 |
-
# 1. β
Logging for debugging
|
109 |
logging.info("β‘ generate_content() called")
|
110 |
-
|
111 |
-
# 2. β
Extract prompt
|
112 |
data = request.get_json()
|
113 |
scene_idea = data.get('scene_idea', '').strip()
|
114 |
logging.info(f"π₯ Received prompt: {scene_idea}")
|
115 |
|
116 |
if not scene_idea:
|
117 |
-
logging.warning("β No scene idea provided")
|
118 |
return jsonify({'error': 'Please provide a scene idea'}), 400
|
119 |
|
120 |
if not cohere_client:
|
121 |
-
logging.error("β Cohere client
|
122 |
-
return jsonify({'error': 'Text generation
|
123 |
|
124 |
-
# 3. β
Try generating story
|
125 |
try:
|
126 |
story_prompt = f"""Transform this scene idea into a vivid paragraph:\nScene idea: {scene_idea}"""
|
127 |
-
logging.info("π§ Sending prompt to Cohere")
|
128 |
-
|
129 |
story_response = cohere_client.generate(
|
130 |
model='command',
|
131 |
prompt=story_prompt,
|
@@ -133,25 +133,18 @@ def generate_content():
|
|
133 |
temperature=0.7,
|
134 |
k=0
|
135 |
)
|
136 |
-
|
137 |
-
logging.info("β
Cohere response received")
|
138 |
-
|
139 |
expanded_story = story_response.generations[0].text.strip()
|
140 |
-
logging.info(
|
141 |
-
|
142 |
except Exception as e:
|
143 |
logging.error(f"β Cohere generation error: {str(e)}")
|
144 |
return jsonify({'error': 'Failed to generate story'}), 500
|
145 |
|
146 |
-
# 4. β
Generate image (optional)
|
147 |
image_url = None
|
148 |
try:
|
149 |
image_url = generate_image_hf(scene_idea, expanded_story)
|
150 |
-
logging.info(f"πΌοΈ Image generated: {image_url[:50]}...")
|
151 |
except Exception as e:
|
152 |
logging.warning(f"β οΈ Image generation failed: {e}")
|
153 |
|
154 |
-
# 5. β
Save to DB
|
155 |
creation_id = str(uuid.uuid4())
|
156 |
creation = Creation(
|
157 |
id=creation_id,
|
@@ -174,21 +167,23 @@ def generate_content():
|
|
174 |
@app.route('/save_journal', methods=['POST'])
|
175 |
@login_required
|
176 |
def save_journal():
|
177 |
-
from models import Creation
|
178 |
data = request.get_json()
|
179 |
-
creation_id
|
|
|
|
|
180 |
creation = Creation.query.filter_by(id=creation_id, user_id=current_user.id).first()
|
181 |
if not creation:
|
182 |
return jsonify({'error': 'Not found'}), 404
|
|
|
183 |
creation.journal_entry = journal_entry
|
184 |
creation.updated_at = datetime.utcnow()
|
185 |
db.session.commit()
|
186 |
return jsonify({'success': True})
|
187 |
|
|
|
188 |
@app.route('/get_creations')
|
189 |
@login_required
|
190 |
def get_creations():
|
191 |
-
from models import Creation
|
192 |
creations = Creation.query.filter_by(user_id=current_user.id).order_by(Creation.created_at.desc()).all()
|
193 |
return jsonify({'creations': [{
|
194 |
'id': c.id,
|
@@ -199,7 +194,13 @@ def get_creations():
|
|
199 |
'created_at': c.created_at.isoformat()
|
200 |
} for c in creations]})
|
201 |
|
|
|
|
|
|
|
202 |
def generate_image_hf(scene_idea, story):
|
|
|
|
|
|
|
203 |
prompt = f"An artistic illustration of: {scene_idea}, dreamy and vivid"
|
204 |
models = [
|
205 |
"runwayml/stable-diffusion-v1-5",
|
@@ -209,32 +210,24 @@ def generate_image_hf(scene_idea, story):
|
|
209 |
|
210 |
for model in models:
|
211 |
try:
|
212 |
-
logging.info(f"Trying model: {model}")
|
213 |
res = requests.post(
|
214 |
f"https://api-inference.huggingface.co/models/{model}",
|
215 |
headers={"Authorization": f"Bearer {HUGGINGFACE_API_KEY}"},
|
216 |
json={"inputs": prompt},
|
217 |
-
timeout=
|
218 |
)
|
219 |
-
|
220 |
-
logging.info(f"Status: {res.status_code}")
|
221 |
if res.status_code == 200:
|
222 |
-
import base64
|
223 |
return f"data:image/png;base64,{base64.b64encode(res.content).decode()}"
|
|
|
|
|
224 |
|
225 |
-
|
226 |
-
logging.warning(f"Model {model} failed: {res.status_code} - {res.text[:200]}")
|
227 |
|
228 |
-
except Exception as e:
|
229 |
-
logging.error(f"Error from model {model}: {e}")
|
230 |
-
continue
|
231 |
|
232 |
-
|
233 |
-
|
234 |
-
# DB setup
|
235 |
-
from models import User, Creation
|
236 |
with app.app_context():
|
237 |
db.create_all()
|
238 |
|
239 |
if __name__ == '__main__':
|
240 |
-
app.run(host='0.0.0.0', port=5000
|
|
|
1 |
import os
|
2 |
import logging
|
3 |
+
import uuid
|
4 |
+
import base64
|
5 |
+
import requests
|
6 |
+
from datetime import datetime
|
7 |
+
from flask import Flask, render_template, request, jsonify, redirect, url_for, flash
|
8 |
from flask_sqlalchemy import SQLAlchemy
|
9 |
from flask_login import LoginManager, login_user, logout_user, login_required, current_user
|
10 |
from sqlalchemy.orm import DeclarativeBase
|
11 |
import cohere
|
|
|
|
|
|
|
12 |
|
13 |
+
# ------------------ Setup ------------------
|
14 |
+
|
15 |
logging.basicConfig(level=logging.INFO)
|
16 |
|
17 |
class Base(DeclarativeBase):
|
18 |
pass
|
19 |
|
20 |
+
app = Flask(__name__)
|
|
|
21 |
app.secret_key = os.environ.get("SESSION_SECRET", os.urandom(24).hex())
|
22 |
|
23 |
+
# ------------------ Database ------------------
|
24 |
+
|
25 |
app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get("DATABASE_URL", "sqlite:///inkboard.db")
|
26 |
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
|
27 |
app.config["SQLALCHEMY_ENGINE_OPTIONS"] = {
|
|
|
31 |
|
32 |
db = SQLAlchemy(app, model_class=Base)
|
33 |
|
34 |
+
# ------------------ Flask-Login ------------------
|
35 |
+
|
36 |
login_manager = LoginManager()
|
37 |
login_manager.init_app(app)
|
38 |
login_manager.login_view = 'login'
|
39 |
login_manager.login_message = 'Please log in to access InkBoard'
|
40 |
|
41 |
+
# ------------------ API Keys ------------------
|
42 |
+
|
43 |
+
API_KEY = os.environ.get("API_KEY") # Make sure this is your cohere key name
|
44 |
HUGGINGFACE_API_KEY = os.environ.get("HUGGINGFACE_API_KEY")
|
|
|
45 |
|
46 |
cohere_client = cohere.Client(API_KEY) if API_KEY else None
|
47 |
+
|
48 |
+
# ------------------ Models ------------------
|
49 |
+
|
50 |
+
from models import User, Creation
|
51 |
|
52 |
@login_manager.user_loader
|
53 |
def load_user(user_id):
|
|
|
54 |
return User.query.get(int(user_id))
|
55 |
|
56 |
+
# ------------------ Routes ------------------
|
57 |
+
|
58 |
@app.route('/')
|
59 |
def index():
|
60 |
return render_template('dashboard.html', user=current_user) if current_user.is_authenticated else render_template('index.html')
|
61 |
|
62 |
+
|
63 |
@app.route('/register', methods=['GET', 'POST'])
|
64 |
def register():
|
|
|
65 |
if request.method == 'POST':
|
66 |
data = request.get_json() if request.is_json else request.form
|
67 |
username, email, password = data.get('username', '').strip(), data.get('email', '').strip(), data.get('password', '').strip()
|
|
|
83 |
|
84 |
return render_template('register.html')
|
85 |
|
86 |
+
|
87 |
@app.route('/login', methods=['GET', 'POST'])
|
88 |
def login():
|
|
|
89 |
if request.method == 'POST':
|
90 |
data = request.get_json() if request.is_json else request.form
|
91 |
+
username = data.get('username', '').strip()
|
92 |
+
password = data.get('password', '').strip()
|
93 |
|
94 |
user = User.query.filter((User.username == username) | (User.email == username)).first()
|
95 |
|
|
|
101 |
|
102 |
return render_template('login.html')
|
103 |
|
104 |
+
|
105 |
@app.route('/logout')
|
106 |
@login_required
|
107 |
def logout():
|
|
|
112 |
@app.route('/generate', methods=['POST'])
|
113 |
@login_required
|
114 |
def generate_content():
|
|
|
|
|
|
|
115 |
logging.info("β‘ generate_content() called")
|
|
|
|
|
116 |
data = request.get_json()
|
117 |
scene_idea = data.get('scene_idea', '').strip()
|
118 |
logging.info(f"π₯ Received prompt: {scene_idea}")
|
119 |
|
120 |
if not scene_idea:
|
|
|
121 |
return jsonify({'error': 'Please provide a scene idea'}), 400
|
122 |
|
123 |
if not cohere_client:
|
124 |
+
logging.error("β Cohere client not configured")
|
125 |
+
return jsonify({'error': 'Text generation unavailable'}), 500
|
126 |
|
|
|
127 |
try:
|
128 |
story_prompt = f"""Transform this scene idea into a vivid paragraph:\nScene idea: {scene_idea}"""
|
|
|
|
|
129 |
story_response = cohere_client.generate(
|
130 |
model='command',
|
131 |
prompt=story_prompt,
|
|
|
133 |
temperature=0.7,
|
134 |
k=0
|
135 |
)
|
|
|
|
|
|
|
136 |
expanded_story = story_response.generations[0].text.strip()
|
137 |
+
logging.info("β
Story generated")
|
|
|
138 |
except Exception as e:
|
139 |
logging.error(f"β Cohere generation error: {str(e)}")
|
140 |
return jsonify({'error': 'Failed to generate story'}), 500
|
141 |
|
|
|
142 |
image_url = None
|
143 |
try:
|
144 |
image_url = generate_image_hf(scene_idea, expanded_story)
|
|
|
145 |
except Exception as e:
|
146 |
logging.warning(f"β οΈ Image generation failed: {e}")
|
147 |
|
|
|
148 |
creation_id = str(uuid.uuid4())
|
149 |
creation = Creation(
|
150 |
id=creation_id,
|
|
|
167 |
@app.route('/save_journal', methods=['POST'])
|
168 |
@login_required
|
169 |
def save_journal():
|
|
|
170 |
data = request.get_json()
|
171 |
+
creation_id = data.get('creation_id')
|
172 |
+
journal_entry = data.get('journal_entry', '').strip()
|
173 |
+
|
174 |
creation = Creation.query.filter_by(id=creation_id, user_id=current_user.id).first()
|
175 |
if not creation:
|
176 |
return jsonify({'error': 'Not found'}), 404
|
177 |
+
|
178 |
creation.journal_entry = journal_entry
|
179 |
creation.updated_at = datetime.utcnow()
|
180 |
db.session.commit()
|
181 |
return jsonify({'success': True})
|
182 |
|
183 |
+
|
184 |
@app.route('/get_creations')
|
185 |
@login_required
|
186 |
def get_creations():
|
|
|
187 |
creations = Creation.query.filter_by(user_id=current_user.id).order_by(Creation.created_at.desc()).all()
|
188 |
return jsonify({'creations': [{
|
189 |
'id': c.id,
|
|
|
194 |
'created_at': c.created_at.isoformat()
|
195 |
} for c in creations]})
|
196 |
|
197 |
+
|
198 |
+
# ------------------ Image Generation ------------------
|
199 |
+
|
200 |
def generate_image_hf(scene_idea, story):
|
201 |
+
if not HUGGINGFACE_API_KEY:
|
202 |
+
return None
|
203 |
+
|
204 |
prompt = f"An artistic illustration of: {scene_idea}, dreamy and vivid"
|
205 |
models = [
|
206 |
"runwayml/stable-diffusion-v1-5",
|
|
|
210 |
|
211 |
for model in models:
|
212 |
try:
|
|
|
213 |
res = requests.post(
|
214 |
f"https://api-inference.huggingface.co/models/{model}",
|
215 |
headers={"Authorization": f"Bearer {HUGGINGFACE_API_KEY}"},
|
216 |
json={"inputs": prompt},
|
217 |
+
timeout=60
|
218 |
)
|
|
|
|
|
219 |
if res.status_code == 200:
|
|
|
220 |
return f"data:image/png;base64,{base64.b64encode(res.content).decode()}"
|
221 |
+
except Exception as e:
|
222 |
+
logging.warning(f"Model {model} failed: {str(e)}")
|
223 |
|
224 |
+
return "<svg>SVG fallback</svg>"
|
|
|
225 |
|
|
|
|
|
|
|
226 |
|
227 |
+
# ------------------ Run App ------------------
|
228 |
+
|
|
|
|
|
229 |
with app.app_context():
|
230 |
db.create_all()
|
231 |
|
232 |
if __name__ == '__main__':
|
233 |
+
app.run(host='0.0.0.0', port=5000)
|