At a high level, the architecture of Crosstalk is fairly simple:
- Chat model conversations are facilitated by chat providers. All chat providers implement the
ChatProvider
trait. This interface boundary abstracts away provider-specific semantics. - When the program starts, the enabled providers are registered in the registry. The registry determines which model should serve the user. The user can influence which model serves the request by providing a model specification which is resolved to a specific provider and model to serve the request. This is accomplished using dynamic dispatch.
- Each provider has specific conditions under which it is enabled. Only the enabled providers are registered. The registry is populated during program startup.
All providers are located under src/providers
. Providers have a two-part structure:
- A "native" API in a module called
api
. This provides a Rust API that mirrors the provider's API semantics and errors as closely as possible while remaining incompatible with the chat provider API. - A "provider" wrapper located under the
provider
module. This implements theChatProvider
interface, performs type conversions, and uses the underlyingapi
to resolve the request.
Providers use the apireq
module, which provides utilities for making API requests and handling low-level errors.