Update app.py
Browse files
app.py
CHANGED
@@ -8,39 +8,44 @@ import cohere
|
|
8 |
import requests
|
9 |
import uuid
|
10 |
from datetime import datetime
|
|
|
11 |
|
12 |
-
#
|
13 |
logging.basicConfig(level=logging.INFO)
|
14 |
|
|
|
15 |
class Base(DeclarativeBase):
|
16 |
pass
|
17 |
|
18 |
-
|
|
|
19 |
app.secret_key = os.environ.get("SESSION_SECRET", os.urandom(24).hex())
|
20 |
|
21 |
# Database config
|
22 |
-
app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get("DATABASE_URL", "sqlite
|
23 |
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
|
24 |
-
app.config["SQLALCHEMY_ENGINE_OPTIONS"] = {
|
25 |
-
'pool_pre_ping': True,
|
26 |
-
"pool_recycle": 300,
|
27 |
-
}
|
28 |
|
29 |
db = SQLAlchemy(app, model_class=Base)
|
30 |
|
31 |
-
#
|
32 |
login_manager = LoginManager()
|
33 |
login_manager.init_app(app)
|
34 |
login_manager.login_view = 'login'
|
35 |
-
login_manager.login_message = 'Please log in to access InkBoard'
|
36 |
|
37 |
-
# API keys
|
38 |
-
API_KEY = os.environ.get("
|
39 |
HUGGINGFACE_API_KEY = os.environ.get("HUGGINGFACE_API_KEY")
|
40 |
-
HF_IMAGE_MODEL = "stabilityai/stable-diffusion-2-1"
|
41 |
|
42 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
43 |
|
|
|
44 |
@login_manager.user_loader
|
45 |
def load_user(user_id):
|
46 |
from models import User
|
@@ -65,13 +70,11 @@ def register():
|
|
65 |
|
66 |
user = User(username=username, email=email)
|
67 |
user.set_password(password)
|
68 |
-
|
69 |
db.session.add(user)
|
70 |
db.session.commit()
|
71 |
login_user(user)
|
72 |
|
73 |
return jsonify({'success': True, 'redirect': url_for('index')}) if request.is_json else redirect(url_for('index'))
|
74 |
-
|
75 |
return render_template('register.html')
|
76 |
|
77 |
@app.route('/login', methods=['GET', 'POST'])
|
@@ -82,13 +85,10 @@ def login():
|
|
82 |
username, password = data.get('username', '').strip(), data.get('password', '').strip()
|
83 |
|
84 |
user = User.query.filter((User.username == username) | (User.email == username)).first()
|
85 |
-
|
86 |
if user and user.check_password(password):
|
87 |
login_user(user)
|
88 |
return jsonify({'success': True, 'redirect': url_for('index')}) if request.is_json else redirect(url_for('index'))
|
89 |
-
|
90 |
return jsonify({'error': 'Invalid credentials'}), 401
|
91 |
-
|
92 |
return render_template('login.html')
|
93 |
|
94 |
@app.route('/logout')
|
@@ -101,54 +101,71 @@ def logout():
|
|
101 |
@login_required
|
102 |
def generate_content():
|
103 |
from models import Creation
|
|
|
104 |
data = request.get_json()
|
105 |
scene_idea = data.get('scene_idea', '').strip()
|
|
|
106 |
|
107 |
if not scene_idea:
|
108 |
return jsonify({'error': 'Please provide a scene idea'}), 400
|
109 |
|
110 |
-
|
|
|
|
|
|
|
|
|
111 |
Scene idea: {scene_idea}"""
|
112 |
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
120 |
|
121 |
-
|
|
|
|
|
|
|
|
|
|
|
122 |
|
123 |
-
image_url = None
|
124 |
-
try:
|
125 |
-
image_url = generate_image_hf(scene_idea, expanded_story)
|
126 |
except Exception as e:
|
127 |
-
logging.
|
128 |
-
|
129 |
-
creation_id = str(uuid.uuid4())
|
130 |
-
creation = Creation(
|
131 |
-
id=creation_id,
|
132 |
-
user_id=current_user.id,
|
133 |
-
scene_idea=scene_idea,
|
134 |
-
story=expanded_story,
|
135 |
-
image_url=image_url
|
136 |
-
)
|
137 |
-
|
138 |
-
db.session.add(creation)
|
139 |
-
db.session.commit()
|
140 |
-
|
141 |
-
return jsonify({'success': True, 'story': expanded_story, 'image_url': image_url, 'creation_id': creation_id})
|
142 |
|
143 |
@app.route('/save_journal', methods=['POST'])
|
144 |
@login_required
|
145 |
def save_journal():
|
146 |
from models import Creation
|
147 |
data = request.get_json()
|
148 |
-
creation_id
|
|
|
|
|
149 |
creation = Creation.query.filter_by(id=creation_id, user_id=current_user.id).first()
|
150 |
if not creation:
|
151 |
return jsonify({'error': 'Not found'}), 404
|
|
|
152 |
creation.journal_entry = journal_entry
|
153 |
creation.updated_at = datetime.utcnow()
|
154 |
db.session.commit()
|
@@ -169,30 +186,24 @@ def get_creations():
|
|
169 |
} for c in creations]})
|
170 |
|
171 |
def generate_image_hf(scene_idea, story):
|
172 |
-
prompt = f"An artistic illustration of: {scene_idea}, dreamy
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
)
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
return generate_svg_placeholder(scene_idea, story)
|
191 |
-
|
192 |
-
def generate_svg_placeholder(scene_idea, story):
|
193 |
-
return "<svg>Your beautiful fallback SVG here</svg>"
|
194 |
-
|
195 |
-
# DB setup
|
196 |
from models import User, Creation
|
197 |
with app.app_context():
|
198 |
db.create_all()
|
|
|
8 |
import requests
|
9 |
import uuid
|
10 |
from datetime import datetime
|
11 |
+
import base64
|
12 |
|
13 |
+
# Logging setup
|
14 |
logging.basicConfig(level=logging.INFO)
|
15 |
|
16 |
+
# SQLAlchemy base class
|
17 |
class Base(DeclarativeBase):
|
18 |
pass
|
19 |
|
20 |
+
# Flask app
|
21 |
+
app = Flask(__name__)
|
22 |
app.secret_key = os.environ.get("SESSION_SECRET", os.urandom(24).hex())
|
23 |
|
24 |
# Database config
|
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"] = {'pool_pre_ping': True, "pool_recycle": 300}
|
|
|
|
|
|
|
28 |
|
29 |
db = SQLAlchemy(app, model_class=Base)
|
30 |
|
31 |
+
# Login manager setup
|
32 |
login_manager = LoginManager()
|
33 |
login_manager.init_app(app)
|
34 |
login_manager.login_view = 'login'
|
|
|
35 |
|
36 |
+
# API keys and clients
|
37 |
+
API_KEY = os.environ.get("Api_key")
|
38 |
HUGGINGFACE_API_KEY = os.environ.get("HUGGINGFACE_API_KEY")
|
|
|
39 |
|
40 |
+
if not API_KEY:
|
41 |
+
logging.warning("⚠️ Missing Cohere API key!")
|
42 |
+
cohere_client = None
|
43 |
+
else:
|
44 |
+
cohere_client = cohere.Client(API_KEY)
|
45 |
+
|
46 |
+
HF_IMAGE_MODEL = "stabilityai/stable-diffusion-2-1"
|
47 |
|
48 |
+
# Load user from session
|
49 |
@login_manager.user_loader
|
50 |
def load_user(user_id):
|
51 |
from models import User
|
|
|
70 |
|
71 |
user = User(username=username, email=email)
|
72 |
user.set_password(password)
|
|
|
73 |
db.session.add(user)
|
74 |
db.session.commit()
|
75 |
login_user(user)
|
76 |
|
77 |
return jsonify({'success': True, 'redirect': url_for('index')}) if request.is_json else redirect(url_for('index'))
|
|
|
78 |
return render_template('register.html')
|
79 |
|
80 |
@app.route('/login', methods=['GET', 'POST'])
|
|
|
85 |
username, password = data.get('username', '').strip(), data.get('password', '').strip()
|
86 |
|
87 |
user = User.query.filter((User.username == username) | (User.email == username)).first()
|
|
|
88 |
if user and user.check_password(password):
|
89 |
login_user(user)
|
90 |
return jsonify({'success': True, 'redirect': url_for('index')}) if request.is_json else redirect(url_for('index'))
|
|
|
91 |
return jsonify({'error': 'Invalid credentials'}), 401
|
|
|
92 |
return render_template('login.html')
|
93 |
|
94 |
@app.route('/logout')
|
|
|
101 |
@login_required
|
102 |
def generate_content():
|
103 |
from models import Creation
|
104 |
+
|
105 |
data = request.get_json()
|
106 |
scene_idea = data.get('scene_idea', '').strip()
|
107 |
+
logging.info(f"🎨 New prompt: {scene_idea}")
|
108 |
|
109 |
if not scene_idea:
|
110 |
return jsonify({'error': 'Please provide a scene idea'}), 400
|
111 |
|
112 |
+
if not cohere_client:
|
113 |
+
return jsonify({'error': 'Cohere API key not configured'}), 500
|
114 |
+
|
115 |
+
try:
|
116 |
+
story_prompt = f"""Transform this scene idea into a vivid, poetic paragraph with beautiful imagery:
|
117 |
Scene idea: {scene_idea}"""
|
118 |
|
119 |
+
story_response = cohere_client.generate(
|
120 |
+
model='command',
|
121 |
+
prompt=story_prompt,
|
122 |
+
max_tokens=200,
|
123 |
+
temperature=0.7,
|
124 |
+
k=0
|
125 |
+
)
|
126 |
+
|
127 |
+
expanded_story = story_response.generations[0].text.strip()
|
128 |
+
logging.info(f"📝 Story generated: {expanded_story[:60]}...")
|
129 |
+
|
130 |
+
image_url = None
|
131 |
+
if HUGGINGFACE_API_KEY:
|
132 |
+
image_url = generate_image_hf(scene_idea, expanded_story)
|
133 |
+
else:
|
134 |
+
logging.warning("⚠️ Missing HuggingFace API key. No image will be generated.")
|
135 |
+
|
136 |
+
creation = Creation(
|
137 |
+
id=str(uuid.uuid4()),
|
138 |
+
user_id=current_user.id,
|
139 |
+
scene_idea=scene_idea,
|
140 |
+
story=expanded_story,
|
141 |
+
image_url=image_url
|
142 |
+
)
|
143 |
+
db.session.add(creation)
|
144 |
+
db.session.commit()
|
145 |
|
146 |
+
return jsonify({
|
147 |
+
'success': True,
|
148 |
+
'story': expanded_story,
|
149 |
+
'image_url': image_url,
|
150 |
+
'creation_id': creation.id
|
151 |
+
})
|
152 |
|
|
|
|
|
|
|
153 |
except Exception as e:
|
154 |
+
logging.error(f"❌ Error generating content: {e}")
|
155 |
+
return jsonify({'error': 'An error occurred during generation. Check logs.'}), 500
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
156 |
|
157 |
@app.route('/save_journal', methods=['POST'])
|
158 |
@login_required
|
159 |
def save_journal():
|
160 |
from models import Creation
|
161 |
data = request.get_json()
|
162 |
+
creation_id = data.get('creation_id')
|
163 |
+
journal_entry = data.get('journal_entry', '').strip()
|
164 |
+
|
165 |
creation = Creation.query.filter_by(id=creation_id, user_id=current_user.id).first()
|
166 |
if not creation:
|
167 |
return jsonify({'error': 'Not found'}), 404
|
168 |
+
|
169 |
creation.journal_entry = journal_entry
|
170 |
creation.updated_at = datetime.utcnow()
|
171 |
db.session.commit()
|
|
|
186 |
} for c in creations]})
|
187 |
|
188 |
def generate_image_hf(scene_idea, story):
|
189 |
+
prompt = f"An artistic illustration of: {scene_idea}, dreamy, vivid, high detail"
|
190 |
+
model = HF_IMAGE_MODEL
|
191 |
+
try:
|
192 |
+
response = requests.post(
|
193 |
+
f"https://api-inference.huggingface.co/models/{model}",
|
194 |
+
headers={"Authorization": f"Bearer {HUGGINGFACE_API_KEY}"},
|
195 |
+
json={"inputs": prompt},
|
196 |
+
timeout=60
|
197 |
+
)
|
198 |
+
if response.status_code == 200:
|
199 |
+
return f"data:image/png;base64,{base64.b64encode(response.content).decode()}"
|
200 |
+
else:
|
201 |
+
logging.warning(f"Model failed: {response.status_code} - {response.text}")
|
202 |
+
except Exception as e:
|
203 |
+
logging.error(f"Exception generating image: {e}")
|
204 |
+
return None # fallback
|
205 |
+
|
206 |
+
# Import models and initialize DB
|
|
|
|
|
|
|
|
|
|
|
|
|
207 |
from models import User, Creation
|
208 |
with app.app_context():
|
209 |
db.create_all()
|