diff --git a/.gitignore b/.gitignore index 5d1220a..a9a9a5c 100644 --- a/.gitignore +++ b/.gitignore @@ -36,8 +36,10 @@ ENV/ # Local data data/user_queries/* !data/user_queries/.gitkeep -backend/data/vector_store/* -!backend/data/vector_store/.gitkeep +data/past_campaigns/* +!data/past_campaigns/.gitkeep +data/vector_store/* +data/training_data/* # Logs logs/* diff --git a/backend/main.py b/backend/main.py index 4aace71..3c76b7c 100644 --- a/backend/main.py +++ b/backend/main.py @@ -382,28 +382,36 @@ 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.""" + """List user queries with pagination.""" 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 + # Calculate offset + offset = (page - 1) * limit + + # Get files from user_queries directory + query_dir = Path(config.DATA_DIR) / "user_queries" + query_dir.mkdir(exist_ok=True) + + # List all JSON files and sort by name (timestamp) in descending order + files = sorted(query_dir.glob("*.json"), reverse=True) + total = len(files) # Apply pagination - start_idx = (page - 1) * limit - end_idx = start_idx + limit - page_files = query_files[start_idx:end_idx] + files = files[offset:offset + limit] items = [] - for file_path in page_files: - with open(file_path, 'r') as f: + for file in files: + with open(file, 'r') as f: query_data = json.load(f) items.append(query_data) return { "items": items, - "total": len(query_files), - "page": page, - "limit": limit + "pagination": { + "total": total, + "page": page, + "limit": limit, + "pages": (total + limit - 1) // limit + } } except Exception as e: logger.error(f"Error listing user queries: {str(e)}") diff --git a/data/training_data.db b/data/training_data.db index 1691c6d..189ca4e 100644 Binary files a/data/training_data.db and b/data/training_data.db differ diff --git a/data/vector_store/faiss_index.bin b/data/vector_store/faiss_index.bin index 44c1eb2..ea6344d 100644 Binary files a/data/vector_store/faiss_index.bin and b/data/vector_store/faiss_index.bin differ diff --git a/data/vector_store/metadata.pkl b/data/vector_store/metadata.pkl index 2ca0503..78ae91a 100644 Binary files a/data/vector_store/metadata.pkl and b/data/vector_store/metadata.pkl differ diff --git a/frontend/app.js b/frontend/app.js index 17e6711..32444ca 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -1,3 +1,4 @@ + // DOM Elements document.addEventListener('DOMContentLoaded', function() { // Navigation @@ -23,6 +24,11 @@ document.addEventListener('DOMContentLoaded', function() { const improvementFeedback = document.getElementById('improvement-feedback'); const submitImprovement = document.getElementById('submit-improvement'); + // History Page + const historyFilterType = document.getElementById('history-filter-type'); + const historySearch = document.getElementById('history-search'); + const historyList = document.querySelector('.history-list'); + // Brand Style Page const toneSelector = document.getElementById('tone-selector'); const voiceSelector = document.getElementById('voice-selector'); @@ -45,6 +51,9 @@ document.addEventListener('DOMContentLoaded', function() { const openRate = document.getElementById('open-rate'); const clickRate = document.getElementById('click-rate'); const conversionRate = document.getElementById('conversion-rate'); + const trainingFilterType = document.getElementById('training-filter-type'); + const trainingSearch = document.getElementById('training-search'); + const trainingList = document.querySelector('.training-list'); // API Base URL const API_URL = 'http://localhost:8000'; @@ -62,6 +71,16 @@ document.addEventListener('DOMContentLoaded', function() { pages.forEach(page => { if (page.id === `${pageName}-page`) { page.classList.add('active'); + + // Load data for specific pages when they're opened + if (pageName === 'history') { + loadUserQueries(); + } else if (pageName === 'training') { + // Check if the view training tab is active + if (document.querySelector('.tab[data-tab="view-training"]').classList.contains('active')) { + loadTrainingData(); + } + } } else { page.classList.remove('active'); } @@ -226,8 +245,278 @@ document.addEventListener('DOMContentLoaded', function() { if (saveBtn) { saveBtn.addEventListener('click', function() { alert('Content saved to history!'); - // In a real implementation, you would save this to local storage - // or call an API to save it to the backend + // Note: The backend automatically saves the query as part of the generate-copy endpoint + // so we don't need to make another API call here + }); + } + + // Load User Queries (History) + function loadUserQueries(page = 1, contentType = '') { + if (!historyList) return; + + // Show loading state + historyList.innerHTML = '
Loading history...
'; + + // Build the query parameters + let queryParams = `?page=${page}&limit=10`; + + // Call the API + fetch(`${API_URL}/user-queries${queryParams}`) + .then(response => { + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + return response.json(); + }) + .then(data => { + historyList.innerHTML = ''; + + if (data.items.length === 0) { + historyList.innerHTML = '
No history found.
'; + return; + } + + // Filter by content type if provided + let filteredItems = data.items; + if (contentType) { + filteredItems = data.items.filter(item => + item.parameters && item.parameters.content_type === contentType + ); + } + + // Create history items + filteredItems.forEach(item => { + const contentType = item.parameters?.content_type || 'general'; + const timestamp = item.timestamp ? new Date(item.timestamp).toLocaleDateString() : 'Unknown date'; + const promptPreview = item.prompt.length > 80 ? item.prompt.substring(0, 80) + '...' : item.prompt; + + const historyItem = document.createElement('div'); + historyItem.className = 'history-item'; + historyItem.innerHTML = ` +
${getContentTypeLabel(contentType)}
+
+

${getPromptTitle(item.prompt)}

+

${promptPreview}

+
+
${timestamp}
+
+ + +
+ `; + + historyList.appendChild(historyItem); + }); + + // Add event listeners for view and delete buttons + document.querySelectorAll('.view-query').forEach(btn => { + btn.addEventListener('click', function() { + const timestamp = this.getAttribute('data-timestamp'); + viewUserQuery(timestamp); + }); + }); + + document.querySelectorAll('.delete-query').forEach(btn => { + btn.addEventListener('click', function() { + const timestamp = this.getAttribute('data-timestamp'); + if (confirm('Are you sure you want to delete this query?')) { + deleteUserQuery(timestamp); + } + }); + }); + + // Add pagination if needed + if (data.pagination && data.pagination.pages > 1) { + // Remove existing pagination if any + const existingPagination = document.querySelector('.pagination'); + if (existingPagination) { + existingPagination.remove(); + } + + const paginationElement = document.createElement('div'); + paginationElement.className = 'pagination'; + + let paginationHTML = ''; + for (let i = 1; i <= data.pagination.pages; i++) { + paginationHTML += ``; + } + + paginationElement.innerHTML = paginationHTML; + historyList.after(paginationElement); + + // Add event listeners for pagination buttons + document.querySelectorAll('.page-btn').forEach(btn => { + btn.addEventListener('click', function() { + const pageNum = parseInt(this.getAttribute('data-page')); + loadUserQueries(pageNum, contentType); + }); + }); + } + }) + .catch(error => { + console.error('Error loading user queries:', error); + historyList.innerHTML = '
Error loading history. Please try again.
'; + }); + } + + // Helper function to extract timestamp from ISO date + function getTimestampFromISODate(isoDate) { + if (!isoDate) return ''; + const date = new Date(isoDate); + return date.toISOString().replace(/[-:T.]/g, '').slice(0, 14); + } + + // Helper function to generate a title from prompt + function getPromptTitle(prompt) { + if (!prompt) return 'Untitled Query'; + const words = prompt.split(' '); + if (words.length <= 5) return prompt; + return words.slice(0, 5).join(' ') + '...'; + } + + // Helper function to get display label for content type + function getContentTypeLabel(contentType) { + if (!contentType) return 'General'; + + const labels = { + 'email': 'Email', + 'social_media': 'Social', + 'blog_post': 'Blog', + 'website_copy': 'Website', + 'sales_copy': 'Sales', + 'ad_copy': 'Ad', + 'video_script': 'Video', + 'case_study': 'Case Study', + 'product_description': 'Product', + 'landing_page': 'Landing', + 'press_release': 'Press', + 'newsletter': 'Newsletter', + 'general': 'General' + }; + + return labels[contentType] || contentType.charAt(0).toUpperCase() + contentType.slice(1); + } + + // View User Query + function viewUserQuery(timestamp) { + fetch(`${API_URL}/user-queries/${timestamp}`) + .then(response => { + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + return response.json(); + }) + .then(data => { + // Create a modal to display the query details + const modal = document.createElement('div'); + modal.className = 'modal'; + + const modalContent = document.createElement('div'); + modalContent.className = 'modal-content'; + + const parameters = data.parameters || {}; + const contentType = parameters.content_type || 'Not specified'; + const length = parameters.length || 'Not specified'; + const includeCTA = parameters.include_cta ? 'Yes' : 'No'; + + modalContent.innerHTML = ` + + + `; + + modal.appendChild(modalContent); + document.body.appendChild(modal); + + // Close button functionality + modal.querySelector('.modal-close').addEventListener('click', function() { + document.body.removeChild(modal); + }); + + // Close when clicking outside the modal + window.addEventListener('click', function(event) { + if (event.target === modal) { + document.body.removeChild(modal); + } + }); + }) + .catch(error => { + console.error('Error viewing user query:', error); + alert('Error viewing query details. Please try again.'); + }); + } + + // Delete User Query + function deleteUserQuery(timestamp) { + fetch(`${API_URL}/user-queries/${timestamp}`, { + method: 'DELETE' + }) + .then(response => { + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + return response.json(); + }) + .then(data => { + alert('Query successfully deleted.'); + + // Reload the user queries + loadUserQueries(); + }) + .catch(error => { + console.error('Error deleting user query:', error); + alert('Error deleting query. Please try again.'); + }); + } + + // History Filter Handlers + if (historyFilterType) { + historyFilterType.addEventListener('change', function() { + loadUserQueries(1, this.value); + }); + } + + if (historySearch) { + historySearch.addEventListener('input', function() { + // Client-side filtering - this would ideally be server-side, + // but we'll implement a simple client-side filter for now + const searchTerm = this.value.toLowerCase(); + + document.querySelectorAll('.history-item').forEach(item => { + const content = item.querySelector('.history-item-content').textContent.toLowerCase(); + if (content.includes(searchTerm)) { + item.style.display = 'flex'; + } else { + item.style.display = 'none'; + } + }); }); } @@ -408,6 +697,11 @@ document.addEventListener('DOMContentLoaded', function() { tabContents.forEach(content => { if (content.id === `${tabName}-tab`) { content.classList.add('active'); + + // Load training data when the View tab is selected + if (tabName === 'view-training') { + loadTrainingData(); + } } else { content.classList.remove('active'); } @@ -479,8 +773,6 @@ document.addEventListener('DOMContentLoaded', function() { // Switch to view tab document.querySelector('.tab[data-tab="view-training"]').click(); - - // In a real implementation, you would also refresh the training data list }) .catch(error => { console.error('Error:', error); @@ -489,6 +781,257 @@ document.addEventListener('DOMContentLoaded', function() { }); } + // Load Training Data + function loadTrainingData(page = 1, contentType = '') { + if (!trainingList) return; + + // Show loading state + trainingList.innerHTML = '
Loading training data...
'; + + // Build the query parameters + let queryParams = `?page=${page}&limit=10`; + if (contentType) { + queryParams += `&content_type=${contentType}`; + } + + // Call the API + fetch(`${API_URL}/training-data${queryParams}`) + .then(response => { + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + return response.json(); + }) + .then(data => { + trainingList.innerHTML = ''; + + if (data.items.length === 0) { + trainingList.innerHTML = '
No training data found.
'; + return; + } + + // Create training items + data.items.forEach(item => { + const trainingItem = document.createElement('div'); + trainingItem.className = 'training-item'; + + // Generate metrics HTML + let metricsHTML = ''; + if (item.metadata && item.metadata.performance_metrics) { + const metrics = item.metadata.performance_metrics; + if (metrics.open_rate) { + metricsHTML += `Open Rate: ${(metrics.open_rate * 100).toFixed(1)}%`; + } + if (metrics.click_rate) { + metricsHTML += `Click Rate: ${(metrics.click_rate * 100).toFixed(1)}%`; + } + if (metrics.conversion_rate) { + metricsHTML += `Conversion: ${(metrics.conversion_rate * 100).toFixed(1)}%`; + } + } + + if (!metricsHTML) { + metricsHTML = 'No metrics available'; + } + + const campaignName = item.metadata?.campaign_name || 'Untitled'; + + trainingItem.innerHTML = ` +
${getContentTypeLabel(item.content_type)}
+
+

${campaignName}

+

Added on: ${new Date(item.added_at).toLocaleDateString()}

+
+ ${metricsHTML} +
+
+ +
+ + +
+ `; + + trainingList.appendChild(trainingItem); + }); + + // Add event listeners for view and delete buttons + document.querySelectorAll('.view-training').forEach(btn => { + btn.addEventListener('click', function() { + const id = this.getAttribute('data-id'); + viewTrainingData(id); + }); + }); + + document.querySelectorAll('.delete-training').forEach(btn => { + btn.addEventListener('click', function() { + const id = this.getAttribute('data-id'); + if (confirm('Are you sure you want to delete this training data?')) { + deleteTrainingData(id); + } + }); + }); + + // Add pagination for training data + if (data.pagination && data.pagination.pages > 1) { + // Remove existing pagination if any + const existingPagination = document.querySelector('.pagination'); + if (existingPagination) { + existingPagination.remove(); + } + + const paginationElement = document.createElement('div'); + paginationElement.className = 'pagination'; + + let paginationHTML = ''; + for (let i = 1; i <= data.pagination.pages; i++) { + paginationHTML += ``; + } + + paginationElement.innerHTML = paginationHTML; + trainingList.after(paginationElement); + + // Add event listeners for pagination buttons + document.querySelectorAll('.page-btn').forEach(btn => { + btn.addEventListener('click', function() { + const pageNum = parseInt(this.getAttribute('data-page')); + loadTrainingData(pageNum, contentType); + }); + }); + } + }) + .catch(error => { + console.error('Error loading training data:', error); + trainingList.innerHTML = '
Error loading training data. Please try again.
'; + }); + } + + // View Training Data + function viewTrainingData(id) { + fetch(`${API_URL}/training-data/${id}`) + .then(response => { + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + return response.json(); + }) + .then(data => { + // Create a modal to display the training data details + const modal = document.createElement('div'); + modal.className = 'modal'; + + const modalContent = document.createElement('div'); + modalContent.className = 'modal-content'; + + const campaignName = data.metadata?.campaign_name || 'Untitled'; + + let metricsHTML = ''; + if (data.metadata && data.metadata.performance_metrics) { + const metrics = data.metadata.performance_metrics; + if (metrics.open_rate !== undefined) { + metricsHTML += `
Open Rate: ${(metrics.open_rate * 100).toFixed(1)}%
`; + } + if (metrics.click_rate !== undefined) { + metricsHTML += `
Click Rate: ${(metrics.click_rate * 100).toFixed(1)}%
`; + } + if (metrics.conversion_rate !== undefined) { + metricsHTML += `
Conversion Rate: ${(metrics.conversion_rate * 100).toFixed(1)}%
`; + } + } + + modalContent.innerHTML = ` + + + `; + + modal.appendChild(modalContent); + document.body.appendChild(modal); + + // Close button functionality + modal.querySelector('.modal-close').addEventListener('click', function() { + document.body.removeChild(modal); + }); + + // Close when clicking outside the modal + window.addEventListener('click', function(event) { + if (event.target === modal) { + document.body.removeChild(modal); + } + }); + }) + .catch(error => { + console.error('Error viewing training data:', error); + alert('Error viewing training data details. Please try again.'); + }); + } + + // Delete Training Data + function deleteTrainingData(id) { + fetch(`${API_URL}/training-data/${id}`, { + method: 'DELETE' + }) + .then(response => { + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + return response.json(); + }) + .then(data => { + alert('Training data successfully deleted.'); + + // Reload the training data + loadTrainingData(); + }) + .catch(error => { + console.error('Error deleting training data:', error); + alert('Error deleting training data. Please try again.'); + }); + } + + // Training Filter Handlers + if (trainingFilterType) { + trainingFilterType.addEventListener('change', function() { + loadTrainingData(1, this.value); + }); + } + + if (trainingSearch) { + trainingSearch.addEventListener('input', function() { + // Client-side filtering + const searchTerm = this.value.toLowerCase(); + + document.querySelectorAll('.training-item').forEach(item => { + const content = item.querySelector('.training-item-content').textContent.toLowerCase(); + if (content.includes(searchTerm)) { + item.style.display = 'flex'; + } else { + item.style.display = 'none'; + } + }); + }); + } + // Load Brand Style on Page Load fetch(`${API_URL}/brand-style`) .then(response => { @@ -508,4 +1051,122 @@ 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 = 'Generate an email campaign for a product launch'; + + // Add CSS for modal + const modalStyle = document.createElement('style'); + modalStyle.textContent = ` + .modal { + display: block; + position: fixed; + z-index: 1000; + left: 0; + top: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + overflow: auto; + } + + .modal-content { + background-color: white; + margin: 5% auto; + padding: 0; + border-radius: var(--radius-lg); + box-shadow: var(--shadow-lg); + width: 80%; + max-width: 800px; + animation: modalOpen 0.3s ease-out; + } + + @keyframes modalOpen { + from {opacity: 0; transform: translateY(-20px);} + to {opacity: 1; transform: translateY(0);} + } + + .modal-header { + padding: 20px 25px; + border-bottom: 1px solid var(--grey-200); + display: flex; + justify-content: space-between; + align-items: center; + } + + .modal-header h3 { + margin: 0; + } + + .modal-close { + background: transparent; + border: none; + font-size: 24px; + cursor: pointer; + color: var(--grey-600); + } + + .modal-close:hover { + color: var(--grey-800); + } + + .modal-body { + padding: 25px; + } + + .detail-item { + margin-bottom: 15px; + } + + .detail-label { + font-weight: 600; + color: var(--grey-700); + display: block; + margin-bottom: 5px; + } + + .detail-value { + color: var(--grey-800); + } + + .content-preview { + margin-top: 25px; + } + + .content-box { + background-color: var(--grey-100); + border: 1px solid var(--grey-200); + border-radius: var(--radius-md); + padding: 15px; + margin-top: 10px; + white-space: pre-wrap; + max-height: 300px; + overflow-y: auto; + } + + .loading-state, .empty-state, .error-state { + text-align: center; + padding: 30px; + color: var(--grey-500); + } + + .pagination { + display: flex; + justify-content: center; + gap: 5px; + margin-top: 20px; + } + + .page-btn { + padding: 8px 12px; + border: 1px solid var(--grey-300); + background-color: white; + border-radius: var(--radius-md); + cursor: pointer; + } + + .page-btn.active { + background-color: var(--primary-color); + color: white; + border-color: var(--primary-color); + } + `; + document.head.appendChild(modalStyle); }); diff --git a/frontend/index.html b/frontend/index.html index 76be6f3..d349df1 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,3 +1,4 @@ + @@ -198,9 +199,18 @@
@@ -209,45 +219,7 @@
-
- -
-

Transformation Masterclass Invitation

-

Subject: Transform Your Potential with Adriana James' Exclusive Workshop...

-
-
Apr 17, 2025
-
- - - -
-
-
- -
-

3-Step Framework Post

-

BREAKTHROUGH MOMENT ✨ Ever feel stuck in patterns that hold you back...

-
-
Apr 16, 2025
-
- - - -
-
-
-
Blog
-
-

5 Ways to Overcome Limiting Beliefs

-

Are limiting beliefs holding you back from achieving your full potential?...

-
-
Apr 14, 2025
-
- - - -
-
+
@@ -391,7 +363,7 @@ - + +
@@ -446,38 +419,7 @@
-
- -
-

Transformation Masterclass Promotion

-

Added on: Apr 15, 2025

-
- Open Rate: 42% - Click Rate: 18% - Conversion: 8% -
-
-
- - -
-
-
- -
-

Breakthrough Framework

-

Added on: Apr 10, 2025

-
- Engagement: 6.4% - Saves: 178 - Shares: 92 -
-
-
- - -
-
+
diff --git a/logs/app.log b/logs/app.log index 2412080..95a31a6 100644 --- a/logs/app.log +++ b/logs/app.log @@ -882,3 +882,89 @@ 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 +2025-04-18 18:11:18.236 | INFO | brand_style:update_style_guidelines:178 - Updated brand style guidelines +2025-04-18 18:11:18.236 | INFO | brand_style:update_style_guidelines:178 - Updated brand style guidelines +2025-04-18 18:11:40.648 | INFO | brand_style:update_style_guidelines:178 - Updated brand style guidelines +2025-04-18 18:11:40.648 | INFO | brand_style:update_style_guidelines:178 - Updated brand style guidelines +2025-04-18 18:13:01.347 | INFO | brand_style:update_style_guidelines:178 - Updated brand style guidelines +2025-04-18 18:13:01.347 | INFO | brand_style:update_style_guidelines:178 - Updated brand style guidelines +2025-04-18 18:36:18.449 | ERROR | main:delete_user_query:461 - Error deleting user query: 404: Query with timestamp 20250418164155 not found +2025-04-18 18:36:18.449 | ERROR | main:delete_user_query:461 - Error deleting user query: 404: Query with timestamp 20250418164155 not found +2025-04-18 18:36:43.730 | ERROR | main:delete_user_query:461 - Error deleting user query: 404: Query with timestamp 20250418161237 not found +2025-04-18 18:36:43.730 | ERROR | main:delete_user_query:461 - Error deleting user query: 404: Query with timestamp 20250418161237 not found +2025-04-18 18:37:32.259 | ERROR | copywriter:_call_llm_api:139 - Error calling Cohere API: +2025-04-18 18:37:32.259 | ERROR | copywriter:_call_llm_api:139 - Error calling Cohere API: +2025-04-18 18:38:06.509 | ERROR | copywriter:_call_llm_api:139 - Error calling Cohere API: +2025-04-18 18:38:06.509 | ERROR | copywriter:_call_llm_api:139 - Error calling Cohere API: +2025-04-18 18:38:48.267 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 18:38:48.267 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 18:38:48.272 | INFO | copywriter:generate_copy:90 - Generated content with 3812 characters +2025-04-18 18:38:48.272 | INFO | copywriter:generate_copy:90 - Generated content with 3812 characters +2025-04-18 18:38:51.235 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 18:38:51.235 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 18:39:59.364 | ERROR | main:get_user_query:437 - Error getting user query: 404: Query with timestamp 20250418173851 not found +2025-04-18 18:39:59.364 | ERROR | main:get_user_query:437 - Error getting user query: 404: Query with timestamp 20250418173851 not found +2025-04-18 18:40:23.978 | ERROR | main:get_user_query:437 - Error getting user query: 404: Query with timestamp 20250418173851 not found +2025-04-18 18:40:23.978 | ERROR | main:get_user_query:437 - Error getting user query: 404: Query with timestamp 20250418173851 not found +2025-04-18 18:43:01.011 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 18:43:01.011 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 18:43:07.295 | ERROR | main:get_training_data:306 - Error retrieving 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 18:43:07.295 | ERROR | main:get_training_data:306 - Error retrieving 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 18:44:21.955 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 18:44:21.955 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 18:44:28.293 | INFO | vector_store:delete_document:256 - Marked document 2 as deleted +2025-04-18 18:44:28.293 | INFO | vector_store:delete_document:256 - Marked document 2 as deleted +2025-04-18 18:44:32.713 | ERROR | main:get_training_data:306 - Error retrieving 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 18:44:32.713 | ERROR | main:get_training_data:306 - Error retrieving 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 18:47:11.756 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 18:47:11.756 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 18:47:12.100 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 18:47:12.100 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 18:47:21.198 | ERROR | main:get_training_data:306 - Error retrieving 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 18:47:21.198 | ERROR | main:get_training_data:306 - Error retrieving 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 18:48:22.623 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 18:48:22.623 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 18:48:22.636 | INFO | copywriter:generate_copy:90 - Generated content with 1802 characters +2025-04-18 18:48:22.636 | INFO | copywriter:generate_copy:90 - Generated content with 1802 characters +2025-04-18 18:48:23.411 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 18:48:23.411 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 18:48:46.063 | ERROR | main:delete_user_query:461 - Error deleting user query: 404: Query with timestamp 20250418153242 not found +2025-04-18 18:48:46.063 | ERROR | main:delete_user_query:461 - Error deleting user query: 404: Query with timestamp 20250418153242 not found +2025-04-18 18:49:03.774 | INFO | vector_store:delete_document:256 - Marked document 3 as deleted +2025-04-18 18:49:03.774 | INFO | vector_store:delete_document:256 - Marked document 3 as deleted +2025-04-18 18:49:06.850 | INFO | vector_store:delete_document:256 - Marked document 2 as deleted +2025-04-18 18:49:06.850 | INFO | vector_store:delete_document:256 - Marked document 2 as deleted +2025-04-18 18:49:11.443 | INFO | vector_store:delete_document:256 - Marked document 1 as deleted +2025-04-18 18:49:11.443 | INFO | vector_store:delete_document:256 - Marked document 1 as deleted +2025-04-18 18:51:53.774 | ERROR | main:add_training_data:214 - Error adding training data: no such table: training_data +2025-04-18 18:51:53.774 | ERROR | main:add_training_data:214 - Error adding training data: no such table: training_data +2025-04-18 18:52:03.005 | ERROR | main:add_training_data:214 - Error adding training data: no such table: training_data +2025-04-18 18:52:03.005 | ERROR | main:add_training_data:214 - Error adding training data: no such table: training_data +2025-04-18 18:52:49.739 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 18:52:49.739 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 18:52:55.266 | INFO | vector_store:delete_document:256 - Marked document 1 as deleted +2025-04-18 18:52:55.266 | INFO | vector_store:delete_document:256 - Marked document 1 as deleted +2025-04-18 18:56:30.386 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 18:56:30.386 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 18:56:30.388 | INFO | copywriter:generate_copy:90 - Generated content with 1580 characters +2025-04-18 18:56:30.388 | INFO | copywriter:generate_copy:90 - Generated content with 1580 characters +2025-04-18 18:56:30.997 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 18:56:30.997 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 18:57:15.893 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 18:57:15.893 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 18:58:37.976 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 18:58:37.976 | INFO | copywriter:_generate_headline_suggestions:188 - Generated 3 headline suggestions +2025-04-18 18:58:37.980 | INFO | copywriter:generate_copy:90 - Generated content with 579 characters +2025-04-18 18:58:37.980 | INFO | copywriter:generate_copy:90 - Generated content with 579 characters +2025-04-18 18:58:38.798 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 18:58:38.798 | INFO | vector_store:add_documents:131 - Added 1 documents to vector store +2025-04-18 18:59:13.625 | INFO | copywriter:improve_copy:224 - Improved content based on feedback +2025-04-18 18:59:13.625 | INFO | copywriter:improve_copy:224 - Improved content based on feedback +2025-04-18 18:59:58.642 | INFO | brand_style:update_style_guidelines:178 - Updated brand style guidelines +2025-04-18 18:59:58.642 | INFO | brand_style:update_style_guidelines:178 - Updated brand style guidelines +2025-04-18 19:00:09.875 | INFO | brand_style:update_style_guidelines:178 - Updated brand style guidelines +2025-04-18 19:00:09.875 | INFO | brand_style:update_style_guidelines:178 - Updated brand style guidelines +2025-04-18 19:00:20.643 | INFO | brand_style:update_style_guidelines:178 - Updated brand style guidelines +2025-04-18 19:00:20.643 | INFO | brand_style:update_style_guidelines:178 - Updated brand style guidelines +2025-04-18 19:05:10.093 | INFO | brand_style:update_style_guidelines:178 - Updated brand style guidelines +2025-04-18 19:05:10.093 | INFO | brand_style:update_style_guidelines:178 - Updated brand style guidelines