mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-11 03:13:51 +00:00
Dashboard: indicate when a newer Serena version is available
This commit is contained in:
1 parent
4903da5ac0
commit
4c82b2bf19
5 files changed
+83
-1
No files matched your search
@@ -70,6 +70,7 @@ Status of the `main` branch. Changes prior to the next official version change w
|
||||
when using plugin version 2023.3.3+
|
||||
|
||||
* Dashboard:
|
||||
- The version display now indicates when a newer Serena version is available
|
||||
- Fix: the "Last Execution" panel stayed on "Loading..." forever when there was no logged execution
|
||||
(e.g. a fresh server / no tool run yet), because `loadLastExecution()` only rendered the panel when
|
||||
the backend returned a non-null execution; the empty state is now rendered via the existing
|
||||
|
||||
@@ -10,6 +10,7 @@ import urllib.error
|
||||
import urllib.request
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from functools import cached_property
|
||||
from html import escape
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any, Optional, Self
|
||||
@@ -26,7 +27,9 @@ from serena.config.serena_config import SerenaConfig, SerenaPaths
|
||||
from serena.constants import SERENA_DASHBOARD_DIR, SerenaPorts
|
||||
from serena.task_executor import TaskExecutor
|
||||
from serena.util.logging import MemoryLogHandler
|
||||
from serena.util.pypi import PyPIPackageInfo
|
||||
from serena.util.pywebview import WebViewWithTray
|
||||
from serena.util.version import Version
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from serena.agent import SerenaAgent
|
||||
@@ -71,6 +74,7 @@ class ResponseConfigOverview(BaseModel):
|
||||
encoding: str | None
|
||||
current_client: str | None
|
||||
serena_version: str
|
||||
newer_serena_version: str | None
|
||||
|
||||
|
||||
class ResponseAvailableLanguages(BaseModel):
|
||||
@@ -224,6 +228,27 @@ class SerenaDashboardAPI:
|
||||
def memory_log_handler(self) -> MemoryLogHandler:
|
||||
return self._memory_log_handler
|
||||
|
||||
@cached_property
|
||||
def _newer_serena_version(self) -> str | None:
|
||||
"""The version of a newer serena-agent package release on PyPI, if any.
|
||||
|
||||
The result is computed only once (on first access) and cached thereafter.
|
||||
|
||||
:return: the latest version available on PyPI if it is newer than the running version, else None
|
||||
"""
|
||||
try:
|
||||
# query PyPI for the latest released version
|
||||
latest_serena_version = PyPIPackageInfo("serena-agent").get_latest_version(timeout_secs=3)
|
||||
|
||||
# compare against the running version
|
||||
latest_version = Version(latest_serena_version)
|
||||
current_version = Version(self._agent.version)
|
||||
if not current_version.is_at_least(*latest_version.components):
|
||||
return latest_serena_version
|
||||
except Exception as e:
|
||||
log.warning("Failed to check for newer Serena version on PyPI: %s", e)
|
||||
return None
|
||||
|
||||
def _setup_routes(self) -> None:
|
||||
@self._app.route("/")
|
||||
def redirect_to_dashboard() -> Response:
|
||||
@@ -606,6 +631,7 @@ class SerenaDashboardAPI:
|
||||
encoding=encoding,
|
||||
current_client=Tool.get_last_tool_call_client_str(),
|
||||
serena_version=self._agent.version,
|
||||
newer_serena_version=self._newer_serena_version,
|
||||
)
|
||||
|
||||
def _on_agent_config_changed(self) -> None:
|
||||
|
||||
@@ -556,6 +556,24 @@ code, pre, kbd, samp,
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.newer-version-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 2px 10px;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.newer-version-badge:hover {
|
||||
background: var(--btn-secondary-hover);
|
||||
}
|
||||
|
||||
.config-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
|
||||
@@ -618,7 +618,12 @@ class Dashboard {
|
||||
|
||||
let html = '<div class="config-grid">';
|
||||
|
||||
html += '<div class="config-label">Version:</div><div class="config-value">' + config.serena_version + '</div>';
|
||||
html += '<div class="config-label">Version:</div><div class="config-value">' + config.serena_version;
|
||||
if (config.newer_serena_version) {
|
||||
html += ' <a class="newer-version-badge" href="https://github.com/oraios/serena/releases" target="_blank" rel="noopener">'
|
||||
+ 'newer version available: ' + config.newer_serena_version + '</a>';
|
||||
}
|
||||
html += '</div>';
|
||||
|
||||
// Project info
|
||||
html += '<div class="config-label">Active Project:</div>';
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
"""Utilities for querying package information from the Python Package Index (PyPI)."""
|
||||
|
||||
import requests
|
||||
|
||||
|
||||
class PyPIPackageInfo:
|
||||
"""Provider of information on a package hosted on the Python Package Index (PyPI).
|
||||
|
||||
Information is retrieved on demand via PyPI's JSON API.
|
||||
"""
|
||||
|
||||
JSON_API_URL_TEMPLATE = "https://pypi.org/pypi/%s/json"
|
||||
|
||||
def __init__(self, package_name: str) -> None:
|
||||
"""
|
||||
:param package_name: the name of the package on PyPI (e.g. "requests")
|
||||
"""
|
||||
self.package_name = package_name
|
||||
|
||||
def get_latest_version(self, timeout_secs: int = 5) -> str:
|
||||
"""Retrieves the latest released version of the package from PyPI.
|
||||
|
||||
:return: the latest version string (e.g. "2.33.0")
|
||||
:raises requests.RequestException: if the HTTP request fails or PyPI responds with an error status
|
||||
"""
|
||||
# query PyPI's JSON API for the package metadata
|
||||
url = self.JSON_API_URL_TEMPLATE % self.package_name
|
||||
response = requests.get(url, timeout=timeout_secs)
|
||||
response.raise_for_status()
|
||||
|
||||
# extract the latest version from the metadata
|
||||
return response.json()["info"]["version"]
|
||||
Reference in new issue
Block a user