infinity_provider_protocol/lib.rs
1//! Extensible model provider abstraction and its remote wire protocol.
2//!
3//! This crate is intentionally lightweight (no HTTP stack, no LLM SDKs) so
4//! that both the agent runtime and out-of-process provider implementations
5//! (e.g. `infinity-provider-bedrock`) can depend on it.
6//!
7//! It defines:
8//!
9//! * The chat [`message`] types and [`completion`] request/stream types that
10//! form the entire model API surface of this project.
11//! * [`ModelProvider`] — an async trait for listing models and invoking one
12//! of them, returning a [`ModelStream`] of [`StreamChunk`]s.
13//! * [`remote`] (feature `remote`) — a Unix-socket transport so providers
14//! can run out of process.
15
16use async_trait::async_trait;
17use serde::{Deserialize, Serialize};
18
19pub mod completion;
20pub mod message;
21#[cfg(feature = "mock")]
22pub mod mock;
23// The remote transport is unix-only for now (Unix domain sockets); the
24// `remote` feature is the stable switch so a Windows transport can slot in
25// behind the same flag later.
26#[cfg(all(unix, feature = "remote"))]
27pub mod remote;
28
29pub use completion::{
30 CompletionError, CompletionRequest, ErrorClass, FinalResponse, ModelStream, StreamChunk,
31 ToolCallDeltaContent, ToolDefinition, Usage,
32};
33pub use message::{
34 AssistantContent, Image, ImageMediaType, ImageSource, Message, Reasoning, ReasoningContent,
35 Text, ToolCall, ToolFunction, ToolResult, ToolResultContent, UserContent,
36};
37
38type BoxError = Box<dyn std::error::Error + Send + Sync>;
39
40/// A model offered by a [`ModelProvider`].
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct ModelEntry {
43 /// Provider-scoped identifier for this model. Must be unique within the
44 /// provider, but need not match the upstream API's model id (providers may
45 /// expose multiple configurations of the same upstream model).
46 pub model_id: String,
47 /// Human-readable name shown in pickers.
48 pub display_name: String,
49 /// Context window size in tokens (used for compaction thresholds).
50 pub context_window: usize,
51 /// Maximum number of output tokens the model can generate per request.
52 /// `None` falls back to the provider's default.
53 pub max_output_tokens: Option<u64>,
54 /// Whether the model accepts image content in its input (e.g. image
55 /// tool results). When `false`, the runtime replaces image content with
56 /// a text placeholder before invoking the model.
57 #[serde(default)]
58 pub supports_image_input: bool,
59}
60
61/// A backend that can list and invoke completion models.
62///
63/// Implementations handle any provider-specific request parameters inside
64/// [`invoke_model`](Self::invoke_model). Providers have no identity of their
65/// own — callers that manage multiple providers (e.g. the daemon) assign each
66/// one a stable unique id at registration time.
67#[async_trait]
68pub trait ModelProvider: Send + Sync {
69 /// List the models available from this provider. The first entry is the
70 /// provider's default model.
71 async fn list_models(&self) -> Result<Vec<ModelEntry>, BoxError>;
72
73 /// Invoke a model by its provider-scoped id, streaming the completion
74 /// response.
75 async fn invoke_model(
76 &self,
77 model_id: &str,
78 request: CompletionRequest,
79 ) -> Result<ModelStream, CompletionError>;
80}
81
82/// A single completion model (as opposed to a provider, which is a catalog
83/// of models). Mostly useful for tests; see [`SingleModelProvider`].
84#[async_trait]
85pub trait CompletionModel: Send + Sync {
86 /// Stream one completion.
87 async fn stream(&self, request: CompletionRequest) -> Result<ModelStream, CompletionError>;
88}
89
90/// Adapter exposing a single [`CompletionModel`] as a [`ModelProvider`].
91/// Useful for tests and simple single-model deployments.
92pub struct SingleModelProvider<M> {
93 entry: ModelEntry,
94 model: M,
95}
96
97impl<M: CompletionModel> SingleModelProvider<M> {
98 pub fn new(entry: ModelEntry, model: M) -> Self {
99 Self { entry, model }
100 }
101}
102
103#[async_trait]
104impl<M: CompletionModel> ModelProvider for SingleModelProvider<M> {
105 async fn list_models(&self) -> Result<Vec<ModelEntry>, BoxError> {
106 Ok(vec![self.entry.clone()])
107 }
108
109 async fn invoke_model(
110 &self,
111 _model_id: &str,
112 request: CompletionRequest,
113 ) -> Result<ModelStream, CompletionError> {
114 self.model.stream(request).await
115 }
116}