Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Playground - At41rvAI</title> | |
| <link rel="icon" type="image/png" href="/apple-touch-icon.png"> | |
| <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"> | |
| <link rel="shortcut icon" href="/apple-touch-icon.png"> | |
| <style> | |
| /* General Reset & Base Styles */ | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Inter', -apple-system, BlinkMacMacSystemFont, 'Segoe UI', Roboto, sans-serif; | |
| background: #000000; | |
| color: #ffffff; | |
| line-height: 1.6; | |
| overflow-x: hidden; | |
| } | |
| /* Glassmorphism Effect */ | |
| .glass { | |
| background: rgba(255, 255, 255, 0.05); | |
| backdrop-filter: blur(20px); | |
| -webkit-backdrop-filter: blur(20px); | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| border-radius: 16px; | |
| } | |
| .glass-strong { | |
| background: rgba(255, 255, 255, 0.1); | |
| backdrop-filter: blur(30px); | |
| -webkit-backdrop-filter: blur(30px); | |
| border: 1px solid rgba(255, 255, 255, 0.2); | |
| border-radius: 20px; | |
| } | |
| /* Navigation */ | |
| nav { | |
| position: fixed; | |
| top: 20px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| z-index: 1000; | |
| padding: 16px 32px; | |
| max-width: 800px; | |
| width: 90%; | |
| border-radius: 16px; | |
| /* Ensure rounded corners on nav */ | |
| } | |
| .nav-container { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .logo { | |
| font-size: 24px; | |
| font-weight: 800; | |
| color: #ffffff; | |
| } | |
| .nav-links { | |
| display: flex; | |
| gap: 24px; | |
| list-style: none; | |
| } | |
| .nav-links a { | |
| color: rgba(255, 255, 255, 0.8); | |
| text-decoration: none; | |
| font-weight: 500; | |
| transition: all 0.3s ease; | |
| padding: 8px 16px; | |
| border-radius: 12px; | |
| } | |
| .nav-links a:hover { | |
| color: #ffffff; | |
| background: rgba(255, 255, 255, 0.1); | |
| } | |
| /* Main Layout */ | |
| .main-container { | |
| display: flex; | |
| min-height: 100vh; | |
| padding-top: 100px; | |
| /* Space for fixed nav */ | |
| padding-bottom: 20px; | |
| /* Bottom padding for content */ | |
| } | |
| /* Sidebar (Modified to be minimal for Chatbot only) */ | |
| .sidebar { | |
| width: 350px; | |
| padding: 20px; | |
| position: fixed; | |
| left: 0; | |
| top: 100px; | |
| bottom: 0; | |
| overflow-y: auto; | |
| z-index: 100; | |
| } | |
| .sidebar-content { | |
| padding: 24px; | |
| height: 100%; | |
| } | |
| .sidebar h2 { | |
| font-size: 1.5rem; | |
| margin-bottom: 20px; | |
| color: #ffffff; | |
| } | |
| .sidebar-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 20px; | |
| } | |
| .sidebar-close-btn { | |
| display: none; | |
| /* Hidden by default on larger screens */ | |
| background: rgba(255, 255, 255, 0.1); | |
| border: 1px solid rgba(255, 255, 255, 0.2); | |
| color: #ffffff; | |
| width: 32px; | |
| height: 32px; | |
| border-radius: 50%; | |
| cursor: pointer; | |
| font-size: 16px; | |
| line-height: 1; | |
| transition: all 0.3s ease; | |
| justify-content: center; | |
| /* Center content */ | |
| align-items: center; | |
| /* Center content */ | |
| } | |
| .sidebar-close-btn:hover { | |
| background: rgba(255, 255, 255, 0.2); | |
| transform: scale(1.1); | |
| } | |
| @media (max-width: 1024px) { | |
| .sidebar-close-btn { | |
| display: flex; | |
| /* Show on smaller screens */ | |
| } | |
| } | |
| .endpoint-list { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 12px; | |
| } | |
| .endpoint-item { | |
| padding: 16px; | |
| border-radius: 12px; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| } | |
| .endpoint-item:hover { | |
| background: rgba(255, 255, 255, 0.1); | |
| transform: translateX(4px); | |
| } | |
| .endpoint-item.active { | |
| background: rgba(255, 255, 255, 0.15); | |
| border-color: rgba(255, 255, 255, 0.3); | |
| } | |
| .endpoint-method { | |
| font-size: 0.75rem; | |
| font-weight: 700; | |
| padding: 4px 8px; | |
| border-radius: 6px; | |
| background: #ffffff; | |
| color: #000000; | |
| display: inline-block; | |
| margin-bottom: 8px; | |
| } | |
| .endpoint-name { | |
| font-weight: 600; | |
| margin-bottom: 4px; | |
| } | |
| .endpoint-desc { | |
| font-size: 0.875rem; | |
| color: rgba(255, 255, 255, 0.7); | |
| } | |
| /* Main Content */ | |
| .content { | |
| flex: 1; | |
| margin-left: 350px; | |
| /* Space for fixed sidebar */ | |
| padding: 20px; | |
| } | |
| .playground-header { | |
| text-align: center; | |
| margin-bottom: 40px; | |
| } | |
| .playground-title { | |
| font-size: 3rem; | |
| font-weight: 800; | |
| margin-bottom: 16px; | |
| background: linear-gradient(135deg, #ffffff 0%, rgba(255, 255, 255, 0.7) 100%); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| } | |
| .playground-subtitle { | |
| font-size: 1.25rem; | |
| color: rgba(255, 255, 255, 0.7); | |
| } | |
| /* Playground Interface */ | |
| .playground-interface { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 24px; | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| } | |
| .input-panel, | |
| .output-panel { | |
| padding: 24px; | |
| min-height: 500px; | |
| } | |
| .panel-title { | |
| font-size: 1.25rem; | |
| font-weight: 600; | |
| margin-bottom: 20px; | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| .form-group { | |
| margin-bottom: 20px; | |
| } | |
| .form-label { | |
| display: block; | |
| margin-bottom: 8px; | |
| font-weight: 500; | |
| color: rgba(255, 255, 255, 0.9); | |
| } | |
| .form-input, | |
| .form-select, | |
| .form-textarea { | |
| width: 100%; | |
| padding: 12px 16px; | |
| border: 1px solid rgba(255, 255, 255, 0.2); | |
| border-radius: 8px; | |
| background: rgba(255, 255, 255, 0.05); | |
| color: #ffffff; | |
| font-size: 14px; | |
| transition: all 0.3s ease; | |
| font-family: inherit; | |
| /* Inherit font for inputs */ | |
| } | |
| .form-input:focus, | |
| .form-select:focus, | |
| .form-textarea:focus { | |
| outline: none; | |
| border-color: rgba(255, 255, 255, 0.5); | |
| background: rgba(255, 255, 255, 0.1); | |
| } | |
| .form-textarea { | |
| min-height: 120px; | |
| resize: vertical; | |
| } | |
| .btn { | |
| padding: 12px 24px; | |
| border: none; | |
| border-radius: 8px; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| font-size: 14px; | |
| } | |
| .btn-primary { | |
| background: #ffffff; | |
| color: #000000; | |
| } | |
| .btn-primary:hover { | |
| background: rgba(255, 255, 255, 0.9); | |
| transform: translateY(-2px); | |
| } | |
| .btn-primary:disabled { | |
| opacity: 0.5; | |
| cursor: not-allowed; | |
| transform: none; | |
| } | |
| .output-content { | |
| background: rgba(255, 255, 255, 0.05); | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| border-radius: 8px; | |
| padding: 16px; | |
| font-family: 'SF Mono', Monaco, monospace; | |
| font-size: 14px; | |
| white-space: pre-wrap; | |
| overflow-y: auto; | |
| max-height: calc(100vh - 300px); | |
| /* Adjust based on viewport height */ | |
| } | |
| .loading { | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 8px; | |
| color: rgba(255, 255, 255, 0.7); | |
| } | |
| .spinner { | |
| width: 20px; | |
| height: 20px; | |
| border: 2px solid rgba(255, 255, 255, 0.3); | |
| border-top: 2px solid #ffffff; | |
| border-radius: 50%; | |
| animation: spin 1s linear infinite; | |
| } | |
| @keyframes spin { | |
| 0% { | |
| transform: rotate(0deg); | |
| } | |
| 100% { | |
| transform: rotate(360deg); | |
| } | |
| } | |
| .error { | |
| color: #ff6b6b; | |
| background: rgba(255, 107, 107, 0.1); | |
| border: 1px solid rgba(255, 107, 107, 0.3); | |
| padding: 12px; | |
| border-radius: 8px; | |
| margin-top: 12px; | |
| } | |
| .success { | |
| color: #51cf66; | |
| background: rgba(81, 207, 102, 0.1); | |
| border: 1px solid rgba(81, 207, 102, 0.3); | |
| padding: 12px; | |
| border-radius: 8px; | |
| margin-top: 12px; | |
| } | |
| /* Responsive */ | |
| @media (max-width: 1024px) { | |
| .sidebar { | |
| position: fixed; | |
| left: 0; | |
| top: 0; | |
| bottom: 0; | |
| width: 320px; | |
| transform: translateX(-100%); | |
| transition: transform 0.3s ease; | |
| z-index: 1000; | |
| background: rgba(0, 0, 0, 0.95); | |
| backdrop-filter: blur(20px); | |
| -webkit-backdrop-filter: blur(20px); | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| border-radius: 0; | |
| } | |
| .sidebar.open { | |
| transform: translateX(0); | |
| } | |
| .content { | |
| margin-left: 0; | |
| padding: 20px 15px; | |
| width: 100%; | |
| /* Take full width on mobile */ | |
| } | |
| .playground-interface { | |
| grid-template-columns: 1fr; | |
| gap: 20px; | |
| } | |
| .mobile-menu-btn { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| position: fixed; | |
| top: 100px; | |
| left: 20px; | |
| z-index: 1002; | |
| background: rgba(255, 255, 255, 0.15); | |
| backdrop-filter: blur(10px); | |
| border: 1px solid rgba(255, 255, 255, 0.3); | |
| color: #ffffff; | |
| padding: 12px 16px; | |
| border-radius: 12px; | |
| cursor: pointer; | |
| font-size: 14px; | |
| font-weight: 600; | |
| transition: all 0.3s ease; | |
| box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); | |
| } | |
| .mobile-menu-btn:hover { | |
| background: rgba(255, 255, 255, 0.2); | |
| transform: translateY(-2px); | |
| } | |
| .hamburger-icon { | |
| transition: transform 0.3s ease; | |
| display: inline-block; | |
| } | |
| .mobile-menu-btn.active .hamburger-icon { | |
| transform: rotate(90deg); | |
| } | |
| } | |
| @media (max-width: 768px) { | |
| .nav-links { | |
| display: none; | |
| /* Hide navigation links on smaller screens */ | |
| } | |
| .nav-container { | |
| justify-content: center; | |
| /* Center logo when links are hidden */ | |
| } | |
| .playground-title { | |
| font-size: 2.5rem; | |
| } | |
| .playground-subtitle { | |
| font-size: 1.125rem; | |
| } | |
| .input-panel, | |
| .output-panel { | |
| padding: 20px 16px; | |
| } | |
| .sidebar { | |
| width: 300px; | |
| overflow-y: auto; | |
| -webkit-overflow-scrolling: touch; | |
| } | |
| .sidebar-content { | |
| padding: 24px 20px; | |
| height: auto; | |
| min-height: 100vh; | |
| padding-top: 120px; | |
| /* Account for nav height */ | |
| } | |
| .endpoint-item { | |
| padding: 14px 12px; | |
| margin-bottom: 8px; | |
| } | |
| .endpoint-name { | |
| font-size: 15px; | |
| font-weight: 600; | |
| } | |
| .endpoint-desc { | |
| font-size: 13px; | |
| margin-top: 4px; | |
| } | |
| .endpoint-method { | |
| font-size: 11px; | |
| padding: 3px 6px; | |
| } | |
| } | |
| @media (max-width: 480px) { | |
| nav { | |
| width: 95%; | |
| padding: 12px 16px; | |
| } | |
| .logo { | |
| font-size: 18px; | |
| } | |
| .playground-title { | |
| font-size: 2rem; | |
| } | |
| .playground-subtitle { | |
| font-size: 1rem; | |
| } | |
| .input-panel, | |
| .output-panel { | |
| padding: 16px 12px; | |
| } | |
| .form-group { | |
| margin-bottom: 16px; | |
| } | |
| .form-input, | |
| .form-select, | |
| .form-textarea { | |
| padding: 10px 12px; | |
| font-size: 16px; | |
| /* Prevents zoom on iOS */ | |
| } | |
| .btn { | |
| padding: 12px 20px; | |
| font-size: 14px; | |
| width: 100%; | |
| } | |
| .sidebar { | |
| width: 260px; | |
| } | |
| .sidebar-content { | |
| padding: 20px 16px; | |
| } | |
| .endpoint-list { | |
| gap: 8px; | |
| } | |
| .endpoint-item { | |
| padding: 10px; | |
| } | |
| .mobile-menu-btn { | |
| top: 110px; | |
| left: 15px; | |
| padding: 10px; | |
| } | |
| } | |
| .mobile-menu-btn { | |
| display: none; | |
| /* Default to hidden unless @media rule applies */ | |
| } | |
| /* Ensure mobile menu is always visible on small screens */ | |
| @media (max-width: 1024px) { | |
| .mobile-menu-btn { | |
| display: flex ; | |
| } | |
| } | |
| /* Mobile backdrop */ | |
| .mobile-backdrop { | |
| display: none; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100vw; | |
| height: 100vh; | |
| background: rgba(0, 0, 0, 0.6); | |
| z-index: 999; | |
| opacity: 0; | |
| transition: opacity 0.3s ease; | |
| backdrop-filter: blur(2px); | |
| -webkit-backdrop-filter: blur(2px); | |
| } | |
| .mobile-backdrop.show { | |
| display: block; | |
| opacity: 1; | |
| } | |
| @media (max-width: 1024px) { | |
| .mobile-backdrop.show { | |
| display: block; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <nav class="glass"> | |
| <div class="nav-container"> | |
| <div class="logo">At41rvAI</div> | |
| <ul class="nav-links"> | |
| <li><a href='/'>Home</a></li> | |
| <li><a href="/playground">Playground</a></li> | |
| </ul> | |
| </div> | |
| </nav> | |
| <button class="mobile-menu-btn" onclick="toggleSidebar()" id="mobile-menu-btn"> | |
| <span class="hamburger-icon">☰</span> Menu | |
| </button> | |
| <div class="mobile-backdrop" id="mobile-backdrop" onclick="closeSidebar()"></div> | |
| <div class="main-container"> | |
| <div class="sidebar glass" id="sidebar"> | |
| <div class="sidebar-content"> | |
| <div class="sidebar-header"> | |
| <h2>API Endpoints</h2> | |
| <button class="sidebar-close-btn" onclick="closeSidebar()">✕</button> | |
| </div> | |
| <div class="endpoint-list"> | |
| <div class="endpoint-item active" onclick="selectEndpoint('chat')"> | |
| <div class="endpoint-method">POST</div> | |
| <div class="endpoint-name">Chat Completions</div> | |
| <div class="endpoint-desc">Generate AI responses</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="content"> | |
| <div class="playground-header"> | |
| <h1 class="playground-title">At41rvAI Playground</h1> | |
| <p class="playground-subtitle">Interact with your intelligent chat companion</p> | |
| </div> | |
| <div class="playground-interface"> | |
| <div class="input-panel glass"> | |
| <h3 class="panel-title"> | |
| <span>⚙️</span> Request | |
| </h3> | |
| <div id="chat-form" class="endpoint-form"> | |
| <div class="form-group"> | |
| <label class="form-label">Model</label> | |
| <select class="form-select" id="chat-model"> | |
| <option value="">Loading models...</option> | |
| </select> | |
| </div> | |
| <div class="form-group"> | |
| <label class="form-label">Prompt</label> | |
| <textarea class="form-textarea" id="chat-prompt" placeholder="Enter your message here...">Hello At41rvAI! How are you today?</textarea> | |
| </div> | |
| <button class="btn btn-primary" onclick="testChatCompletion()">Send Request</button> | |
| </div> | |
| </div> | |
| <div class="output-panel glass"> | |
| <h3 class="panel-title"> | |
| <span>📤</span> Response | |
| </h3> | |
| <div class="output-content" id="output"> | |
| Send a request to see the AI's response here. | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // API Configuration | |
| // IMPORTANT: This should be the direct URL to your NEW, PRIVATE proxy Space. | |
| // Format: https://<username>-<proxy-space-name>.hf.space | |
| const PROXY_API_BASE_URL = 'https://at41rv-ai-api-proxy.hf.space'; | |
| // Sidebar and Mobile Menu Functions (unchanged, not related to API calls) | |
| function toggleSidebar() { | |
| const sidebar = document.getElementById('sidebar'); | |
| const backdrop = document.getElementById('mobile-backdrop'); | |
| const menuBtn = document.getElementById('mobile-menu-btn'); | |
| sidebar.classList.toggle('open'); | |
| backdrop.classList.toggle('show'); | |
| menuBtn.classList.toggle('active'); | |
| if (sidebar.classList.contains('open')) { | |
| document.body.style.overflow = 'hidden'; | |
| } else { | |
| document.body.style.overflow = ''; | |
| } | |
| } | |
| function closeSidebar() { | |
| const sidebar = document.getElementById('sidebar'); | |
| const backdrop = document.getElementById('mobile-backdrop'); | |
| const menuBtn = document.getElementById('mobile-menu-btn'); | |
| sidebar.classList.remove('open'); | |
| backdrop.classList.remove('show'); | |
| menuBtn.classList.remove('active'); | |
| document.body.style.overflow = ''; | |
| } | |
| function selectEndpoint(endpoint) { | |
| document.querySelectorAll('.endpoint-item').forEach(item => { | |
| item.classList.remove('active'); | |
| }); | |
| document.querySelector(`.endpoint-item`).classList.add('active'); | |
| document.querySelectorAll('.endpoint-form').forEach(form => { | |
| form.style.display = 'none'; | |
| }); | |
| document.getElementById(endpoint + '-form').style.display = 'block'; | |
| document.getElementById('output').textContent = 'Send a request to see the AI\'s response here.'; | |
| } | |
| // UI Feedback Functions (unchanged, visual feedback) | |
| function showLoading() { | |
| document.getElementById('output').innerHTML = ` | |
| <div class="loading"> | |
| <div class="spinner"></div> | |
| <span>At41rvAI is thinking...</span> | |
| </div> | |
| `; | |
| } | |
| function showError(message) { | |
| document.getElementById('output').innerHTML = ` | |
| <div class="error"> | |
| <strong>Error:</strong> ${message} | |
| </div> | |
| `; | |
| } | |
| function showSuccess(data) { | |
| // Updated to handle the nested structure of the AI response | |
| let aiResponseContent = "No content received."; | |
| if (data && data.choices && data.choices.length > 0 && data.choices[0].message && data.choices[0].message.content) { | |
| aiResponseContent = data.choices[0].message.content; | |
| } else { | |
| aiResponseContent = JSON.stringify(data, null, 2); // Fallback to full JSON if content not found | |
| } | |
| document.getElementById('output').innerHTML = ` | |
| <div class="success"> | |
| <strong>✅ Success!</strong> | |
| </div> | |
| <pre style="background: rgba(0, 0, 0, 0.3); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 8px; padding: 12px; font-family: 'SF Mono', Monaco, monospace; font-size: 12px; color: rgba(255, 255, 255, 0.9); white-space: pre-wrap; overflow-x: auto; max-height: 200px; overflow-y: auto;">${aiResponseContent}</pre> | |
| `; | |
| } | |
| // Load chat models (unchanged, hardcoded for consistency) | |
| async function loadChatModels() { | |
| const chatModelSelect = document.getElementById('chat-model'); | |
| chatModelSelect.innerHTML = ''; | |
| const chatGPTModels = ['gpt-4o', 'gpt-4o-mini']; | |
| const optgroup = document.createElement('optgroup'); | |
| optgroup.label = 'ChatGPT Models'; | |
| chatGPTModels.forEach(model => { | |
| const option = document.createElement('option'); | |
| option.value = model; | |
| option.textContent = model; | |
| optgroup.appendChild(option); | |
| }); | |
| chatModelSelect.appendChild(optgroup); | |
| chatModelSelect.value = 'gpt-4o'; // Set default | |
| console.log('ChatGPT models loaded:', chatGPTModels); | |
| } | |
| // Function to test Chat Completion - Calls the new proxy endpoint | |
| async function testChatCompletion() { | |
| showLoading(); | |
| const model = document.getElementById('chat-model').value; | |
| const prompt = document.getElementById('chat-prompt').value; | |
| if (!model) { | |
| showError('Please select a model.'); | |
| return; | |
| } | |
| if (!prompt.trim()) { | |
| showError('Please enter a prompt.'); | |
| return; | |
| } | |
| try { | |
| // Call your new proxy Space's /proxy-chat endpoint | |
| const response = await fetch(`${PROXY_API_BASE_URL}/proxy-chat`, { // <--- THIS IS THE CRUCIAL CHANGE | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| // NO API KEY IS SENT FROM THE CLIENT-SIDE HERE. | |
| // The proxy handles the secure API_KEY. | |
| }, | |
| body: JSON.stringify({ | |
| messages: [{ | |
| role: "user", | |
| content: prompt | |
| }], | |
| model: model | |
| }) | |
| }); | |
| const data = await response.json(); | |
| if (response.ok) { | |
| showSuccess(data); | |
| } else { | |
| // Assuming the proxy returns error details in 'detail' field | |
| showError(data.detail || `Request failed with status: ${response.status}`); | |
| } | |
| } catch (error) { | |
| showError(`Network Error: ${error.message}. Please check console for more details.`); | |
| console.error('Fetch error:', error); | |
| } | |
| } | |
| // Initialize on page load | |
| document.addEventListener('DOMContentLoaded', function() { | |
| loadChatModels(); | |
| selectEndpoint('chat'); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |