fix(migration): improve runner migration and plugin installation feedback

This commit is contained in:
RockChinQ
2026-09-18 13:10:32 +08:00
parent e77acfa3ab
commit 60606e1997
28 changed files with 1197 additions and 131 deletions
+26 -19
View File
@@ -23,6 +23,11 @@ from langbot_plugin.api.entities.builtin.pipeline.query import provider_session
from ..core import app
from . import handler
from .errors import (
PluginRuntimeNotConnectedError,
PluginInstallationFailedError,
MarketplacePluginVersionNotFoundError,
)
from .archive import inspect_plugin_archive_metadata
from .github import (
validate_github_plugin_install_info,
@@ -89,8 +94,10 @@ async def _read_httpx_response_limited(
response: httpx.Response,
*,
max_bytes: int,
task_context: taskmgr.TaskContext | None = None,
) -> bytes:
content_length = response.headers.get('content-length')
declared_size = None
if content_length is not None:
try:
declared_size = int(content_length)
@@ -99,9 +106,16 @@ async def _read_httpx_response_limited(
if declared_size is not None and declared_size > max_bytes:
raise ValueError(f'Remote response exceeds the {max_bytes}-byte limit')
started = time.monotonic()
if task_context is not None:
task_context.metadata.update(download_current=0, download_total=max(declared_size or 0, 0))
body = bytearray()
async for chunk in response.aiter_bytes(chunk_size=64 * 1024):
body.extend(chunk)
if task_context is not None:
task_context.metadata.update(
download_current=len(body), download_speed=len(body) / max(time.monotonic() - started, 0.001)
)
if len(body) > max_bytes:
raise ValueError(f'Remote response exceeds the {max_bytes}-byte limit')
return bytes(body)
@@ -113,14 +127,25 @@ async def _marketplace_get(
*,
max_bytes: int,
allow_not_found: bool = False,
task_context: taskmgr.TaskContext | None = None,
) -> tuple[int, bytes]:
async with client.stream('GET', url) as response:
if allow_not_found and response.status_code == 404:
return response.status_code, b''
if response.is_error:
body = await _read_httpx_response_limited(response, max_bytes=min(max_bytes, 64 * 1024))
try:
payload = json.loads(body)
except (ValueError, UnicodeDecodeError):
payload = {}
# Space currently returns HTTP 500 for a missing plugin release.
if isinstance(payload, dict) and str(payload.get('msg', '')).startswith('plugin version not found:'):
raise MarketplacePluginVersionNotFoundError('The requested plugin version is not available')
response.raise_for_status()
return response.status_code, await _read_httpx_response_limited(
response,
max_bytes=max_bytes,
task_context=task_context,
)
@@ -134,25 +159,6 @@ def _decode_json_object(body: bytes, *, subject: str) -> dict[str, Any]:
return payload
class PluginRuntimeNotConnectedError(RuntimeError):
"""Raised when plugin runtime operations are requested before connection."""
class PluginInstallationFailedError(RuntimeError):
"""Stable Runtime desired-state failure for one plugin installation."""
def __init__(
self,
installation_uuid: str,
error_code: str,
message: str,
) -> None:
self.installation_uuid = installation_uuid
self.error_code = error_code
self.runtime_message = message
super().__init__(f'Plugin installation {installation_uuid} failed [{error_code}]: {message}')
class PluginRuntimeConnector(ManagedRuntimeConnector):
"""Plugin runtime connector"""
@@ -1654,6 +1660,7 @@ class PluginRuntimeConnector(ManagedRuntimeConnector):
client,
f'{space_url}/api/v1/marketplace/plugins/download/{plugin_author}/{plugin_name}/{version}',
max_bytes=_MARKETPLACE_PLUGIN_DOWNLOAD_MAX_BYTES,
task_context=task_context,
)
return package, version
+24
View File
@@ -0,0 +1,24 @@
"""Shared plugin operation errors without runtime or application imports."""
class PluginRuntimeNotConnectedError(RuntimeError):
"""Raised when plugin runtime operations are requested before connection."""
class PluginInstallationFailedError(RuntimeError):
"""Stable Runtime desired-state failure for one plugin installation."""
def __init__(
self,
installation_uuid: str,
error_code: str,
message: str,
) -> None:
self.installation_uuid = installation_uuid
self.error_code = error_code
self.runtime_message = message
super().__init__(f'Plugin installation {installation_uuid} failed [{error_code}]: {message}')
class MarketplacePluginVersionNotFoundError(ValueError):
"""The requested release is not available from the marketplace."""