Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>DevOps AI Assistant</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<link href="https://cdn.jsdelivr.net/npm/[email protected]/fonts/remixicon.css" rel="stylesheet"/> | |
<style> | |
.model-card:hover { | |
transform: translateY(-5px); | |
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1); | |
} | |
.terminal { | |
background-color: #1e1e1e; | |
font-family: 'Courier New', monospace; | |
border-radius: 0.375rem; | |
color: #e0e0e0; | |
} | |
.prompt { | |
color: #4ade80; | |
font-weight: bold; | |
} | |
.user { | |
color: #60a5fa; | |
} | |
.ai { | |
color: #f59e0b; | |
} | |
.cursor { | |
animation: blink 1s infinite; | |
} | |
@keyframes blink { | |
0%, 100% { opacity: 1; } | |
50% { opacity: 0; } | |
} | |
.sidebar { | |
transition: all 0.3s ease; | |
} | |
.sidebar.collapsed { | |
width: 70px; | |
} | |
.sidebar.collapsed .sidebar-text { | |
display: none; | |
} | |
.sidebar.collapsed .sidebar-icon { | |
margin-right: 0; | |
} | |
.progress-bar { | |
transition: width 0.3s ease; | |
} | |
@keyframes spin { | |
0% { transform: rotate(0deg); } | |
100% { transform: rotate(360deg); } | |
} | |
.animate-spin { | |
animation: spin 1s linear infinite; | |
} | |
#model-upload::-webkit-file-upload-button { | |
visibility: hidden; | |
} | |
#model-upload::before { | |
content: 'Select GGUF File'; | |
display: inline-block; | |
background: linear-gradient(to bottom, #f9f9f9, #e3e3e3); | |
border: 1px solid #999; | |
border-radius: 3px; | |
padding: 5px 8px; | |
outline: none; | |
white-space: nowrap; | |
cursor: pointer; | |
text-shadow: 1px 1px #fff; | |
font-weight: 700; | |
font-size: 10pt; | |
} | |
#model-upload:hover::before { | |
border-color: black; | |
} | |
#model-upload:active::before { | |
background: linear-gradient(to bottom, #e3e3e3, #f9f9f9); | |
} | |
.dark-mode { | |
background-color: #1a202c; | |
color: #e2e8f0; | |
} | |
.dark-mode .sidebar { | |
background-color: #2d3748; | |
border-color: #4a5568; | |
} | |
.dark-mode .terminal { | |
background-color: #111827; | |
} | |
.dark-mode .bg-white { | |
background-color: #2d3748 ; | |
color: #e2e8f0; | |
} | |
.dark-mode .text-gray-800 { | |
color: #e2e8f0 ; | |
} | |
.dark-mode .text-gray-700 { | |
color: #cbd5e0 ; | |
} | |
.dark-mode .text-gray-500 { | |
color: #a0aec0 ; | |
} | |
.dark-mode .border-gray-200 { | |
border-color: #4a5568 ; | |
} | |
.dark-mode .bg-gray-50 { | |
background-color: #4a5568 ; | |
} | |
.active-section { | |
background-color: #eff6ff ; | |
} | |
.dark-mode .active-section { | |
background-color: #3b4d71 ; | |
color: white ; | |
} | |
</style> | |
</head> | |
<body class="bg-gray-50 text-gray-800"> | |
<div class="flex h-screen overflow-hidden"> | |
<!-- Sidebar --> | |
<div id="sidebar" class="sidebar bg-white w-64 border-r border-gray-200 flex flex-col"> | |
<div class="p-4 border-b border-gray-200 flex items-center justify-between"> | |
<div class="flex items-center"> | |
<i class="ri-cpu-line text-2xl text-blue-600"></i> | |
<span class="sidebar-text ml-2 font-bold text-xl">DevAI</span> | |
</div> | |
<button id="collapse-btn" class="text-gray-500 hover:text-gray-700"> | |
<i class="ri-arrow-left-s-line"></i> | |
</button> | |
</div> | |
<nav class="flex-1 overflow-y-auto p-2"> | |
<div class="space-y-1"> | |
<a href="#" class="active-section flex items-center px-3 py-2 text-sm font-medium rounded-md" data-section="dashboard"> | |
<i class="ri-dashboard-line sidebar-icon mr-3"></i> | |
<span class="sidebar-text">Dashboard</span> | |
</a> | |
<a href="#" class="flex items-center px-3 py-2 text-sm font-medium rounded-md text-gray-700 hover:bg-gray-100" data-section="github"> | |
<i class="ri-github-fill sidebar-icon mr-3"></i> | |
<span class="sidebar-text">GitHub Actions</span> | |
</a> | |
<a href="#" class="flex items-center px-3 py-2 text-sm font-medium rounded-md text-gray-700 hover:bg-gray-100" data-section="ai"> | |
<i class="ri-robot-line sidebar-icon mr-3"></i> | |
<span class="sidebar-text">AI Assistant</span> | |
</a> | |
<a href="#" class="flex items-center px-3 py-2 text-sm font-medium rounded-md text-gray-700 hover:bg-gray-100" data-section="model"> | |
<i class="ri-brain-line sidebar-icon mr-3"></i> | |
<span class="sidebar-text">Model Runner</span> | |
</a> | |
<a href="#" class="flex items-center px-3 py-2 text-sm font-medium rounded-md text-gray-700 hover:bg-gray-100" data-section="settings"> | |
<i class="ri-settings-3-line sidebar-icon mr-3"></i> | |
<span class="sidebar-text">Settings</span> | |
</a> | |
</div> | |
</nav> | |
<div class="p-4 border-t border-gray-200"> | |
<div class="flex items-center"> | |
<div class="h-10 w-10 rounded-full bg-blue-100 flex items-center justify-center"> | |
<i class="ri-user-line text-blue-600"></i> | |
</div> | |
<div class="ml-3 sidebar-text"> | |
<p class="text-sm font-medium">DevOps User</p> | |
<p class="text-xs text-gray-500">admin</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Main Content --> | |
<div class="flex-1 overflow-auto"> | |
<div class="p-6"> | |
<!-- Dashboard --> | |
<div id="dashboard"> | |
<div class="flex justify-between items-center mb-6"> | |
<h1 class="text-2xl font-bold">DevOps Dashboard</h1> | |
<div class="flex space-x-2"> | |
<button class="px-4 py-2 bg-blue-600 text-white rounded-md flex items-center"> | |
<i class="ri-add-line mr-2"></i> | |
<span>New Project</span> | |
</button> | |
</div> | |
</div> | |
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6"> | |
<div class="bg-white p-6 rounded-lg shadow"> | |
<div class="flex items-center justify-between"> | |
<h3 class="text-lg font-medium">GitHub Projects</h3> | |
<i class="ri-github-fill text-2xl text-gray-700"></i> | |
</div> | |
<p class="text-3xl font-bold mt-2" id="github-project-count">42</p> | |
<div class="mt-4 flex items-center text-sm text-green-600"> | |
<i class="ri-arrow-up-line mr-1"></i> | |
<span id="github-project-change">5 new this week</span> | |
</div> | |
</div> | |
<div class="bg-white p-6 rounded-lg shadow"> | |
<div class="flex items-center justify-between"> | |
<h3 class="text-lg font-medium">Active Pipelines</h3> | |
<i class="ri-flow-chart text-2xl text-blue-500"></i> | |
</div> | |
<p class="text-3xl font-bold mt-2" id="pipeline-count">18</p> | |
<div class="mt-4 flex items-center text-sm text-yellow-600"> | |
<i class="ri-alert-line mr-1"></i> | |
<span id="pipeline-status">2 warnings</span> | |
</div> | |
</div> | |
<div class="bg-white p-6 rounded-lg shadow"> | |
<div class="flex items-center justify-between"> | |
<h3 class="text-lg font-medium">AI Usage</h3> | |
<i class="ri-robot-line text-2xl text-purple-500"></i> | |
</div> | |
<p class="text-3xl font-bold mt-2" id="ai-usage-count">237</p> | |
<div class="mt-4 flex items-center text-sm text-blue-600"> | |
<i class="ri-timer-line mr-1"></i> | |
<span id="ai-usage-time">14.7h total</span> | |
</div> | |
</div> | |
</div> | |
<div class="bg-white p-6 rounded-lg shadow mb-6"> | |
<h3 class="text-lg font-medium mb-4">Activity Feed</h3> | |
<div id="activity-feed" class="space-y-4"> | |
<div class="flex items-center p-3 border border-gray-200 rounded-lg"> | |
<div class="bg-green-100 p-2 rounded-full"> | |
<i class="ri-git-branch-line text-green-600"></i> | |
</div> | |
<div class="ml-3"> | |
<p class="text-sm font-medium">New branch created</p> | |
<p class="text-xs text-gray-500">feature/api-integration created by user on 10 min ago</p> | |
</div> | |
</div> | |
<div class="flex items-center p-3 border border-gray-200 rounded-lg"> | |
<div class="bg-blue-100 p-2 rounded-full"> | |
<i class="ri-play-line text-blue-600"></i> | |
</div> | |
<div class="ml-3"> | |
<p class="text-sm font-medium">Pipeline started</p> | |
<p class="text-xs text-gray-500">CI/CD for project "analytics-service" started 25 min ago</p> | |
</div> | |
</div> | |
<div class="flex items-center p-3 border border-gray-200 rounded-lg"> | |
<div class="bg-purple-100 p-2 rounded-full"> | |
<i class="ri-robot-line text-purple-600"></i> | |
</div> | |
<div class="ml-3"> | |
<p class="text-sm font-medium">AI suggestion used</p> | |
<p class="text-xs text-gray-500">Optimized deployment config for frontend at 1h ago</p> | |
</div> | |
</div> | |
<div class="flex items-center p-3 border border-gray-200 rounded-lg"> | |
<div class="bg-yellow-100 p-2 rounded-full"> | |
<i class="ri-checkbox-circle-line text-yellow-600"></i> | |
</div> | |
<div class="ml-3"> | |
<p class="text-sm font-medium">Test suite passed</p> | |
<p class="text-xs text-gray-500">All 247 tests passed for pull request #42</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- GitHub Section --> | |
<div id="github" class="hidden"> | |
<div class="flex justify-between items-center mb-6"> | |
<h1 class="text-2xl font-bold">GitHub Integration</h1> | |
<div class="flex space-x-2"> | |
<button id="sync-github-btn" class="px-4 py-2 bg-green-600 text-white rounded-md flex items-center"> | |
<i class="ri-refresh-line mr-2"></i> | |
<span>Sync Repos</span> | |
</button> | |
</div> | |
</div> | |
<div class="bg-white p-6 rounded-lg shadow mb-6"> | |
<h3 class="text-lg font-medium mb-4">Connected Repositories</h3> | |
<div class="overflow-x-auto"> | |
<table class="min-w-full divide-y divide-gray-200"> | |
<thead> | |
<tr> | |
<th class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Repository</th> | |
<th class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Last Commit</th> | |
<th class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th> | |
<th class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th> | |
</tr> | |
</thead> | |
<tbody id="github-repos" class="bg-white divide-y divide-gray-200"> | |
<tr> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<div class="flex items-center"> | |
<i class="ri-book-2-line text-gray-500 mr-2"></i> | |
<div> | |
<div class="text-sm font-medium">analytics-service</div> | |
<div class="text-xs text-gray-500">main branch</div> | |
</div> | |
</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<div class="text-sm">Update database schema</div> | |
<div class="text-xs text-gray-500">2 hours ago</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">Active</span> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> | |
<button class="text-blue-600 hover:text-blue-900 mr-3">View</button> | |
<button class="text-indigo-600 hover:text-indigo-900 mr-3">Run CI</button> | |
<button class="text-gray-600 hover:text-gray-900">Settings</button> | |
</td> | |
</tr> | |
<tr> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<div class="flex items-center"> | |
<i class="ri-book-2-line text-gray-500 mr-2"></i> | |
<div> | |
<div class="text-sm font-medium">user-api</div> | |
<div class="text-xs text-gray-500">feature/auth branch</div> | |
</div> | |
</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<div class="text-sm">Add JWT support</div> | |
<div class="text-xs text-gray-500">5 hours ago</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-yellow-100 text-yellow-800">Pending</span> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> | |
<button class="text-blue-600 hover:text-blue-900 mr-3">View</button> | |
<button class="text-indigo-600 hover:text-indigo-900 mr-3">Run CI</button> | |
<button class="text-gray-600 hover:text-gray-900">Settings</button> | |
</td> | |
</tr> | |
<tr> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<div class="flex items-center"> | |
<i class="ri-book-2-line text-gray-500 mr-2"></i> | |
<div> | |
<div class="text-sm font-medium">frontend</div> | |
<div class="text-xs text-gray-500">main branch</div> | |
</div> | |
</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<div class="text-sm">Fix login page styling</div> | |
<div class="text-xs text-gray-500">1 day ago</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">Active</span> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> | |
<button class="text-blue-600 hover:text-blue-900 mr-3">View</button> | |
<button class="text-indigo-600 hover:text-indigo-900 mr-3">Run CI</button> | |
<button class="text-gray-600 hover:text-gray-900">Settings</button> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
</div> | |
</div> | |
<div class="grid grid-cols-1 md:grid-cols-2 gap-6"> | |
<div class="bg-white p-6 rounded-lg shadow"> | |
<h3 class="text-lg font-medium mb-4">Workflow Actions</h3> | |
<div class="space-y-3"> | |
<button id="create-workflow-btn" class="w-full flex items-center justify-between px-4 py-2 border border-gray-200 rounded-md hover:bg-gray-50"> | |
<span>Create New Workflow</span> | |
<i class="ri-arrow-right-line"></i> | |
</button> | |
<button id="manage-secrets-btn" class="w-full flex items-center justify-between px-4 py-2 border border-gray-200 rounded-md hover:bg-gray-50"> | |
<span>Manage Secrets</span> | |
<i class="ri-arrow-right-line"></i> | |
</button> | |
<button id="review-prs-btn" class="w-full flex items-center justify-between px-4 py-2 border border-gray-200 rounded-md hover:bg-gray-50"> | |
<span>Review Pull Requests</span> | |
<i class="ri-arrow-right-line"></i> | |
</button> | |
</div> | |
</div> | |
<div class="bg-white p-6 rounded-lg shadow"> | |
<h3 class="text-lg font-medium mb-4">Workflow Status</h3> | |
<div class="space-y-4" id="workflow-stats"> | |
<div> | |
<div class="flex justify-between text-sm mb-1"> | |
<span>CI Pipeline</span> | |
<span id="ci-pipeline-time">Avg: 4.2 min</span> | |
</div> | |
<div class="w-full bg-gray-200 rounded-full h-2.5"> | |
<div id="ci-pipeline-bar" class="h-2.5 rounded-full bg-blue-600" style="width: 75%"></div> | |
</div> | |
</div> | |
<div> | |
<div class="flex justify-between text-sm mb-1"> | |
<span>CD Deployment</span> | |
<span id="cd-deployment-time">Avg: 1.5 min</span> | |
</div> | |
<div class="w-full bg-gray-200 rounded-full h-2.5"> | |
<div id="cd-deployment-bar" class="h-2.5 rounded-full bg-green-600" style="width: 90%"></div> | |
</div> | |
</div> | |
<div> | |
<div class="flex justify-between text-sm mb-1"> | |
<span>Test Coverage</span> | |
<span id="test-coverage">78%</span> | |
</div> | |
<div class="w-full bg-gray-200 rounded-full h-2.5"> | |
<div id="test-coverage-bar" class="h-2.5 rounded-full bg-purple-600" style="width: 78%"></div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- AI Assistant Section --> | |
<div id="ai" class="hidden"> | |
<div class="flex justify-between items-center mb-6"> | |
<h1 class="text-2xl font-bold" id="ai-model-name">AI Assistant</h1> | |
<div class="flex space-x-2"> | |
<button id="test-ai-btn" class="px-4 py-2 bg-purple-600 text-white rounded-md flex items-center"> | |
<i class="ri-flask-line mr-2"></i> | |
<span>Test Connection</span> | |
</button> | |
</div> | |
</div> | |
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6"> | |
<div class="md:col-span-2 bg-white p-6 rounded-lg shadow"> | |
<div class="flex justify-between items-center mb-4"> | |
<h3 class="text-lg font-medium">DevOps Query</h3> | |
<div class="flex space-x-2"> | |
<button id="clear-chat-btn" class="p-2 rounded-full bg-gray-100 hover:bg-gray-200"> | |
<i class="ri-history-line text-gray-600"></i> | |
</button> | |
<button id="ai-settings-btn" class="p-2 rounded-full bg-gray-100 hover:bg-gray-200"> | |
<i class="ri-settings-3-line text-gray-600"></i> | |
</button> | |
</div> | |
</div> | |
<div id="ai-chat" class="terminal p-4 rounded-lg h-64 overflow-y-auto mb-4"> | |
<div class="mb-2"> | |
<span class="prompt">system:</span> | |
<span class="text-gray-300">AI Assistant is ready. Ask any DevOps-related question.</span> | |
</div> | |
<div class="mb-2 hidden" id="ai-thinking"> | |
<span class="prompt">system:</span> | |
<span class="text-gray-300"><i class="ri-loader-4-line animate-spin"></i> Thinking...</span> | |
</div> | |
</div> | |
<div class="flex"> | |
<input id="ai-input" type="text" placeholder="Ask me anything about DevOps..." class="flex-1 px-4 py-2 border border-gray-300 rounded-l-md focus:outline-none focus:ring-2 focus:ring-blue-500"> | |
<button id="send-ai-query" class="px-4 py-2 bg-blue-600 text-white rounded-r-md hover:bg-blue-700"> | |
<i class="ri-send-plane-2-line"></i> | |
</button> | |
</div> | |
</div> | |
<div class="bg-white p-6 rounded-lg shadow"> | |
<h3 class="text-lg font-medium mb-4">AI Capabilities</h3> | |
<div class="space-y-3"> | |
<div class="flex items-start"> | |
<div class="flex-shrink-0 h-10 w-10 rounded-full bg-blue-100 flex items-center justify-center mt-1 mr-3"> | |
<i class="ri-code-box-line text-blue-600"></i> | |
</div> | |
<div> | |
<h4 class="font-medium">Code Generation</h4> | |
<p class="text-sm text-gray-500">Generate pipelines, scripts, and configurations</p> | |
</div> | |
</div> | |
<div class="flex items-start"> | |
<div class="flex-shrink-0 h-10 w-10 rounded-full bg-green-100 flex items-center justify-center mt-1 mr-3"> | |
<i class="ri-bug-line text-green-600"></i> | |
</div> | |
<div> | |
<h4 class="font-medium">Troubleshooting</h4> | |
<p class="text-sm text-gray-500">Diagnose and suggest fixes for common issues</p> | |
</div> | |
</div> | |
<div class="flex items-start"> | |
<div class="flex-shrink-0 h-10 w-10 rounded-full bg-purple-100 flex items-center justify-center mt-1 mr-3"> | |
<i class="ri-lightbulb-flash-line text-purple-600"></i> | |
</div> | |
<div> | |
<h4 class="font-medium">Best Practices</h4> | |
<p class="text-sm text-gray-500">Recommend industry-standard approaches</p> | |
</div> | |
</div> | |
<div class="flex items-start"> | |
<div class="flex-shrink-0 h-10 w-10 rounded-full bg-yellow-100 flex items-center justify-center mt-1 mr-3"> | |
<i class="ri-shield-keyhole-line text-yellow-600"></i> | |
</div> | |
<div> | |
<h4 class="font-medium">Security Analysis</h4> | |
<p class="text-sm text-gray-500">Identify potential vulnerabilities in your setup</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="bg-white p-6 rounded-lg shadow"> | |
<h3 class="text-lg font-medium mb-4">Recent AI Sessions</h3> | |
<div class="overflow-x-auto"> | |
<table class="min-w-full divide-y divide-gray-200"> | |
<thead> | |
<tr> | |
<th class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Query</th> | |
<th class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Response Summary</th> | |
<th class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Time</th> | |
</tr> | |
</thead> | |
<tbody id="ai-history" class="bg-white divide-y divide-gray-200"> | |
<tr> | |
<td class="px-6 py-4 whitespace-nowrap text-sm"> | |
How to optimize Dockerfile? | |
</td> | |
<td class="px-6 py-4 text-sm"> | |
<div class="text-xs inline-flex items-center font-bold leading-sm px-3 py-1 bg-green-100 text-green-700 rounded-full"> | |
<i class="ri-check-line mr-1"></i> Completed | |
</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> | |
25 minutes ago | |
</td> | |
</tr> | |
<tr> | |
<td class="px-6 py-4 whitespace-nowrap text-sm"> | |
Best practices for Kubernetes security | |
</td> | |
<td class="px-6 py-4 text-sm"> | |
<div class="text-xs inline-flex items-center font-bold leading-sm px-3 py-1 bg-green-100 text-green-700 rounded-full"> | |
<i class="ri-check-line mr-1"></i> Completed | |
</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> | |
3 hours ago | |
</td> | |
</tr> | |
<tr> | |
<td class="px-6 py-4 whitespace-nowrap text-sm"> | |
CI/CD pipeline for Python project | |
</td> | |
<td class="px-6 py-4 text-sm"> | |
<div class="text-xs inline-flex items-center font-bold leading-sm px-3 py-1 bg-green-100 text-green-700 rounded-full"> | |
<i class="ri-check-line mr-1"></i> Completed | |
</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> | |
Yesterday | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
</div> | |
</div> | |
</div> | |
<!-- Model Runner Section --> | |
<div id="model" class="hidden"> | |
<div class="flex justify-between items-center mb-6"> | |
<h1 class="text-2xl font-bold">Model Runner (GGUF)</h1> | |
<div class="flex space-x-2"> | |
<label for="model-upload" class="px-4 py-2 bg-green-600 text-white rounded-md flex items-center cursor-pointer"> | |
<i class="ri-upload-line mr-2"></i> | |
<span>Upload Model</span> | |
</label> | |
<input type="file" id="model-upload" accept=".gguf" class="hidden"> | |
<button id="refresh-models-btn" class="px-4 py-2 bg-blue-600 text-white rounded-md flex items-center"> | |
<i class="ri-refresh-line mr-2"></i> | |
<span>Refresh</span> | |
</button> | |
</div> | |
</div> | |
<div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-6" id="model-cards"> | |
<div class="model-card bg-white p-4 rounded-lg shadow border border-blue-100"> | |
<div class="flex items-center mb-2"> | |
<i class="ri-brain-line text-2xl text-blue-500 mr-2"></i> | |
<h3 class="text-lg font-medium">Phi-2 2.7B</h3> | |
</div> | |
<p class="text-xs text-gray-500 mb-3">Microsoft's lightweight model good for code.</p> | |
<div class="flex justify-between text-xs text-gray-500 mb-3"> | |
<span>Size: 1.7GB</span> | |
<span>Q4_0</span> | |
</div> | |
<button class="w-full py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 flex items-center justify-center load-model-btn" data-model="phi-2"> | |
<i class="ri-play-line mr-1"></i> | |
<span>Load</span> | |
</button> | |
</div> | |
<div class="model-card bg-white p-4 rounded-lg shadow border border-gray-200"> | |
<div class="flex items-center mb-2"> | |
<i class="ri-brain-line text-2xl text-green-500 mr-2"></i> | |
<h3 class="text-lg font-medium">Mistral 7B</h3> | |
</div> | |
<p class="text-xs text-gray-500 mb-3">Excellent balance of size and performance.</p> | |
<div class="flex justify-between text-xs text-gray-500 mb-3"> | |
<span>Size: 4.1GB</span> | |
<span>Q4_K_M</span> | |
</div> | |
<button class="w-full py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 flex items-center justify-center load-model-btn" data-model="mistral-7b"> | |
<i class="ri-play-line mr-1"></i> | |
<span>Load</span> | |
</button> | |
</div> | |
<div class="model-card bg-white p-4 rounded-lg shadow border border-gray-200"> | |
<div class="flex items-center mb-2"> | |
<i class="ri-brain-line text-2xl text-purple-500 mr-2"></i> | |
<h3 class="text-lg font-medium">Llama 2 13B</h3> | |
</div> | |
<p class="text-xs text-gray-500 mb-3">Larger model for more complex tasks.</p> | |
<div class="flex justify-between text-xs text-gray-500 mb-3"> | |
<span>Size: 7.3GB</span> | |
<span>Q4_K_S</span> | |
</div> | |
<button class="w-full py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 flex items-center justify-center load-model-btn" data-model="llama2-13b"> | |
<i class="ri-play-line mr-1"></i> | |
<span>Load</span> | |
</button> | |
</div> | |
<div class="model-card bg-white p-4 rounded-lg shadow border border-gray-200"> | |
<div class="flex items-center mb-2"> | |
<i class="ri-brain-line text-2xl text-red-500 mr-2"></i> | |
<h3 class="text-lg font-medium">Custom Model</h3> | |
</div> | |
<p class="text-xs text-gray-500 mb-3">Upload your own GGUF model file.</p> | |
<div class="flex justify-between text-xs text-gray-500 mb-3"> | |
<span>-</span> | |
<span>-</span> | |
</div> | |
<label for="model-upload" class="w-full py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 flex items-center justify-center cursor-pointer"> | |
<i class="ri-upload-line mr-1"></i> | |
<span>Upload</span> | |
</label> | |
</div> | |
</div> | |
<div class="grid grid-cols-1 md:grid-cols-3 gap-6"> | |
<div class="md:col-span-2 bg-white p-6 rounded-lg shadow"> | |
<h3 class="text-lg font-medium mb-4">Model Playground</h3> | |
<div class="terminal p-4 rounded-lg h-96 overflow-y-auto mb-4" id="model-output"> | |
<div class="mb-2"> | |
<span class="prompt">system:</span> | |
<span class="text-gray-300">No model loaded. Select a model from above or upload your own GGUF file.</span> | |
</div> | |
<div class="mb-2 hidden" id="model-thinking"> | |
<span class="prompt">system:</span> | |
<span class="text-gray-300"><i class="ri-loader-4-line animate-spin"></i> Generating response...</span> | |
</div> | |
</div> | |
<div class="flex"> | |
<input type="text" id="model-input" placeholder="Type your message..." class="flex-1 px-4 py-2 border border-gray-300 rounded-l-md focus:outline-none focus:ring-2 focus:ring-blue-500"> | |
<button id="send-model-query" class="px-4 py-2 bg-blue-600 text-white rounded-r-md hover:bg-blue-700" disabled> | |
<i class="ri-send-plane-2-line"></i> | |
</button> | |
</div> | |
</div> | |
<div class="bg-white p-6 rounded-lg shadow"> | |
<h3 class="text-lg font-medium mb-4">Model Parameters</h3> | |
<div class="space-y-4"> | |
<div> | |
<label class="block text-sm font-medium text-gray-700 mb-1">Temperature</label> | |
<div class="flex items-center"> | |
<input type="range" id="temperature-slider" min="0" max="2" step="0.1" value="0.7" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"> | |
<span id="temperature-value" class="ml-3 text-sm text-gray-500">0.7</span> | |
</div> | |
</div> | |
<div> | |
<label class="block text-sm font-medium text-gray-700 mb-1">Top P</label> | |
<div class="flex items-center"> | |
<input type="range" id="top-p-slider" min="0" max="1" step="0.05" value="0.9" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"> | |
<span id="top-p-value" class="ml-3 text-sm text-gray-500">0.9</span> | |
</div> | |
</div> | |
<div> | |
<label class="block text-sm font-medium text-gray-700 mb-1">Max Tokens</label> | |
<input type="number" id="max-tokens-input" min="1" max="4096" value="512" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"> | |
</div> | |
<div class="pt-2"> | |
<button id="generate-btn" class="w-full px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 flex items-center justify-center" disabled> | |
<i class="ri-play-line mr-2"></i> | |
<span>Generate</span> | |
</button> | |
</div> | |
</div> | |
<div id="model-info" class="mt-6 pt-4 border-t border-gray-200"> | |
<h3 class="text-lg font-medium mb-2">Current Model</h3> | |
<p class="text-sm text-gray-700 mb-1">Name: <span id="current-model-name" class="font-medium">None loaded</span></p> | |
<p class="text-sm text-gray-700 mb-1">Size: <span id="current-model-size" class="font-medium">-</span></p> | |
<p class="text-sm text-gray-700 mb-1">Loaded: <span id="current-model-loaded" class="font-medium">-</span></p> | |
<div class="mt-2"> | |
<button id="unload-model-btn" class="w-full px-4 py-2 bg-red-600 text-white rounded-md hover:bg-red-700 flex items-center justify-center"> | |
<i class="ri-close-line mr-2"></i> | |
<span>Unload Model</span> | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Settings Section --> | |
<div id="settings" class="hidden"> | |
<div class="flex justify-between items-center mb-6"> | |
<h1 class="text-2xl font-bold">Settings</h1> | |
</div> | |
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6"> | |
<div class="md:col-span-2 bg-white p-6 rounded-lg shadow"> | |
<h3 class="text-lg font-medium mb-4">AI Provider Configuration</h3> | |
<form id="settings-form"> | |
<div class="mb-4"> | |
<label class="block text-sm font-medium text-gray-700 mb-1">AI Provider</label> | |
<select id="ai-provider" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"> | |
<option value="gemini">Gemini (Google AI Studio)</option> | |
<option value="openai">OpenAI</option> | |
<option value="anthropic">Anthropic</option> | |
<option value="custom">Custom</option> | |
</select> | |
</div> | |
<div class="mb-4"> | |
<label class="block text-sm font-medium text-gray-700 mb-1">API Key</label> | |
<div class="flex"> | |
<input id="api-key-input" type="password" placeholder="Enter your API key" class="flex-1 px-3 py-2 border border-gray-300 rounded-l-md focus:outline-none focus:ring-2 focus:ring-blue-500"> | |
<button type="button" id="toggle-key-visibility" class="px-3 py-2 bg-gray-100 border border-l-0 border-gray-300 rounded-r-md"> | |
<i class="ri-eye-line"></i> | |
</button> | |
</div> | |
<p class="mt-1 text-xs text-gray-500" id="api-key-help">Get your API key from <a href="https://ai.google.dev/" target="_blank" class="text-blue-600 hover:underline">Google AI Studio</a></p> | |
</div> | |
<div class="mb-4"> | |
<label class="block text-sm font-medium text-gray-700 mb-1">Base URL</label> | |
<input id="api-base-url" type="text" placeholder="Leave empty for default" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"> | |
</div> | |
<div class="mb-4"> | |
<div class="flex items-center"> | |
<input id="save-api-key" type="checkbox" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"> | |
<label for="save-api-key" class="ml-2 block text-sm text-gray-700">Save API key in browser storage</label> | |
</div> | |
</div> | |
<div class="flex justify-end"> | |
<button type="button" id="test-api-btn" class="mr-2 px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50"> | |
Test Connection | |
</button> | |
<button type="button" id="save-settings-btn" class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700"> | |
Save Settings | |
</button> | |
</div> | |
</form> | |
</div> | |
<div class="bg-white p-6 rounded-lg shadow"> | |
<h3 class="text-lg font-medium mb-4">Connection Status</h3> | |
<div class="space-y-4"> | |
<div class="flex items-center"> | |
<div id="ai-status-indicator" class="h-3 w-3 rounded-full bg-gray-300 mr-2"></div> | |
<span>AI Provider</span> | |
<button id="refresh-ai-status" class="ml-auto p-1 text-gray-500 hover:text-gray-700"> | |
<i class="ri-refresh-line"></i> | |
</button> | |
</div> | |
<div class="flex items-center"> | |
<div id="github-status-indicator" class="h-3 w-3 rounded-full bg-gray-300 mr-2"></div> | |
<span>GitHub Integration</span> | |
<button id="refresh-github-status" class="ml-auto p-1 text-gray-500 hover:text-gray-700"> | |
<i class="ri-refresh-line"></i> | |
</button> | |
</div> | |
<div class="flex items-center"> | |
<div id="model-status-indicator" class="h-3 w-3 rounded-full bg-gray-300 mr-2"></div> | |
<span>Model Runner</span> | |
<button id="refresh-model-status" class="ml-auto p-1 text-gray-500 hover:text-gray-700"> | |
<i class="ri-refresh-line"></i> | |
</button> | |
</div> | |
</div> | |
<div class="mt-6 pt-4 border-t border-gray-200"> | |
<h3 class="text-lg font-medium mb-4">Application Settings</h3> | |
<div class="space-y-3"> | |
<div class="flex items-center justify-between"> | |
<label class="block text-sm text-gray-700">Dark Mode</label> | |
<label class="relative inline-flex items-center cursor-pointer"> | |
<input type="checkbox" id="dark-mode-toggle" class="sr-only peer"> | |
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div> | |
</label> | |
</div> | |
<div class="flex items-center justify-between"> | |
<label class="block text-sm text-gray-700">Notifications</label> | |
<label class="relative inline-flex items-center cursor-pointer"> | |
<input type="checkbox" id="notifications-toggle" checked class="sr-only peer"> | |
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div> | |
</label> | |
</div> | |
<div class="flex items-center justify-between"> | |
<label class="block text-sm text-gray-700">Auto-update</label> | |
<label class="relative inline-flex items-center cursor-pointer"> | |
<input type="checkbox" id="auto-update-toggle" class="sr-only peer"> | |
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div> | |
</label> | |
</div> | |
</div> | |
</div> | |
<div class="mt-6 pt-4 border-t border-gray-200"> | |
<button id="clear-storage-btn" class="w-full px-4 py-2 bg-red-50 text-red-600 rounded-md hover:bg-red-100"> | |
Clear Local Storage | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<script> | |
document.addEventListener('DOMContentLoaded', function() { | |
// Sidebar toggle | |
const sidebar = document.getElementById('sidebar'); | |
const collapseBtn = document.getElementById('collapse-btn'); | |
collapseBtn.addEventListener('click', () => { | |
sidebar.classList.toggle('collapsed'); | |
collapseBtn.querySelector('i').classList.toggle('ri-arrow-left-s-line'); | |
collapseBtn.querySelector('i').classList.toggle('ri-arrow-right-s-line'); | |
}); | |
// Navigation between sections | |
const navLinks = document.querySelectorAll('[data-section]'); | |
const sections = { | |
dashboard: document.getElementById('dashboard'), | |
github: document.getElementById('github'), | |
ai: document.getElementById('ai'), | |
model: document.getElementById('model'), | |
settings: document.getElementById('settings') | |
}; | |
navLinks.forEach(link => { | |
link.addEventListener('click', (e) => { | |
e.preventDefault(); | |
const sectionName = link.getAttribute('data-section'); | |
// Hide all sections | |
Object.values(sections).forEach(section => { | |
section.classList.add('hidden'); | |
}); | |
// Show selected section | |
if (sections[sectionName]) { | |
sections[sectionName].classList.remove('hidden'); | |
} | |
// Update active nav link | |
navLinks.forEach(navLink => { | |
navLink.classList.remove('active-section'); | |
}); | |
link.classList.add('active-section'); | |
}); | |
}); | |
// Model parameters sliders | |
const temperatureSlider = document.getElementById('temperature-slider'); | |
const temperatureValue = document.getElementById('temperature-value'); | |
const topPSlider = document.getElementById('top-p-slider'); | |
const topPValue = document.getElementById('top-p-value'); | |
temperatureSlider.addEventListener('input', () => { | |
temperatureValue.textContent = temperatureSlider.value; | |
}); | |
topPSlider.addEventListener('input', () => { | |
topPValue.textContent = topPSlider.value; | |
}); | |
// Dark mode toggle | |
const darkModeToggle = document.getElementById('dark-mode-toggle'); | |
darkModeToggle.addEventListener('change', () => { | |
document.body.classList.toggle('dark-mode'); | |
localStorage.setItem('darkMode', darkModeToggle.checked ? 'enabled' : 'disabled'); | |
}); | |
// Load dark mode preference | |
if (localStorage.getItem('darkMode') === 'enabled') { | |
darkModeToggle.checked = true; | |
document.body.classList.add('dark-mode'); | |
} | |
// API key visibility toggle | |
const apiKeyInput = document.getElementById('api-key-input'); | |
const toggleKeyVisibility = document.getElementById('toggle-key-visibility'); | |
toggleKeyVisibility.addEventListener('click', () => { | |
if (apiKeyInput.type === 'password') { | |
apiKeyInput.type = 'text'; | |
toggleKeyVisibility.innerHTML = '<i class="ri-eye-off-line"></i>'; | |
} else { | |
apiKeyInput.type = 'password'; | |
toggleKeyVisibility.innerHTML = '<i class="ri-eye-line"></i>'; | |
} | |
}); | |
// Save settings | |
const saveSettingsBtn = document.getElementById('save-settings-btn'); | |
saveSettingsBtn.addEventListener('click', () => { | |
const saveKey = document.getElementById('save-api-key').checked; | |
const settings = { | |
provider: document.getElementById('ai-provider').value, | |
apiKey: document.getElementById('api-key-input').value, | |
baseUrl: document.getElementById('api-base-url').value, | |
saveKey | |
}; | |
localStorage.setItem('aiSettings', JSON.stringify(settings)); | |
if (saveKey) { | |
localStorage.setItem('apiKey', settings.apiKey); | |
} else { | |
localStorage.removeItem('apiKey'); | |
} | |
alert('Settings saved successfully!'); | |
}); | |
// Load settings | |
const loadSettings = () => { | |
const savedSettings = localStorage.getItem('aiSettings'); | |
if (savedSettings) { | |
const settings = JSON.parse(savedSettings); | |
document.getElementById('ai-provider').value = settings.provider; | |
document.getElementById('api-key-input').value = settings.apiKey; | |
document.getElementById('api-base-url').value = settings.baseUrl; | |
document.getElementById('save-api-key').checked = settings.saveKey; | |
} else if (localStorage.getItem('apiKey')) { | |
document.getElementById('api-key-input').value = localStorage.getItem('apiKey'); | |
document.getElementById('save-api-key').checked = true; | |
} | |
// Update provider info | |
updateProviderInfo(); | |
}; | |
// Update provider info based on selection | |
const updateProviderInfo = () => { | |
const provider = document.getElementById('ai-provider').value; | |
let helpText = ''; | |
switch(provider) { | |
case 'gemini': | |
helpText = 'Get your API key from <a href="https://ai.google.dev/" target="_blank" class="text-blue-600 hover:underline">Google AI Studio</a>'; | |
break; | |
case 'openai': | |
helpText = 'Get your API key from <a href="https://platform.openai.com/api-keys" target="_blank" class="text-blue-600 hover:underline">OpenAI</a>'; | |
break; | |
case 'anthropic': | |
helpText = 'Get your API key from <a href="https://console.anthropic.com/settings/keys" target="_blank" class="text-blue-600 hover:underline">Anthropic</a>'; | |
break; | |
case 'custom': | |
helpText = 'Enter your custom API key and endpoint URL'; | |
break; | |
} | |
document.getElementById('api-key-help').innerHTML = helpText; | |
}; | |
document.getElementById('ai-provider').addEventListener('change', updateProviderInfo); | |
// Test API connection | |
document.getElementById('test-api-btn').addEventListener('click', () => { | |
const provider = document.getElementById('ai-provider').value; | |
const apiKey = document.getElementById('api-key-input').value; | |
if (!apiKey) { | |
alert('Please enter an API key'); | |
return; | |
} | |
// Simulate API test (in a real app, you'd make an actual API call) | |
const statusIndicator = document.getElementById('ai-status-indicator'); | |
statusIndicator.classList.remove('bg-green-500', 'bg-red-500', 'bg-gray-300'); | |
statusIndicator.classList.add('bg-yellow-500'); | |
setTimeout(() => { | |
statusIndicator.classList.remove('bg-yellow-500'); | |
statusIndicator.classList.add('bg-green-500'); | |
const aiModelName = document.getElementById('ai-model-name'); | |
switch(provider) { | |
case 'gemini': | |
aiModelName.textContent = 'Gemini AI Assistant'; | |
break; | |
case 'openai': | |
aiModelName.textContent = 'OpenAI Assistant'; | |
break; | |
case 'anthropic': | |
aiModelName.textContent = 'Claude AI Assistant'; | |
break; | |
case 'custom': | |
aiModelName.textContent = 'Custom AI Assistant'; | |
break; | |
} | |
}, 1000); | |
}); | |
// Model loading functionality | |
const loadModelButtons = document.querySelectorAll('.load-model-btn'); | |
const sendModelQueryBtn = document.getElementById('send-model-query'); | |
const generateBtn = document.getElementById('generate-btn'); | |
const modelOutput = document.getElementById('model-output'); | |
const modelInput = document.getElementById('model-input'); | |
let currentModel = null; | |
loadModelButtons.forEach(button => { | |
button.addEventListener('click', () => { | |
const model = button.getAttribute('data-model'); | |
currentModel = model; | |
// Update model info | |
document.getElementById('current-model-name').textContent = model.replace('-', ' ').toUpperCase(); | |
document.getElementById('current-model-size').textContent = model === 'phi-2' ? '1.7GB' : | |
model === 'mistral-7b' ? '4.1GB' : | |
model === 'llama2-13b' ? '7.3GB' : '1.7GB'; | |
document.getElementById('current-model-loaded').textContent = new Date().toLocaleTimeString(); | |
// Enable query and generate buttons | |
sendModelQueryBtn.disabled = false; | |
generateBtn.disabled = false; | |
// Update model info display | |
document.getElementById('model-info').classList.remove('hidden'); | |
// Update model status indicator | |
const statusIndicator = document.getElementById('model-status-indicator'); | |
statusIndicator.classList.remove('bg-gray-300', 'bg-red-500'); | |
statusIndicator.classList.add('bg-green-500'); | |
// Add to model output | |
addToModelOutput('system', `Model ${model.replace('-', ' ').toUpperCase()} loaded successfully`); | |
}); | |
}); | |
// Unload model | |
document.getElementById('unload-model-btn').addEventListener('click', () => { | |
if (!currentModel) return; | |
addToModelOutput('system', `Unloading model ${currentModel.replace('-', ' ').toUpperCase()}...`); | |
// Reset model info | |
document.getElementById('current-model-name').textContent = 'None loaded'; | |
document.getElementById('current-model-size').textContent = '-'; | |
document.getElementById('current-model-loaded').textContent = '-'; | |
// Disable query and generate buttons | |
sendModelQueryBtn.disabled = true; | |
generateBtn.disabled = true; | |
// Update model status indicator | |
const statusIndicator = document.getElementById('model-status-indicator'); | |
statusIndicator.classList.remove('bg-green-500'); | |
statusIndicator.classList.add('bg-gray-300'); | |
currentModel = null; | |
setTimeout(() => { | |
addToModelOutput('system', 'Model unloaded. Select a model from above to continue.'); | |
}, 500); | |
}); | |
// Add message to model output | |
function addToModelOutput(sender, message) { | |
const thinkingDiv = document.getElementById('model-thinking'); | |
if (thinkingDiv) thinkingDiv.classList.add('hidden'); | |
const div = document.createElement('div'); | |
div.className = 'mb-2'; | |
const senderSpan = document.createElement('span'); | |
senderSpan.className = 'prompt'; | |
senderSpan.textContent = sender + ':'; | |
const messageSpan = document.createElement('span'); | |
messageSpan.className = 'text-gray-300 ml-1'; | |
messageSpan.textContent = message; | |
div.appendChild(senderSpan); | |
div.appendChild(messageSpan); | |
modelOutput.appendChild(div); | |
modelOutput.scrollTop = modelOutput.scrollHeight; | |
} | |
// Send model query | |
sendModelQueryBtn.addEventListener('click', () => { | |
const query = modelInput.value.trim(); | |
if (!query || !currentModel) return; | |
addToModelOutput('user', query); | |
modelInput.value = ''; | |
// Show thinking indicator | |
const thinkingDiv = document.getElementById('model-thinking'); | |
if (thinkingDiv) thinkingDiv.classList.remove('hidden'); | |
// Simulate response after a delay | |
setTimeout(() => { | |
const responses = { | |
'phi-2': `For your query about "${query}", the Phi-2 model suggests reviewing the official Microsoft documentation. As a lightweight model, Phi-2 is particularly good for code-related queries. Would you like me to generate example code?`, | |
'mistral-7b': `Regarding "${query}", Mistral 7B recommends checking system logs first. This model excels at troubleshooting and offers balanced performance for most DevOps tasks. I can help analyze specific error messages if you provide them.`, | |
'llama2-13b': `For your inquiry about "${query}", the larger Llama 2 13B model suggests a comprehensive approach with multiple verification steps. Due to its size, it can provide more detailed responses but requires more resources. Here's a step-by-step solution...` | |
}; | |
addToModelOutput('ai', responses[currentModel] || `I've processed your query about "${query}". As a general AI model, my suggestion would be to consult the official documentation and check system logs for any error messages.`); | |
}, 1000); | |
}); | |
// AI Chat functionality | |
const aiChat = document.getElementById('ai-chat'); | |
const aiInput = document.getElementById('ai-input'); | |
const sendAiQueryBtn = document.getElementById('send-ai-query'); | |
function addToAiChat(sender, message) { | |
const thinkingDiv = document.getElementById('ai-thinking'); | |
if (thinkingDiv) thinkingDiv.classList.add('hidden'); | |
const div = document.createElement('div'); | |
div.className = 'mb-2'; | |
const senderSpan = document.createElement('span'); | |
senderSpan.className = sender === 'user' ? 'user' : 'ai'; | |
senderSpan.style.fontWeight = 'bold'; | |
senderSpan.textContent = sender + ':'; | |
const messageSpan = document.createElement('span'); | |
messageSpan.className = 'text-gray-300 ml-1'; | |
messageSpan.textContent = message; | |
div.appendChild(senderSpan); | |
div.appendChild(messageSpan); | |
aiChat.appendChild(div); | |
aiChat.scrollTop = aiChat.scrollHeight; | |
} | |
sendAiQueryBtn.addEventListener('click', () => { | |
const query = aiInput.value.trim(); | |
if (!query) return; | |
addToAiChat('user', query); | |
aiInput.value = ''; | |
// Show thinking indicator | |
document.getElementById('ai-thinking').classList.remove('hidden'); | |
// Simulate AI response | |
setTimeout(() => { | |
const responses = [ | |
`For your DevOps query "${query}", I recommend following industry best practices. First, check your system logs for any errors. Then verify all configurations are correct. Would you like me to provide specific commands for your environment?`, | |
`Regarding "${query}", this appears to be a common issue in DevOps workflows. The standard approach involves 3 steps: 1) Verification, 2) Isolation, 3) Resolution. I can guide you through each step if helpful.`, | |
`Your question about "${query}" touches on several important aspects. Based on similar cases, I suggest reviewing these key areas: deployment pipelines, environment variables, and resource allocation.`, | |
`For "${query}", the solution depends on your specific stack. Please provide details about your technology (Docker, Kubernetes, CI/CD platform etc.) for more tailored advice.`, | |
`The answer to "${query}" typically involves these considerations: security requirements, performance impact, maintainability, and team familiarity with the solution.` | |
]; | |
addToAiChat('ai', responses[Math.floor(Math.random() * responses.length)]); | |
}, 1500); | |
}); | |
// Allow Enter key to send messages | |
aiInput.addEventListener('keypress', (e) => { | |
if (e.key === 'Enter') { | |
sendAiQueryBtn.click(); | |
} | |
}); | |
modelInput.addEventListener('keypress', (e) => { | |
if (e.key === 'Enter') { | |
sendModelQueryBtn.click(); | |
} | |
}); | |
// Clear chat history | |
document.getElementById('clear-chat-btn').addEventListener('click', () => { | |
aiChat.innerHTML = '<div class="mb-2"><span class="prompt">system:</span><span class="text-gray-300">Chat history cleared. Ask me anything about DevOps.</span></div>'; | |
}); | |
// GGUF model upload functionality | |
const modelUpload = document.getElementById('model-upload'); | |
modelUpload.addEventListener('change', (e) => { | |
const file = e.target.files[0]; | |
if (!file) return; | |
if (!file.name.endsWith('.gguf')) { | |
alert('Please select a GGUF model file (.gguf extension)'); | |
return; | |
} | |
// Simulate file processing and model loading | |
const fileName = file.name.replace('.gguf', ''); | |
currentModel = fileName.toLowerCase().replace(/ /g, '-'); | |
// Update model info | |
document.getElementById('current-model-name').textContent = fileName; | |
document.getElementById('current-model-size').textContent = `${(file.size / (1024 * 1024)).toFixed(1)}MB`; | |
document.getElementById('current-model-loaded').textContent = new Date().toLocaleTimeString(); | |
// Enable query and generate buttons | |
sendModelQueryBtn.disabled = false; | |
generateBtn.disabled = false; | |
// Update model info display | |
document.getElementById('model-info').classList.remove('hidden'); | |
// Update model status indicator | |
const statusIndicator = document.getElementById('model-status-indicator'); | |
statusIndicator.classList.remove('bg-gray-300', 'bg-red-500'); | |
statusIndicator.classList.add('bg-green-500'); | |
// Add to model output | |
addToModelOutput('system', `Custom model ${fileName} loaded successfully`); | |
// Reset file input | |
e.target.value = ''; | |
}); | |
// Clear local storage | |
document.getElementById('clear-storage-btn').addEventListener('click', () => { | |
if (confirm('Are you sure you want to clear all locally stored data (settings, API keys, etc.)?')) { | |
localStorage.clear(); | |
alert('Local storage cleared. Page will now reload.'); | |
location.reload(); | |
} | |
}); | |
// Initial setup | |
loadSettings(); | |
// Initialize connection status indicators | |
document.getElementById('ai-status-indicator').classList.add('bg-gray-300'); | |
document.getElementById('github-status-indicator').classList.add('bg-yellow-500'); | |
document.getElementById('model-status-indicator').classList.add('bg-gray-300'); | |
// Refresh status buttons | |
document.getElementById('refresh-ai-status').addEventListener('click', () => { | |
const indicator = document.getElementById('ai-status-indicator'); | |
indicator.classList.remove('bg-green-500', 'bg-red-500', 'bg-gray-300'); | |
indicator.classList.add('bg-yellow-500'); | |
setTimeout(() => { | |
indicator.classList.remove('bg-yellow-500'); | |
indicator.classList.add('bg-red-500'); | |
}, 1000); | |
}); | |
document.getElementById('refresh-github-status').addEventListener('click', () => { | |
const indicator = document.getElementById('github-status-indicator'); | |
indicator.classList.remove('bg-green-500', 'bg-red-500', 'bg-yellow-500'); | |
indicator.classList.add('bg-gray-300'); | |
setTimeout(() => { | |
indicator.classList.remove('bg-gray-300'); | |
indicator.classList.add('bg-green-500'); | |
}, 1000); | |
}); | |
document.getElementById('refresh-model-status').addEventListener('click', () => { | |
const indicator = document.getElementById('model-status-indicator'); | |
if (!currentModel) { | |
indicator.classList.remove('bg-green-500', 'bg-yellow-500'); | |
indicator.classList.add('bg-gray-300'); | |
} else { | |
indicator.classList.remove('bg-green-500', 'bg-gray-300'); | |
indicator.classList.add('bg-yellow-500'); | |
setTimeout(() => { | |
indicator.classList.remove('bg-yellow-500'); | |
indicator.classList.add('bg-green-500'); | |
}, 1000); | |
} | |
}); | |
// Generate button - combines slider settings | |
document.getElementById('generate-btn').addEventListener('click', () => { | |
if (!currentModel) return; | |
const temperature = temperatureSlider.value; | |
const topP = topPSlider.value; | |
const maxTokens = document.getElementById('max-tokens-input').value; | |
addToModelOutput('system', `Generating with params: temp=${temperature}, top_p=${topP}, max_tokens=${maxTokens}`); | |
// Show thinking indicator | |
const thinkingDiv = document.getElementById('model-thinking'); | |
if (thinkingDiv) thinkingDiv.classList.remove('hidden'); | |
// Simulate generation | |
setTimeout(() => { | |
const examples = [ | |
`Here's an optimized Kubernetes deployment configuration for your requirements:\n\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: optimized-deployment\nspec:\n replicas: 3\n selector:\n matchLabels:\n app: my-app\n template:\n metadata:\n labels:\n app: my-app\n spec:\n containers:\n - name: my-container\n image: my-image:latest\n ports:\n - containerPort: 8080\n resources:\n limits:\n cpu: "1"\n memory: "512Mi"`, | |
`For your CI/CD pipeline, consider this GitHub Actions workflow:\n\nname: CI/CD Pipeline\n\non:\n push:\n branches: [ main ]\n pull_request:\n branches: [ main ]\n\njobs:\n build:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v3\n - name: Set up Node.js\n uses: actions/setup-node@v3\n with:\n node-version: '18'\n - name: Install dependencies\n run: npm ci\n - name: Run tests\n run: npm test\n - name: Build | |
</html> |