<?php
// ask-deepseek-convo.php
// Modern DeepSeek chat with session-based memory + mobile-friendly UI
session_start();
// === CONFIG ===
$DEEPSEEK_API_KEY = getenv('DEEPSEEK_API_KEY') ?: 'sk-b1d3560509194a4182ace023c083476a';
$API_URL = 'https://api.deepseek.com/chat/completions';
$DEFAULT_MODEL = 'deepseek-chat';
$DEFAULT_TEMPERATURE = 0.7;
$DEFAULT_MAXTOKENS = 800;
// session bucket
if (!isset($_SESSION['deepseek_convo'])) {
$_SESSION['deepseek_convo'] = [];
}
// helpers
function h($s){ return htmlspecialchars($s ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); }
function render($text){
$text = str_replace(["\r\n","\r"], "\n", $text);
$pat = '/```([a-zA-Z0-9_\-]+)?\s*\n([\s\S]*?)```/m';
$html=''; $pos=0; $codeBlockId = 0;
while (preg_match($pat,$text,$m,PREG_OFFSET_CAPTURE,$pos)) {
$start=$m[0][1]; $len=strlen($m[0][0]);
$lang=$m[1][0] ?? 'text';
$code=$m[2][0] ?? '';
$codeBlockId++;
$before = substr($text,$pos,$start-$pos);
if ($before !== '') {
$html .= '<div class="message-text">'.nl2br(h($before)).'</div>';
}
$html .= '<div class="code-block-container">';
$html .= '<div class="code-header">';
$html .= '<span class="code-lang">'.h($lang).'</span>';
$html .= '<button class="copy-btn" onclick="copyCode(\'code-'.$codeBlockId.'\')">Copy</button>';
$html .= '</div>';
$html .= '<div class="ace-editor" id="code-'.$codeBlockId.'" data-lang="'.h($lang).'">'.h($code).'</div>';
$html .= '</div>';
$pos = $start+$len;
}
if ($pos < strlen($text)) {
$tail = substr($text,$pos);
if ($tail !== '') {
$html .= '<div class="message-text">'.nl2br(h($tail)).'</div>';
}
}
if ($html === '') {
$html = '<div class="message-text">'.nl2br(h($text)).'</div>';
}
return $html;
}
// handle actions
$error = null; $answer = null; $rawJson = null;
$action = $_POST['action'] ?? '';
if ($action === 'clear') {
$_SESSION['deepseek_convo'] = [];
// Redirect to prevent resubmission
header('Location: ' . $_SERVER['PHP_SELF']);
exit;
}
if ($action === 'delete_last') {
$n = count($_SESSION['deepseek_convo']);
if ($n > 0) {
$last = array_pop($_SESSION['deepseek_convo']);
if ($last['role'] === 'assistant' && $n-2 >= 0) {
$prev = end($_SESSION['deepseek_convo']);
if ($prev && $prev['role'] === 'user') array_pop($_SESSION['deepseek_convo']);
}
}
// Redirect to prevent resubmission
header('Location: ' . $_SERVER['PHP_SELF']);
exit;
}
if ($action === 'ask') {
$question = trim($_POST['question'] ?? '');
$model = $_POST['model'] ?? $DEFAULT_MODEL;
$continueChat = isset($_POST['continue']) ? true : false;
$maxTokens = max(50, min(8000, (int)($_POST['max_tokens'] ?? $DEFAULT_MAXTOKENS)));
$temperature = max(0.0, min(2.0, (float)($_POST['temperature'] ?? $DEFAULT_TEMPERATURE)));
if ($DEEPSEEK_API_KEY === 'REPLACE_WITH_YOUR_KEY' || $DEEPSEEK_API_KEY === '') {
$error = 'Missing API key. Set DEEPSEEK_API_KEY on the server or paste it in the file (not recommended).';
} elseif ($question === '') {
$error = 'Please enter a question.';
} else {
$messages = [['role'=>'system','content'=>'You are a helpful assistant.']];
if ($continueChat) {
foreach ($_SESSION['deepseek_convo'] as $m) {
if ($m['role']==='user' || $m['role']==='assistant') $messages[] = $m;
}
}
$messages[] = ['role'=>'user','content'=>$question];
$payload = [
'model' => $model,
'messages' => $messages,
'temperature' => $temperature,
'max_tokens' => $maxTokens,
];
$ch = curl_init($API_URL);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . $DEEPSEEK_API_KEY,
],
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 120,
]);
$raw = curl_exec($ch);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$cerr = curl_error($ch);
curl_close($ch);
if ($cerr) {
$error = 'cURL error: ' . $cerr;
} elseif ($http < 200 || $http >= 300) {
$error = "HTTP $http: " . $raw;
} else {
$json = json_decode($raw, true);
$rawJson = $json;
$answer = $json['choices'][0]['message']['content'] ?? '(no content)';
if ($continueChat) {
$_SESSION['deepseek_convo'][] = ['role'=>'user','content'=>$question];
$_SESSION['deepseek_convo'][] = ['role'=>'assistant','content'=>$answer];
} else {
$_SESSION['deepseek_convo'] = [
['role'=>'user','content'=>$question],
['role'=>'assistant','content'=>$answer],
];
}
}
}
}
// sticky UI values - clear question after successful non-ask actions
$stickyQuestion = ($action === 'clear' || $action === 'delete_last') ? '' : ($_POST['question'] ?? '');
$stickyModel = $_POST['model'] ?? $DEFAULT_MODEL;
$stickyTemp = $_POST['temperature'] ?? $DEFAULT_TEMPERATURE;
$stickyMaxTokens = (int)($_POST['max_tokens'] ?? $DEFAULT_MAXTOKENS);
$stickyContinue = isset($_POST['continue']) ? 'checked' : '';
$history = $_SESSION['deepseek_convo'];
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DeepSeek Chat</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ace/1.32.2/theme/github.min.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--primary: #2563eb;
--primary-hover: #1d4ed8;
--primary-light: #dbeafe;
--surface: #ffffff;
--background: #f8fafc;
--border: #e2e8f0;
--text-primary: #1e293b;
--text-secondary: #64748b;
--success: #10b981;
--error: #ef4444;
--warning: #f59e0b;
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
--radius: 12px;
--radius-sm: 8px;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background: var(--background);
color: var(--text-primary);
line-height: 1.6;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 1rem;
}
.header {
background: var(--surface);
border-radius: var(--radius);
padding: 1.5rem;
margin-bottom: 1.5rem;
box-shadow: var(--shadow-sm);
border: 1px solid var(--border);
}
.header h1 {
font-size: 1.875rem;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 0.5rem;
}
.header p {
color: var(--text-secondary);
font-size: 0.875rem;
}
.chat-form {
background: var(--surface);
border-radius: var(--radius);
padding: 1.5rem;
margin-bottom: 1.5rem;
box-shadow: var(--shadow-sm);
border: 1px solid var(--border);
}
.form-group {
margin-bottom: 1rem;
}
.form-group label {
display: block;
font-weight: 500;
margin-bottom: 0.5rem;
color: var(--text-primary);
}
textarea {
width: 100%;
min-height: 120px;
padding: 0.875rem;
border: 1px solid var(--border);
border-radius: var(--radius-sm);
font-size: 0.875rem;
font-family: inherit;
resize: vertical;
transition: border-color 0.2s, box-shadow 0.2s;
}
textarea:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px var(--primary-light);
}
.form-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin-bottom: 1rem;
}
select, input[type="number"] {
width: 100%;
padding: 0.75rem;
border: 1px solid var(--border);
border-radius: var(--radius-sm);
font-size: 0.875rem;
transition: border-color 0.2s, box-shadow 0.2s;
}
select:focus, input[type="number"]:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px var(--primary-light);
}
.checkbox-group {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 1rem;
}
.checkbox-group input[type="checkbox"] {
width: 1rem;
height: 1rem;
accent-color: var(--primary);
}
.button-group {
display: flex;
gap: 0.75rem;
flex-wrap: wrap;
}
.btn {
padding: 0.75rem 1.5rem;
border-radius: var(--radius-sm);
border: 1px solid var(--border);
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
display: inline-flex;
align-items: center;
gap: 0.5rem;
text-decoration: none;
}
.btn-primary {
background: var(--primary);
color: white;
border-color: var(--primary);
}
.btn-primary:hover {
background: var(--primary-hover);
border-color: var(--primary-hover);
}
.btn-secondary {
background: var(--surface);
color: var(--text-primary);
}
.btn-secondary:hover {
background: var(--background);
}
.btn-danger {
color: var(--error);
border-color: var(--error);
}
.btn-danger:hover {
background: var(--error);
color: white;
}
.alert {
padding: 1rem;
border-radius: var(--radius-sm);
margin-bottom: 1rem;
border: 1px solid;
}
.alert-error {
background: #fef2f2;
border-color: #fecaca;
color: #dc2626;
}
.chat-container {
background: var(--surface);
border-radius: var(--radius);
padding: 1.5rem;
margin-bottom: 1.5rem;
box-shadow: var(--shadow-sm);
border: 1px solid var(--border);
}
.chat-container h3 {
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 1rem;
color: var(--text-primary);
}
.message {
margin-bottom: 1.5rem;
}
.message-role {
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
color: var(--text-secondary);
margin-bottom: 0.5rem;
letter-spacing: 0.05em;
}
.message-role.user {
color: var(--primary);
}
.message-role.assistant {
color: var(--success);
}
.message-bubble {
background: var(--background);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 1rem;
}
.message-text {
line-height: 1.6;
margin-bottom: 0.75rem;
}
.message-text:last-child {
margin-bottom: 0;
}
.code-block-container {
margin: 1rem 0;
border: 1px solid var(--border);
border-radius: var(--radius-sm);
overflow: hidden;
}
.code-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.75rem 1rem;
background: var(--background);
border-bottom: 1px solid var(--border);
}
.code-lang {
font-size: 0.75rem;
font-weight: 600;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.copy-btn {
font-size: 0.75rem;
padding: 0.25rem 0.5rem;
background: var(--primary);
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background 0.2s;
}
.copy-btn:hover {
background: var(--primary-hover);
}
.ace-editor {
height: 200px;
font-size: 14px;
}
.details-container {
margin-top: 1rem;
}
.details-container details {
border: 1px solid var(--border);
border-radius: var(--radius-sm);
}
.details-container summary {
padding: 0.75rem;
background: var(--background);
cursor: pointer;
font-weight: 500;
}
.details-container pre {
padding: 1rem;
background: var(--surface);
overflow: auto;
font-size: 0.875rem;
margin: 0;
}
.notes-container {
background: var(--surface);
border-radius: var(--radius);
padding: 1.5rem;
box-shadow: var(--shadow-sm);
border: 1px solid var(--border);
}
.notes-container h3 {
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 1rem;
color: var(--text-primary);
}
.notes-container ul {
padding-left: 1.5rem;
}
.notes-container li {
margin-bottom: 0.5rem;
color: var(--text-secondary);
}
.notes-container code {
background: var(--background);
padding: 0.125rem 0.25rem;
border-radius: 4px;
font-size: 0.875rem;
color: var(--text-primary);
}
.loading {
display: none;
text-align: center;
padding: 2rem;
color: var(--text-secondary);
}
.spinner {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid var(--border);
border-radius: 50%;
border-top-color: var(--primary);
animation: spin 1s ease-in-out infinite;
margin-right: 0.5rem;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Mobile responsiveness */
@media (max-width: 768px) {
.container {
padding: 0.5rem;
}
.header, .chat-form, .chat-container, .notes-container {
padding: 1rem;
margin-bottom: 1rem;
}
.form-row {
grid-template-columns: 1fr;
}
.button-group {
flex-direction: column;
}
.btn {
justify-content: center;
}
.code-header {
padding: 0.5rem;
}
.ace-editor {
height: 150px;
}
}
/* Dark mode support */
@media (prefers-color-scheme: dark) {
:root {
--background: #0f172a;
--surface: #1e293b;
--border: #334155;
--text-primary: #f1f5f9;
--text-secondary: #94a3b8;
--primary-light: #1e40af;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>đ¤ DeepSeek Chat</h1>
<p>Modern AI chat interface with session-based memory and advanced code editing</p>
</div>
<form method="post" class="chat-form" id="chatForm">
<div class="form-group">
<label for="question">Your Message</label>
<textarea
name="question"
id="question"
placeholder="Type your message here..."
<?= $action === 'ask' ? 'required' : '' ?>
><?= h($stickyQuestion) ?></textarea>
</div>
<div class="form-row">
<div class="form-group">
<label for="model">Model</label>
<select name="model" id="model">
<option value="deepseek-chat" <?= $stickyModel==='deepseek-chat'?'selected':''; ?>>
đŦ DeepSeek Chat
</option>
<option value="deepseek-reasoner" <?= $stickyModel==='deepseek-reasoner'?'selected':''; ?>>
đ§ DeepSeek Reasoner (R1-style)
</option>
</select>
</div>
<div class="form-group">
<label for="max_tokens">Max Tokens</label>
<select name="max_tokens" id="max_tokens">
<?php foreach ([200,500,800,1000,1500,2000,4000,6000,8000] as $c): ?>
<option value="<?= (int)$c ?>" <?= $stickyMaxTokens==$c?'selected':''; ?>>
<?= number_format($c) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label for="temperature">Temperature</label>
<input name="temperature" id="temperature" type="number" step="0.1" min="0" max="2" value="<?= h($stickyTemp) ?>">
</div>
</div>
<div class="checkbox-group">
<input type="checkbox" name="continue" id="continue" <?= $stickyContinue ?: 'checked' ?>>
<label for="continue">Continue this conversation</label>
</div>
<div class="button-group">
<button type="button" onclick="submitAction('ask')" class="btn btn-primary" id="sendBtn">
<span>đ¤</span> Send Message
</button>
<button type="button" onclick="submitAction('delete_last')" class="btn btn-secondary" title="Remove the most recent exchange">
<span>âŠī¸</span> Delete Last
</button>
<button type="button" onclick="submitAction('clear')" class="btn btn-danger" title="Clear entire conversation">
<span>đī¸</span> Clear All
</button>
</div>
<input type="hidden" name="action" id="actionInput" value="">
<div class="loading" id="loading">
<div class="spinner"></div>
<span id="loadingText">Processing...</span>
</div>
</form>
<?php if ($error): ?>
<div class="alert alert-error">
<strong>â Error:</strong> <?= h($error) ?>
</div>
<?php endif; ?>
<?php if (!empty($history)): ?>
<div class="chat-container">
<h3>đŦ Conversation History</h3>
<?php foreach ($history as $m): ?>
<div class="message">
<div class="message-role <?= $m['role'] ?>"><?= h(ucfirst($m['role'])) ?></div>
<div class="message-bubble"><?= render($m['content']) ?></div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if ($answer): ?>
<div class="chat-container">
<h3>⨠Latest Response</h3>
<div class="message-bubble">
<?= render($answer) ?>
</div>
<?php if ($rawJson): ?>
<div class="details-container">
<details>
<summary>đ Raw JSON Response</summary>
<pre><code><?= h(json_encode($rawJson, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES)) ?></code></pre>
</details>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
<div class="notes-container">
<h3>đ Notes</h3>
<ul>
<li><strong>Memory:</strong> Implemented with PHP sessions. For persistence across browsers/servers, use MySQL with a <code>conversation_id</code>.</li>
<li><strong>Privacy:</strong> Conversations are stored locally in your session. Clear them using the controls above.</li>
<li><strong>API:</strong> Uses OpenAI-compatible <code>POST /chat/completions</code> endpoint.</li>
<li><strong>Code Editing:</strong> Code blocks now use ACE Editor for better syntax highlighting and editing.</li>
</ul>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.32.2/ace.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.32.2/ext-language_tools.min.js"></script>
<script>
// Initialize ACE editors for code blocks
document.addEventListener('DOMContentLoaded', function() {
const codeBlocks = document.querySelectorAll('.ace-editor');
codeBlocks.forEach(function(block) {
const editor = ace.edit(block);
const lang = block.getAttribute('data-lang') || 'text';
// Set theme and mode
editor.setTheme('ace/theme/github');
editor.setReadOnly(true);
editor.setShowPrintMargin(false);
// Map language to ACE mode
const langMap = {
'javascript': 'javascript',
'python': 'python',
'php': 'php',
'html': 'html',
'css': 'css',
'json': 'json',
'sql': 'sql',
'bash': 'sh',
'shell': 'sh',
'xml': 'xml',
'yaml': 'yaml',
'markdown': 'markdown',
'java': 'java',
'cpp': 'c_cpp',
'c': 'c_cpp',
'text': 'text'
};
const mode = langMap[lang.toLowerCase()] || 'text';
editor.session.setMode(`ace/mode/${mode}`);
// Configure editor
editor.setOptions({
maxLines: 30,
minLines: 3,
fontSize: 14,
wrap: true,
showLineNumbers: true,
showGutter: true
});
});
});
// Copy code functionality
function copyCode(editorId) {
const editor = ace.edit(editorId);
const code = editor.getValue();
navigator.clipboard.writeText(code).then(function() {
const btn = document.querySelector(`#${editorId}`).parentNode.querySelector('.copy-btn');
const originalText = btn.textContent;
btn.textContent = 'Copied!';
btn.style.background = 'var(--success)';
setTimeout(function() {
btn.textContent = originalText;
btn.style.background = 'var(--primary)';
}, 2000);
}).catch(function(err) {
console.error('Failed to copy code: ', err);
});
}
// Handle form submission with proper action
function submitAction(action) {
const form = document.getElementById('chatForm');
const actionInput = document.getElementById('actionInput');
const loading = document.getElementById('loading');
const loadingText = document.getElementById('loadingText');
const buttons = document.querySelectorAll('.btn');
const questionField = document.getElementById('question');
// Validate input for ask action
if (action === 'ask' && questionField.value.trim() === '') {
alert('Please enter a message before sending.');
return;
}
// Set the action
actionInput.value = action;
// Show loading state
loading.style.display = 'block';
buttons.forEach(btn => {
btn.disabled = true;
btn.style.opacity = '0.6';
});
// Update loading text based on action
switch(action) {
case 'ask':
loadingText.textContent = 'Thinking...';
break;
case 'delete_last':
loadingText.textContent = 'Deleting last message...';
break;
case 'clear':
loadingText.textContent = 'Clearing conversation...';
break;
}
// Submit the form
form.submit();
}
// Auto-resize textarea
const textarea = document.getElementById('question');
textarea.addEventListener('input', function() {
this.style.height = 'auto';
this.style.height = Math.min(this.scrollHeight, 300) + 'px';
});
// Keyboard shortcuts
document.addEventListener('keydown', function(e) {
// Ctrl/Cmd + Enter to submit
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
e.preventDefault();
submitAction('ask');
}
});
// Clear question field after non-ask actions
<?php if ($action === 'clear' || $action === 'delete_last'): ?>
document.getElementById('question').value = '';
<?php endif; ?>
</script>
</body>
</html>