Chat assistant

This commit is contained in:
2026-02-15 17:24:48 +01:00
parent 31150000fd
commit f772659201
7 changed files with 740 additions and 19 deletions

View File

@@ -1354,7 +1354,9 @@ async def posts_page(request: Request):
"profile_picture": profile_picture
})
except Exception as e:
import traceback
logger.error(f"Error loading posts: {e}")
logger.error(f"Traceback: {traceback.format_exc()}")
return templates.TemplateResponse("posts.html", {
"request": request,
"page": "posts",
@@ -1628,7 +1630,9 @@ async def post_detail_page(request: Request, post_id: str):
"media_items_dict": media_items_dict
})
except Exception as e:
import traceback
logger.error(f"Error loading post detail: {e}")
logger.error(f"Traceback: {traceback.format_exc()}")
return RedirectResponse(url="/posts", status_code=302)
@@ -1690,6 +1694,35 @@ async def create_post_page(request: Request):
})
@user_router.get("/chat-create", response_class=HTMLResponse)
async def chat_create_page(request: Request):
"""Chat-based post creation page."""
session = require_user_session(request)
if not session:
return RedirectResponse(url="/login", status_code=302)
user_id = UUID(session.user_id)
# Get post types
post_types = await db.get_post_types(user_id)
if not post_types:
return templates.TemplateResponse("error.html", {
"request": request,
"session": session,
"error": "Keine Post-Typen gefunden. Bitte erstelle zuerst Post-Typen."
})
profile_picture = await get_user_avatar(session, user_id)
return templates.TemplateResponse("chat_create.html", {
"request": request,
"page": "chat-create",
"session": session,
"post_types": post_types,
"profile_picture": profile_picture
})
@user_router.get("/status", response_class=HTMLResponse)
async def status_page(request: Request):
"""User's status page."""
@@ -3252,6 +3285,271 @@ async def save_all_and_reanalyze(request: Request, background_tasks: BackgroundT
return JSONResponse({"error": str(e)}, status_code=500)
@user_router.post("/api/employee/chat/generate")
async def chat_generate_post(request: Request):
"""Generate initial post from chat message."""
session = require_user_session(request)
if not session:
raise HTTPException(status_code=401, detail="Not authenticated")
try:
data = await request.json()
message = data.get("message", "").strip()
post_type_id = data.get("post_type_id")
if not message:
return JSONResponse({"success": False, "error": "Nachricht erforderlich"})
if not post_type_id:
return JSONResponse({"success": False, "error": "Post-Typ erforderlich"})
user_id = UUID(session.user_id)
# Get post type info
post_type = await db.get_post_type(UUID(post_type_id))
if not post_type:
return JSONResponse({"success": False, "error": "Post-Typ nicht gefunden"})
# Get profile analysis
profile_analysis = await db.get_profile_analysis(user_id)
if not profile_analysis:
return JSONResponse({"success": False, "error": "Profil-Analyse nicht gefunden"})
# Get company strategy if available
company_strategy = None
profile = await db.get_profile(user_id)
if profile and profile.company_id:
company = await db.get_company(profile.company_id)
if company and company.company_strategy:
company_strategy = company.company_strategy
# Get example posts for style reference
linkedin_posts = await db.get_posts_by_type(user_id, UUID(post_type_id))
if len(linkedin_posts) < 3:
linkedin_posts = await db.get_linkedin_posts(user_id)
example_post_texts = [
post.post_text for post in linkedin_posts
if post.post_text and len(post.post_text) > 100
][:10]
# Generate post using writer agent with user's content as primary focus
from src.agents.writer import WriterAgent
writer = WriterAgent()
# Create a topic structure from user's message
topic = {
"title": message[:100],
"fact": message,
"relevance": "User-specified content"
}
# Generate post
post_content = await writer.process(
topic=topic,
profile_analysis=profile_analysis.full_analysis,
example_posts=example_post_texts,
post_type=post_type,
user_thoughts=message, # CRITICAL: User's input as primary content
company_strategy=company_strategy,
strategy_weight=post_type.strategy_weight
)
# Generate conversation ID
import uuid
conversation_id = str(uuid.uuid4())
return JSONResponse({
"success": True,
"post": post_content,
"conversation_id": conversation_id,
"explanation": "Hier ist dein erster Entwurf basierend auf deiner Beschreibung:"
})
except Exception as e:
logger.error(f"Error generating chat post: {e}")
return JSONResponse({"success": False, "error": str(e)}, status_code=500)
@user_router.post("/api/employee/chat/refine")
async def chat_refine_post(request: Request):
"""Refine existing post based on user feedback."""
session = require_user_session(request)
if not session:
raise HTTPException(status_code=401, detail="Not authenticated")
try:
data = await request.json()
message = data.get("message", "").strip()
current_post = data.get("current_post", "")
post_type_id = data.get("post_type_id")
chat_history = data.get("chat_history", [])
if not message:
return JSONResponse({"success": False, "error": "Nachricht erforderlich"})
if not current_post:
return JSONResponse({"success": False, "error": "Kein Post zum Verfeinern vorhanden"})
if not post_type_id:
return JSONResponse({"success": False, "error": "Post-Typ erforderlich"})
user_id = UUID(session.user_id)
# Get post type info
post_type = await db.get_post_type(UUID(post_type_id))
if not post_type:
return JSONResponse({"success": False, "error": "Post-Typ nicht gefunden"})
# Get profile analysis
profile_analysis = await db.get_profile_analysis(user_id)
if not profile_analysis:
return JSONResponse({"success": False, "error": "Profil-Analyse nicht gefunden"})
# Ensure full_analysis is a dict
full_analysis = profile_analysis.full_analysis if profile_analysis.full_analysis else {}
if not isinstance(full_analysis, dict):
logger.warning(f"full_analysis is not a dict: {type(full_analysis)}")
full_analysis = {}
# Get company strategy if available
company_strategy = None
profile = await db.get_profile(user_id)
if profile and profile.company_id:
company = await db.get_company(profile.company_id)
if company and company.company_strategy:
company_strategy = company.company_strategy
# Ensure it's a dict
if not isinstance(company_strategy, dict):
logger.warning(f"company_strategy is not a dict: {type(company_strategy)}")
company_strategy = None
# Get example posts
linkedin_posts = await db.get_posts_by_type(user_id, UUID(post_type_id))
if len(linkedin_posts) < 3:
linkedin_posts = await db.get_linkedin_posts(user_id)
example_post_texts = [
post.post_text for post in linkedin_posts
if post.post_text and len(post.post_text) > 100
][:10]
# Refine post using writer with feedback
from src.agents.writer import WriterAgent
writer = WriterAgent()
topic = {
"title": "Chat refinement",
"fact": message,
"relevance": "User refinement request"
}
# Use writer's revision capability
refined_post = await writer.process(
topic=topic,
profile_analysis=full_analysis,
example_posts=example_post_texts,
feedback=message, # User's refinement instruction
previous_version=current_post,
post_type=post_type,
user_thoughts=message,
company_strategy=company_strategy,
strategy_weight=getattr(post_type, 'strategy_weight', 0.5)
)
return JSONResponse({
"success": True,
"post": refined_post,
"conversation_id": data.get("conversation_id"),
"explanation": "Ich habe den Post angepasst:"
})
except Exception as e:
import traceback
logger.error(f"Error refining chat post: {e}")
logger.error(f"Traceback: {traceback.format_exc()}")
return JSONResponse({"success": False, "error": str(e)}, status_code=500)
@user_router.post("/api/employee/chat/save")
async def chat_save_post(request: Request):
"""Save chat-generated post to database."""
session = require_user_session(request)
if not session:
raise HTTPException(status_code=401, detail="Not authenticated")
try:
data = await request.json()
post_content = data.get("post_content", "").strip()
post_type_id = data.get("post_type_id")
chat_history = data.get("chat_history", [])
if not post_content:
return JSONResponse({"success": False, "error": "Post-Inhalt erforderlich"})
if not post_type_id:
return JSONResponse({"success": False, "error": "Post-Typ erforderlich"})
user_id = UUID(session.user_id)
# Extract title from first sentence of post
first_sentence = post_content.split('\n')[0].strip()
if len(first_sentence) > 100:
title = first_sentence[:97] + "..."
else:
title = first_sentence if first_sentence else "Chat-generierter Post"
# Create GeneratedPost with status draft
from src.database.models import GeneratedPost
import uuid as uuid_lib
post_id = uuid_lib.uuid4()
# Extract all AI-generated versions from chat history
writer_versions = []
critic_feedback_list = []
for item in chat_history:
if 'ai' in item and item['ai']:
writer_versions.append(item['ai'])
# Store user feedback as "critic feedback"
if 'user' in item and item['user']:
critic_feedback_list.append({
'feedback': item['user'],
'explanation': item.get('explanation', '')
})
# Add final version
writer_versions.append(post_content)
num_iterations = len(writer_versions)
generated_post = GeneratedPost(
id=post_id,
user_id=user_id,
post_content=post_content,
post_type_id=UUID(post_type_id),
status="draft",
iterations=num_iterations,
writer_versions=writer_versions, # All iterations saved here
critic_feedback=critic_feedback_list, # User feedback saved here
topic_title=title,
created_at=datetime.now(timezone.utc)
)
saved_post = await db.save_generated_post(generated_post)
return JSONResponse({
"success": True,
"post_id": str(saved_post.id),
"message": "Post erfolgreich gespeichert"
})
except Exception as e:
logger.error(f"Error saving chat post: {e}")
return JSONResponse({"success": False, "error": str(e)}, status_code=500)
@user_router.post("/api/company/invite")
async def send_company_invitation(request: Request):
"""Send invitation to a new employee."""