crw_server/routes/
capabilities.rs1use axum::Json;
9use axum::extract::State;
10use serde::Serialize;
11
12use crate::state::AppState;
13
14#[derive(Debug, Serialize)]
15#[serde(rename_all = "camelCase")]
16pub struct Capabilities {
17 pub version: &'static str,
18 pub llm: LlmCapabilities,
19 pub formats: FormatCapabilities,
20 pub search: SearchCapabilities,
21 pub documents: DocumentCapabilities,
22}
23
24#[derive(Debug, Serialize)]
25#[serde(rename_all = "camelCase")]
26pub struct DocumentCapabilities {
27 pub parsers: Vec<&'static str>,
31 pub file_upload: FileUploadCapabilities,
33}
34
35#[derive(Debug, Serialize)]
36#[serde(rename_all = "camelCase")]
37pub struct FileUploadCapabilities {
38 pub supported: bool,
39 pub endpoint: &'static str,
40 pub max_bytes: usize,
41 pub types: Vec<&'static str>,
42 pub ocr: bool,
44}
45
46#[derive(Debug, Serialize)]
47#[serde(rename_all = "camelCase")]
48pub struct LlmCapabilities {
49 pub providers: Vec<&'static str>,
51 pub supports_base_url: bool,
52 pub server_key_configured: bool,
56 pub max_concurrency: usize,
59 #[serde(skip_serializing_if = "Option::is_none")]
62 pub require_byok_header: Option<String>,
63}
64
65#[derive(Debug, Serialize)]
66#[serde(rename_all = "camelCase")]
67pub struct FormatCapabilities {
68 pub supported: Vec<&'static str>,
69 pub change_tracking_modes: Vec<&'static str>,
73}
74
75#[derive(Debug, Serialize)]
76#[serde(rename_all = "camelCase")]
77pub struct SearchCapabilities {
78 pub answer: bool,
79 pub summarize_results: bool,
80}
81
82pub async fn capabilities(State(state): State<AppState>) -> Json<Capabilities> {
83 let llm_cfg = state.config.extraction.llm.as_ref();
84 Json(Capabilities {
85 version: env!("CARGO_PKG_VERSION"),
86 llm: LlmCapabilities {
87 providers: vec![
88 "anthropic",
89 "openai",
90 "deepseek",
91 "openai-compatible",
92 "azure",
93 ],
94 supports_base_url: true,
95 server_key_configured: llm_cfg.map(|c| !c.api_key.is_empty()).unwrap_or(false),
96 max_concurrency: llm_cfg.map(|c| c.max_concurrency).unwrap_or(0),
97 require_byok_header: llm_cfg.and_then(|c| c.require_byok_header.clone()),
98 },
99 formats: FormatCapabilities {
100 supported: vec![
101 "markdown",
102 "html",
103 "rawHtml",
104 "plainText",
105 "links",
106 "json",
107 "summary",
108 "changeTracking",
109 ],
110 change_tracking_modes: vec!["gitDiff", "json"],
111 },
112 search: SearchCapabilities {
113 answer: true,
114 summarize_results: true,
115 },
116 documents: {
117 let pdf_on = crw_extract::pdf::PDF_SUPPORTED && state.config.document.enabled;
118 DocumentCapabilities {
119 parsers: if pdf_on { vec!["pdf"] } else { vec![] },
120 file_upload: FileUploadCapabilities {
121 supported: pdf_on,
122 endpoint: "/v2/parse",
123 max_bytes: state.config.document.max_upload_bytes,
124 types: if pdf_on {
125 vec!["application/pdf"]
126 } else {
127 vec![]
128 },
129 ocr: false,
130 },
131 }
132 },
133 })
134}