Merge remote-tracking branch 'origin/master' into feat/workspace-operation-traceability

This commit is contained in:
TyperBody
2026-09-27 02:46:00 +08:00
14 changed files with 767 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
@@ -80,13 +81,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
@@ -98,7 +100,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.11",
"asyncpg>=0.30.0",
"line-bot-sdk>=3.19.0",
"matrix-nio>=0.25.2",
@@ -0,0 +1,138 @@
"""Backfill raw artifact digests for legacy shared certificates.
Revision ID: 0032_cert_artifact_digest
Revises: 0031_merge_totp_assistant
Older certified-plugin rows persisted every shared-admission fact except the raw
archive digest. Placement now requires that digest to match the installation's
immutable artifact digest, so this migration fills only records whose remaining
facts already prove the exact legacy shared-admission shape.
"""
from __future__ import annotations
from collections.abc import Mapping
import sqlalchemy as sa
from alembic import op
revision = '0032_cert_artifact_digest'
down_revision = '0031_merge_totp_assistant'
branch_labels = None
depends_on = None
_TABLE = 'plugin_settings'
_SHARED_RUNTIME = 'shared-runtime-v1'
_SHARED_ADMISSION_CODE = 'CERTIFIED_PLUGIN_SHARED_ELIGIBLE'
_LOWER_HEX = frozenset('0123456789abcdef')
_HEX = frozenset('0123456789abcdefABCDEF')
def _is_digest(value: object, *, lowercase: bool) -> bool:
allowed = _LOWER_HEX if lowercase else _HEX
return isinstance(value, str) and len(value) == 64 and all(character in allowed for character in value)
def _eligible_certification(install_info: object, artifact_digest: object) -> Mapping[object, object] | None:
if not _is_digest(artifact_digest, lowercase=True) or not isinstance(install_info, Mapping):
return None
certification = install_info.get('_certification')
if not isinstance(certification, Mapping) or 'artifact_digest' in certification:
return None
certificate_id = certification.get('certificate_id')
if not isinstance(certificate_id, str) or not certificate_id.strip():
return None
if not _is_digest(certification.get('normalized_digest'), lowercase=False):
return None
required_facts = {
'verification': 'valid',
'certificate_runtime_profile': _SHARED_RUNTIME,
'runtime_profile': _SHARED_RUNTIME,
'admission_code': _SHARED_ADMISSION_CODE,
}
if not all(certification.get(key) == value for key, value in required_facts.items()):
return None
return certification
def _suspend_postgres_rls(conn: sa.Connection) -> tuple[bool, bool]:
if conn.dialect.name != 'postgresql':
return False, False
state = conn.execute(
sa.text('SELECT relrowsecurity, relforcerowsecurity FROM pg_class WHERE oid = to_regclass(:table_name)'),
{'table_name': _TABLE},
).one()
rls_enabled, rls_forced = bool(state.relrowsecurity), bool(state.relforcerowsecurity)
if rls_forced:
conn.execute(sa.text(f'ALTER TABLE {_TABLE} NO FORCE ROW LEVEL SECURITY'))
if rls_enabled:
conn.execute(sa.text(f'ALTER TABLE {_TABLE} DISABLE ROW LEVEL SECURITY'))
return rls_enabled, rls_forced
def _restore_postgres_rls(conn: sa.Connection, state: tuple[bool, bool]) -> None:
if conn.dialect.name != 'postgresql':
return
rls_enabled, rls_forced = state
if rls_enabled:
conn.execute(sa.text(f'ALTER TABLE {_TABLE} ENABLE ROW LEVEL SECURITY'))
if rls_forced:
conn.execute(sa.text(f'ALTER TABLE {_TABLE} FORCE ROW LEVEL SECURITY'))
def backfill_certification_artifact_digests(conn: sa.Connection) -> None:
inspector = sa.inspect(conn)
if _TABLE not in inspector.get_table_names():
return
columns = {column['name'] for column in inspector.get_columns(_TABLE)}
required_columns = {
'workspace_uuid',
'plugin_author',
'plugin_name',
'artifact_digest',
'install_info',
}
if not required_columns <= columns:
return
plugin_settings = sa.table(
_TABLE,
sa.column('workspace_uuid', sa.String(36)),
sa.column('plugin_author', sa.String(255)),
sa.column('plugin_name', sa.String(255)),
sa.column('artifact_digest', sa.String(64)),
sa.column('install_info', sa.JSON()),
)
rows = conn.execute(sa.select(plugin_settings)).mappings().all()
for row in rows:
certification = _eligible_certification(row['install_info'], row['artifact_digest'])
if certification is None:
continue
updated_certification = dict(certification)
updated_certification['artifact_digest'] = row['artifact_digest']
updated_install_info = dict(row['install_info'])
updated_install_info['_certification'] = updated_certification
conn.execute(
plugin_settings.update()
.where(plugin_settings.c.workspace_uuid == row['workspace_uuid'])
.where(plugin_settings.c.plugin_author == row['plugin_author'])
.where(plugin_settings.c.plugin_name == row['plugin_name'])
.values(install_info=updated_install_info)
)
def upgrade() -> None:
conn = op.get_bind()
if _TABLE not in sa.inspect(conn).get_table_names():
return
rls_state = _suspend_postgres_rls(conn)
try:
backfill_certification_artifact_digests(conn)
finally:
_restore_postgres_rls(conn, rls_state)
def downgrade() -> None:
# The source digest cannot be distinguished from a digest persisted by
# current Core, so downgrade intentionally preserves the safe enrichment.
pass
+35
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'
@@ -270,3 +271,37 @@ 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 len(artifact_digest) != 64 or any(character not in '0123456789abcdef' for character in artifact_digest.lower()):
return PluginExecutionMode.DEDICATED
if not isinstance(install_info, Mapping):
return PluginExecutionMode.DEDICATED
certification = install_info.get('_certification')
if not isinstance(certification, Mapping):
return PluginExecutionMode.DEDICATED
normalized_digest = certification.get('normalized_digest')
if not isinstance(normalized_digest, str) or len(normalized_digest) != 64:
return PluginExecutionMode.DEDICATED
if any(character not in '0123456789abcdef' for character in normalized_digest.lower()):
return PluginExecutionMode.DEDICATED
certificate_id = certification.get('certificate_id')
if not isinstance(certificate_id, str) or not certificate_id.strip():
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,
@@ -16,6 +16,7 @@ import uuid
import pytest
import sqlalchemy as sa
from sqlalchemy.ext.asyncio import create_async_engine
from langbot_plugin.entities.io.context import PluginExecutionMode
from langbot.pkg.entity import persistence
from langbot.pkg.entity.persistence.base import Base
@@ -25,6 +26,7 @@ from langbot.pkg.persistence.alembic_runner import (
run_alembic_downgrade,
run_alembic_upgrade,
)
from langbot.pkg.plugin.certification import execution_mode_for_persisted_installation
from langbot.pkg.utils import importutil
@@ -353,6 +355,143 @@ async def test_empty_database_startup_schema_then_real_migrations(convergence_en
assert await get_alembic_current(engine) == get_alembic_head()
def _legacy_shared_certification(**overrides):
certification = {
'normalized_digest': 'B' * 64,
'verification': 'valid',
'certificate_runtime_profile': 'shared-runtime-v1',
'certificate_id': 'ed25519:trusted-issuer',
'runtime_profile': 'shared-runtime-v1',
'admission_code': 'CERTIFIED_PLUGIN_SHARED_ELIGIBLE',
'preserved_certificate_key': {'nested': True},
}
certification.update(overrides)
return certification
@pytest.mark.asyncio
async def test_certification_artifact_digest_backfill_is_safe_and_enables_shared_placement(convergence_engine):
engine = convergence_engine
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
await run_alembic_upgrade(engine, '0031_merge_totp_assistant')
valid_digest = 'a' * 64
rows = {
'eligible-a': (_legacy_shared_certification(), valid_digest),
'eligible-b': (_legacy_shared_certification(), valid_digest),
'present-matching': (_legacy_shared_certification(artifact_digest=valid_digest), valid_digest),
'present-mismatched': (_legacy_shared_certification(artifact_digest='c' * 64), valid_digest),
'invalid': (_legacy_shared_certification(verification='invalid'), valid_digest),
'incomplete': (_legacy_shared_certification(certificate_id=' '), valid_digest),
'missing-fact': (
{
key: value
for key, value in _legacy_shared_certification().items()
if key != 'certificate_runtime_profile'
},
valid_digest,
),
'malformed-normalized': (_legacy_shared_certification(normalized_digest='g' * 64), valid_digest),
'dedicated-certificate': (
_legacy_shared_certification(certificate_runtime_profile='dedicated'),
valid_digest,
),
'dedicated': (_legacy_shared_certification(runtime_profile='dedicated'), valid_digest),
'wrong-admission': (_legacy_shared_certification(admission_code='SHARED_ELIGIBLE'), valid_digest),
'uppercase-row-digest': (_legacy_shared_certification(), 'A' * 64),
'nonhex-row-digest': (_legacy_shared_certification(), 'g' * 64),
}
plugin_settings = Base.metadata.tables['plugin_settings']
workspaces = Base.metadata.tables['workspaces']
async with engine.begin() as conn:
for index, (name, (certification, artifact_digest)) in enumerate(rows.items(), start=1):
workspace_uuid = f'41100000-0000-4000-8000-{index:012d}'
await conn.execute(
workspaces.insert().values(
uuid=workspace_uuid,
instance_uuid=f'cert-backfill-{index}',
name=name,
slug=name,
)
)
await conn.execute(
plugin_settings.insert().values(
workspace_uuid=workspace_uuid,
plugin_author='langbot',
plugin_name=name,
installation_uuid=f'51100000-0000-4000-8000-{index:012d}',
artifact_digest=artifact_digest,
runtime_revision=1,
install_info={
'_certification': certification,
'preserved_install_key': ['keep', {'nested': True}],
},
)
)
await run_alembic_upgrade(engine, 'head')
# Re-running the data revision itself must also be harmless.
migration = __import__(
'langbot.pkg.persistence.alembic.versions.0032_certification_artifact_digest_backfill',
fromlist=['upgrade'],
)
async with engine.begin() as conn:
await conn.run_sync(lambda sync: migration.backfill_certification_artifact_digests(sync))
stored = {
name: (artifact_digest, install_info)
for name, artifact_digest, install_info in (
await conn.execute(
sa.select(
plugin_settings.c.plugin_name,
plugin_settings.c.artifact_digest,
plugin_settings.c.install_info,
)
)
).all()
}
for name in ('eligible-a', 'eligible-b'):
eligible_digest, eligible_info = stored[name]
assert eligible_info['preserved_install_key'] == ['keep', {'nested': True}]
assert eligible_info['_certification']['preserved_certificate_key'] == {'nested': True}
assert eligible_info['_certification']['artifact_digest'] == eligible_digest == valid_digest
assert (
execution_mode_for_persisted_installation(
artifact_digest=eligible_digest,
install_info=eligible_info,
)
is PluginExecutionMode.SHARED_CERTIFIED
)
for name, (original_certification, artifact_digest) in rows.items():
if name in {'eligible-a', 'eligible-b'}:
continue
stored_digest, stored_info = stored[name]
assert stored_digest == artifact_digest
assert stored_info['_certification'] == original_certification
if name != 'present-matching':
assert (
execution_mode_for_persisted_installation(
artifact_digest=stored_digest,
install_info=stored_info,
)
is PluginExecutionMode.DEDICATED
)
@pytest.mark.asyncio
async def test_certification_artifact_digest_backfill_accepts_fresh_current_schema(convergence_engine):
engine = convergence_engine
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
await run_alembic_upgrade(engine, 'head')
await run_alembic_upgrade(engine, 'head')
assert await get_alembic_current(engine) == get_alembic_head()
@pytest.mark.asyncio
async def test_merge_only_downgrade_preserves_both_branch_schemas(convergence_engine):
engine = convergence_engine
@@ -329,6 +329,76 @@ class TestPostgreSQLMigrationBaseline:
rev = await get_alembic_current(postgres_engine)
assert rev == '0001_baseline'
@pytest.mark.asyncio
async def test_certification_artifact_digest_backfill_updates_only_complete_legacy_shared_rows(
self,
postgres_engine,
clean_tables,
clean_alembic_version,
):
async with postgres_engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
await run_alembic_stamp(postgres_engine, '0031_merge_totp_assistant')
plugin_settings = Base.metadata.tables['plugin_settings']
workspaces = Base.metadata.tables['workspaces']
digest = 'a' * 64
complete = {
'normalized_digest': 'B' * 64,
'verification': 'valid',
'certificate_runtime_profile': 'shared-runtime-v1',
'certificate_id': 'ed25519:trusted-issuer',
'runtime_profile': 'shared-runtime-v1',
'admission_code': 'CERTIFIED_PLUGIN_SHARED_ELIGIBLE',
'preserved': {'key': True},
}
rows = {
'eligible': complete,
'invalid': {**complete, 'verification': 'invalid'},
'present': {**complete, 'artifact_digest': 'c' * 64},
}
async with postgres_engine.begin() as conn:
for index, (name, certification) in enumerate(rows.items(), start=1):
workspace_uuid = f'61100000-0000-4000-8000-{index:012d}'
await conn.execute(
workspaces.insert().values(
uuid=workspace_uuid,
instance_uuid=f'postgres-cert-backfill-{index}',
name=name,
slug=name,
)
)
await conn.execute(
plugin_settings.insert().values(
workspace_uuid=workspace_uuid,
plugin_author='langbot',
plugin_name=name,
installation_uuid=f'71100000-0000-4000-8000-{index:012d}',
artifact_digest=digest,
runtime_revision=1,
install_info={'_certification': certification, 'preserved': ['install-info']},
)
)
await run_alembic_upgrade(postgres_engine, 'head')
async with postgres_engine.connect() as conn:
stored = dict(
(await conn.execute(sa.select(plugin_settings.c.plugin_name, plugin_settings.c.install_info))).all()
)
assert stored['eligible'] == {
'_certification': {**complete, 'artifact_digest': digest},
'preserved': ['install-info'],
}
assert stored['invalid'] == {
'_certification': rows['invalid'],
'preserved': ['install-info'],
}
assert stored['present'] == {
'_certification': rows['present'],
'preserved': ['install-info'],
}
@pytest.mark.asyncio
async def test_fresh_postgres_schema_accepts_application_casefold_identity(
self,
@@ -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,19 @@ 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)
if archive_kind == 'signed_shared':
assert persisted_info['_certification']['certificate_id'] == 'ephemeral'
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
),
)
@@ -124,6 +133,7 @@ async def test_oss_admits_unresolvable_declaration_on_the_dedicated_profile() ->
binding,
artifact_package=package,
enabled=True,
execution_mode=PluginExecutionMode.DEDICATED,
)
@@ -183,7 +193,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', 'certificate_id', 'force', 'expected_disposition', 'expected_code'),
@@ -187,6 +189,162 @@ def test_log_visibility_policy_only_scopes_valid_shared_certifications(
assert visibility.value == expected_visibility
def _complete_persisted_certification() -> dict[str, object]:
return {
'artifact_digest': 'a' * 64,
'normalized_digest': 'b' * 64,
'verification': 'valid',
'certificate_runtime_profile': 'shared-runtime-v1',
'certificate_id': 'ed25519:trusted-issuer',
'runtime_profile': 'shared-runtime-v1',
'admission_code': 'CERTIFIED_PLUGIN_SHARED_ELIGIBLE',
}
@pytest.mark.parametrize(
('certification', 'expected_mode'),
[
(_complete_persisted_certification(), PluginExecutionMode.SHARED_CERTIFIED),
(None, PluginExecutionMode.DEDICATED),
({}, PluginExecutionMode.DEDICATED),
(
{
**_complete_persisted_certification(),
'verification': 'invalid',
},
PluginExecutionMode.DEDICATED,
),
(
{
**_complete_persisted_certification(),
'artifact_digest': 'b' * 64,
},
PluginExecutionMode.DEDICATED,
),
(
{
**_complete_persisted_certification(),
'runtime_profile': 'dedicated',
},
PluginExecutionMode.DEDICATED,
),
(
{
**_complete_persisted_certification(),
'admission_code': 'CERTIFIED_PLUGIN_OSS_FORCED_DEDICATED',
},
PluginExecutionMode.DEDICATED,
),
],
)
def test_persisted_certification_selects_shared_execution_only_for_exact_admitted_artifact(
certification: dict[str, object] | 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
)
@pytest.mark.parametrize(
'missing_field',
[
'artifact_digest',
'normalized_digest',
'verification',
'certificate_runtime_profile',
'certificate_id',
'runtime_profile',
'admission_code',
],
)
def test_persisted_certification_requires_every_shared_admission_fact(missing_field: str) -> None:
from langbot.pkg.plugin.certification import execution_mode_for_persisted_installation
certification = _complete_persisted_certification()
del certification[missing_field]
assert (
execution_mode_for_persisted_installation(
artifact_digest='a' * 64,
install_info={'_certification': certification},
)
is PluginExecutionMode.DEDICATED
)
@pytest.mark.parametrize(
('field', 'malformed_value'),
[
('artifact_digest', None),
('artifact_digest', 123),
('artifact_digest', ''),
('normalized_digest', None),
('normalized_digest', 123),
('normalized_digest', ''),
('normalized_digest', ' ' * 64),
('normalized_digest', 'b' * 63),
('normalized_digest', 'g' * 64),
('certificate_id', None),
('certificate_id', 123),
('certificate_id', ''),
('certificate_id', ' '),
('verification', None),
('verification', 123),
('verification', ''),
('certificate_runtime_profile', None),
('certificate_runtime_profile', 123),
('certificate_runtime_profile', ''),
('runtime_profile', None),
('runtime_profile', 123),
('runtime_profile', ''),
('admission_code', None),
('admission_code', 123),
('admission_code', ''),
],
)
def test_persisted_certification_rejects_malformed_shared_admission_fact(
field: str,
malformed_value: object,
) -> None:
from langbot.pkg.plugin.certification import execution_mode_for_persisted_installation
certification = _complete_persisted_certification()
certification[field] = malformed_value
assert (
execution_mode_for_persisted_installation(
artifact_digest='a' * 64,
install_info={'_certification': certification},
)
is PluginExecutionMode.DEDICATED
)
@pytest.mark.parametrize('malformed_digest', ['', 'a' * 63, 'g' * 64])
def test_persisted_certification_rejects_malformed_matching_raw_digest(malformed_digest: str) -> None:
from langbot.pkg.plugin.certification import execution_mode_for_persisted_installation
certification = _complete_persisted_certification()
certification['artifact_digest'] = malformed_digest
assert (
execution_mode_for_persisted_installation(
artifact_digest=malformed_digest,
install_info={'_certification': certification},
)
is PluginExecutionMode.DEDICATED
)
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,18 @@ 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,
'normalized_digest': 'b' * 64,
'verification': 'valid',
'certificate_runtime_profile': 'shared-runtime-v1',
'certificate_id': 'ed25519:trusted-issuer',
'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 +788,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,18 @@ 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',
'certificate_id': 'ed25519:trusted-issuer',
'runtime_profile': 'shared-runtime-v1',
'admission_code': 'CERTIFIED_PLUGIN_SHARED_ELIGIBLE',
},
},
SimpleNamespace(for_installation=lambda _uuid: SimpleNamespace(artifact_digest=digest)),
)
)
@@ -56,6 +67,7 @@ def plugin_setting(
artifact_digest: str,
*,
durable: bool = True,
certification: dict[str, str] | None = None,
) -> SimpleNamespace:
return SimpleNamespace(
plugin_author='author',
@@ -67,7 +79,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 +178,45 @@ 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,
'normalized_digest': 'b' * 64,
'verification': 'valid',
'certificate_runtime_profile': 'shared-runtime-v1',
'certificate_id': 'ed25519:trusted-issuer',
'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 +277,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 +359,7 @@ async def test_local_install_persists_verified_package_before_runtime_apply():
binding,
artifact_package=package,
enabled=True,
execution_mode=PluginExecutionMode.DEDICATED,
)
@@ -396,6 +452,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 +489,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.11" },
{ 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.11"
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/41/13/7f913f88494205abb1a6390746e88de4e1148fbf6d0a273bbe2450f5fb0c/langbot_plugin-0.6.11.tar.gz", hash = "sha256:2ecddc2ef3a2eb92925c08cf0f19887e9be573b93e7d5de49494c01c083cfac7", size = 655797, upload-time = "2026-09-26T18:15:19.81Z" }
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/25/10/00e3d5d985d82aa08dd5f2f01aa2b85e8b46880dcfa2aa9b923f7be285da/langbot_plugin-0.6.11-py3-none-any.whl", hash = "sha256:85458919c943894780db85bbd96c0a61354e231bc396a3f74072a8eddc978b07", size = 427659, upload-time = "2026-09-26T18:15:18.003Z" },
]
[[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 = [