Spaces:
Sleeping
Sleeping
Gaurav vashistha commited on
Commit ·
87b646c
1
Parent(s): 39eba57
Tech Debt: Migrate to unified google-genai SDK
Browse files- agents/memory_agent.py +7 -7
- agents/visual_analyst.py +8 -6
- fix_browse_button.py +57 -0
- fix_dashboard_api.py +66 -0
- fix_js_syntax.py +147 -0
- requirements.txt +1 -1
- upgrade_docker.py +51 -0
agents/memory_agent.py
CHANGED
|
@@ -2,7 +2,7 @@ import os
|
|
| 2 |
import time
|
| 3 |
from dotenv import load_dotenv
|
| 4 |
from pinecone import Pinecone, ServerlessSpec
|
| 5 |
-
|
| 6 |
|
| 7 |
load_dotenv()
|
| 8 |
|
|
@@ -12,7 +12,7 @@ class MemoryAgent:
|
|
| 12 |
self.gemini_api_key = os.getenv("GEMINI_API_KEY")
|
| 13 |
if not self.gemini_api_key:
|
| 14 |
raise ValueError("GEMINI_API_KEY not found")
|
| 15 |
-
genai.
|
| 16 |
|
| 17 |
# Configure Pinecone
|
| 18 |
self.pinecone_api_key = os.getenv("PINECONE_API_KEY")
|
|
@@ -44,12 +44,12 @@ class MemoryAgent:
|
|
| 44 |
|
| 45 |
def _get_embedding(self, text):
|
| 46 |
# Using models/text-embedding-004
|
| 47 |
-
result =
|
| 48 |
-
model="
|
| 49 |
-
|
| 50 |
-
|
| 51 |
)
|
| 52 |
-
return result[
|
| 53 |
|
| 54 |
def seed_database(self):
|
| 55 |
# Check if empty
|
|
|
|
| 2 |
import time
|
| 3 |
from dotenv import load_dotenv
|
| 4 |
from pinecone import Pinecone, ServerlessSpec
|
| 5 |
+
from google import genai
|
| 6 |
|
| 7 |
load_dotenv()
|
| 8 |
|
|
|
|
| 12 |
self.gemini_api_key = os.getenv("GEMINI_API_KEY")
|
| 13 |
if not self.gemini_api_key:
|
| 14 |
raise ValueError("GEMINI_API_KEY not found")
|
| 15 |
+
self.client = genai.Client(api_key=self.gemini_api_key)
|
| 16 |
|
| 17 |
# Configure Pinecone
|
| 18 |
self.pinecone_api_key = os.getenv("PINECONE_API_KEY")
|
|
|
|
| 44 |
|
| 45 |
def _get_embedding(self, text):
|
| 46 |
# Using models/text-embedding-004
|
| 47 |
+
result = self.client.models.embed_content(
|
| 48 |
+
model="text-embedding-004",
|
| 49 |
+
contents=text,
|
| 50 |
+
config={"task_type": "retrieval_document"}
|
| 51 |
)
|
| 52 |
+
return result.embeddings[0].values
|
| 53 |
|
| 54 |
def seed_database(self):
|
| 55 |
# Check if empty
|
agents/visual_analyst.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
import os
|
| 2 |
import json
|
| 3 |
import re
|
| 4 |
-
|
| 5 |
from dotenv import load_dotenv
|
| 6 |
|
| 7 |
load_dotenv()
|
|
@@ -12,9 +12,8 @@ class VisualAnalyst:
|
|
| 12 |
if not self.api_key:
|
| 13 |
raise ValueError("GEMINI_API_KEY not found")
|
| 14 |
|
| 15 |
-
genai.
|
| 16 |
-
self.model_name = "
|
| 17 |
-
self.model = genai.GenerativeModel(self.model_name)
|
| 18 |
print(f"✅ VisualAnalyst stored Gemini model: {self.model_name}")
|
| 19 |
|
| 20 |
async def analyze_image(self, image_path: str):
|
|
@@ -35,9 +34,12 @@ class VisualAnalyst:
|
|
| 35 |
"Return ONLY valid JSON with keys: main_color, product_type, design_style, visual_features."
|
| 36 |
)
|
| 37 |
|
| 38 |
-
# Gemini 1.5 Flash supports JSON response schema, but simple prompting often works well too.
|
| 39 |
# We'll stick to prompt engineering for now to match the "Return ONLY valid JSON" instruction.
|
| 40 |
-
response = self.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
|
| 42 |
response_text = response.text
|
| 43 |
|
|
|
|
| 1 |
import os
|
| 2 |
import json
|
| 3 |
import re
|
| 4 |
+
from google import genai
|
| 5 |
from dotenv import load_dotenv
|
| 6 |
|
| 7 |
load_dotenv()
|
|
|
|
| 12 |
if not self.api_key:
|
| 13 |
raise ValueError("GEMINI_API_KEY not found")
|
| 14 |
|
| 15 |
+
self.client = genai.Client(api_key=self.api_key)
|
| 16 |
+
self.model_name = "gemini-1.5-flash"
|
|
|
|
| 17 |
print(f"✅ VisualAnalyst stored Gemini model: {self.model_name}")
|
| 18 |
|
| 19 |
async def analyze_image(self, image_path: str):
|
|
|
|
| 34 |
"Return ONLY valid JSON with keys: main_color, product_type, design_style, visual_features."
|
| 35 |
)
|
| 36 |
|
|
|
|
| 37 |
# We'll stick to prompt engineering for now to match the "Return ONLY valid JSON" instruction.
|
| 38 |
+
response = self.client.models.generate_content(
|
| 39 |
+
model=self.model_name,
|
| 40 |
+
contents=[user_prompt, img],
|
| 41 |
+
config={'timeout': 15.0} # Alternatively, timeouts might be configured at the client level, but we maintain the logic.
|
| 42 |
+
)
|
| 43 |
|
| 44 |
response_text = response.text
|
| 45 |
|
fix_browse_button.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
import subprocess
|
| 3 |
+
import re
|
| 4 |
+
|
| 5 |
+
def fix_html():
|
| 6 |
+
with open('dashboard.html', 'r', encoding='utf-8') as f:
|
| 7 |
+
content = f.read()
|
| 8 |
+
|
| 9 |
+
js_snippet = """
|
| 10 |
+
const browseBtn = document.getElementById('browseBtn');
|
| 11 |
+
browseBtn.addEventListener('click', (e) => {
|
| 12 |
+
e.preventDefault();
|
| 13 |
+
fileInput.click();
|
| 14 |
+
});
|
| 15 |
+
fileInput.addEventListener('change', () => {
|
| 16 |
+
if (fileInput.files.length > 0) {
|
| 17 |
+
// Provide a visual cue that a file was selected
|
| 18 |
+
const fileName = fileInput.files[0].name;
|
| 19 |
+
browseBtn.innerHTML = `<span class="material-symbols-outlined text-xl">check_circle</span> ${fileName}`;
|
| 20 |
+
}
|
| 21 |
+
});
|
| 22 |
+
"""
|
| 23 |
+
|
| 24 |
+
if "browseBtn.addEventListener('click', (e) => {" in content and "Provide a visual cue that a file was selected" in content:
|
| 25 |
+
print("Snippet already exists. Skipping injection.")
|
| 26 |
+
else:
|
| 27 |
+
# Find where the DOM elements are defined
|
| 28 |
+
target = "const downloadBtn = document.getElementById('downloadBtn');"
|
| 29 |
+
if target in content:
|
| 30 |
+
new_content = content.replace(target, target + "\n" + js_snippet)
|
| 31 |
+
with open('dashboard.html', 'w', encoding='utf-8') as f:
|
| 32 |
+
f.write(new_content)
|
| 33 |
+
print("Injected JS successfully.")
|
| 34 |
+
else:
|
| 35 |
+
print("Could not find insertion point!")
|
| 36 |
+
return False
|
| 37 |
+
|
| 38 |
+
# Run git commands
|
| 39 |
+
subprocess.run(['git', 'add', 'dashboard.html'], check=True)
|
| 40 |
+
try:
|
| 41 |
+
subprocess.run(['git', 'commit', '-m', 'Bugfix: Wire up Browse Files button to hidden input'], check=True)
|
| 42 |
+
except subprocess.CalledProcessError:
|
| 43 |
+
print("Nothing to commit")
|
| 44 |
+
|
| 45 |
+
subprocess.run(['git', 'push', '--force', 'space', 'HEAD:main'], check=True)
|
| 46 |
+
|
| 47 |
+
# Push to origin main as well
|
| 48 |
+
try:
|
| 49 |
+
subprocess.run(['git', 'push', 'origin', 'HEAD:main'], check=True)
|
| 50 |
+
except subprocess.CalledProcessError:
|
| 51 |
+
print("Push to origin failed or not needed")
|
| 52 |
+
|
| 53 |
+
print("Deployment triggered successfully.")
|
| 54 |
+
return True
|
| 55 |
+
|
| 56 |
+
if __name__ == '__main__':
|
| 57 |
+
fix_html()
|
fix_dashboard_api.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
import subprocess
|
| 3 |
+
import re
|
| 4 |
+
|
| 5 |
+
def patch_dashboard():
|
| 6 |
+
with open('dashboard.html', 'r', encoding='utf-8') as f:
|
| 7 |
+
content = f.read()
|
| 8 |
+
|
| 9 |
+
# Locate the setTimeout block inside startBtn.addEventListener and replace it with fetch logic
|
| 10 |
+
pattern = re.compile(r"setTimeout\(\(\) => \{[\s\S]*?\}, 1500\);", re.DOTALL)
|
| 11 |
+
|
| 12 |
+
new_block = """try {
|
| 13 |
+
const formData = new FormData();
|
| 14 |
+
formData.append('file', fileInput.files[0]);
|
| 15 |
+
|
| 16 |
+
const response = await fetch('/generate-catalog', {
|
| 17 |
+
method: 'POST',
|
| 18 |
+
body: formData
|
| 19 |
+
});
|
| 20 |
+
|
| 21 |
+
const data = await response.json();
|
| 22 |
+
jsonOutput.textContent = JSON.stringify(data, null, 2);
|
| 23 |
+
isCatalogGenerated = true;
|
| 24 |
+
} catch (error) {
|
| 25 |
+
console.error("Error generating catalog:", error);
|
| 26 |
+
} finally {
|
| 27 |
+
startBtn.innerHTML = '<div class="absolute inset-0 flex items-center justify-center gap-2 lg:gap-3 relative z-10"><span class="text-white text-base lg:text-lg font-bold tracking-wide group-hover:scale-105 transition-transform">Start Agent Workflow</span><span class="material-symbols-outlined text-white text-lg lg:text-xl group-hover:translate-x-1 transition-transform">arrow_forward</span></div>';
|
| 28 |
+
startBtn.disabled = false;
|
| 29 |
+
startBtn.classList.add('animate-pulse-slow', 'animate-glow-pulse');
|
| 30 |
+
}"""
|
| 31 |
+
|
| 32 |
+
if not pattern.search(content):
|
| 33 |
+
print("Error: Could not find the target setTimeout block in dashboard.html.")
|
| 34 |
+
return False
|
| 35 |
+
|
| 36 |
+
new_content = pattern.sub(new_block, content)
|
| 37 |
+
|
| 38 |
+
with open('dashboard.html', 'w', encoding='utf-8') as f:
|
| 39 |
+
f.write(new_content)
|
| 40 |
+
|
| 41 |
+
print("Successfully patched dashboard.html")
|
| 42 |
+
return True
|
| 43 |
+
|
| 44 |
+
def run_git_commands():
|
| 45 |
+
commands = [
|
| 46 |
+
['git', 'add', 'dashboard.html'],
|
| 47 |
+
['git', 'commit', '-m', 'Bugfix: Restore real API connection to Glassmorphism UI'],
|
| 48 |
+
['git', 'push', '--force', 'space', 'HEAD:main']
|
| 49 |
+
]
|
| 50 |
+
|
| 51 |
+
for cmd in commands:
|
| 52 |
+
print(f"Running: {' '.join(cmd)}")
|
| 53 |
+
result = subprocess.run(cmd, capture_output=True, text=True)
|
| 54 |
+
if result.returncode != 0:
|
| 55 |
+
print(f"Command failed with {result.returncode}: \\n{result.stderr}")
|
| 56 |
+
# Don't break here, let it try the other commands just in case, though push might fail if commit fails.
|
| 57 |
+
if cmd[1] == 'commit' and "nothing to commit" in result.stdout + result.stderr:
|
| 58 |
+
continue
|
| 59 |
+
if cmd[1] == 'push':
|
| 60 |
+
pass
|
| 61 |
+
else:
|
| 62 |
+
print(f"Success!\\n{result.stdout}")
|
| 63 |
+
|
| 64 |
+
if __name__ == '__main__':
|
| 65 |
+
if patch_dashboard():
|
| 66 |
+
run_git_commands()
|
fix_js_syntax.py
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
import subprocess
|
| 3 |
+
|
| 4 |
+
def fix_html():
|
| 5 |
+
with open('dashboard.html', 'r', encoding='utf-8') as f:
|
| 6 |
+
content = f.read()
|
| 7 |
+
|
| 8 |
+
parts = content.split('</footer>')
|
| 9 |
+
if len(parts) < 2:
|
| 10 |
+
print("Could not find </footer> in dashboard.html")
|
| 11 |
+
return False
|
| 12 |
+
|
| 13 |
+
top_part = parts[0] + '</footer>\n'
|
| 14 |
+
|
| 15 |
+
new_script = """<script>
|
| 16 |
+
tailwind.config.theme.extend.animation = { shine: 'shine 1.5s infinite' }
|
| 17 |
+
tailwind.config.theme.extend.keyframes = {
|
| 18 |
+
shine: { '0%': { left: '-100%' }, '100%': { left: '200%' } }
|
| 19 |
+
}
|
| 20 |
+
const dropZone = document.getElementById('dropZone');
|
| 21 |
+
const fileInput = document.getElementById('fileInput');
|
| 22 |
+
const startBtn = document.getElementById('startBtn');
|
| 23 |
+
const jsonOutput = document.getElementById('jsonOutput');
|
| 24 |
+
const deployBtn = document.getElementById('deployBtn');
|
| 25 |
+
const copyBtn = document.getElementById('copyBtn');
|
| 26 |
+
const copyIcon = document.getElementById('copyIcon');
|
| 27 |
+
let selectedFile = null;
|
| 28 |
+
let isCatalogGenerated = false;
|
| 29 |
+
const defaultDropZoneContent = `
|
| 30 |
+
<div class="absolute w-16 h-16 lg:w-20 lg:h-20 bg-amber-500/10 rounded-full blur-xl group-hover:bg-amber-500/20 transition-all"></div>
|
| 31 |
+
<div class="size-14 lg:size-16 relative z-10 rounded-2xl bg-gradient-to-br from-neutral-800 to-black border border-white/10 shadow-lg flex items-center justify-center transition-transform group-hover:scale-110 duration-300">
|
| 32 |
+
<span class="material-symbols-outlined text-2xl lg:text-3xl text-amber-500">cloud_upload</span>
|
| 33 |
+
</div>
|
| 34 |
+
<div class="flex flex-col items-center gap-1 relative z-10">
|
| 35 |
+
<p class="text-white text-base lg:text-lg font-bold leading-tight tracking-tight text-center">Drop Product Image Here</p>
|
| 36 |
+
<p class="text-neutral-500 text-xs lg:text-sm font-medium text-center">Supports JPG, PNG, WEBP</p>
|
| 37 |
+
</div>
|
| 38 |
+
<button id="browseBtn" class="mt-2 relative z-10 flex items-center justify-center rounded-full h-8 lg:h-9 px-4 lg:px-5 bg-white/5 hover:bg-white/10 border border-white/10 text-white text-[10px] lg:text-xs font-bold transition-all uppercase tracking-wide">
|
| 39 |
+
Browse Files
|
| 40 |
+
</button>
|
| 41 |
+
`;
|
| 42 |
+
function initDropZone() {
|
| 43 |
+
const currentBrowseBtn = document.getElementById('browseBtn');
|
| 44 |
+
if (currentBrowseBtn) {
|
| 45 |
+
currentBrowseBtn.addEventListener('click', (e) => {
|
| 46 |
+
e.preventDefault(); e.stopPropagation(); fileInput.click();
|
| 47 |
+
});
|
| 48 |
+
}
|
| 49 |
+
}
|
| 50 |
+
initDropZone();
|
| 51 |
+
fileInput.addEventListener('change', (e) => {
|
| 52 |
+
if (e.target.files.length > 0) handleFile(e.target.files[0]);
|
| 53 |
+
});
|
| 54 |
+
dropZone.addEventListener('dragover', (e) => {
|
| 55 |
+
e.preventDefault(); dropZone.classList.add('border-amber-500', 'bg-amber-500/5');
|
| 56 |
+
});
|
| 57 |
+
dropZone.addEventListener('dragleave', (e) => {
|
| 58 |
+
e.preventDefault(); dropZone.classList.remove('border-amber-500', 'bg-amber-500/5');
|
| 59 |
+
});
|
| 60 |
+
dropZone.addEventListener('drop', (e) => {
|
| 61 |
+
e.preventDefault(); dropZone.classList.remove('border-amber-500', 'bg-amber-500/5');
|
| 62 |
+
if (e.dataTransfer.files.length > 0) {
|
| 63 |
+
fileInput.files = e.dataTransfer.files;
|
| 64 |
+
handleFile(e.dataTransfer.files[0]);
|
| 65 |
+
}
|
| 66 |
+
});
|
| 67 |
+
function handleFile(file) {
|
| 68 |
+
selectedFile = file;
|
| 69 |
+
dropZone.innerHTML = `
|
| 70 |
+
<div class="flex flex-col items-center justify-center gap-4 z-10">
|
| 71 |
+
<div class="size-14 lg:size-16 rounded-2xl bg-gradient-to-br from-neutral-800 to-black border border-white/10 shadow-lg flex items-center justify-center">
|
| 72 |
+
<span class="material-symbols-outlined text-2xl lg:text-3xl text-amber-500">check_circle</span>
|
| 73 |
+
</div>
|
| 74 |
+
<div class="flex flex-col items-center gap-1">
|
| 75 |
+
<p class="text-white text-base lg:text-lg font-bold text-center">${file.name}</p>
|
| 76 |
+
<p class="text-neutral-500 text-xs lg:text-sm text-center">${(file.size / 1024).toFixed(1)} KB</p>
|
| 77 |
+
</div>
|
| 78 |
+
<button id="removeFileBtn" class="mt-2 flex items-center justify-center gap-2 rounded-full h-8 lg:h-9 px-4 lg:px-5 bg-red-500/10 hover:bg-red-500/20 text-red-400 border border-red-500/20 transition-all text-[10px] lg:text-xs font-bold uppercase tracking-wide">
|
| 79 |
+
<span class="material-symbols-outlined text-sm lg:text-base">close</span>
|
| 80 |
+
<span>Remove File</span>
|
| 81 |
+
</button>
|
| 82 |
+
</div>
|
| 83 |
+
`;
|
| 84 |
+
document.getElementById('removeFileBtn').addEventListener('click', (e) => {
|
| 85 |
+
e.stopPropagation(); e.preventDefault(); resetUploadUI();
|
| 86 |
+
});
|
| 87 |
+
}
|
| 88 |
+
function resetUploadUI() {
|
| 89 |
+
selectedFile = null; fileInput.value = ""; dropZone.innerHTML = defaultDropZoneContent; initDropZone();
|
| 90 |
+
}
|
| 91 |
+
startBtn.addEventListener('click', async (e) => {
|
| 92 |
+
e.preventDefault();
|
| 93 |
+
if (!fileInput.files || fileInput.files.length === 0) {
|
| 94 |
+
alert("Please select or drop an image first."); return;
|
| 95 |
+
}
|
| 96 |
+
const originalBtnContent = startBtn.innerHTML;
|
| 97 |
+
startBtn.innerHTML = '<div class="absolute inset-0 flex items-center justify-center gap-2 lg:gap-3"><svg class="animate-spin h-4 w-4 lg:h-5 lg:w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg><span class="text-white text-base lg:text-lg font-bold tracking-wide">Synthesizing...</span></div>';
|
| 98 |
+
startBtn.disabled = true; startBtn.classList.remove('animate-pulse-slow', 'animate-glow-pulse');
|
| 99 |
+
|
| 100 |
+
try {
|
| 101 |
+
const formData = new FormData(); formData.append('file', fileInput.files[0]);
|
| 102 |
+
const response = await fetch('/generate-catalog', { method: 'POST', body: formData });
|
| 103 |
+
if (!response.ok) throw new Error("Server Error " + response.status);
|
| 104 |
+
const data = await response.json();
|
| 105 |
+
jsonOutput.textContent = JSON.stringify(data, null, 2);
|
| 106 |
+
isCatalogGenerated = true;
|
| 107 |
+
} catch (error) {
|
| 108 |
+
console.error("Agent Error:", error); alert("Pipeline failed: " + error.message);
|
| 109 |
+
} finally {
|
| 110 |
+
startBtn.innerHTML = originalBtnContent; startBtn.disabled = false; startBtn.classList.add('animate-pulse-slow', 'animate-glow-pulse');
|
| 111 |
+
}
|
| 112 |
+
});
|
| 113 |
+
copyBtn.addEventListener('click', () => {
|
| 114 |
+
navigator.clipboard.writeText(jsonOutput.innerText).then(() => {
|
| 115 |
+
const originalIcon = copyIcon.innerText; copyIcon.innerText = 'check'; copyIcon.classList.add('text-green-400');
|
| 116 |
+
setTimeout(() => { copyIcon.innerText = originalIcon; copyIcon.classList.remove('text-green-400'); }, 2000);
|
| 117 |
+
});
|
| 118 |
+
});
|
| 119 |
+
</script>
|
| 120 |
+
</body>
|
| 121 |
+
</html>
|
| 122 |
+
"""
|
| 123 |
+
|
| 124 |
+
with open('dashboard.html', 'w', encoding='utf-8') as f:
|
| 125 |
+
f.write(top_part + new_script)
|
| 126 |
+
|
| 127 |
+
print("Replaced script successfully.")
|
| 128 |
+
|
| 129 |
+
# Run git commands
|
| 130 |
+
subprocess.run(['git', 'add', 'dashboard.html'], check=True)
|
| 131 |
+
try:
|
| 132 |
+
subprocess.run(['git', 'commit', '-m', 'Critical Bugfix: Resolve corrupted JS syntax and restore core agent loop'], check=True)
|
| 133 |
+
except subprocess.CalledProcessError:
|
| 134 |
+
print("Nothing to commit")
|
| 135 |
+
|
| 136 |
+
subprocess.run(['git', 'push', '--force', 'space', 'HEAD:main'], check=True)
|
| 137 |
+
|
| 138 |
+
# Push to origin main as well
|
| 139 |
+
try:
|
| 140 |
+
subprocess.run(['git', 'push', 'origin', 'HEAD:main'], check=True)
|
| 141 |
+
except subprocess.CalledProcessError:
|
| 142 |
+
print("Push to origin failed or not needed")
|
| 143 |
+
|
| 144 |
+
print("Deployment triggered successfully.")
|
| 145 |
+
|
| 146 |
+
if __name__ == '__main__':
|
| 147 |
+
fix_html()
|
requirements.txt
CHANGED
|
@@ -8,7 +8,7 @@ langchain-groq
|
|
| 8 |
pinecone>=3.0.0
|
| 9 |
pydantic
|
| 10 |
python-dotenv
|
| 11 |
-
google-
|
| 12 |
groq
|
| 13 |
Pillow
|
| 14 |
huggingface_hub
|
|
|
|
| 8 |
pinecone>=3.0.0
|
| 9 |
pydantic
|
| 10 |
python-dotenv
|
| 11 |
+
google-genai
|
| 12 |
groq
|
| 13 |
Pillow
|
| 14 |
huggingface_hub
|
upgrade_docker.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
import subprocess
|
| 3 |
+
|
| 4 |
+
def main():
|
| 5 |
+
dockerfile_path = "Dockerfile"
|
| 6 |
+
|
| 7 |
+
# Read & Replace
|
| 8 |
+
try:
|
| 9 |
+
with open(dockerfile_path, "r", encoding="utf-8") as f:
|
| 10 |
+
lines = f.readlines()
|
| 11 |
+
except FileNotFoundError:
|
| 12 |
+
print(f"Error: Could not find {dockerfile_path}")
|
| 13 |
+
sys.exit(1)
|
| 14 |
+
|
| 15 |
+
updated = False
|
| 16 |
+
for i, line in enumerate(lines):
|
| 17 |
+
if line.startswith("FROM python:3.9"):
|
| 18 |
+
lines[i] = "FROM python:3.11-slim\n"
|
| 19 |
+
updated = True
|
| 20 |
+
break
|
| 21 |
+
|
| 22 |
+
if not updated:
|
| 23 |
+
print("Warning: Could not find 'FROM python:3.9' in Dockerfile. The file might already be updated or use a different base image.")
|
| 24 |
+
|
| 25 |
+
# Save
|
| 26 |
+
try:
|
| 27 |
+
with open(dockerfile_path, "w", encoding="utf-8") as f:
|
| 28 |
+
f.writelines(lines)
|
| 29 |
+
print("Successfully updated Dockerfile.")
|
| 30 |
+
except Exception as e:
|
| 31 |
+
print(f"Error writing to Dockerfile: {e}")
|
| 32 |
+
sys.exit(1)
|
| 33 |
+
|
| 34 |
+
# Deploy
|
| 35 |
+
print("Deploying to Hugging Face...")
|
| 36 |
+
commands = [
|
| 37 |
+
["git", "add", "Dockerfile"],
|
| 38 |
+
["git", "commit", "-m", "Chore: Upgrade Docker container to Python 3.11-slim"],
|
| 39 |
+
["git", "push", "space", "clean_deploy:main"]
|
| 40 |
+
]
|
| 41 |
+
|
| 42 |
+
for cmd in commands:
|
| 43 |
+
print(f"Running: {' '.join(cmd)}")
|
| 44 |
+
result = subprocess.run(cmd, text=True, capture_output=True)
|
| 45 |
+
print(result.stdout)
|
| 46 |
+
if result.returncode != 0:
|
| 47 |
+
print(f"Command failed (stderr): {result.stderr}")
|
| 48 |
+
# we don't exit here so we can see all errors if any
|
| 49 |
+
|
| 50 |
+
if __name__ == "__main__":
|
| 51 |
+
main()
|