<?php
// ask-deepseek-coding.php
// Minimal coding-focused DeepSeek client that only sends:
// - a system rule
// - the saved "working code doc" (optional)
// - the current prompt
// No rolling conversation history.
session_start();
// === CONFIG ===
$DEEPSEEK_API_KEY = getenv('DEEPSEEK_API_KEY') ?: 'sk-b1d3560509194a4182ace023c083476a';
$API_URL = 'https://api.deepseek.com/chat/completions';
$DEFAULT_MODEL = 'deepseek-chat'; // or 'deepseek-reasoner'
$DEFAULT_TEMPERATURE = 0.4; // lower for coding determinism
$DEFAULT_MAXTOKENS = 800; // your cap
// Session bucket for working code
if (!isset($_SESSION['working_code_doc'])) $_SESSION['working_code_doc'] = '';
// Helpers
function h($s){ return htmlspecialchars($s ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); }
function render_markdown_with_code_separators($text) {
$text = str_replace(["\r\n", "\r"], "\n", $text);
$pat = '/```([a-zA-Z0-9_\-]+)?\s*\n([\s\S]*?)```/m';
$html=''; $pos=0;
while (preg_match($pat,$text,$m,PREG_OFFSET_CAPTURE,$pos)) {
$start=$m[0][1]; $len=strlen($m[0][0]); $lang=$m[1][0]??''; $code=$m[2][0]??'';
$before = substr($text,$pos,$start-$pos);
if ($before!=='') $html.='<div class="md-p">'.nl2br(h($before)).'</div>';
$html.='<div class="code-sep"></div>';
$html.='<div class="codeblock">'.($lang?'<div class="code-lang">'.h($lang).'</div>':'')
.'<pre><code>'.h($code).'</code></pre></div>';
$pos = $start+$len;
}
if ($pos < strlen($text)) {
$tail = substr($text,$pos);
if ($tail!=='') $html.='<div class="md-p">'.nl2br(h($tail)).'</div>';
}
if ($html==='') $html = '<div class="md-p">'.nl2br(h($text)).'</div>';
return $html;
}
// Handle actions
$error = null; $answer = null; $rawJson = null; $usage = null;
$action = $_POST['action'] ?? '';
if ($action === 'save_working') {
$_SESSION['working_code_doc'] = $_POST['working_code_doc'] ?? '';
}
if ($action === 'clear_working') {
$_SESSION['working_code_doc'] = '';
}
if ($action === 'ask') {
$question = trim($_POST['question'] ?? '');
$model = $_POST['model'] ?? $DEFAULT_MODEL;
$maxTokens = max(50, min(8000, (int)($_POST['max_tokens'] ?? $DEFAULT_MAXTOKENS)));
$temperature = max(0.0, min(2.0, (float)($_POST['temperature'] ?? $DEFAULT_TEMPERATURE)));
$includeCode = isset($_POST['include_working']) ? true : false;
if ($DEEPSEEK_API_KEY === 'REPLACE_WITH_YOUR_KEY' || $DEEPSEEK_API_KEY === '') {
$error = 'Missing API key. Set DEEPSEEK_API_KEY on the server.';
} elseif ($question === '') {
$error = 'Please enter a prompt.';
} else {
// Build minimal message list: system -> (optional code doc) -> user prompt
$messages = [
['role'=>'system','content'=>
"You are a meticulous coding assistant. Only use the provided 'working code doc' as context. ".
"Do not invent prior history. If you modify code, show a concise diff or the full updated snippet."
]
];
if ($includeCode && $_SESSION['working_code_doc'] !== '') {
// Keep it short by wrapping in a label + fenced code
$messages[] = [
'role' => 'system',
'content' => "CURRENT WORKING CODE DOC (single file):\n```text\n" .
$_SESSION['working_code_doc'] . "\n```"
];
}
$messages[] = ['role'=>'user','content'=>$question];
$payload = [
'model' => $model,
'messages' => $messages,
'temperature' => $temperature,
'max_tokens' => $maxTokens,
// 'stream' => false,
];
$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)';
$usage = $json['usage'] ?? null;
}
}
}
// Sticky UI
$stickyQuestion = $_POST['question'] ?? '';
$stickyModel = $_POST['model'] ?? $DEFAULT_MODEL;
$stickyTemp = $_POST['temperature'] ?? $DEFAULT_TEMPERATURE;
$stickyMaxTokens = (int)($_POST['max_tokens'] ?? $DEFAULT_MAXTOKENS);
$workingDoc = $_SESSION['working_code_doc'];
$stickyInclude = isset($_POST['include_working']) ? 'checked' : 'checked'; // default ON
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>DeepSeek Coding (prompt + working doc only)</title>
<style>
:root{--card:#fafafa;--br:#e6e6e6;--sep:#d0d7de;--ink:#111;--muted:#666;--overlay-bg:rgba(0,0,0,0.5)}
body{font-family:system-ui,Segoe UI,Roboto,Helvetica,Arial,sans-serif;margin:2rem;max-width:1000px;color:var(--ink);padding-bottom:120px}
h1{margin:0 0 .5rem}
.header{display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem}
.nav-buttons{display:flex;gap:.5rem}
.nav-btn{padding:.5rem .75rem;border-radius:8px;border:1px solid var(--br);background:#fff;cursor:pointer;font-size:.9rem}
.nav-btn:hover{border-color:#2f6feb}
/* Fixed bottom question area */
.question-area{position:fixed;bottom:0;left:0;right:0;background:#fff;border-top:1px solid var(--br);padding:1rem;box-shadow:0 -2px 10px rgba(0,0,0,0.1)}
.question-form{display:flex;gap:.75rem;align-items:center;max-width:1000px;margin:0 auto}
.question-input{flex:1;padding:.75rem;border:1px solid var(--br);border-radius:8px;font-size:1rem}
.send-btn{padding:.75rem 1.5rem;border-radius:8px;border:1px solid var(--br);background:#2f6feb;color:#fff;cursor:pointer;font-weight:500}
.send-btn:hover{background:#1a5feb}
/* Overlay styles */
.overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:var(--overlay-bg);display:none;align-items:center;justify-content:center;z-index:1000}
.overlay-content{background:#fff;border-radius:12px;padding:2rem;max-width:90vw;max-height:90vh;overflow:auto;box-shadow:0 10px 30px rgba(0,0,0,0.3)}
.overlay-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem}
.close-btn{background:none;border:none;font-size:1.5rem;cursor:pointer;color:var(--muted)}
.close-btn:hover{color:var(--ink)}
textarea{min-height:120px;padding:.75rem;font-size:1rem;width:100%;box-sizing:border-box}
textarea.code{min-height:400px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
select,input[type="number"]{padding:.5rem .6rem;font-size:1rem}
button{padding:.6rem .9rem;border-radius:10px;border:1px solid var(--br);background:#fff;cursor:pointer}
button:hover{border-color:#2f6feb}
.card{border:1px solid var(--br);border-radius:14px;padding:1rem;margin-top:1rem;background:var(--card)}
.meta{color:var(--muted);font-size:.9rem}
.md-p{margin:.4rem 0;line-height:1.45}
.code-sep{height:10px;border-top:3px double var(--sep);margin:.8rem 0}
.codeblock{border:1px solid var(--sep);border-radius:10px;background:#fff}
.code-lang{font-size:.8rem;color:var(--muted);padding:.4rem .6rem;border-bottom:1px solid var(--sep);background:#f6f8fa;border-top-left-radius:10px;border-top-right-radius:10px}
pre{margin:0;padding:.75rem;overflow:auto;white-space:pre}
code{font-size:.95rem}
.settings-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:1rem;margin-bottom:1rem}
.overlay-actions{display:flex;gap:.75rem;justify-content:flex-end;margin-top:1rem}
</style>
</head>
<body>
<div class="header">
<div>
<h1>DeepSeek Coding</h1>
<p class="meta">Each request uses only the <strong>working code doc</strong> (optional) + your <strong>current prompt</strong>. No full chat memory.</p>
</div>
<div class="nav-buttons">
<button class="nav-btn" onclick="showCodeOverlay()">Code Doc</button>
<button class="nav-btn" onclick="showSettingsOverlay()">Settings</button>
</div>
</div>
<!-- Main content area for responses -->
<?php if ($error): ?>
<div class="card" style="color:#b00020"><strong>Error:</strong> <?= h($error) ?></div>
<?php endif; ?>
<?php if ($answer): ?>
<div class="card">
<h3>Answer</h3>
<div><?= render_markdown_with_code_separators($answer) ?></div>
<?php if ($rawJson): ?>
<details><summary>Raw JSON</summary>
<pre><code><?= h(json_encode($rawJson, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES)) ?></code></pre>
</details>
<?php endif; ?>
<?php if ($usage): ?>
<div class="meta" style="margin-top:.5rem">
<strong>Usage:</strong>
prompt_tokens = <?= (int)($usage['prompt_tokens'] ?? 0) ?>,
completion_tokens = <?= (int)($usage['completion_tokens'] ?? 0) ?>,
total_tokens = <?= (int)($usage['total_tokens'] ?? 0) ?>
</div>
<?php endif; ?>
<div style="margin-top:1rem">
<button onclick="updateCodeFromAnswer()" class="nav-btn">Update Code Doc</button>
</div>
</div>
<?php endif; ?>
<div class="card">
<h3>How it works</h3>
<ul>
<li>We do <em>not</em> resend prior turns. Only the <strong>system rule</strong>, optional <strong>working code doc</strong>, and your <strong>current prompt</strong> are included.</li>
<li>Use a single-file "working doc." If you need multi-file context, paste the relevant parts or switch to a short summary of the non-active files to save tokens.</li>
<li>Lower <code>temperature</code> (e.g., 0.2–0.5) for more deterministic code.</li>
<li>Adjust <code>max_tokens</code> to cap output length (your real usage prints above).</li>
</ul>
</div>
<!-- Fixed bottom question area -->
<div class="question-area">
<form method="post" class="question-form" id="mainForm">
<input type="hidden" name="model" id="hiddenModel" value="<?= h($stickyModel) ?>">
<input type="hidden" name="max_tokens" id="hiddenMaxTokens" value="<?= (int)$stickyMaxTokens ?>">
<input type="hidden" name="temperature" id="hiddenTemperature" value="<?= h($stickyTemp) ?>">
<input type="hidden" name="include_working" id="hiddenIncludeWorking" value="<?= $stickyInclude ? '1' : '0' ?>">
<input type="hidden" name="working_code_doc" id="hiddenWorkingDoc" value="<?= h($workingDoc) ?>">
<input type="text" name="question" class="question-input" placeholder="Describe the change, ask for a fix, or request a new function..." value="<?= $action === 'ask' ? '' : h($stickyQuestion) ?>" autofocus>
<button type="submit" name="action" value="ask" class="send-btn">Send</button>
</form>
</div>
<!-- Code Doc Overlay -->
<div class="overlay" id="codeOverlay">
<div class="overlay-content" style="width:80vw">
<div class="overlay-header">
<h3>Working Code Doc</h3>
<button class="close-btn" onclick="hideCodeOverlay()">×</button>
</div>
<form method="post" id="codeForm">
<textarea class="code" name="working_code_doc" id="workingCodeDoc" placeholder="// Paste your current single-file code here..."><?= h($workingDoc) ?></textarea>
<p class="meta">This is the ONLY persistent context we send (if included). No other history is remembered.</p>
<div class="overlay-actions">
<button type="submit" name="action" value="clear_working">Clear</button>
<button type="submit" name="action" value="save_working">Save</button>
</div>
</form>
</div>
</div>
<!-- Settings Overlay -->
<div class="overlay" id="settingsOverlay">
<div class="overlay-content">
<div class="overlay-header">
<h3>Settings</h3>
<button class="close-btn" onclick="hideSettingsOverlay()">×</button>
</div>
<div class="settings-grid">
<label>Model
<select id="modelSelect">
<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>
</label>
<label>Max tokens
<select id="maxTokensSelect">
<?php foreach ([200,500,800,1000,1500,2000,4000,6000,8000] as $c): ?>
<option value="<?= (int)$c ?>" <?= $stickyMaxTokens==$c?'selected':''; ?>><?= (int)$c ?></option>
<?php endforeach; ?>
</select>
</label>
<label>Temperature
<input id="temperatureInput" type="number" step="0.1" min="0" max="2" value="<?= h($stickyTemp) ?>">
</label>
</div>
<label style="margin:1rem 0;display:block">
<input type="checkbox" id="includeWorkingCheck" <?= $stickyInclude ?>> Include working code doc
</label>
<div class="overlay-actions">
<button onclick="hideSettingsOverlay()">Close</button>
</div>
</div>
</div>
<script>
function updateCodeFromAnswer() {
// Extract code blocks from the answer
const answerDiv = document.querySelector('.card h3:contains("Answer")');
if (!answerDiv) return;
const codeBlocks = answerDiv.parentElement.querySelectorAll('.codeblock pre code');
if (codeBlocks.length === 0) {
alert('No code blocks found in the answer to update.');
return;
}
// If multiple code blocks, take the largest one
let selectedCode = '';
let maxLength = 0;
codeBlocks.forEach(block => {
if (block.textContent.length > maxLength) {
maxLength = block.textContent.length;
selectedCode = block.textContent;
}
});
// Update the working code doc
document.getElementById('workingCodeDoc').value = selectedCode;
document.getElementById('hiddenWorkingDoc').value = selectedCode;
// Show confirmation
alert('Code doc updated! You can view it in the Code Doc overlay.');
}
function showCodeOverlay() {
document.getElementById('codeOverlay').style.display = 'flex';
}
function hideCodeOverlay() {
document.getElementById('codeOverlay').style.display = 'none';
// Sync the working doc to hidden field
document.getElementById('hiddenWorkingDoc').value = document.getElementById('workingCodeDoc').value;
}
function showSettingsOverlay() {
document.getElementById('settingsOverlay').style.display = 'flex';
}
function hideSettingsOverlay() {
document.getElementById('settingsOverlay').style.display = 'none';
// Sync all settings to hidden fields
document.getElementById('hiddenModel').value = document.getElementById('modelSelect').value;
document.getElementById('hiddenMaxTokens').value = document.getElementById('maxTokensSelect').value;
document.getElementById('hiddenTemperature').value = document.getElementById('temperatureInput').value;
document.getElementById('hiddenIncludeWorking').value = document.getElementById('includeWorkingCheck').checked ? '1' : '0';
// Save settings to localStorage
localStorage.setItem('deepseek_settings', JSON.stringify({
model: document.getElementById('modelSelect').value,
maxTokens: document.getElementById('maxTokensSelect').value,
temperature: document.getElementById('temperatureInput').value,
includeWorking: document.getElementById('includeWorkingCheck').checked
}));
}
// Close overlays when clicking outside
document.getElementById('codeOverlay').addEventListener('click', function(e) {
if (e.target === this) hideCodeOverlay();
});
document.getElementById('settingsOverlay').addEventListener('click', function(e) {
if (e.target === this) hideSettingsOverlay();
});
// Close overlays with Escape key
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
hideCodeOverlay();
hideSettingsOverlay();
}
});
// Load saved settings on page load
document.addEventListener('DOMContentLoaded', function() {
const savedSettings = localStorage.getItem('deepseek_settings');
if (savedSettings) {
const settings = JSON.parse(savedSettings);
document.getElementById('modelSelect').value = settings.model || '<?= h($stickyModel) ?>';
document.getElementById('maxTokensSelect').value = settings.maxTokens || '<?= (int)$stickyMaxTokens ?>';
document.getElementById('temperatureInput').value = settings.temperature || '<?= h($stickyTemp) ?>';
document.getElementById('includeWorkingCheck').checked = settings.includeWorking !== undefined ? settings.includeWorking : <?= $stickyInclude ? 'true' : 'false' ?>;
// Update hidden fields too
document.getElementById('hiddenModel').value = settings.model || '<?= h($stickyModel) ?>';
document.getElementById('hiddenMaxTokens').value = settings.maxTokens || '<?= (int)$stickyMaxTokens ?>';
document.getElementById('hiddenTemperature').value = settings.temperature || '<?= h($stickyTemp) ?>';
document.getElementById('hiddenIncludeWorking').value = (settings.includeWorking !== undefined ? settings.includeWorking : <?= $stickyInclude ? 'true' : 'false' ?>) ? '1' : '0';
}
});
// Update hidden fields when settings change
document.getElementById('modelSelect').addEventListener('change', function() {
document.getElementById('hiddenModel').value = this.value;
});
document.getElementById('maxTokensSelect').addEventListener('change', function() {
document.getElementById('hiddenMaxTokens').value = this.value;
});
document.getElementById('temperatureInput').addEventListener('change', function() {
document.getElementById('hiddenTemperature').value = this.value;
});
document.getElementById('includeWorkingCheck').addEventListener('change', function() {
document.getElementById('hiddenIncludeWorking').value = this.checked ? '1' : '0';
});
document.getElementById('workingCodeDoc').addEventListener('input', function() {
document.getElementById('hiddenWorkingDoc').value = this.value;
});
</script>
</body>
</html>