origin_ai/service.rs
1use crate::{Completion, Prompt};
2use async_trait::async_trait;
3use origin_domain::{AppError, Result};
4use std::fmt::Debug;
5
6/// Inference, as a port.
7///
8/// Implementations live in `adapters/origin-ai-*`. Domain code never names a provider,
9/// so switching one — or letting the user pick — changes the composition root and
10/// nothing else.
11///
12/// Errors follow the usual model: a provider outage is `ExternalService`, a rejected
13/// key is `Authentication`, a quota is `RateLimited`. Callers can therefore treat an
14/// unavailable model exactly like an unavailable API.
15#[async_trait]
16pub trait AiService: Debug + Send + Sync + 'static {
17 /// The model this service will use, for display and for the record.
18 fn model(&self) -> &str;
19
20 async fn complete(&self, prompt: Prompt) -> Result<Completion>;
21}
22
23/// Refuses every request.
24///
25/// The default for a product with AI features switched off, and for anything that
26/// must not silently reach the network. It fails rather than returning an empty
27/// answer: a feature that quietly produces nothing is harder to diagnose than one that
28/// says it is unavailable.
29#[derive(Debug, Clone, Copy, Default)]
30pub struct NoopAiService;
31
32#[async_trait]
33impl AiService for NoopAiService {
34 fn model(&self) -> &str {
35 "none"
36 }
37
38 async fn complete(&self, _prompt: Prompt) -> Result<Completion> {
39 Err(AppError::configuration(
40 "this application has no AI provider configured",
41 ))
42 }
43}