Skip to main content

context69_contracts/
search.rs

1use chrono::{DateTime, NaiveDate, Utc};
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use utoipa::ToSchema;
6use uuid::Uuid;
7
8use crate::Visibility;
9
10#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, JsonSchema)]
11pub struct SearchRequest {
12    pub query: String,
13    #[serde(default = "default_limit")]
14    pub limit: usize,
15    #[serde(default)]
16    pub source_key: Option<String>,
17    #[serde(default)]
18    pub group_path: Option<String>,
19    #[serde(default)]
20    pub published_after: Option<NaiveDate>,
21    #[serde(default)]
22    pub published_before: Option<NaiveDate>,
23}
24
25fn default_limit() -> usize {
26    8
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
30#[serde(rename_all = "snake_case")]
31pub enum SearchMode {
32    Vector,
33    Hybrid,
34}
35
36impl SearchMode {
37    pub fn as_str(self) -> &'static str {
38        match self {
39            Self::Vector => "vector",
40            Self::Hybrid => "hybrid",
41        }
42    }
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, JsonSchema)]
46pub struct SearchHit {
47    pub chunk_id: Uuid,
48    pub document_id: i64,
49    pub group_key: String,
50    pub group_path: String,
51    pub visibility: Visibility,
52    pub source_key: String,
53    pub external_id: String,
54    pub title: String,
55    pub summary: Option<String>,
56    pub source_uri: String,
57    pub published_at: Option<NaiveDate>,
58    pub chunk_index: i32,
59    pub chunk_text: String,
60    pub score: f32,
61    #[serde(default, skip_serializing_if = "Option::is_none")]
62    pub vector_score: Option<f32>,
63    #[serde(default, skip_serializing_if = "Option::is_none")]
64    pub keyword_score: Option<f32>,
65    #[serde(default, skip_serializing_if = "Option::is_none")]
66    pub rerank_score: Option<f32>,
67    #[serde(default, skip_serializing_if = "Option::is_none")]
68    pub match_reason: Option<String>,
69    #[serde(default)]
70    #[schema(value_type = Object)]
71    pub metadata_json: Value,
72    #[serde(default, skip_serializing_if = "Option::is_none")]
73    pub library_file_id: Option<Uuid>,
74    #[serde(default, skip_serializing_if = "Option::is_none")]
75    pub library_section_label: Option<String>,
76    #[serde(default, skip_serializing_if = "Option::is_none")]
77    pub library_path: Option<String>,
78    #[serde(default)]
79    pub is_library_file: bool,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, JsonSchema)]
83pub struct SearchResponse {
84    pub query: String,
85    pub hits: Vec<SearchHit>,
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, JsonSchema)]
89pub struct DocumentResponse {
90    pub document_id: i64,
91    pub group_key: String,
92    pub group_path: String,
93    pub visibility: Visibility,
94    pub source_key: String,
95    pub external_id: String,
96    pub title: String,
97    pub summary: Option<String>,
98    pub source_uri: String,
99    pub published_at: Option<NaiveDate>,
100    pub updated_at: DateTime<Utc>,
101    pub record_hash: String,
102    #[serde(default)]
103    #[schema(value_type = Object)]
104    pub metadata_json: Value,
105    #[serde(default, skip_serializing_if = "Option::is_none")]
106    pub library_file_id: Option<Uuid>,
107    #[serde(default, skip_serializing_if = "Option::is_none")]
108    pub library_section_label: Option<String>,
109    #[serde(default, skip_serializing_if = "Option::is_none")]
110    pub library_path: Option<String>,
111    #[serde(default)]
112    pub is_library_file: bool,
113    pub chunks: Vec<DocumentChunkResponse>,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, JsonSchema)]
117pub struct DocumentChunkResponse {
118    pub chunk_id: Uuid,
119    pub chunk_index: i32,
120    pub text: String,
121}