Skip to main content

hanzo_client/apis/
ask_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 [`post_ask`]
19#[derive(Debug, Clone, Serialize, Deserialize)]
20#[serde(untagged)]
21pub enum PostAskError {
22    UnknownValue(serde_json::Value),
23}
24
25/// struct for typed errors of method [`research_web`]
26#[derive(Debug, Clone, Serialize, Deserialize)]
27#[serde(untagged)]
28pub enum ResearchWebError {
29    UnknownValue(serde_json::Value),
30}
31
32
33/// Answers a natural-language question about the CALLER'S OWN org, from real figures rather than from the model's memory.  The question is classified to a grounded domain, that domain's read runs IN-PROCESS under the caller's own credentials, and only then is the result narrated. So the figures and their sources are the domain's, resolved before any model call and never altered by one — a wrong answer is a wrong query, never an invention.  Domains: books (the org's ledger), projects (what is built and what of it is deployed), git (the org's repositories and what changed in them), and web (search, news, research, deep). A validated principal is required; the answer is scoped to that principal's org and nothing else.
34pub async fn post_ask(configuration: &configuration::Configuration, ask_request: Option<models::AskRequest>) -> Result<(), Error<PostAskError>> {
35    // add a prefix to parameters to efficiently prevent name collisions
36    let p_ask_request = ask_request;
37
38    let uri_str = format!("{}/v1/ask", configuration.base_path);
39    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
40
41    if let Some(ref user_agent) = configuration.user_agent {
42        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
43    }
44    if let Some(ref token) = configuration.bearer_access_token {
45        req_builder = req_builder.bearer_auth(token.to_owned());
46    };
47    req_builder = req_builder.json(&p_ask_request);
48
49    let req = req_builder.build()?;
50    let resp = configuration.client.execute(req).await?;
51
52    let status = resp.status();
53
54    if !status.is_client_error() && !status.is_server_error() {
55        Ok(())
56    } else {
57        let content = resp.text().await?;
58        let entity: Option<PostAskError> = serde_json::from_str(&content).ok();
59        Err(Error::ResponseError(ResponseContent { status, content, entity }))
60    }
61}
62
63/// Researches a question on the live web and answers it with its sources cited.  This is the DEEP one. It plans the question into topics, runs several web searches, FETCHES AND READS the pages it finds, ranks them, and writes a grounded answer with inline markdown citations. Use it for anything that needs evidence, comparison or current fact — \"what changed in X\", \"compare A and B\", \"is this claim true\". For a plain list of links, use search_web instead; for one page you already have the URL of, use read_page.  `mode` buys depth: `search` is a single fast pass, `news` biases to recency, `research` plans and iterates, `deep` surveys widest. `sources` narrows the evidence to `web`, `news`, `academic`, `github`, `reddit` or `x` — each becomes a site-scoped search, which is how this reaches X/Twitter posts.  EVERY CITATION IS A PAGE THIS CALL FETCHED. That is a property of the text and not an instruction to the model: each source is fenced with a per-request nonce so a crawled page cannot print itself a source number, and every markdown link in the answer is checked against the gathered set before it is returned. So a link in `answer` always appears in `sources`, and a page that was not read cannot be cited.  It is BOUNDED and it degrades rather than failing: a mode's rounds, wall clock and token ceiling all cap it, and a search that finds little or a page that will not load yields a thinner answer, never an error. A validated principal is required, and the answer is billed once to that principal's org.
64pub async fn research_web(configuration: &configuration::Configuration, web_question: models::WebQuestion) -> Result<models::Report, Error<ResearchWebError>> {
65    // add a prefix to parameters to efficiently prevent name collisions
66    let p_web_question = web_question;
67
68    let uri_str = format!("{}/v1/ask/web", configuration.base_path);
69    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
70
71    if let Some(ref user_agent) = configuration.user_agent {
72        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
73    }
74    if let Some(ref token) = configuration.bearer_access_token {
75        req_builder = req_builder.bearer_auth(token.to_owned());
76    };
77    req_builder = req_builder.json(&p_web_question);
78
79    let req = req_builder.build()?;
80    let resp = configuration.client.execute(req).await?;
81
82    let status = resp.status();
83    let content_type = resp
84        .headers()
85        .get("content-type")
86        .and_then(|v| v.to_str().ok())
87        .unwrap_or("application/octet-stream");
88    let content_type = super::ContentType::from(content_type);
89
90    if !status.is_client_error() && !status.is_server_error() {
91        let content = resp.text().await?;
92        match content_type {
93            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
94            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::Report`"))),
95            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::Report`")))),
96        }
97    } else {
98        let content = resp.text().await?;
99        let entity: Option<ResearchWebError> = serde_json::from_str(&content).ok();
100        Err(Error::ResponseError(ResponseContent { status, content, entity }))
101    }
102}
103