1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
//! The seam that makes catalog discovery a provider capability.
//!
//! Discovery began as one Vertex-shaped function. The parts that are actually
//! Vertex-shaped are narrow — which host counts, which credential can list it,
//! and the wire format of the listing call — and everything else (deciding
//! what is priced, what wins against an explicit declaration, what goes in the
//! report) is policy this deployment applies to any upstream that can be
//! asked what it serves.
//!
//! So a [`CatalogSource`] answers only the narrow questions, in a vocabulary
//! that carries no Google in it: given a provider and the credential its
//! secret parsed into, do I apply, and if so what does this upstream say it
//! serves? The next source — an Azure deployment listing, a self-hosted
//! `/v1/models` — is a new implementation and no change to the caller.
//!
//! Copyright (c) systemprompt.io — Business Source License 1.1.
//! See <https://systemprompt.io> for licensing details.
use async_trait;
use ProviderEntry;
use ;
use Error;
/// How far along an upstream considers a model to be.
///
/// Only two states matter to the pricing decision: a model an upstream calls
/// generally available, and everything else, which is served only where the
/// rate card explicitly opts in.
/// One model an upstream says it serves, reduced to what the decision needs.
///
/// `upstream` is how the rate card names it (e.g. `google/gemini-2.5-pro`)
/// and `serverless` is whether it can be called without the operator
/// deploying anything first.
/// What one source returned for one provider.
///
/// Failures travel beside the models rather than instead of them: a publisher
/// we are not entitled to answers 403, and that must not cost us the
/// publishers we are entitled to. Each string is already in the shape
/// [`DiscoveryReport::failed_publishers`](systemprompt_models::services::DiscoveryReport)
/// takes.
/// A listing that could not be attempted at all.
/// An upstream that can be asked which models it serves.
///
/// `name` labels the source in report lines and logs. `matches_provider` is
/// the cheap, credential-free question — could this source ever list this
/// provider? — kept separate from `applies` (the same question with the
/// secret parsed) so the caller knows whether a provider is worth parsing a
/// secret for before it parses one, and so a malformed secret on an unrelated
/// provider is not reported as a discovery failure. `list` asks the upstream
/// what it serves.
///
/// `#[async_trait]` because sources are held as `dyn CatalogSource`.