Files
LangBot/plugins/KnowledgeEngine/certification-tests/verify_candidates.py
T

101 lines
5.5 KiB
Python

"""Build exact unsigned archives and exercise genuine SDK 0.7.4 admission.
Uses SDK ArtifactStore, DependencyEnvironmentStore.prepare and its real direct
pip installer. This is NOT nsjail/cgroup certification or a production install.
"""
import argparse
import asyncio
import base64
import csv
import hashlib
import importlib.metadata
import io
import json
import os
from pathlib import Path
import subprocess
import sys
import zipfile
from langbot_plugin.runtime.plugin.artifact import PluginArtifactStore
from langbot_plugin.runtime.plugin.dependency_environment import PluginDependencyEnvironmentStore
from langbot_plugin.runtime.plugin.worker_launcher import PluginWorkerLauncher
from langbot_plugin.entities.io.context import PluginWorkerPolicy
NAMES = ['DifyDatasetsConnector', 'FastGPTConnector', 'LangRAG', 'RAGFlowConnector', 'LongTermMemory']
ROOT = Path(__file__).resolve().parents[1]
async def verify(out):
assert importlib.metadata.version('langbot-plugin') == '0.7.4'
out.mkdir(parents=True, exist_ok=True)
dist = importlib.metadata.distribution('langbot-plugin')
verified = []
for name, encoded, size in csv.reader(io.StringIO(dist.read_text('RECORD'))):
if name.startswith('langbot_plugin/') and name.endswith('.py'):
digest = hashlib.sha256(Path(dist.locate_file(name)).read_bytes()).digest()
assert encoded == 'sha256=' + base64.urlsafe_b64encode(digest).decode().rstrip('=')
verified.append(name)
(out / 'sdk-environment.json').write_text(json.dumps({
'sdk': dist.version, 'python': sys.version, 'sdk_record_verified_files': len(verified),
'source': 'installed PyPI distribution, not local SDK source',
'runtime_distributions': sorted(f'{d.metadata["Name"]}=={d.version}' for d in importlib.metadata.distributions()),
}, indent=2))
artifacts = PluginArtifactStore(out / 'runtime')
deps = PluginDependencyEnvironmentStore(out / 'runtime')
launcher = PluginWorkerLauncher()
launcher.configure(PluginWorkerPolicy(max_cpus=1, max_memory_mb=512, max_pids=128, max_open_files=256, max_file_size_mb=512, require_hard_limits=False), 'oss_dev')
inventory = []
for name in NAMES:
dest = out / 'packages' / name
dest.mkdir(parents=True, exist_ok=True)
result = subprocess.run([str(Path(sys.executable).parent / 'lbp'), 'build', '-o', str(dest)],
cwd=ROOT / name, capture_output=True, text=True, timeout=60)
(out / (name + '-build.log')).write_text(result.stdout + result.stderr)
assert result.returncode == 0, result.stdout + result.stderr
packages = list(dest.glob('*.lbpkg'))
assert len(packages) == 1
package = packages[0]
data = package.read_bytes()
digest = hashlib.sha256(data).hexdigest()
with zipfile.ZipFile(package) as z:
assert not z.comment, 'Candidates must remain unsigned'
entries = [{'path': p, 'sha256': hashlib.sha256(z.read(p)).hexdigest(), 'bytes': len(z.read(p))}
for p in sorted(z.namelist()) if not p.endswith('/')]
artifact = artifacts.install_package(data, digest)
assert artifacts.require_verified(digest) == artifact
env = await launcher.prepare_dependency_environment(deps, artifact)
assert deps.get_ready(env.digest) == env
assert await launcher.prepare_dependency_environment(deps, artifact) == env
assert not list(env.site_packages_path.glob('langbot_plugin*')), 'SDK must not be shadow installed'
row = {'name': name, 'author': artifact.plugin_author, 'version': artifact.plugin_version,
'package': str(package), 'sha256': digest, 'bytes': len(data), 'files': entries,
'code_path': str(artifact.code_path), 'dependency_path': str(env.site_packages_path),
'environment_digest': env.digest, 'requirements_digest': env.requirements_digest,
'dependency_prepare_passed': True, 'dependency_cache_readback_passed': True,
'sdk_shadow_installed': False, 'unsigned': True,
'resolved_dependency_distributions': sorted(
f'{d.metadata["Name"]}=={d.version}' for d in
importlib.metadata.distributions(path=[str(env.site_packages_path)]))}
inventory.append(row)
(out / 'candidate-inventory.json').write_text(json.dumps(inventory, indent=2))
print(name, artifact.plugin_version, digest, flush=True)
child_env = {**os.environ, 'PYTHONDONTWRITEBYTECODE': '1',
'PYTHONPATH': os.pathsep.join([str(dist.locate_file('')), str(env.site_packages_path)])}
result = subprocess.run([sys.executable, str(Path(__file__).with_name('sdk_rpc_probe.py')),
str(artifact.code_path), str(out), name, digest],
cwd=artifact.code_path, env=child_env, text=True, capture_output=True, timeout=100)
(out / (name + '-rpc.log')).write_text(result.stdout + result.stderr)
assert result.returncode == 0, result.stdout + result.stderr
saved = json.loads((out / 'candidate-inventory.json').read_text())
assert len(saved) == len({r['name'] for r in saved}) == 5
assert {r['name'] for r in saved} == set(NAMES)
print('VERIFIED 5 exact unsigned candidates, dependencies, discovery and RPC isolation')
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--evidence', required=True, type=Path)
args = parser.parse_args()
asyncio.run(verify(args.evidence.resolve()))