Spaces:
Running
Running
from nc_py_api import Nextcloud | |
import json | |
from typing import Dict | |
from datetime import datetime | |
import os | |
import re | |
import config | |
# Initialize Nextcloud client | |
nc = Nextcloud( | |
nextcloud_url=config.NEXTCLOUD_URL, | |
nc_auth_user=config.NEXTCLOUD_USERNAME, | |
nc_auth_pass=config.NEXTCLOUD_PASSWORD | |
) | |
def validate_model_url(url: str) -> bool: | |
""" | |
Validate if the provided URL matches the expected format. | |
Accepts both direct Ollama models and HuggingFace GGUF models. | |
""" | |
# Pattern for HuggingFace GGUF models | |
hf_pattern = r'^hf\.co/[\w-]+/[\w\.-]+(?:-GGUF)?:Q[0-9]+(?:_[A-Z0-9_]+)?$' | |
# Pattern for direct Ollama models | |
ollama_pattern = r'^[\w\.-]+(?::\d+(?:\.\d+)?[b])?(?:-[\w-]+)?:Q[0-9]+(?:_[A-Z0-9_]+)?$' | |
return bool(re.match(hf_pattern, url) or re.match(ollama_pattern, url)) | |
def load_suggestions() -> Dict: | |
"""Load suggestions from Nextcloud with local file fallback.""" | |
try: | |
# Try to load from Nextcloud | |
remote_data = nc.files.download(config.NEXTCLOUD_SUGGESTIONS_PATH) | |
if remote_data: | |
suggestions = json.loads(remote_data.decode('utf-8')) | |
# Update local cache | |
with open('model_suggestions.json', 'w') as f: | |
json.dump(suggestions, f, indent=2) | |
return suggestions | |
except Exception as e: | |
print(f"Could not load from Nextcloud: {e}") | |
# Try local cache | |
if os.path.exists('model_suggestions.json'): | |
try: | |
with open('model_suggestions.json', 'r') as f: | |
return json.load(f) | |
except Exception as e: | |
print(f"Could not load from local cache: {e}") | |
# Initialize new suggestions if both attempts fail | |
return { | |
"suggestions": {}, | |
"last_updated": datetime.now().isoformat(), | |
"total_suggestions": 0 | |
} | |
def save_suggestions(suggestions: Dict) -> bool: | |
"""Save suggestions to both Nextcloud and local cache.""" | |
try: | |
# Update metadata | |
suggestions["last_updated"] = datetime.now().isoformat() | |
suggestions["total_suggestions"] = sum(s["count"] for s in suggestions["suggestions"].values()) | |
# Save to Nextcloud | |
json_data = json.dumps(suggestions, indent=2) | |
nc.files.upload(config.NEXTCLOUD_SUGGESTIONS_PATH, json_data.encode('utf-8')) | |
# Update local cache | |
with open('model_suggestions.json', 'w') as f: | |
json.dump(suggestions, f, indent=2) | |
return True | |
except Exception as e: | |
print(f"Error saving suggestions: {e}") | |
return False | |
def add_suggestion(model_url: str) -> str: | |
"""Add or update a model suggestion with validation.""" | |
# Validate model URL format | |
if not validate_model_url(model_url): | |
return ("β Invalid model URL format. Please use either:\n" | |
"- Ollama format: model-name:Q4_K_M\n" | |
"- HuggingFace format: hf.co/username/model-name-GGUF:Q4_K_M") | |
# Check if model is already approved | |
if model_url in dict(config.get_approved_models()): | |
return "βΉοΈ This model is already in the arena!" | |
suggestions = load_suggestions() | |
current_time = datetime.now().isoformat() | |
if model_url in suggestions["suggestions"]: | |
suggestions["suggestions"][model_url].update({ | |
"count": suggestions["suggestions"][model_url]["count"] + 1, | |
"last_suggested": current_time | |
}) | |
message = (f"β¨ Model suggestion updated! " | |
f"This model has been suggested {suggestions['suggestions'][model_url]['count']} times.") | |
else: | |
suggestions["suggestions"][model_url] = { | |
"count": 1, | |
"first_suggested": current_time, | |
"last_suggested": current_time | |
} | |
message = "β New model suggestion recorded successfully!" | |
if save_suggestions(suggestions): | |
return message | |
return "β Error saving suggestion. Please try again later." | |
def get_suggestions_html() -> str: | |
"""Generate HTML table of model suggestions with improved styling.""" | |
suggestions = load_suggestions() | |
# Sort suggestions by count (descending) and last suggested date | |
sorted_suggestions = sorted( | |
suggestions["suggestions"].items(), | |
key=lambda x: (x[1]["count"], x[1]["last_suggested"]), | |
reverse=True | |
) | |
stats_header = f""" | |
<div class="stats-header"> | |
Total Suggestions: {suggestions.get("total_suggestions", 0)} | Last Updated: {suggestions.get("last_updated", "Never").split("T")[0]} | |
</div> | |
""" | |
html = f""" | |
<style> | |
.suggestions-table {{ | |
width: 100%; | |
border-collapse: collapse; | |
font-family: Arial, sans-serif; | |
margin-top: 20px; | |
}} | |
.suggestions-table th, .suggestions-table td {{ | |
border: 1px solid #ddd; | |
padding: 12px; | |
text-align: left; | |
}} | |
.suggestions-table th {{ | |
background-color: rgba(255, 255, 255, 0.1); | |
font-weight: bold; | |
}} | |
.rank-column {{ | |
width: 60px; | |
text-align: center; | |
}} | |
.count-badge {{ | |
background-color: rgba(34, 87, 122, 0.7); | |
color: white; | |
padding: 4px 8px; | |
border-radius: 12px; | |
font-size: 0.9em; | |
}} | |
.stats-header {{ | |
font-size: 0.9em; | |
color: #888; | |
margin-bottom: 10px; | |
}} | |
</style> | |
{stats_header} | |
<table class='suggestions-table'> | |
<tr> | |
<th class='rank-column'>Rank</th> | |
<th>Model URL</th> | |
<th>Suggestions</th> | |
<th>First Suggested</th> | |
<th>Last Suggested</th> | |
</tr> | |
""" | |
for index, (model_url, data) in enumerate(sorted_suggestions, start=1): | |
rank_display = {1: "π₯", 2: "π₯", 3: "π₯"}.get(index, f"{index}") | |
html += f""" | |
<tr> | |
<td class='rank-column'>{rank_display}</td> | |
<td>{model_url}</td> | |
<td><span class="count-badge">{data['count']}</span></td> | |
<td>{data['first_suggested'].split('T')[0]}</td> | |
<td>{data['last_suggested'].split('T')[0]}</td> | |
</tr> | |
""" | |
html += "</table>" | |
return html |