Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions frontend_multi_user/src/admin_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -406,9 +406,9 @@ def generate():
return response


@admin_routes_bp.route("/demo_run")
@admin_routes_bp.route("/admin/demo_run")
@_admin_required
def demo_run():
def admin_demo_run():
from src.app import _model_profile_options, DEMO_FORM_RUN_PROMPT_UUIDS
user_id = str(current_user.id)
nonce = "DEMO_" + str(uuid.uuid4())
Expand All @@ -417,14 +417,18 @@ def demo_run():
for prompt_uuid in DEMO_FORM_RUN_PROMPT_UUIDS:
prompt_item = prompt_catalog.find(prompt_uuid)
if prompt_item is None:
logger.error(f"Prompt item not found for uuid: {prompt_uuid} in demo_run")
logger.error(f"Prompt item not found for uuid: {prompt_uuid} in admin_demo_run")
return "Error: Demo prompt configuration missing.", 500
prompts.append(prompt_item.prompt)

return render_template(
"demo_run.html",
admin_ext = current_app.extensions.get("admin", [None])
admin_obj = admin_ext[0] if isinstance(admin_ext, list) and admin_ext else None
template_args = dict(
user_id=user_id,
prompts=prompts,
nonce=nonce,
model_profile_options=_model_profile_options(),
)
if admin_obj:
return admin_obj.index_view.render("admin/demo_run.html", **template_args)
return render_template("admin/demo_run.html", **template_args)
5 changes: 3 additions & 2 deletions frontend_multi_user/src/plan_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -626,8 +626,9 @@ def run():
)

if current_user.is_admin:
admin_account = _get_current_user_account()
user_id_param = str(admin_account.id) if admin_account else current_app.config["ADMIN_USERNAME"]
if not user_id_param:
admin_account = _get_current_user_account()
user_id_param = str(admin_account.id) if admin_account else current_app.config["ADMIN_USERNAME"]
else:
user_id_param = str(current_user.id)

Expand Down
279 changes: 279 additions & 0 deletions frontend_multi_user/templates/admin/demo_run.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
{% extends 'admin/master.html' %}

{% block head_css %}
{{ super() }}
<style>
.demo-run-wrap {
max-width: 1200px;
margin: 0 auto;
padding: 1.5rem;
}
.iframe-container {
width: 100%;
height: 80vh;
border: 1px solid #ddd;
border-radius: 8px;
overflow: hidden;
background-color: white;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
display: none;
}
iframe {
width: 100%;
height: 100%;
border: none;
}
.input-container {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.example-prompt-buttons {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.example-prompt-buttons button {
padding: 10px 20px;
border: none;
border-radius: 6px;
background-color: #f0f0f0;
color: #333;
cursor: pointer;
transition: all 0.2s ease;
font-weight: 500;
}
.example-prompt-buttons button:hover {
background-color: #e0e0e0;
transform: translateY(-1px);
}
#prompt {
width: 100%;
height: 200px;
margin-bottom: 20px;
padding: 15px;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-size: 16px;
line-height: 1.5;
resize: vertical;
transition: border-color 0.2s ease;
box-sizing: border-box;
}
#prompt:focus {
outline: none;
border-color: #0066cc;
box-shadow: 0 0 0 3px rgba(0,102,204,0.1);
}
.checkbox-container {
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 10px;
}
.checkbox-container input[type="checkbox"] {
width: 18px;
height: 18px;
cursor: pointer;
}
.checkbox-container label {
font-size: 16px;
font-weight: 500;
color: #333;
cursor: pointer;
}
.select-container {
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 10px;
}
.select-container label {
font-size: 16px;
font-weight: 500;
color: #333;
}
.select-container select {
padding: 10px 15px;
border: 2px solid #e0e0e0;
border-radius: 6px;
font-size: 16px;
background-color: white;
cursor: pointer;
transition: border-color 0.2s ease;
}
.select-container select:focus {
outline: none;
border-color: #0066cc;
}
.method-info {
font-size: 13px;
color: #666;
margin-left: 10px;
font-style: italic;
}
#submit-button {
display: block;
width: 100%;
padding: 15px 30px;
background-color: #0066cc;
color: white;
border: none;
border-radius: 8px;
font-size: 18px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
}
#submit-button:hover {
background-color: #0052a3;
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
#submit-button:active {
transform: translateY(0);
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.char-count-label {
margin-top: 5px;
text-align: right;
font-size: 12px;
color: #666;
font-family: monospace;
}
</style>
{% endblock %}

{% block body %}
{{ super() }}
<div class="demo-run-wrap">
<div class="input-container">
<div class="example-prompt-buttons"></div>
<textarea id="prompt"></textarea>
<div class="char-count-label" id="char-count">Characters: 0 | Bytes: 0</div>
<div class="select-container">
<label for="speed-vs-detail-select">Speed vs detail:</label>
<select id="speed-vs-detail-select">
<option value="fast_but_skip_details">Fast, but fewer details</option>
<option value="all_details_but_slow">All details, but slow</option>
<option value="ping_llm" selected>Ping LLM (single request)</option>
</select>
</div>
<div class="select-container">
<label for="model-profile-select">Model profile:</label>
<select id="model-profile-select">
{% for profile_option in model_profile_options %}
<option value="{{ profile_option.value }}" {% if profile_option.value == "baseline" %}selected{% endif %}>{{ profile_option.title }} - {{ profile_option.subtitle }}</option>
{% endfor %}
</select>
</div>
<div class="checkbox-container">
<input type="checkbox" id="developer-checkbox" checked>
<label for="developer-checkbox">Developer mode when checked. When unchecked it's production mode.</label>
</div>
<div class="select-container">
<label for="method-select">Submit method:</label>
<select id="method-select">
<option value="POST" selected>POST</option>
<option value="GET">GET</option>
</select>
<span class="method-info" id="method-info">Recommended: payload not captured in logs</span>
</div>
<div class="select-container">
<label for="user-id-override">User ID override:</label>
<input type="text" id="user-id-override" placeholder="{{ user_id }}" style="font-family:monospace; font-size:14px; padding:4px 8px; border:1px solid #ccc; border-radius:4px; width:320px;">
<span style="font-size:12px; color:#999;">Leave empty to use current user</span>
</div>
<button id="submit-button">Submit</button>
</div>
<div class="iframe-container">
<iframe name="run-iframe" title="Run Page"></iframe>
</div>
</div>

<form id="run-form" action="/run" method="POST" target="run-iframe" style="display: none">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="prompt" id="form-prompt" value="">
<input type="hidden" name="user_id" id="form-user-id" value="{{ user_id }}">
<input type="hidden" name="nonce" value="{{ nonce }}">
<input type="hidden" name="speed_vs_detail" id="form-speed-vs-detail" value="ping_llm">
<input type="hidden" name="model_profile" id="form-model-profile" value="baseline">
<input type="hidden" name="developer" id="form-developer" value="true">
</form>

<script>
const prompts = {{ prompts|tojson|safe }};

function updateCharCount() {
const textarea = document.getElementById('prompt');
const charCountLabel = document.getElementById('char-count');
const text = textarea.value;
const charCount = text.length;
const byteCount = new TextEncoder().encode(text).length;
charCountLabel.textContent = `Characters: ${charCount} | Bytes: ${byteCount}`;
}

function updateMethodInfo() {
const methodSelect = document.getElementById('method-select');
const methodInfo = document.getElementById('method-info');
if (methodSelect.value === 'POST') {
methodInfo.textContent = 'Recommended: payload not captured in logs';
} else {
methodInfo.textContent = 'Caution: prompt visible in URL and logs';
}
}

window.onload = function() {
document.getElementById('prompt').value = prompts[0];
updateCharCount();
document.getElementById('prompt').addEventListener('input', updateCharCount);
document.getElementById('method-select').addEventListener('change', updateMethodInfo);

const buttonContainer = document.querySelector('.example-prompt-buttons');
prompts.forEach((prompt, index) => {
const button = document.createElement('button');
button.id = `example-${index + 1}-button`;
button.textContent = `Example ${index + 1}`;
button.addEventListener('click', function() {
document.getElementById('prompt').value = prompts[index];
updateCharCount();
});
buttonContainer.appendChild(button);
});
};

document.getElementById('submit-button').addEventListener('click', function() {
const promptValue = document.getElementById('prompt').value;
const speedVsDetailValue = document.getElementById('speed-vs-detail-select').value;
const modelProfileValue = document.getElementById('model-profile-select').value;
const developerChecked = document.getElementById('developer-checkbox').checked;
const methodSelect = document.getElementById('method-select');
const iframeContainer = document.querySelector('.iframe-container');
const inputContainer = document.querySelector('.input-container');
const iframe = iframeContainer.querySelector('iframe');
const userIdOverride = document.getElementById('user-id-override').value.trim();
const effectiveUserId = userIdOverride || '{{ user_id }}';

if (methodSelect.value === 'GET') {
let url = `/run?prompt=${encodeURIComponent(promptValue)}&user_id=${encodeURIComponent(effectiveUserId)}&nonce={{ nonce }}&speed_vs_detail=${encodeURIComponent(speedVsDetailValue)}&model_profile=${encodeURIComponent(modelProfileValue)}`;
if (developerChecked) {
url += '&developer';
}
iframe.src = url;
} else {
const form = document.getElementById('run-form');
document.getElementById('form-prompt').value = promptValue;
document.getElementById('form-model-profile').value = modelProfileValue;
document.getElementById('form-user-id').value = effectiveUserId;
const developerInput = document.getElementById('form-developer');
developerInput.disabled = !developerChecked;
document.getElementById('form-speed-vs-detail').value = speedVsDetailValue;
form.submit();
}

iframeContainer.style.display = 'block';
inputContainer.style.display = 'none';
});
</script>
{% endblock %}
2 changes: 1 addition & 1 deletion frontend_multi_user/templates/admin/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ <h2>Primary links</h2>
Ping LLMs
<div class="description">Check if they are all running</div>
</a>
<a href="/demo_run">
<a href="/admin/demo_run">
Demo Run - End to end test
<div class="description">Wraps the Run page inside an &lt;iframe&gt;.</div>
</a>
Expand Down
Loading