<?php
// ask-deepseek-convo.php
// DeepSeek chat with session-based memory + Clear/Delete-last controls.
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; // sensible default cap
// session bucket
if (!isset($_SESSION['deepseek_convo'])) {
$_SESSION['deepseek_convo'] = []; // array of ["role" => "user|assistant|system", "content" => "..."]
}
// helpers
function h($s){ return htmlspecialchars($s ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); }
function render($text){
// lightweight renderer: code fences with separators + paragraphs
$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;
$action = $_POST['action'] ?? '';
if ($action === 'clear') {
$_SESSION['deepseek_convo'] = [];
}
if ($action === 'delete_last') {
// delete last two turns if they are user+assistant pair; otherwise just last one
$n = count($_SESSION['deepseek_convo']);
if ($n > 0) {
$last = array_pop($_SESSION['deepseek_convo']);
if ($last['role'] === 'assistant' && $n-2 >= 0) {
// try to remove the preceding user turn of the same exchange
$prev = end($_SESSION['deepseek_convo']);
if ($prev && $prev['role'] === 'user') array_pop($_SESSION['deepseek_convo']);
}
}
}
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 {
// Build messages: optional system prompt, prior convo (if continue), then new user
$messages = [['role'=>'system','content'=>'You are a helpful assistant.']];
if ($continueChat) {
// include prior turns we stored in session
foreach ($_SESSION['deepseek_convo'] as $m) {
// only keep user/assistant to avoid duplicating system multiple times
if ($m['role']==='user' || $m['role']==='assistant') $messages[] = $m;
}
} else {
// new conversation: wipe prior convo for this turn only
// (do not touch session; just don't include prior messages)
}
$messages[] = ['role'=>'user','content'=>$question];
// call DeepSeek (OpenAI-compatible chat completions)
$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)';
// persist to session if we’re continuing (so next turn can remember)
if ($continueChat) {
$_SESSION['deepseek_convo'][] = ['role'=>'user','content'=>$question];
$_SESSION['deepseek_convo'][] = ['role'=>'assistant','content'=>$answer];
} else {
// if not continuing, optionally start a fresh thread with just this turn:
$_SESSION['deepseek_convo'] = [
['role'=>'user','content'=>$question],
['role'=>'assistant','content'=>$answer],
];
}
}
}
}
// sticky UI values
$stickyQuestion = $_POST['question'] ?? '';
$stickyModel = $_POST['model'] ?? $DEFAULT_MODEL;
$stickyTemp = $_POST['temperature'] ?? $DEFAULT_TEMPERATURE;
$stickyMaxTokens = (int)($_POST['max_tokens'] ?? $DEFAULT_MAXTOKENS);
$stickyContinue = isset($_POST['continue']) ? 'checked' : '';
// limit how many past pairs we render (UI only)
$history = $_SESSION['deepseek_convo'];
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>DeepSeek Chat (with memory)</title>
<style>
:root{--card:#fafafa;--br:#e6e6e6;--sep:#d0d7de;--ink:#111;--muted:#666}
body{font-family:system-ui,Segoe UI,Roboto,Helvetica,Arial,sans-serif;margin:2rem;max-width:960px;color:var(--ink)}
h1{margin:0 0 .5rem}
form{display:grid;gap:.75rem}
.row{display:flex;gap:.75rem;flex-wrap:wrap}
textarea{min-height:120px;padding:.75rem;font-size:1rem}
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}
.role{font-size:.85rem;color:var(--muted);margin-bottom:.25rem}
.bubble{border:1px solid var(--br);border-radius:12px;padding:.75rem;background:#fff}
.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-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:.95rem}
.toolbar{display:flex;gap:.5rem;flex-wrap:wrap}
</style>
</head>
<body>
<h1>DeepSeek Chat</h1>
<p class="meta">Multi-turn memory is handled by your app (stateless API). You can clear or delete the last exchange anytime. 2</p>
<form method="post">
<textarea name="question" placeholder="Type your message..."><?= h($stickyQuestion) ?></textarea>
<div class="row">
<label>Model
<select name="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>
</label>
<label>Max tokens
<select name="max_tokens">
<?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 name="temperature" type="number" step="0.1" min="0" max="2" value="<?= h($stickyTemp) ?>">
</label>
<label>
<input type="checkbox" name="continue" <?= $stickyContinue ?: 'checked' ?>> Continue this conversation
</label>
</div>
<div class="toolbar">
<button type="submit" name="action" value="ask">Send</button>
<button type="submit" name="action" value="delete_last" title="Remove the most recent assistant reply (and its user message if present)">Delete last</button>
<button type="submit" name="action" value="clear" title="Remove all turns from memory">Clear conversation</button>
</div>
<p class="meta">API: <code>POST /chat/completions</code> (OpenAI-compatible). Provider doesn’t auto-remember; your app sends prior messages each time. 3</p>
</form>
<?php if ($error): ?>
<div class="card" style="color:#b00020"><strong>Error:</strong> <?= h($error) ?></div>
<?php endif; ?>
<?php if (!empty($history)): ?>
<div class="card">
<h3>Conversation so far</h3>
<?php foreach ($history as $m): ?>
<div class="role"><?= h(strtoupper($m['role'])) ?></div>
<div class="bubble"><?= render($m['content']) ?></div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if ($answer): ?>
<div class="card">
<h3>Latest answer</h3>
<div class="bubble"><?= render($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; ?>
</div>
<?php endif; ?>
<div class="card">
<h3>Notes</h3>
<ul>
<li>“Memory” is implemented here with PHP <code>$_SESSION</code>. For persistence across browsers or servers, store messages in MySQL and load them by a <em>conversation_id</em> you define.</li>
<li>To truly “remove” a conversation, clear it from your storage (session/DB). The API itself does not hold your chat state. 4</li>
<li>Providers may retain operational logs per their privacy policy—check your compliance needs. 5</li>
</ul>
</div>
</body>
</html>