Skip to main content

hanzo_client/apis/
code_api.rs

1/*
2 * Hanzo Cloud API
3 *
4 * The Hanzo Cloud API as a customer calls it: every operation under /v1/ except the operator's admin product, relay routes, legacy spellings and capabilities still reached by flag. Tagged by product: the first path segment after /v1/.
5 *
6 * The version of the OpenAPI document: v1
7 * 
8 * Generated by: https://openapi-generator.tech
9 */
10
11
12use reqwest;
13use serde::{Deserialize, Serialize, de::Error as _};
14use crate::{apis::ResponseContent, models};
15use super::{Error, configuration, ContentType};
16
17
18/// struct for typed errors of method [`get_code_ask`]
19#[derive(Debug, Clone, Serialize, Deserialize)]
20#[serde(untagged)]
21pub enum GetCodeAskError {
22    UnknownValue(serde_json::Value),
23}
24
25/// struct for typed errors of method [`get_code_file`]
26#[derive(Debug, Clone, Serialize, Deserialize)]
27#[serde(untagged)]
28pub enum GetCodeFileError {
29    UnknownValue(serde_json::Value),
30}
31
32/// struct for typed errors of method [`get_code_search`]
33#[derive(Debug, Clone, Serialize, Deserialize)]
34#[serde(untagged)]
35pub enum GetCodeSearchError {
36    UnknownValue(serde_json::Value),
37}
38
39/// struct for typed errors of method [`get_code_tree`]
40#[derive(Debug, Clone, Serialize, Deserialize)]
41#[serde(untagged)]
42pub enum GetCodeTreeError {
43    UnknownValue(serde_json::Value),
44}
45
46/// struct for typed errors of method [`post_code_ask`]
47#[derive(Debug, Clone, Serialize, Deserialize)]
48#[serde(untagged)]
49pub enum PostCodeAskError {
50    UnknownValue(serde_json::Value),
51}
52
53/// struct for typed errors of method [`post_code_context`]
54#[derive(Debug, Clone, Serialize, Deserialize)]
55#[serde(untagged)]
56pub enum PostCodeContextError {
57    UnknownValue(serde_json::Value),
58}
59
60/// struct for typed errors of method [`post_code_index`]
61#[derive(Debug, Clone, Serialize, Deserialize)]
62#[serde(untagged)]
63pub enum PostCodeIndexError {
64    UnknownValue(serde_json::Value),
65}
66
67
68/// Answers a question about the caller org's code with a CITED answer: retrieval packs grounding context, then the synthesizer writes the answer over exactly those spans, which come back alongside it. It never answers without grounding — with no matched code the answer is empty and says so, and with no synthesizer available the citations still come back with \"degraded\": true so the caller can reason over the spans itself.
69pub async fn get_code_ask(configuration: &configuration::Configuration, q: Option<&str>, repo: Option<&str>) -> Result<models::AskAnswer, Error<GetCodeAskError>> {
70    // add a prefix to parameters to efficiently prevent name collisions
71    let p_q = q;
72    let p_repo = repo;
73
74    let uri_str = format!("{}/v1/code/ask", configuration.base_path);
75    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
76
77    if let Some(ref param_value) = p_q {
78        req_builder = req_builder.query(&[("q", &param_value.to_string())]);
79    }
80    if let Some(ref param_value) = p_repo {
81        req_builder = req_builder.query(&[("repo", &param_value.to_string())]);
82    }
83    if let Some(ref user_agent) = configuration.user_agent {
84        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
85    }
86    if let Some(ref token) = configuration.bearer_access_token {
87        req_builder = req_builder.bearer_auth(token.to_owned());
88    };
89
90    let req = req_builder.build()?;
91    let resp = configuration.client.execute(req).await?;
92
93    let status = resp.status();
94    let content_type = resp
95        .headers()
96        .get("content-type")
97        .and_then(|v| v.to_str().ok())
98        .unwrap_or("application/octet-stream");
99    let content_type = super::ContentType::from(content_type);
100
101    if !status.is_client_error() && !status.is_server_error() {
102        let content = resp.text().await?;
103        match content_type {
104            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
105            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::AskAnswer`"))),
106            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::AskAnswer`")))),
107        }
108    } else {
109        let content = resp.text().await?;
110        let entity: Option<GetCodeAskError> = serde_json::from_str(&content).ok();
111        Err(Error::ResponseError(ResponseContent { status, content, entity }))
112    }
113}
114
115/// Returns the INDEXED content of one file — read_file over the chunks the search tiers hold, for pulling up code an agent just found. It is NOT byte-verbatim: the git object plane is the source of record for exact bytes, history and blame. A file absent from the index is a 404, so an agent can tell \"not indexed\" from \"empty file\".
116pub async fn get_code_file(configuration: &configuration::Configuration, path: Option<&str>, repo: Option<&str>) -> Result<models::FileContent, Error<GetCodeFileError>> {
117    // add a prefix to parameters to efficiently prevent name collisions
118    let p_path = path;
119    let p_repo = repo;
120
121    let uri_str = format!("{}/v1/code/file", configuration.base_path);
122    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
123
124    if let Some(ref param_value) = p_path {
125        req_builder = req_builder.query(&[("path", &param_value.to_string())]);
126    }
127    if let Some(ref param_value) = p_repo {
128        req_builder = req_builder.query(&[("repo", &param_value.to_string())]);
129    }
130    if let Some(ref user_agent) = configuration.user_agent {
131        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
132    }
133    if let Some(ref token) = configuration.bearer_access_token {
134        req_builder = req_builder.bearer_auth(token.to_owned());
135    };
136
137    let req = req_builder.build()?;
138    let resp = configuration.client.execute(req).await?;
139
140    let status = resp.status();
141    let content_type = resp
142        .headers()
143        .get("content-type")
144        .and_then(|v| v.to_str().ok())
145        .unwrap_or("application/octet-stream");
146    let content_type = super::ContentType::from(content_type);
147
148    if !status.is_client_error() && !status.is_server_error() {
149        let content = resp.text().await?;
150        match content_type {
151            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
152            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::FileContent`"))),
153            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::FileContent`")))),
154        }
155    } else {
156        let content = resp.text().await?;
157        let entity: Option<GetCodeFileError> = serde_json::from_str(&content).ok();
158        Err(Error::ResponseError(ResponseContent { status, content, entity }))
159    }
160}
161
162/// Finds code in the caller org's index across three orthogonal retrieval tiers fused by reciprocal-rank fusion: lexical (FTS5 trigram over code-tokenized text), symbolic (real definition and reference edges), and semantic (embedding cosine over AST-boundary chunks). Pick one tier with `type`, or leave it to run all three as hybrid, which is what a coding agent usually wants. It is FAIL-HONEST: a retrieval outage answers 200 with an empty result set and \"degraded\": true rather than a 5xx, so an agent degrades instead of stalling. A malformed regex is a 400.
163pub async fn get_code_search(configuration: &configuration::Configuration, q: Option<&str>, r#type: Option<&str>, repo: Option<&str>, limit: Option<i32>) -> Result<models::SearchResults, Error<GetCodeSearchError>> {
164    // add a prefix to parameters to efficiently prevent name collisions
165    let p_q = q;
166    let p_type = r#type;
167    let p_repo = repo;
168    let p_limit = limit;
169
170    let uri_str = format!("{}/v1/code/search", configuration.base_path);
171    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
172
173    if let Some(ref param_value) = p_q {
174        req_builder = req_builder.query(&[("q", &param_value.to_string())]);
175    }
176    if let Some(ref param_value) = p_type {
177        req_builder = req_builder.query(&[("type", &param_value.to_string())]);
178    }
179    if let Some(ref param_value) = p_repo {
180        req_builder = req_builder.query(&[("repo", &param_value.to_string())]);
181    }
182    if let Some(ref param_value) = p_limit {
183        req_builder = req_builder.query(&[("limit", &param_value.to_string())]);
184    }
185    if let Some(ref user_agent) = configuration.user_agent {
186        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
187    }
188    if let Some(ref token) = configuration.bearer_access_token {
189        req_builder = req_builder.bearer_auth(token.to_owned());
190    };
191
192    let req = req_builder.build()?;
193    let resp = configuration.client.execute(req).await?;
194
195    let status = resp.status();
196    let content_type = resp
197        .headers()
198        .get("content-type")
199        .and_then(|v| v.to_str().ok())
200        .unwrap_or("application/octet-stream");
201    let content_type = super::ContentType::from(content_type);
202
203    if !status.is_client_error() && !status.is_server_error() {
204        let content = resp.text().await?;
205        match content_type {
206            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
207            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::SearchResults`"))),
208            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::SearchResults`")))),
209        }
210    } else {
211        let content = resp.text().await?;
212        let entity: Option<GetCodeSearchError> = serde_json::from_str(&content).ok();
213        Err(Error::ResponseError(ResponseContent { status, content, entity }))
214    }
215}
216
217/// Returns one repository's file structure with a per-file symbol count — get_repo_structure over the org's own index, with no git checkout involved. A repository that has not been indexed answers an empty tree rather than an error, so an agent can tell \"nothing here\" without handling a failure.
218pub async fn get_code_tree(configuration: &configuration::Configuration, repo: Option<&str>) -> Result<models::RepoTree, Error<GetCodeTreeError>> {
219    // add a prefix to parameters to efficiently prevent name collisions
220    let p_repo = repo;
221
222    let uri_str = format!("{}/v1/code/tree", configuration.base_path);
223    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
224
225    if let Some(ref param_value) = p_repo {
226        req_builder = req_builder.query(&[("repo", &param_value.to_string())]);
227    }
228    if let Some(ref user_agent) = configuration.user_agent {
229        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
230    }
231    if let Some(ref token) = configuration.bearer_access_token {
232        req_builder = req_builder.bearer_auth(token.to_owned());
233    };
234
235    let req = req_builder.build()?;
236    let resp = configuration.client.execute(req).await?;
237
238    let status = resp.status();
239    let content_type = resp
240        .headers()
241        .get("content-type")
242        .and_then(|v| v.to_str().ok())
243        .unwrap_or("application/octet-stream");
244    let content_type = super::ContentType::from(content_type);
245
246    if !status.is_client_error() && !status.is_server_error() {
247        let content = resp.text().await?;
248        match content_type {
249            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
250            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::RepoTree`"))),
251            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::RepoTree`")))),
252        }
253    } else {
254        let content = resp.text().await?;
255        let entity: Option<GetCodeTreeError> = serde_json::from_str(&content).ok();
256        Err(Error::ResponseError(ResponseContent { status, content, entity }))
257    }
258}
259
260/// Is askGet with the question in the request BODY, for a question too long or too awkward to put in a URL. `query` and `repo` in the body take precedence over `?q=` and `?repo=`; either source works alone.
261pub async fn post_code_ask(configuration: &configuration::Configuration, ask_post_in: models::AskPostIn) -> Result<models::AskAnswer, Error<PostCodeAskError>> {
262    // add a prefix to parameters to efficiently prevent name collisions
263    let p_ask_post_in = ask_post_in;
264
265    let uri_str = format!("{}/v1/code/ask", configuration.base_path);
266    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
267
268    if let Some(ref user_agent) = configuration.user_agent {
269        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
270    }
271    if let Some(ref token) = configuration.bearer_access_token {
272        req_builder = req_builder.bearer_auth(token.to_owned());
273    };
274    req_builder = req_builder.json(&p_ask_post_in);
275
276    let req = req_builder.build()?;
277    let resp = configuration.client.execute(req).await?;
278
279    let status = resp.status();
280    let content_type = resp
281        .headers()
282        .get("content-type")
283        .and_then(|v| v.to_str().ok())
284        .unwrap_or("application/octet-stream");
285    let content_type = super::ContentType::from(content_type);
286
287    if !status.is_client_error() && !status.is_server_error() {
288        let content = resp.text().await?;
289        match content_type {
290            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
291            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::AskAnswer`"))),
292            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::AskAnswer`")))),
293        }
294    } else {
295        let content = resp.text().await?;
296        let entity: Option<PostCodeAskError> = serde_json::from_str(&content).ok();
297        Err(Error::ResponseError(ResponseContent { status, content, entity }))
298    }
299}
300
301/// Packs the most relevant code for a query into a token budget — THE primitive for a coding agent that has to decide what to put in a prompt. It retrieves seed spans, expands each with the definitions it calls and its key callers, then greedily fills the budget, so the answer is a coherent slice of the codebase rather than a list of disconnected matches. The top match is always included, truncated if it alone overflows, so a matched query never comes back empty. A retrieval outage answers 200 with an empty bundle rather than a 5xx.
302pub async fn post_code_context(configuration: &configuration::Configuration, context_in: models::ContextIn) -> Result<models::ContextBundle, Error<PostCodeContextError>> {
303    // add a prefix to parameters to efficiently prevent name collisions
304    let p_context_in = context_in;
305
306    let uri_str = format!("{}/v1/code/context", configuration.base_path);
307    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
308
309    if let Some(ref user_agent) = configuration.user_agent {
310        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
311    }
312    if let Some(ref token) = configuration.bearer_access_token {
313        req_builder = req_builder.bearer_auth(token.to_owned());
314    };
315    req_builder = req_builder.json(&p_context_in);
316
317    let req = req_builder.build()?;
318    let resp = configuration.client.execute(req).await?;
319
320    let status = resp.status();
321    let content_type = resp
322        .headers()
323        .get("content-type")
324        .and_then(|v| v.to_str().ok())
325        .unwrap_or("application/octet-stream");
326    let content_type = super::ContentType::from(content_type);
327
328    if !status.is_client_error() && !status.is_server_error() {
329        let content = resp.text().await?;
330        match content_type {
331            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
332            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::ContextBundle`"))),
333            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::ContextBundle`")))),
334        }
335    } else {
336        let content = resp.text().await?;
337        let entity: Option<PostCodeContextError> = serde_json::from_str(&content).ok();
338        Err(Error::ResponseError(ResponseContent { status, content, entity }))
339    }
340}
341
342/// (re)indexes a repository for the caller's org, incrementally: files whose content hash is unchanged are skipped, so re-sending a whole tree is cheap. Each file is parsed for symbols, split at AST boundaries and — when the semantic tier is available — embedded, which is what makes it searchable across all three retrieval tiers. Pass `prune` to also DELETE indexed files absent from the request, which turns the call into a full sync; without it the call is an upsert. The index is written to the caller org's own physically separate database.
343pub async fn post_code_index(configuration: &configuration::Configuration, index_in: models::IndexIn) -> Result<models::IndexResult, Error<PostCodeIndexError>> {
344    // add a prefix to parameters to efficiently prevent name collisions
345    let p_index_in = index_in;
346
347    let uri_str = format!("{}/v1/code/index", configuration.base_path);
348    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
349
350    if let Some(ref user_agent) = configuration.user_agent {
351        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
352    }
353    if let Some(ref token) = configuration.bearer_access_token {
354        req_builder = req_builder.bearer_auth(token.to_owned());
355    };
356    req_builder = req_builder.json(&p_index_in);
357
358    let req = req_builder.build()?;
359    let resp = configuration.client.execute(req).await?;
360
361    let status = resp.status();
362    let content_type = resp
363        .headers()
364        .get("content-type")
365        .and_then(|v| v.to_str().ok())
366        .unwrap_or("application/octet-stream");
367    let content_type = super::ContentType::from(content_type);
368
369    if !status.is_client_error() && !status.is_server_error() {
370        let content = resp.text().await?;
371        match content_type {
372            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
373            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::IndexResult`"))),
374            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::IndexResult`")))),
375        }
376    } else {
377        let content = resp.text().await?;
378        let entity: Option<PostCodeIndexError> = serde_json::from_str(&content).ok();
379        Err(Error::ResponseError(ResponseContent { status, content, entity }))
380    }
381}
382