PatChat / chat.html
makaronz's picture
Upload 3 files
74b52e9 verified
{% extends "base.html" %}
{% block title %}Chat - PatChat{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-3">
<!-- System Prompt Configuration -->
<div class="system-prompt-container">
<h6 class="mb-3">
<i class="bi bi-gear"></i> System Prompt
</h6>
<div class="mb-3">
<label for="promptSelect" class="form-label">Choose prompt type:</label>
<select class="form-select" id="promptSelect">
<option value="default">Default</option>
<option value="creative">Creative</option>
<option value="professional">Professional</option>
<option value="educational">Educational</option>
<option value="coding">Coding</option>
<option value="writing">Writing</option>
<option value="custom">Custom</option>
</select>
</div>
<div class="mb-3" id="customPromptContainer" style="display: none;">
<label for="customPrompt" class="form-label">Custom prompt:</label>
<textarea class="form-control" id="customPrompt" rows="3" placeholder="Enter your custom system prompt..."></textarea>
</div>
<div class="mb-3">
<label for="modelSelect" class="form-label">AI Model:</label>
<select class="form-select" id="modelSelect">
{% for model_id, model_info in ai_models.items() %}
<option value="{{ model_id }}" data-description="{{ model_info.description }}" data-cost="{{ model_info.cost_per_1k_tokens }}">
{{ model_info.name }}
</option>
{% endfor %}
</select>
<small class="form-text text-muted" id="modelDescription">
{{ ai_models['gpt-3.5-turbo'].description }}
</small>
<small class="form-text text-muted d-block" id="modelCost">
Cost: ${{ "%.4f"|format(ai_models['gpt-3.5-turbo'].cost_per_1k_tokens) }} per 1K tokens
</small>
</div>
<button class="btn btn-primary btn-sm" id="newConversationBtn">
<i class="bi bi-plus-circle"></i> New Conversation
</button>
</div>
<!-- Current Conversation Info -->
{% if conversation %}
<div class="card mt-3">
<div class="card-body">
<h6 class="card-title">
<i class="bi bi-chat-text"></i> Current Conversation
</h6>
<p class="card-text small">{{ conversation.title }}</p>
<p class="card-text small text-muted">
Created: {{ conversation.created_at.strftime('%Y-%m-%d %H:%M') }}
</p>
</div>
</div>
{% endif %}
</div>
<div class="col-md-9">
<!-- Chat Container -->
<div class="chat-container">
<!-- Messages Area -->
<div class="chat-messages" id="chatMessages">
{% if conversation and conversation.messages %}
{% for message in conversation.messages %}
<div class="message {{ message.role }}">
<div class="message-avatar">
{% if message.role == 'user' %}
<i class="bi bi-person"></i>
{% else %}
<i class="bi bi-robot"></i>
{% endif %}
</div>
<div class="message-content">
{{ message.content | nl2br }}
</div>
</div>
{% endfor %}
{% else %}
<div class="text-center text-muted mt-5">
<i class="bi bi-chat-dots" style="font-size: 3rem;"></i>
<p class="mt-3">Start a conversation with AI</p>
</div>
{% endif %}
</div>
<!-- Loading Indicator -->
<div class="loading" id="loadingIndicator">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-2 text-muted">AI is thinking...</p>
</div>
<!-- Input Area -->
<div class="chat-input-container">
<form id="messageForm">
<div class="input-group">
<textarea
class="form-control"
id="messageInput"
rows="2"
placeholder="Type your message here..."
style="resize: none;"
></textarea>
<button class="btn btn-primary" type="submit" id="sendButton" aria-label="Send message">
<i class="bi bi-send"></i>
</button>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
// Global variables
let currentConversationId = {% if conversation %}{{ conversation.id }}{% else %}null{% endif %};
const systemPrompts = {{ system_prompts | tojson }};
const aiModels = {{ ai_models | tojson }};
// DOM elements
const messageForm = document.getElementById('messageForm');
const messageInput = document.getElementById('messageInput');
const chatMessages = document.getElementById('chatMessages');
const loadingIndicator = document.getElementById('loadingIndicator');
const promptSelect = document.getElementById('promptSelect');
const customPromptContainer = document.getElementById('customPromptContainer');
const customPrompt = document.getElementById('customPrompt');
const modelSelect = document.getElementById('modelSelect');
const newConversationBtn = document.getElementById('newConversationBtn');
// Event listeners
messageForm.addEventListener('submit', handleMessageSubmit);
promptSelect.addEventListener('change', handlePromptChange);
modelSelect.addEventListener('change', handleModelChange);
newConversationBtn.addEventListener('submit', handleNewConversation);
// Handle message submission
async function handleMessageSubmit(e) {
e.preventDefault();
const content = messageInput.value.trim();
if (!content) return;
// Get current settings
const modelType = modelSelect.value;
const promptType = promptSelect.value;
const systemPrompt = promptType === 'custom' ? customPrompt.value : systemPrompts[promptType];
// Clear input
messageInput.value = '';
// Add user message to chat
addMessage(content, 'user');
// Show loading
showLoading(true);
try {
const response = await fetch('/send_message', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
conversation_id: currentConversationId,
content: content,
model_type: modelType,
system_prompt: systemPrompt
})
});
const data = await response.json();
if (data.success) {
// Update conversation ID if it's a new conversation
if (!currentConversationId) {
currentConversationId = data.conversation_id;
// Update URL without page reload
window.history.pushState({}, '', `/conversation/${currentConversationId}`);
}
// Add AI response to chat
addMessage(data.response, 'assistant');
} else {
throw new Error(data.error || 'Failed to send message');
}
} catch (error) {
console.error('Error:', error);
addMessage('Sorry, there was an error processing your message. Please try again.', 'assistant');
} finally {
showLoading(false);
}
}
// Handle new conversation
async function handleNewConversation(e) {
e.preventDefault();
const modelType = modelSelect.value;
const promptType = promptSelect.value;
const systemPrompt = promptType === 'custom' ? customPrompt.value : systemPrompts[promptType];
try {
const response = await fetch('/new_conversation', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
model_type: modelType,
system_prompt: systemPrompt
})
});
const data = await response.json();
if (data.success) {
// Redirect to new conversation
window.location.href = data.redirect_url;
} else {
throw new Error(data.error || 'Failed to create conversation');
}
} catch (error) {
console.error('Error:', error);
alert('Failed to create new conversation. Please try again.');
}
}
// Handle prompt type change
function handlePromptChange() {
const promptType = promptSelect.value;
if (promptType === 'custom') {
customPromptContainer.style.display = 'block';
} else {
customPromptContainer.style.display = 'none';
customPrompt.value = '';
}
}
// Handle model change
function handleModelChange() {
const modelId = modelSelect.value;
const modelInfo = aiModels[modelId];
if (modelInfo) {
document.getElementById('modelDescription').textContent = modelInfo.description;
document.getElementById('modelCost').textContent = `Cost: $${modelInfo.cost_per_1k_tokens.toFixed(4)} per 1K tokens`;
}
}
// Add message to chat
function addMessage(content, role) {
const messageDiv = document.createElement('div');
messageDiv.className = `message ${role}`;
const avatar = document.createElement('div');
avatar.className = 'message-avatar';
avatar.innerHTML = role === 'user' ? '<i class="bi bi-person"></i>' : '<i class="bi bi-robot"></i>';
const messageContent = document.createElement('div');
messageContent.className = 'message-content';
messageContent.textContent = content;
messageDiv.appendChild(avatar);
messageDiv.appendChild(messageContent);
chatMessages.appendChild(messageDiv);
// Scroll to bottom
chatMessages.scrollTop = chatMessages.scrollHeight;
}
// Show/hide loading indicator
function showLoading(show) {
loadingIndicator.style.display = show ? 'block' : 'none';
document.getElementById('sendButton').disabled = show;
}
// Auto-resize textarea
messageInput.addEventListener('input', function() {
this.style.height = 'auto';
this.style.height = Math.min(this.scrollHeight, 120) + 'px';
});
// Handle Enter key (send on Enter, new line on Shift+Enter)
messageInput.addEventListener('keydown', function(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
messageForm.dispatchEvent(new Event('submit'));
}
});
// Initialize
document.addEventListener('DOMContentLoaded', function() {
// Set initial prompt
handlePromptChange();
// Set initial model info
handleModelChange();
// Focus on input
messageInput.focus();
});
</script>
{% endblock %}