diff --git a/backend/data/vector_store/.gitkeep b/backend/data/vector_store/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/backend/data/vector_store/faiss_index.bin b/backend/data/vector_store/faiss_index.bin deleted file mode 100644 index c9f990d..0000000 Binary files a/backend/data/vector_store/faiss_index.bin and /dev/null differ diff --git a/backend/data/vector_store/metadata.pkl b/backend/data/vector_store/metadata.pkl deleted file mode 100644 index 7e07f3f..0000000 Binary files a/backend/data/vector_store/metadata.pkl and /dev/null differ diff --git a/backend/main.py b/backend/main.py index 57be807..4aace71 100644 --- a/backend/main.py +++ b/backend/main.py @@ -5,6 +5,7 @@ Provides API endpoints for generating and managing marketing content. import os import json +import glob from typing import Dict, List, Any, Optional from datetime import datetime from pathlib import Path @@ -13,12 +14,15 @@ from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from loguru import logger from pydantic import BaseModel, Field +from sqlalchemy import select, desc, func +from sqlalchemy.sql import Select import config from copywriter import copywriter from vector_store import vector_store from brand_style import brand_style_manager from embeddings import embeddings_manager +from models import database, training_data # Initialize logging logger.add(config.LOG_FILE, level=config.LOG_LEVEL, rotation="10 MB", retention="1 month") @@ -182,30 +186,29 @@ async def add_training_data(request: TrainingDataRequest): } ) - # Add metadata + # Prepare metadata metadata = request.metadata.copy() metadata["content_type"] = request.content_type metadata["added_at"] = datetime.now().isoformat() metadata["training_data"] = True - # Add to vector store - doc_ids = await vector_store.add_documents([request.content], [metadata]) + # Add to database + query = training_data.insert().values( + content=request.content, + content_type=request.content_type, + metadata=metadata, + added_at=datetime.now(), + is_training_data=True + ) + data_id = await database.execute(query) - # Save to past campaigns - campaign_path = Path(config.DATA_DIR) / "past_campaigns" / f"{datetime.now().strftime('%Y%m%d%H%M%S')}.json" - with open(campaign_path, 'w') as f: - json.dump({ - "content": request.content, - "content_type": request.content_type, - "metadata": metadata, - "document_id": doc_ids[0] if doc_ids else None, - "timestamp": datetime.now().isoformat() - }, f, indent=2) + # Add to vector store for search functionality + doc_ids = await vector_store.add_documents([request.content], [metadata]) return { "status": "success", "message": "Training data added successfully", - "data_id": doc_ids[0] if doc_ids else None + "data_id": data_id } except Exception as e: logger.error(f"Error adding training data: {str(e)}") @@ -222,8 +225,9 @@ async def list_training_data( ): """Retrieve a list of available training data.""" try: - # Build filters - filters = {} + # Build base query + base_query = select(training_data).where(training_data.c.is_training_data == True) + if content_type: if content_type not in config.CONTENT_TYPES: return JSONResponse( @@ -233,38 +237,31 @@ async def list_training_data( "message": f"Invalid content_type. Must be one of: {', '.join(config.CONTENT_TYPES)}" } ) - filters["content_type"] = content_type + base_query = base_query.where(training_data.c.content_type == content_type) - filters["training_data"] = True + # Count total records + count_query = select(func.count()).select_from(training_data).where(training_data.c.is_training_data == True) + if content_type: + count_query = count_query.where(training_data.c.content_type == content_type) + total = await database.fetch_val(count_query) - # Fetch all matching documents first (not efficient for large datasets but works for demo) - all_docs = [] - for i in range(len(vector_store.metadata)): - doc = await vector_store.get_document(i) - if doc and all(doc["metadata"].get(k) == v for k, v in filters.items()): - all_docs.append(doc) + # Add pagination + query = base_query.order_by(training_data.c.added_at.desc()) \ + .offset((page - 1) * limit) \ + .limit(limit) - # Sort by timestamp (newest first) - all_docs.sort(key=lambda x: x["metadata"].get("added_at", ""), reverse=True) + # Execute query + records = await database.fetch_all(query) - # Paginate - total = len(all_docs) - pages = (total + limit - 1) // limit if total > 0 else 1 - start = (page - 1) * limit - end = start + limit - paginated_docs = all_docs[start:end] - - # Format the response + # Format response items = [] - for doc in paginated_docs: - # Get a preview of the text (first 100 characters) - preview = doc["text"][:100] + "..." if len(doc["text"]) > 100 else doc["text"] - + for record in records: + preview = record["content"][:100] + "..." if len(record["content"]) > 100 else record["content"] items.append({ - "id": doc["document_id"], - "content_type": doc["metadata"].get("content_type", "unknown"), + "id": record["id"], + "content_type": record["content_type"], "preview": preview, - "added_at": doc["metadata"].get("added_at", "") + "added_at": record["added_at"].isoformat() }) return { @@ -273,7 +270,7 @@ async def list_training_data( "total": total, "page": page, "limit": limit, - "pages": pages + "pages": (total + limit - 1) // limit } } except Exception as e: @@ -283,21 +280,25 @@ async def list_training_data( detail=f"Failed to list training data: {str(e)}" ) -@app.get("/training-data/{document_id}") -async def get_training_data(document_id: int): +@app.get("/training-data/{data_id}") +async def get_training_data(data_id: int): """Retrieve a specific training document by ID.""" try: - doc = await vector_store.get_document(document_id) - if not doc: + query = select([training_data]).where(training_data.c.id == data_id) + record = await database.fetch_one(query) + + if not record: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, - detail=f"Document with ID {document_id} not found" + detail=f"Document with ID {data_id} not found" ) return { - "id": doc["document_id"], - "content": doc["text"], - "metadata": doc["metadata"] + "id": record["id"], + "content": record["content"], + "content_type": record["content_type"], + "metadata": record["metadata"], + "added_at": record["added_at"].isoformat() } except HTTPException: raise @@ -308,20 +309,25 @@ async def get_training_data(document_id: int): detail=f"Failed to retrieve training data: {str(e)}" ) -@app.delete("/training-data/{document_id}") -async def delete_training_data(document_id: int): +@app.delete("/training-data/{data_id}") +async def delete_training_data(data_id: int): """Delete a specific training document by ID.""" try: - success = await vector_store.delete_document(document_id) - if not success: + query = training_data.delete().where(training_data.c.id == data_id) + result = await database.execute(query) + + if not result: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, - detail=f"Document with ID {document_id} not found or could not be deleted" + detail=f"Document with ID {data_id} not found or could not be deleted" ) + # Also remove from vector store + await vector_store.delete_document(data_id) + return { "status": "success", - "message": f"Document with ID {document_id} successfully deleted" + "message": f"Document with ID {data_id} successfully deleted" } except HTTPException: raise @@ -371,6 +377,85 @@ async def analyze_content(content: str = Body(..., embed=True)): detail=f"Failed to analyze content: {str(e)}" ) +@app.get("/user-queries") +async def list_user_queries( + page: int = Query(1, ge=1, description="Page number"), + limit: int = Query(10, ge=1, le=100, description="Items per page") +): + """Retrieve a list of user queries.""" + try: + # Get all query files + query_files = glob.glob(str(Path(config.DATA_DIR) / "user_queries" / "*.json")) + query_files.sort(reverse=True) # Sort by filename (timestamp) descending + + # Apply pagination + start_idx = (page - 1) * limit + end_idx = start_idx + limit + page_files = query_files[start_idx:end_idx] + + items = [] + for file_path in page_files: + with open(file_path, 'r') as f: + query_data = json.load(f) + items.append(query_data) + + return { + "items": items, + "total": len(query_files), + "page": page, + "limit": limit + } + except Exception as e: + logger.error(f"Error listing user queries: {str(e)}") + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Failed to list user queries: {str(e)}" + ) + +@app.get("/user-queries/{timestamp}") +async def get_user_query(timestamp: str): + """Retrieve a specific user query by timestamp.""" + try: + file_path = Path(config.DATA_DIR) / "user_queries" / f"{timestamp}.json" + if not file_path.exists(): + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Query with timestamp {timestamp} not found" + ) + + with open(file_path, 'r') as f: + return json.load(f) + except Exception as e: + logger.error(f"Error getting user query: {str(e)}") + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Failed to get user query: {str(e)}" + ) + +@app.delete("/user-queries/{timestamp}") +async def delete_user_query(timestamp: str): + """Delete a specific user query by timestamp.""" + try: + file_path = Path(config.DATA_DIR) / "user_queries" / f"{timestamp}.json" + if not file_path.exists(): + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Query with timestamp {timestamp} not found" + ) + + file_path.unlink() # Delete the file + + return { + "status": "success", + "message": f"Query with timestamp {timestamp} successfully deleted" + } + except Exception as e: + logger.error(f"Error deleting user query: {str(e)}") + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Failed to delete user query: {str(e)}" + ) + # Run the application if __name__ == "__main__": import uvicorn @@ -379,4 +464,4 @@ if __name__ == "__main__": host=config.API_HOST, port=config.API_PORT, reload=True - ) \ No newline at end of file + ) diff --git a/backend/models.py b/backend/models.py new file mode 100644 index 0000000..4b435e1 --- /dev/null +++ b/backend/models.py @@ -0,0 +1,23 @@ +from datetime import datetime +from sqlalchemy import Column, Integer, String, JSON, DateTime, Boolean, MetaData, Table, create_engine +from databases import Database +from config import DATA_DIR + +DATABASE_URL = f"sqlite:///{DATA_DIR}/training_data.db" +database = Database(DATABASE_URL) +metadata = MetaData() + +training_data = Table( + "training_data", + metadata, + Column("id", Integer, primary_key=True), + Column("content", String, nullable=False), + Column("content_type", String, nullable=False), + Column("metadata", JSON, nullable=False), + Column("added_at", DateTime, nullable=False, default=datetime.utcnow), + Column("is_training_data", Boolean, nullable=False, default=True) +) + +# Create tables +engine = create_engine(DATABASE_URL) +metadata.create_all(engine) \ No newline at end of file diff --git a/data/training_data.db b/data/training_data.db new file mode 100644 index 0000000..1691c6d Binary files /dev/null and b/data/training_data.db differ diff --git a/data/vector_store/faiss_index.bin b/data/vector_store/faiss_index.bin new file mode 100644 index 0000000..44c1eb2 Binary files /dev/null and b/data/vector_store/faiss_index.bin differ diff --git a/data/vector_store/metadata.pkl b/data/vector_store/metadata.pkl new file mode 100644 index 0000000..2ca0503 Binary files /dev/null and b/data/vector_store/metadata.pkl differ diff --git a/frontend/app.js b/frontend/app.js index 99cc6fe..17e6711 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -507,5 +507,5 @@ document.addEventListener('DOMContentLoaded', function() { // For demonstration purposes, let's create a mocked pre-filled content // In a real implementation, this would be loaded from the backend - document.getElementById('prompt').value = 'Write a social media post about our new coaching program'; + document.getElementById('prompt').value = 'Generate an email campaign for a product launch'; }); diff --git a/frontend/index.html b/frontend/index.html index 67b7a8e..76be6f3 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -48,7 +48,7 @@
- +
@@ -84,12 +84,12 @@
diff --git a/logs/app.log b/logs/app.log index 5f2649b..2412080 100644 --- a/logs/app.log +++ b/logs/app.log @@ -774,3 +774,111 @@ 2025-04-18 04:22:36.139 | INFO | copywriter:generate_copy:90 - Generated content with 3409 characters 2025-04-18 04:22:36.945 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store 2025-04-18 04:22:36.945 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 16:07:13.340 | ERROR | main:list_training_data:268 - Error listing training data: Column expression, FROM clause, or other columns clause element expected, got [Table('training_data', MetaData(), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('content', String(), table=, nullable=False), Column('content_type', String(), table=, nullable=False), Column('metadata', JSON(), table=, nullable=False), Column('added_at', DateTime(), table=, nullable=False, default=CallableColumnDefault()), Column('is_training_data', Boolean(), table=, nullable=False, default=ScalarElementColumnDefault(True)), schema=None)]. Did you mean to say select(Table('training_data', MetaData(), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('content', String(), table=, nullable=False), Column('content_type', String(), table=, nullable=False), Column('metadata', JSON(), table=, nullable=False), Column('added_at', DateTime(), table=, nullable=False, default=CallableColumnDefault()), Column('is_training_data', Boolean(), table=, nullable=False, default=ScalarElementColumnDefault(True)), schema=None))? +2025-04-18 16:07:13.340 | ERROR | main:list_training_data:268 - Error listing training data: Column expression, FROM clause, or other columns clause element expected, got [Table('training_data', MetaData(), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('content', String(), table=, nullable=False), Column('content_type', String(), table=, nullable=False), Column('metadata', JSON(), table=, nullable=False), Column('added_at', DateTime(), table=, nullable=False, default=CallableColumnDefault()), Column('is_training_data', Boolean(), table=, nullable=False, default=ScalarElementColumnDefault(True)), schema=None)]. Did you mean to say select(Table('training_data', MetaData(), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('content', String(), table=, nullable=False), Column('content_type', String(), table=, nullable=False), Column('metadata', JSON(), table=, nullable=False), Column('added_at', DateTime(), table=, nullable=False, default=CallableColumnDefault()), Column('is_training_data', Boolean(), table=, nullable=False, default=ScalarElementColumnDefault(True)), schema=None))? +2025-04-18 16:08:07.769 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 16:08:07.769 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 16:08:07.772 | INFO | copywriter:generate_copy:90 - Generated content with 651 characters +2025-04-18 16:08:07.772 | INFO | copywriter:generate_copy:90 - Generated content with 651 characters +2025-04-18 16:08:09.329 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 16:08:09.329 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 16:08:47.520 | ERROR | main:list_training_data:268 - Error listing training data: Column expression, FROM clause, or other columns clause element expected, got [Table('training_data', MetaData(), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('content', String(), table=, nullable=False), Column('content_type', String(), table=, nullable=False), Column('metadata', JSON(), table=, nullable=False), Column('added_at', DateTime(), table=, nullable=False, default=CallableColumnDefault()), Column('is_training_data', Boolean(), table=, nullable=False, default=ScalarElementColumnDefault(True)), schema=None)]. Did you mean to say select(Table('training_data', MetaData(), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('content', String(), table=, nullable=False), Column('content_type', String(), table=, nullable=False), Column('metadata', JSON(), table=, nullable=False), Column('added_at', DateTime(), table=, nullable=False, default=CallableColumnDefault()), Column('is_training_data', Boolean(), table=, nullable=False, default=ScalarElementColumnDefault(True)), schema=None))? +2025-04-18 16:08:47.520 | ERROR | main:list_training_data:268 - Error listing training data: Column expression, FROM clause, or other columns clause element expected, got [Table('training_data', MetaData(), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('content', String(), table=, nullable=False), Column('content_type', String(), table=, nullable=False), Column('metadata', JSON(), table=, nullable=False), Column('added_at', DateTime(), table=, nullable=False, default=CallableColumnDefault()), Column('is_training_data', Boolean(), table=, nullable=False, default=ScalarElementColumnDefault(True)), schema=None)]. Did you mean to say select(Table('training_data', MetaData(), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('content', String(), table=, nullable=False), Column('content_type', String(), table=, nullable=False), Column('metadata', JSON(), table=, nullable=False), Column('added_at', DateTime(), table=, nullable=False, default=CallableColumnDefault()), Column('is_training_data', Boolean(), table=, nullable=False, default=ScalarElementColumnDefault(True)), schema=None))? +2025-04-18 16:09:56.223 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 16:09:56.223 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 16:10:00.678 | ERROR | main:list_training_data:268 - Error listing training data: Column expression, FROM clause, or other columns clause element expected, got [Table('training_data', MetaData(), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('content', String(), table=, nullable=False), Column('content_type', String(), table=, nullable=False), Column('metadata', JSON(), table=, nullable=False), Column('added_at', DateTime(), table=, nullable=False, default=CallableColumnDefault()), Column('is_training_data', Boolean(), table=, nullable=False, default=ScalarElementColumnDefault(True)), schema=None)]. Did you mean to say select(Table('training_data', MetaData(), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('content', String(), table=, nullable=False), Column('content_type', String(), table=, nullable=False), Column('metadata', JSON(), table=, nullable=False), Column('added_at', DateTime(), table=, nullable=False, default=CallableColumnDefault()), Column('is_training_data', Boolean(), table=, nullable=False, default=ScalarElementColumnDefault(True)), schema=None))? +2025-04-18 16:10:00.678 | ERROR | main:list_training_data:268 - Error listing training data: Column expression, FROM clause, or other columns clause element expected, got [Table('training_data', MetaData(), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('content', String(), table=, nullable=False), Column('content_type', String(), table=, nullable=False), Column('metadata', JSON(), table=, nullable=False), Column('added_at', DateTime(), table=, nullable=False, default=CallableColumnDefault()), Column('is_training_data', Boolean(), table=, nullable=False, default=ScalarElementColumnDefault(True)), schema=None)]. Did you mean to say select(Table('training_data', MetaData(), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('content', String(), table=, nullable=False), Column('content_type', String(), table=, nullable=False), Column('metadata', JSON(), table=, nullable=False), Column('added_at', DateTime(), table=, nullable=False, default=CallableColumnDefault()), Column('is_training_data', Boolean(), table=, nullable=False, default=ScalarElementColumnDefault(True)), schema=None))? +2025-04-18 16:14:28.677 | ERROR | main:list_training_data:268 - Error listing training data: Column expression, FROM clause, or other columns clause element expected, got [Table('training_data', MetaData(), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('content', String(), table=, nullable=False), Column('content_type', String(), table=, nullable=False), Column('metadata', JSON(), table=, nullable=False), Column('added_at', DateTime(), table=, nullable=False, default=CallableColumnDefault()), Column('is_training_data', Boolean(), table=, nullable=False, default=ScalarElementColumnDefault(True)), schema=None)]. Did you mean to say select(Table('training_data', MetaData(), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('content', String(), table=, nullable=False), Column('content_type', String(), table=, nullable=False), Column('metadata', JSON(), table=, nullable=False), Column('added_at', DateTime(), table=, nullable=False, default=CallableColumnDefault()), Column('is_training_data', Boolean(), table=, nullable=False, default=ScalarElementColumnDefault(True)), schema=None))? +2025-04-18 16:14:28.677 | ERROR | main:list_training_data:268 - Error listing training data: Column expression, FROM clause, or other columns clause element expected, got [Table('training_data', MetaData(), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('content', String(), table=, nullable=False), Column('content_type', String(), table=, nullable=False), Column('metadata', JSON(), table=, nullable=False), Column('added_at', DateTime(), table=, nullable=False, default=CallableColumnDefault()), Column('is_training_data', Boolean(), table=, nullable=False, default=ScalarElementColumnDefault(True)), schema=None)]. Did you mean to say select(Table('training_data', MetaData(), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('content', String(), table=, nullable=False), Column('content_type', String(), table=, nullable=False), Column('metadata', JSON(), table=, nullable=False), Column('added_at', DateTime(), table=, nullable=False, default=CallableColumnDefault()), Column('is_training_data', Boolean(), table=, nullable=False, default=ScalarElementColumnDefault(True)), schema=None))? +2025-04-18 16:16:04.245 | ERROR | main:list_training_data:268 - Error listing training data: Column expression, FROM clause, or other columns clause element expected, got [Table('training_data', MetaData(), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('content', String(), table=, nullable=False), Column('content_type', String(), table=, nullable=False), Column('metadata', JSON(), table=, nullable=False), Column('added_at', DateTime(), table=, nullable=False, default=CallableColumnDefault()), Column('is_training_data', Boolean(), table=, nullable=False, default=ScalarElementColumnDefault(True)), schema=None)]. Did you mean to say select(Table('training_data', MetaData(), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('content', String(), table=, nullable=False), Column('content_type', String(), table=, nullable=False), Column('metadata', JSON(), table=, nullable=False), Column('added_at', DateTime(), table=, nullable=False, default=CallableColumnDefault()), Column('is_training_data', Boolean(), table=, nullable=False, default=ScalarElementColumnDefault(True)), schema=None))? +2025-04-18 16:16:04.245 | ERROR | main:list_training_data:268 - Error listing training data: Column expression, FROM clause, or other columns clause element expected, got [Table('training_data', MetaData(), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('content', String(), table=, nullable=False), Column('content_type', String(), table=, nullable=False), Column('metadata', JSON(), table=, nullable=False), Column('added_at', DateTime(), table=, nullable=False, default=CallableColumnDefault()), Column('is_training_data', Boolean(), table=, nullable=False, default=ScalarElementColumnDefault(True)), schema=None)]. Did you mean to say select(Table('training_data', MetaData(), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('content', String(), table=, nullable=False), Column('content_type', String(), table=, nullable=False), Column('metadata', JSON(), table=, nullable=False), Column('added_at', DateTime(), table=, nullable=False, default=CallableColumnDefault()), Column('is_training_data', Boolean(), table=, nullable=False, default=ScalarElementColumnDefault(True)), schema=None))? +2025-04-18 16:19:37.169 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 16:19:37.169 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 16:22:09.127 | WARNING | vector_store:delete_document:246 - Invalid document ID: 1 +2025-04-18 16:22:09.127 | WARNING | vector_store:delete_document:246 - Invalid document ID: 1 +2025-04-18 16:22:27.719 | WARNING | vector_store:delete_document:246 - Invalid document ID: 2 +2025-04-18 16:22:27.719 | WARNING | vector_store:delete_document:246 - Invalid document ID: 2 +2025-04-18 16:30:22.904 | INFO | vector_store:search:212 - Found 1 matching documents for query +2025-04-18 16:30:22.904 | INFO | vector_store:search:212 - Found 1 matching documents for query +2025-04-18 16:30:31.859 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 16:30:31.859 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 16:30:31.859 | INFO | copywriter:generate_copy:90 - Generated content with 604 characters +2025-04-18 16:30:31.859 | INFO | copywriter:generate_copy:90 - Generated content with 604 characters +2025-04-18 16:30:32.289 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 16:30:32.289 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 16:32:33.304 | INFO | vector_store:search:212 - Found 2 matching documents for query +2025-04-18 16:32:33.304 | INFO | vector_store:search:212 - Found 2 matching documents for query +2025-04-18 16:32:42.281 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 16:32:42.281 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 16:32:42.283 | INFO | copywriter:generate_copy:90 - Generated content with 632 characters +2025-04-18 16:32:42.283 | INFO | copywriter:generate_copy:90 - Generated content with 632 characters +2025-04-18 16:32:42.750 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 16:32:42.750 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 17:12:33.909 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 17:12:33.909 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 17:12:33.912 | INFO | copywriter:generate_copy:90 - Generated content with 2740 characters +2025-04-18 17:12:33.912 | INFO | copywriter:generate_copy:90 - Generated content with 2740 characters +2025-04-18 17:12:37.538 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 17:12:37.538 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 17:13:04.600 | INFO | vector_store:search:212 - Found 3 matching documents for query +2025-04-18 17:13:04.600 | INFO | vector_store:search:212 - Found 3 matching documents for query +2025-04-18 17:13:17.051 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 17:13:17.051 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 17:13:17.052 | INFO | copywriter:generate_copy:90 - Generated content with 577 characters +2025-04-18 17:13:17.052 | INFO | copywriter:generate_copy:90 - Generated content with 577 characters +2025-04-18 17:13:17.652 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 17:13:17.652 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 17:14:08.039 | INFO | vector_store:search:212 - Found 3 matching documents for query +2025-04-18 17:14:08.039 | INFO | vector_store:search:212 - Found 3 matching documents for query +2025-04-18 17:14:33.729 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 17:14:33.729 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 17:14:33.741 | INFO | copywriter:generate_copy:90 - Generated content with 1717 characters +2025-04-18 17:14:33.741 | INFO | copywriter:generate_copy:90 - Generated content with 1717 characters +2025-04-18 17:14:34.184 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 17:14:34.184 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 17:28:38.798 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 17:28:38.798 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 17:28:38.800 | INFO | copywriter:generate_copy:90 - Generated content with 1962 characters +2025-04-18 17:28:38.800 | INFO | copywriter:generate_copy:90 - Generated content with 1962 characters +2025-04-18 17:28:39.569 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 17:28:39.569 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 17:29:13.466 | INFO | vector_store:search:212 - Found 3 matching documents for query +2025-04-18 17:29:13.466 | INFO | vector_store:search:212 - Found 3 matching documents for query +2025-04-18 17:29:30.844 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 17:29:30.844 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 17:29:30.845 | INFO | copywriter:generate_copy:90 - Generated content with 1416 characters +2025-04-18 17:29:30.845 | INFO | copywriter:generate_copy:90 - Generated content with 1416 characters +2025-04-18 17:29:31.237 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 17:29:31.237 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 17:31:18.658 | ERROR | main:get_user_query:429 - Error getting user query: 404: Query with timestamp 2025-04-18T16:32:42.751434 not found +2025-04-18 17:31:18.658 | ERROR | main:get_user_query:429 - Error getting user query: 404: Query with timestamp 2025-04-18T16:32:42.751434 not found +2025-04-18 17:31:40.805 | ERROR | main:get_user_query:429 - Error getting user query: 404: Query with timestamp "2025-04-18T16:32:42.751434" not found +2025-04-18 17:31:40.805 | ERROR | main:get_user_query:429 - Error getting user query: 404: Query with timestamp "2025-04-18T16:32:42.751434" not found +2025-04-18 17:32:24.114 | ERROR | main:get_user_query:429 - Error getting user query: 404: Query with timestamp 2025-04-18T17:12:37.541148 not found +2025-04-18 17:32:24.114 | ERROR | main:get_user_query:429 - Error getting user query: 404: Query with timestamp 2025-04-18T17:12:37.541148 not found +2025-04-18 17:34:18.118 | ERROR | main:get_user_query:429 - Error getting user query: 404: Query with timestamp 20250418T171237541148 not found +2025-04-18 17:34:18.118 | ERROR | main:get_user_query:429 - Error getting user query: 404: Query with timestamp 20250418T171237541148 not found +2025-04-18 17:34:40.614 | ERROR | main:get_user_query:429 - Error getting user query: 404: Query with timestamp 20250418T035226 not found +2025-04-18 17:34:40.614 | ERROR | main:get_user_query:429 - Error getting user query: 404: Query with timestamp 20250418T035226 not found +2025-04-18 17:35:36.594 | ERROR | main:get_user_query:429 - Error getting user query: 404: Query with timestamp 20250418171317. not found +2025-04-18 17:35:36.594 | ERROR | main:get_user_query:429 - Error getting user query: 404: Query with timestamp 20250418171317. not found +2025-04-18 17:36:13.263 | ERROR | main:get_user_query:429 - Error getting user query: 404: Query with timestamp 2025-04-18T17:13:17 not found +2025-04-18 17:36:13.263 | ERROR | main:get_user_query:429 - Error getting user query: 404: Query with timestamp 2025-04-18T17:13:17 not found +2025-04-18 17:36:23.158 | ERROR | main:get_user_query:429 - Error getting user query: 404: Query with timestamp 20250418T171317 not found +2025-04-18 17:36:23.158 | ERROR | main:get_user_query:429 - Error getting user query: 404: Query with timestamp 20250418T171317 not found +2025-04-18 17:40:24.503 | INFO | vector_store:search:212 - Found 3 matching documents for query +2025-04-18 17:40:24.503 | INFO | vector_store:search:212 - Found 3 matching documents for query +2025-04-18 17:40:54.614 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 17:40:54.614 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 17:40:54.615 | INFO | copywriter:generate_copy:90 - Generated content with 2080 characters +2025-04-18 17:40:54.615 | INFO | copywriter:generate_copy:90 - Generated content with 2080 characters +2025-04-18 17:40:55.135 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 17:40:55.135 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 17:41:16.349 | INFO | vector_store:search:212 - Found 3 matching documents for query +2025-04-18 17:41:16.349 | INFO | vector_store:search:212 - Found 3 matching documents for query +2025-04-18 17:41:55.042 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 17:41:55.042 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 17:41:55.046 | INFO | copywriter:generate_copy:90 - Generated content with 2070 characters +2025-04-18 17:41:55.046 | INFO | copywriter:generate_copy:90 - Generated content with 2070 characters +2025-04-18 17:41:55.458 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 17:41:55.458 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store