feat(plugin): route certified installs to shared workers

This commit is contained in:
RockChinQ
2026-09-25 12:48:55 +00:00
parent 9631eccaaf
commit 9f296bd57e
11 changed files with 320 additions and 90 deletions
+16 -16
View File
@@ -8,11 +8,12 @@ persistence, or a Plugin Runtime apply request. It calls the SDK public
certificate envelope from the ZIP comment and verifies the signed normalized
ZIP digest without extracting the payload.
Core retains the normalized digest (`normalized_zip_digest()`), verification
state, declared shared-runtime profile, key ID, selected admission profile, and
stable admission code in the durable plugin `install_info._certification`
record. The record belongs to the installation row; no schema migration is
needed for this additive JSON metadata.
Core retains the artifact SHA-256, normalized digest
(`normalized_zip_digest()`), verification state, declared shared-runtime
profile, key ID, selected admission profile, and stable admission code in the
durable plugin `install_info._certification` record. The record belongs to the
installation row; no schema migration is needed for this additive JSON
metadata.
## Trusted issuer configuration
@@ -50,13 +51,14 @@ protects those endpoints. A force never creates a Cloud dedicated fallback.
## Runtime and logs
The current Plugin Runtime control protocol has one process-wide runtime profile
per Core instance. In Cloud that existing profile is `shared`; Cloud admission
therefore prevents an archive that did not select `shared-runtime-v1` from
reaching its apply API. In OSS the existing `oss_dev` runtime remains the
dedicated compatibility profile. Core records the selected profile for every
installation so a future multi-runtime control protocol can consume it without
re-verifying an already persisted archive.
SDK 0.6.2 carries an installation-level execution mode in both apply and
authoritative reconcile payloads. Core selects `shared-runtime-v1` only when
the persisted certification record says verification was valid, both the
certificate and admission profiles are `shared-runtime-v1`, the admission code
is shared-eligible, and the record's artifact SHA-256 exactly matches the
installation row. Missing, malformed, stale, invalid, or dedicated admission
facts select `dedicated`. Install, upgrade, configuration revision, restart,
and reconnect all use this same persisted-fact derivation.
The existing public plugin-log boundary already applies the immutable
installation binding (including workspace UUID) through
@@ -68,7 +70,5 @@ same existing installation scope.
## SDK versioning
Core intentionally continues to declare `langbot-plugin==0.5.8` until the SDK
beta containing this public certification API is released. Local development
and the integration tests may install the SDK source checkout, but this Core
change does not publish or pin a prerelease.
Core pins `langbot-plugin==0.6.2`, the first published SDK release carrying the
canonical installation execution-mode contract.
+1 -1
View File
@@ -71,7 +71,7 @@ dependencies = [
"langchain-text-splitters>=1.1.2",
"chromadb>=1.0.0,<2.0.0",
"qdrant-client (>=1.15.1,<2.0.0)",
"langbot-plugin==0.6.1",
"langbot-plugin==0.6.2",
"asyncpg>=0.30.0",
"line-bot-sdk>=3.19.0",
"matrix-nio>=0.25.2",
+25
View File
@@ -14,6 +14,7 @@ from enum import Enum
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
from langbot_plugin.certification import normalized_zip_digest, verify_archive
from langbot_plugin.entities.io.context import PluginExecutionMode
SHARED_RUNTIME_V1 = 'shared-runtime-v1'
@@ -232,3 +233,27 @@ def decide_plugin_log_visibility(facts: PluginCertificationFacts) -> PluginLogVi
if facts.certificate.is_valid_shared_runtime:
return PluginLogVisibility.TENANT_SCOPED
return PluginLogVisibility.DETAILED_PROCESS
def execution_mode_for_persisted_installation(
*,
artifact_digest: str,
install_info: object,
) -> PluginExecutionMode:
"""Derive placement only from persisted facts bound to the exact artifact."""
if not isinstance(install_info, Mapping):
return PluginExecutionMode.DEDICATED
certification = install_info.get('_certification')
if not isinstance(certification, Mapping):
return PluginExecutionMode.DEDICATED
shared_facts = {
'artifact_digest': artifact_digest,
'verification': CertificateVerification.VALID.value,
'certificate_runtime_profile': SHARED_RUNTIME_V1,
'runtime_profile': SHARED_RUNTIME_V1,
'admission_code': AdmissionCode.SHARED_ELIGIBLE.value,
}
if all(certification.get(key) == value for key, value in shared_facts.items()):
return PluginExecutionMode.SHARED_CERTIFIED
return PluginExecutionMode.DEDICATED
+22 -1
View File
@@ -34,6 +34,7 @@ from .certification import (
PluginCertificationFacts,
VerifiedArchiveCertificate,
decide_plugin_admission,
execution_mode_for_persisted_installation,
verify_plugin_archive_certificate,
)
from .github import (
@@ -62,6 +63,7 @@ from langbot_plugin.runtime.security import (
)
from langbot_plugin.entities.io.context import (
InstallationBinding,
PluginExecutionMode,
PluginInstallationDesiredState,
PluginWorkerPolicy,
RuntimeIdentity,
@@ -343,6 +345,13 @@ class PluginRuntimeConnector(ManagedRuntimeConnector):
artifact_digest=setting.artifact_digest,
)
@staticmethod
def _execution_mode_from_setting(setting: persistence_plugin.PluginSetting) -> PluginExecutionMode:
return execution_mode_for_persisted_installation(
artifact_digest=setting.artifact_digest,
install_info=setting.install_info,
)
def _legacy_oss_bridge_binding(self, execution_context: ExecutionContext) -> InstallationBinding:
seed = f'langbot:oss-plugin-bridge:{execution_context.instance_uuid}:{execution_context.workspace_uuid}'
return InstallationBinding(
@@ -511,6 +520,7 @@ class PluginRuntimeConnector(ManagedRuntimeConnector):
PluginInstallationDesiredState(
binding=binding,
enabled=setting.enabled,
execution_mode=self._execution_mode_from_setting(setting),
)
)
return tuple(desired_states)
@@ -526,6 +536,7 @@ class PluginRuntimeConnector(ManagedRuntimeConnector):
desired.binding,
artifact_package=artifact_package,
enabled=desired.enabled,
execution_mode=desired.execution_mode,
)
self._raise_apply_failure(desired, result)
if result.get('state') != 'artifact_missing':
@@ -551,6 +562,7 @@ class PluginRuntimeConnector(ManagedRuntimeConnector):
desired.binding,
artifact_package=persisted_package,
enabled=desired.enabled,
execution_mode=desired.execution_mode,
)
self._raise_apply_failure(desired, repaired)
if repaired.get('state') == 'artifact_missing':
@@ -1793,6 +1805,7 @@ class PluginRuntimeConnector(ManagedRuntimeConnector):
}:
raise ValueError(decision.code.value)
certification_info = {
'artifact_digest': hashlib.sha256(file_bytes).hexdigest(),
'normalized_digest': facts.artifact_digest,
'verification': facts.certificate.verification.value,
'certificate_runtime_profile': facts.certificate.runtime_profile,
@@ -1893,7 +1906,14 @@ class PluginRuntimeConnector(ManagedRuntimeConnector):
install_info=install_info,
artifact_digest=artifact_digest,
)
desired = PluginInstallationDesiredState(binding=binding, enabled=True)
desired = PluginInstallationDesiredState(
binding=binding,
enabled=True,
execution_mode=execution_mode_for_persisted_installation(
artifact_digest=artifact_digest,
install_info=install_info,
),
)
runtime_handler.register_installation_binding(
binding,
plugin_author=plugin_author,
@@ -2143,6 +2163,7 @@ class PluginRuntimeConnector(ManagedRuntimeConnector):
desired = PluginInstallationDesiredState(
binding=binding,
enabled=setting.enabled,
execution_mode=self._execution_mode_from_setting(setting),
)
is_legacy_oss = self.runtime_profile == 'oss_dev' and (
not isinstance(setting.install_info, dict)
+3
View File
@@ -24,6 +24,7 @@ from langbot_plugin.entities.io.context import (
ActionContext,
ApplyPluginInstallationRequest,
InstallationBinding,
PluginExecutionMode,
PluginInstallationDesiredState,
PluginWorkerPolicy,
ReconcilePluginInstallationsRequest,
@@ -2793,6 +2794,7 @@ class RuntimeConnectionHandler(handler.Handler):
*,
artifact_package: bytes | None,
enabled: bool,
execution_mode: PluginExecutionMode = PluginExecutionMode.DEDICATED,
) -> dict[str, Any]:
with self.installation_scope(binding):
artifact_file_key = None
@@ -2801,6 +2803,7 @@ class RuntimeConnectionHandler(handler.Handler):
request = ApplyPluginInstallationRequest(
artifact_file_key=artifact_file_key,
enabled=enabled,
execution_mode=execution_mode,
)
return await self.call_action(
LangBotToRuntimeAction.APPLY_PLUGIN_INSTALLATION,
@@ -17,6 +17,7 @@ from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from langbot.pkg.api.http.context import ExecutionContext
from langbot.pkg.plugin.connector import PluginRuntimeConnector
from langbot_plugin.entities.io.context import InstallationBinding
from langbot_plugin.entities.io.context import PluginExecutionMode
from langbot_plugin.runtime.plugin.mgr import PluginInstallSource
@@ -57,11 +58,17 @@ async def test_install_plugin_admits_archive_before_persistence_and_applies_sele
)
persisted_info = connector._persist_installation_package.await_args.kwargs['install_info']
assert persisted_info['_certification']['runtime_profile'] == expected_profile
assert persisted_info['_certification']['artifact_digest'] == hashlib.sha256(package).hexdigest()
assert persisted_info['_certification']['normalized_digest'] == _normalized_digest(package)
connector.handler.apply_plugin_installation.assert_awaited_once_with(
binding,
artifact_package=package,
enabled=True,
execution_mode=(
PluginExecutionMode.SHARED_CERTIFIED
if expected_profile == 'shared-runtime-v1'
else PluginExecutionMode.DEDICATED
),
)
@@ -135,7 +142,10 @@ async def test_marketplace_version_selection_keeps_certificate_gate_and_single_a
assert persisted_info['plugin_version'] == '1.0.0'
assert persisted_info['_certification']['runtime_profile'] == 'shared-runtime-v1'
connector.handler.apply_plugin_installation.assert_awaited_once_with(
binding, artifact_package=package, enabled=True
binding,
artifact_package=package,
enabled=True,
execution_mode=PluginExecutionMode.SHARED_CERTIFIED,
)
connector._refresh_runner_registry.assert_awaited_once()
assert task_context.metadata['progress_percent'] == 100
@@ -6,6 +6,8 @@ import zipfile
import pytest
from langbot_plugin.entities.io.context import PluginExecutionMode
@pytest.mark.parametrize(
('deployment', 'certificate', 'force', 'expected_disposition', 'expected_code'),
@@ -150,6 +152,80 @@ def test_log_visibility_policy_only_scopes_valid_shared_certifications(
assert visibility.value == expected_visibility
@pytest.mark.parametrize(
('certification', 'expected_mode'),
[
(
{
'artifact_digest': 'a' * 64,
'verification': 'valid',
'certificate_runtime_profile': 'shared-runtime-v1',
'runtime_profile': 'shared-runtime-v1',
'admission_code': 'CERTIFIED_PLUGIN_SHARED_ELIGIBLE',
},
PluginExecutionMode.SHARED_CERTIFIED,
),
(None, PluginExecutionMode.DEDICATED),
({}, PluginExecutionMode.DEDICATED),
(
{
'artifact_digest': 'a' * 64,
'verification': 'invalid',
'certificate_runtime_profile': 'shared-runtime-v1',
'runtime_profile': 'shared-runtime-v1',
'admission_code': 'CERTIFIED_PLUGIN_SHARED_ELIGIBLE',
},
PluginExecutionMode.DEDICATED,
),
(
{
'artifact_digest': 'b' * 64,
'verification': 'valid',
'certificate_runtime_profile': 'shared-runtime-v1',
'runtime_profile': 'shared-runtime-v1',
'admission_code': 'CERTIFIED_PLUGIN_SHARED_ELIGIBLE',
},
PluginExecutionMode.DEDICATED,
),
(
{
'artifact_digest': 'a' * 64,
'verification': 'valid',
'certificate_runtime_profile': 'shared-runtime-v1',
'runtime_profile': 'dedicated',
'admission_code': 'CERTIFIED_PLUGIN_SHARED_ELIGIBLE',
},
PluginExecutionMode.DEDICATED,
),
(
{
'artifact_digest': 'a' * 64,
'verification': 'valid',
'certificate_runtime_profile': 'shared-runtime-v1',
'runtime_profile': 'shared-runtime-v1',
'admission_code': 'CERTIFIED_PLUGIN_OSS_FORCED_DEDICATED',
},
PluginExecutionMode.DEDICATED,
),
],
)
def test_persisted_certification_selects_shared_execution_only_for_exact_admitted_artifact(
certification: dict[str, str] | None,
expected_mode: PluginExecutionMode,
) -> None:
from langbot.pkg.plugin.certification import execution_mode_for_persisted_installation
install_info = {} if certification is None else {'_certification': certification}
assert (
execution_mode_for_persisted_installation(
artifact_digest='a' * 64,
install_info=install_info,
)
is expected_mode
)
def _archive_bytes(manifest: dict[str, object]) -> bytes:
buffer = io.BytesIO()
with zipfile.ZipFile(buffer, 'w') as archive:
@@ -17,7 +17,7 @@ from unittest.mock import AsyncMock, Mock
from importlib import import_module
from tests.factories import text_query
from langbot_plugin.entities.io.context import InstallationBinding
from langbot_plugin.entities.io.context import InstallationBinding, PluginExecutionMode
from langbot.pkg.api.http.context import ExecutionContext
from langbot.pkg.workspace.errors import WorkspaceNotFoundError
@@ -758,7 +758,16 @@ class TestSetPluginConfig:
runtime_revision=1,
artifact_digest=TEST_INSTALLATION_BINDING.artifact_digest,
enabled=True,
install_info={'_artifact_storage': 'tenant_binary_storage_v1'},
install_info={
'_artifact_storage': 'tenant_binary_storage_v1',
'_certification': {
'artifact_digest': TEST_INSTALLATION_BINDING.artifact_digest,
'verification': 'valid',
'certificate_runtime_profile': 'shared-runtime-v1',
'runtime_profile': 'shared-runtime-v1',
'admission_code': 'CERTIFIED_PLUGIN_SHARED_ELIGIBLE',
},
},
)
connector._setting_for_plugin = AsyncMock(return_value=(TEST_EXECUTION_CONTEXT, setting))
connector.ap.persistence_mgr.execute_async = AsyncMock(return_value=SimpleNamespace(rowcount=1))
@@ -777,6 +786,7 @@ class TestSetPluginConfig:
applied_binding,
artifact_package=None,
enabled=True,
execution_mode=PluginExecutionMode.SHARED_CERTIFIED,
)
@@ -8,7 +8,7 @@ from types import SimpleNamespace
from unittest.mock import AsyncMock, Mock
import pytest
from langbot_plugin.entities.io.context import InstallationBinding
from langbot_plugin.entities.io.context import InstallationBinding, PluginExecutionMode
from langbot_plugin.runtime.plugin.mgr import PluginInstallSource
from langbot.pkg.api.http.context import ExecutionContext
@@ -45,7 +45,17 @@ def mock_archive_admission(connector: PluginRuntimeConnector, digest: str) -> No
# is exercised by integration/plugin/test_certified_plugin_admission.py.
connector._admit_plugin_archive = Mock(
side_effect=lambda _package, info: (
{**info, '_certification': {'normalized_digest': digest}},
{
**info,
'_certification': {
'artifact_digest': digest,
'normalized_digest': digest,
'verification': 'valid',
'certificate_runtime_profile': 'shared-runtime-v1',
'runtime_profile': 'shared-runtime-v1',
'admission_code': 'CERTIFIED_PLUGIN_SHARED_ELIGIBLE',
},
},
SimpleNamespace(for_installation=lambda _uuid: SimpleNamespace(artifact_digest=digest)),
)
)
@@ -56,6 +66,7 @@ def plugin_setting(
artifact_digest: str,
*,
durable: bool = True,
certification: dict[str, str] | None = None,
) -> SimpleNamespace:
return SimpleNamespace(
plugin_author='author',
@@ -67,7 +78,10 @@ def plugin_setting(
priority=0,
created_at=datetime.datetime(2026, 1, 1),
install_source='local',
install_info={'_artifact_storage': 'tenant_binary_storage_v1'} if durable else {},
install_info={
**({'_artifact_storage': 'tenant_binary_storage_v1'} if durable else {}),
**({'_certification': certification} if certification is not None else {}),
},
)
@@ -163,6 +177,43 @@ async def test_shared_reconnect_replays_two_workspaces_and_removes_missing_proje
assert set(connector._known_desired_states) == {setting_a.installation_uuid}
@pytest.mark.asyncio
async def test_reconcile_reload_projects_certified_exact_artifact_to_shared_execution():
binding = execution_binding('workspace-a')
digest = 'a' * 64
setting = plugin_setting(
'01',
digest,
certification={
'artifact_digest': digest,
'verification': 'valid',
'certificate_runtime_profile': 'shared-runtime-v1',
'runtime_profile': 'shared-runtime-v1',
'admission_code': 'CERTIFIED_PLUGIN_SHARED_ELIGIBLE',
},
)
connector = shared_connector([[binding]], {'workspace-a': [setting]})
connector.handler = runtime_handler()
await connector._prepare_connected_runtime()
desired = connector.handler.reconcile_plugin_installations.await_args.args[0][0]
assert desired.execution_mode is PluginExecutionMode.SHARED_CERTIFIED
@pytest.mark.asyncio
async def test_reconcile_reload_defaults_legacy_installation_to_dedicated_execution():
binding = execution_binding('workspace-a')
setting = plugin_setting('01', 'a' * 64)
connector = shared_connector([[binding]], {'workspace-a': [setting]})
connector.handler = runtime_handler()
await connector._prepare_connected_runtime()
desired = connector.handler.reconcile_plugin_installations.await_args.args[0][0]
assert desired.execution_mode is PluginExecutionMode.DEDICATED
@pytest.mark.asyncio
async def test_empty_projected_workspaces_do_not_retain_installation_sets():
binding_a = execution_binding('workspace-a')
@@ -223,6 +274,7 @@ async def test_fresh_shared_runtime_cache_replays_persisted_local_package():
desired.binding,
artifact_package=package,
enabled=True,
execution_mode=PluginExecutionMode.DEDICATED,
)
@@ -304,6 +356,7 @@ async def test_local_install_persists_verified_package_before_runtime_apply():
binding,
artifact_package=package,
enabled=True,
execution_mode=PluginExecutionMode.DEDICATED,
)
@@ -396,6 +449,9 @@ async def test_marketplace_upgrade_reports_multistep_progress():
'download_current': 0,
'download_speed': 0,
}
assert connector.handler.apply_plugin_installation.await_args.kwargs['execution_mode'] is (
PluginExecutionMode.SHARED_CERTIFIED
)
@pytest.mark.asyncio
@@ -430,7 +486,13 @@ async def test_workspace_reads_do_not_wait_for_an_installation_apply():
connector._persist_installation_package = AsyncMock(return_value=(binding, None, False))
connector._wait_for_installed_plugin_ready = AsyncMock()
connector._load_workspace_desired_states = AsyncMock(
return_value=[PluginInstallationDesiredState(binding=binding, enabled=True)]
return_value=[
PluginInstallationDesiredState(
binding=binding,
enabled=True,
execution_mode=PluginExecutionMode.SHARED_CERTIFIED,
)
]
)
apply_started = asyncio.Event()
release_apply = asyncio.Event()
+25 -2
View File
@@ -10,7 +10,12 @@ from unittest.mock import AsyncMock, MagicMock, Mock
import pytest
from langbot_plugin.entities.io.actions.enums import LangBotToRuntimeAction, PluginToRuntimeAction
from langbot_plugin.entities.io.context import ActionContext, InstallationBinding, PluginInstallationDesiredState
from langbot_plugin.entities.io.context import (
ActionContext,
InstallationBinding,
PluginExecutionMode,
PluginInstallationDesiredState,
)
def make_handler(app):
@@ -90,7 +95,25 @@ async def test_reconcile_plugin_installations_accepts_configured_cold_start_time
await runtime_handler.reconcile_plugin_installations((desired,), timeout=900)
assert runtime_handler.call_action.await_args.kwargs["timeout"] == 900
assert runtime_handler.call_action.await_args.kwargs['timeout'] == 900
@pytest.mark.asyncio
async def test_apply_plugin_installation_serializes_certified_shared_execution_mode():
runtime_handler = make_handler(SimpleNamespace())
runtime_handler.send_file = AsyncMock(return_value='artifact-file')
runtime_handler.call_action = AsyncMock(return_value={'state': 'starting'})
binding = next(iter(runtime_handler._installation_bindings.values()))[0]
await runtime_handler.apply_plugin_installation(
binding,
artifact_package=b'package',
enabled=True,
execution_mode=PluginExecutionMode.SHARED_CERTIFIED,
)
assert runtime_handler.call_action.await_args.args[0] == LangBotToRuntimeAction.APPLY_PLUGIN_INSTALLATION
assert runtime_handler.call_action.await_args.args[1]['execution_mode'] == 'shared-runtime-v1'
class TestHandlerQueryVariables:
Generated
+63 -63
View File
@@ -1066,7 +1066,7 @@ name = "cuda-bindings"
version = "13.3.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cuda-pathfinder" },
{ name = "cuda-pathfinder", marker = "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/51/6b/457ca12dad3ee9bfcc9a545cfd6b64b359ba49de40f776f6e028e678f262/cuda_bindings-13.3.1-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5879712accf6e14bb01aa5e67440eb84998b8d104b509cc7a6dc0b8f656a474", size = 6053539, upload-time = "2026-05-29T23:11:43.19Z" },
@@ -1099,34 +1099,34 @@ wheels = [
[package.optional-dependencies]
cudart = [
{ name = "nvidia-cuda-runtime" },
{ name = "nvidia-cuda-runtime", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
]
cufft = [
{ name = "nvidia-cufft" },
{ name = "nvidia-cufft", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
]
cufile = [
{ name = "nvidia-cufile" },
{ name = "nvidia-cufile", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
]
cupti = [
{ name = "nvidia-cuda-cupti" },
{ name = "nvidia-cuda-cupti", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
]
curand = [
{ name = "nvidia-curand" },
{ name = "nvidia-curand", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
]
cusolver = [
{ name = "nvidia-cusolver" },
{ name = "nvidia-cusolver", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
]
cusparse = [
{ name = "nvidia-cusparse" },
{ name = "nvidia-cusparse", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
]
nvjitlink = [
{ name = "nvidia-nvjitlink" },
{ name = "nvidia-nvjitlink", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
]
nvrtc = [
{ name = "nvidia-cuda-nvrtc" },
{ name = "nvidia-cuda-nvrtc", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
]
nvtx = [
{ name = "nvidia-nvtx" },
{ name = "nvidia-nvtx", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
]
[[package]]
@@ -2185,7 +2185,7 @@ requires-dist = [
{ name = "gewechat-client", specifier = ">=0.1.5" },
{ name = "html2text", specifier = ">=2024.2.26" },
{ name = "httpx", extras = ["socks"], specifier = ">=0.28.1" },
{ name = "langbot-plugin", specifier = "==0.6.1" },
{ name = "langbot-plugin", specifier = "==0.6.2" },
{ name = "langchain", specifier = ">=1.3.9" },
{ name = "langchain-core", specifier = ">=1.3.3" },
{ name = "langchain-text-splitters", specifier = ">=1.1.2" },
@@ -2255,7 +2255,7 @@ dev = [
[[package]]
name = "langbot-plugin"
version = "0.6.1"
version = "0.6.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "aiofiles" },
@@ -2276,9 +2276,9 @@ dependencies = [
{ name = "watchdog" },
{ name = "websockets" },
]
sdist = { url = "https://files.pythonhosted.org/packages/1f/70/ade1e2e71e666a80acfffde0fc753173e74b7a528ef495a1705f53c40ea8/langbot_plugin-0.6.1.tar.gz", hash = "sha256:214e415c2cd3c286f4b07cfbbe11e6b865839596ad500c2100944b00b3ba5f2b", size = 625357, upload-time = "2026-09-24T14:55:08.202Z" }
sdist = { url = "https://files.pythonhosted.org/packages/48/df/376749b79b133a3f9e65975243c1b4a734f072cbdbb2a3f5d5add7dc800c/langbot_plugin-0.6.2.tar.gz", hash = "sha256:64dc0f47b625c3595028dadea3e500ee123cb0e5481c97fecd7f4308d703623c", size = 639489, upload-time = "2026-09-25T12:16:11.18Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/42/06/4d8a889b0c28dcbf7ccf5ca40bcc7125ac9639942f7d05340768b28d159c/langbot_plugin-0.6.1-py3-none-any.whl", hash = "sha256:5607b4c787a92259b4a1f7beb4bdca9f371e37b316eaf8f316d2448e888da77e", size = 413828, upload-time = "2026-09-24T14:55:06.517Z" },
{ url = "https://files.pythonhosted.org/packages/d3/6e/d7f725ff4c42e7092368d2651354a84d5f195baf4dfbd5c537979b6d50aa/langbot_plugin-0.6.2-py3-none-any.whl", hash = "sha256:65845f20e65e32b4daeffdf69119b35be8775ce902d9e050e38f2cf5d77e5203", size = 419610, upload-time = "2026-09-25T12:16:09.973Z" },
]
[[package]]
@@ -3306,7 +3306,7 @@ name = "nvidia-cublas"
version = "13.1.1.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "nvidia-cuda-nvrtc" },
{ name = "nvidia-cuda-nvrtc", marker = "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/a7/a1/0bd24ee8c8d03adac032fd2909426a00c88f8c57961b1277ded97f91119f/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b7a210458267ac818974c53038fbec2e969d5c99f305ab15c72522fa9f001dd5", size = 542848918, upload-time = "2026-04-08T18:46:22.985Z" },
@@ -3345,7 +3345,7 @@ name = "nvidia-cudnn-cu13"
version = "9.20.0.48"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "nvidia-cublas" },
{ name = "nvidia-cublas", marker = "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/56/c5/83384d846b2fd17c44bd499b36c75a45ed4f095fbbb2252294e89cea5c5c/nvidia_cudnn_cu13-9.20.0.48-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:e31454ae00094b0c55319d9d15b6fa2fc50a9e1c0f5c8c80fb75258234e731e1", size = 444574296, upload-time = "2026-03-09T19:28:27.751Z" },
@@ -3357,7 +3357,7 @@ name = "nvidia-cufft"
version = "12.0.0.61"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "nvidia-nvjitlink" },
{ name = "nvidia-nvjitlink", marker = "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" },
@@ -3387,9 +3387,9 @@ name = "nvidia-cusolver"
version = "12.0.4.66"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "nvidia-cublas" },
{ name = "nvidia-cusparse" },
{ name = "nvidia-nvjitlink" },
{ name = "nvidia-cublas", marker = "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
{ name = "nvidia-cusparse", marker = "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
{ name = "nvidia-nvjitlink", marker = "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" },
@@ -3401,7 +3401,7 @@ name = "nvidia-cusparse"
version = "12.6.3.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "nvidia-nvjitlink" },
{ name = "nvidia-nvjitlink", marker = "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" },
@@ -4494,7 +4494,7 @@ name = "pylibseekdb"
version = "1.4.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pymysql" },
{ name = "pymysql", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/ae/a8/7413d33218aff55a14ec9d20532b49243ffd0579e7a92244922c1885444e/pylibseekdb-1.4.0-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:5cb2efab9f1321cdb4b034d3a2bd92e41a402fc95e7dc9579c7473a426f96e24", size = 52173499, upload-time = "2026-08-27T13:05:09.347Z" },
@@ -5262,10 +5262,10 @@ name = "scikit-learn"
version = "1.8.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "joblib" },
{ name = "numpy" },
{ name = "scipy" },
{ name = "threadpoolctl" },
{ name = "joblib", marker = "python_full_version >= '3.14'" },
{ name = "numpy", marker = "python_full_version >= '3.14'" },
{ name = "scipy", marker = "python_full_version >= '3.14'" },
{ name = "threadpoolctl", marker = "python_full_version >= '3.14'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585, upload-time = "2025-12-10T07:08:53.618Z" }
wheels = [
@@ -5312,7 +5312,7 @@ name = "scipy"
version = "1.17.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "numpy" },
{ name = "numpy", marker = "python_full_version >= '3.14'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" }
wheels = [
@@ -5383,14 +5383,14 @@ name = "sentence-transformers"
version = "5.2.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "huggingface-hub" },
{ name = "numpy" },
{ name = "scikit-learn" },
{ name = "scipy" },
{ name = "torch" },
{ name = "tqdm" },
{ name = "transformers" },
{ name = "typing-extensions" },
{ name = "huggingface-hub", marker = "python_full_version >= '3.14'" },
{ name = "numpy", marker = "python_full_version >= '3.14'" },
{ name = "scikit-learn", marker = "python_full_version >= '3.14'" },
{ name = "scipy", marker = "python_full_version >= '3.14'" },
{ name = "torch", marker = "python_full_version >= '3.14'" },
{ name = "tqdm", marker = "python_full_version >= '3.14'" },
{ name = "transformers", marker = "python_full_version >= '3.14'" },
{ name = "typing-extensions", marker = "python_full_version >= '3.14'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/5b/30/21664028fc0776eb1ca024879480bbbab36f02923a8ff9e4cae5a150fa35/sentence_transformers-5.2.3.tar.gz", hash = "sha256:3cd3044e1f3fe859b6a1b66336aac502eaae5d3dd7d5c8fc237f37fbf58137c7", size = 381623, upload-time = "2026-02-17T14:05:20.238Z" }
wheels = [
@@ -5772,21 +5772,21 @@ name = "torch"
version = "2.12.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cuda-bindings", marker = "sys_platform == 'linux'" },
{ name = "cuda-toolkit", extra = ["cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "sys_platform == 'linux'" },
{ name = "filelock" },
{ name = "fsspec" },
{ name = "jinja2" },
{ name = "networkx" },
{ name = "nvidia-cublas", marker = "sys_platform == 'linux'" },
{ name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'" },
{ name = "nvidia-cusparselt-cu13", marker = "sys_platform == 'linux'" },
{ name = "nvidia-nccl-cu13", marker = "sys_platform == 'linux'" },
{ name = "nvidia-nvshmem-cu13", marker = "sys_platform == 'linux'" },
{ name = "setuptools" },
{ name = "sympy" },
{ name = "triton", marker = "sys_platform == 'linux'" },
{ name = "typing-extensions" },
{ name = "cuda-bindings", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
{ name = "cuda-toolkit", extra = ["cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
{ name = "filelock", marker = "python_full_version >= '3.14'" },
{ name = "fsspec", marker = "python_full_version >= '3.14'" },
{ name = "jinja2", marker = "python_full_version >= '3.14'" },
{ name = "networkx", marker = "python_full_version >= '3.14'" },
{ name = "nvidia-cublas", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
{ name = "nvidia-cudnn-cu13", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
{ name = "nvidia-cusparselt-cu13", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
{ name = "nvidia-nccl-cu13", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
{ name = "nvidia-nvshmem-cu13", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
{ name = "setuptools", marker = "python_full_version >= '3.14'" },
{ name = "sympy", marker = "python_full_version >= '3.14'" },
{ name = "triton", marker = "python_full_version >= '3.14' and sys_platform == 'linux'" },
{ name = "typing-extensions", marker = "python_full_version >= '3.14'" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/59/38/7028d3be540f1dcdf41660a2b01d0c51d2cb73915fe370d84e4d277a6d47/torch-2.12.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:ef81f503912effea2ce3d9b12a2e3a6ed488943e91271c90c7a829f60baf6aa2", size = 87975425, upload-time = "2026-06-17T21:08:34.094Z" },
@@ -5828,15 +5828,15 @@ name = "transformers"
version = "5.3.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "huggingface-hub" },
{ name = "numpy" },
{ name = "packaging" },
{ name = "pyyaml" },
{ name = "regex" },
{ name = "safetensors" },
{ name = "tokenizers" },
{ name = "tqdm" },
{ name = "typer" },
{ name = "huggingface-hub", marker = "python_full_version >= '3.14'" },
{ name = "numpy", marker = "python_full_version >= '3.14'" },
{ name = "packaging", marker = "python_full_version >= '3.14'" },
{ name = "pyyaml", marker = "python_full_version >= '3.14'" },
{ name = "regex", marker = "python_full_version >= '3.14'" },
{ name = "safetensors", marker = "python_full_version >= '3.14'" },
{ name = "tokenizers", marker = "python_full_version >= '3.14'" },
{ name = "tqdm", marker = "python_full_version >= '3.14'" },
{ name = "typer", marker = "python_full_version >= '3.14'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fc/1a/70e830d53ecc96ce69cfa8de38f163712d2b43ac52fbd743f39f56025c31/transformers-5.3.0.tar.gz", hash = "sha256:009555b364029da9e2946d41f1c5de9f15e6b1df46b189b7293f33a161b9c557", size = 8830831, upload-time = "2026-03-04T17:41:46.119Z" }
wheels = [
@@ -6097,9 +6097,9 @@ name = "valkey-glide"
version = "2.4.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
{ name = "protobuf" },
{ name = "sniffio" },
{ name = "anyio", marker = "sys_platform != 'win32'" },
{ name = "protobuf", marker = "sys_platform != 'win32'" },
{ name = "sniffio", marker = "sys_platform != 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/72/a2/582b34c6acc8dc857c537f6007459cba48dfa0dc404789a657e5c1a998c0/valkey_glide-2.4.1.tar.gz", hash = "sha256:f1155d84156d11b90488aa67e90102f0bf98a45314f5b99308ac9074c05f7241", size = 898030, upload-time = "2026-05-28T21:41:55.881Z" }
wheels = [