Port Interfaces¶
The contract that every adapter must honor.
Why Ports?¶
Ports define what the system needs from the outside world. Adapters provide how. This is Dependency Inversion — the core depends on abstractions, not concrete implementations.
RuntimePort¶
class RuntimePort(Protocol):
backend: RuntimeBackend
def is_available(self) -> bool: ...
def list_installed(self) -> list[ModelRef]: ...
def is_installed(self, ref: ModelRef) -> bool: ...
def pull(self, ref: ModelRef, progress: ProgressPort) -> PullResult: ...
def remove(self, ref: ModelRef) -> None: ...
def get_model_client(self, ref: ModelRef) -> ModelClient: ...
def default_tag_for(self, spec: ModelSpec) -> str: ...
RegistryPort¶
class RegistryPort(Protocol):
def search(self, query: str) -> list: ...
def get(self, ref: ModelRef) -> ModelSpec | None: ...
def by_category(self, cat: Category) -> list: ...
def recommend(self, task: str) -> list: ...
def list_all(self) -> list: ...
DownloaderPort¶
class DownloaderPort(Protocol):
def download(self, spec: ModelSpec, dest: Path, progress: ProgressPort) -> Path: ...
def pull(self, ref: ModelRef, progress: ProgressPort) -> PullResult: ...
CachePort¶
class CachePort(Protocol):
def is_fresh(self, ref: ModelRef) -> bool: ...
def clean(self, force: bool = False) -> list[str]: ...
def path(self) -> str: ...
def status(self) -> list[dict]: ...
ProgressPort¶
class ProgressPort(Protocol):
def start(self, description: str, total: int | None = None) -> None: ...
def update(self, increment: int) -> None: ...
def finish(self) -> None: ...
EventPort¶
class EventPort(Protocol):
def before_pull(self, ref: ModelRef) -> None: ...
def after_install(self, ref: ModelRef) -> None: ...
def on_error(self, ref: ModelRef, error: Exception) -> None: ...
Port-Contract Test Suite¶
Every adapter MUST pass the shared port-contract test suite — parameterized over all implementations. This guarantees new runtimes behave correctly.
Next Steps¶
- Runtime Adapters — implementing ports
- Error Handling — typed exceptions