Coverage for src/lilbee/providers/factory.py: 100%

31 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-11-06 08:17 +0000

1"""Factory for creating LLM provider instances.""" 

2 

3from __future__ import annotations 

4 

5from collections.abc import Callable 

6from dataclasses import dataclass 

7from typing import TYPE_CHECKING, assert_never 

8 

9from lilbee.core.config.enums import LlmProvider 

10from lilbee.providers import availability 

11from lilbee.providers.base import ProviderError 

12 

13if TYPE_CHECKING: 

14 from lilbee.core.config import Config 

15 from lilbee.providers.base import LLMProvider 

16 from lilbee.providers.sdk_backend import LlmSdkBackend 

17 from lilbee.providers.sdk_llm_provider import SdkLLMProvider 

18 

19__all__ = ["SdkLibrary", "create_provider", "create_sdk_provider", "sdk_library"] 

20 

21 

22@dataclass(frozen=True) 

23class SdkLibrary: 

24 """The client library behind the SDK backend.""" 

25 

26 dist: str 

27 """Distribution name, as package metadata lists it.""" 

28 module: str 

29 """Top-level import name.""" 

30 create_backend: Callable[[], LlmSdkBackend] 

31 """Builds a new backend over the library.""" 

32 

33 

34def sdk_library() -> SdkLibrary: 

35 """Return the client library's names and backend builder, importing nothing heavy.""" 

36 # heavy: setup.py imports this module at CLI startup; the backend package 

37 # defers its backend module and the library to create_backend 

38 from lilbee.providers.backends.liter_llm import SDK_DIST, SDK_MODULE, create_sdk_backend 

39 

40 return SdkLibrary(dist=SDK_DIST, module=SDK_MODULE, create_backend=create_sdk_backend) 

41 

42 

43def create_sdk_provider() -> SdkLLMProvider: 

44 """Return a new provider over the SDK backend, for remote and API models.""" 

45 from lilbee.providers.sdk_llm_provider import SdkLLMProvider 

46 

47 return SdkLLMProvider(sdk_library().create_backend()) 

48 

49 

50def create_provider( 

51 config: Config, 

52 *, 

53 hold_warm: bool = False, 

54 config_refresh: Callable[[], None] | None = None, 

55) -> LLMProvider: 

56 """Create a new LLM provider instance from the given config. 

57 

58 *hold_warm* marks the provider as serving an interactive session, so a local 

59 fleet bound to that session keeps its weights resident instead of 

60 idle-unloading for as long as the session owns the process (a fleet shared 

61 with other users, or kept warm, outlives it and keeps its idle window). 

62 Irrelevant to a remote provider, which has no local engine to hold. 

63 *config_refresh* takes config.toml changes now; the local engine runs it 

64 before it retries a call on an engine that went away. 

65 """ 

66 match config.llm_provider: 

67 case LlmProvider.AUTO: 

68 # circular: routing_provider -> factory via create_sdk_provider 

69 from lilbee.providers.routing_provider import RoutingProvider 

70 

71 return RoutingProvider(hold_warm=hold_warm, config_refresh=config_refresh) 

72 

73 case LlmProvider.REMOTE: 

74 if not availability.provider_sdk_available(): 

75 raise ProviderError(availability.PROVIDER_SDK_MISSING_MESSAGE) 

76 return create_sdk_provider() 

77 

78 assert_never(config.llm_provider) # pragma: no cover