1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3use std::path::PathBuf;
4
5pub const QAPP_PACKAGE_MANIFEST: &str = "qapp.json";
6pub const QAPPS_DIR: &str = "Qapps";
7
8#[derive(Serialize, Deserialize, Clone, Debug)]
9pub enum QappTarget {
10 LocalDevDirectory(PathBuf),
11 LocalProxyPort(u16),
12 IsolatedVault(String),
13}
14
15#[derive(Serialize, Deserialize, Clone, Debug, Default)]
16pub struct QappLaunchRequirement {
17 pub capability: String,
18 #[serde(default)]
19 pub required: bool,
20}
21
22#[derive(Serialize, Deserialize, Clone, Debug, Default)]
23pub struct QappWasmConfig {
24 #[serde(default, skip_serializing_if = "String::is_empty")]
25 pub engine_package: String,
26 #[serde(default, skip_serializing_if = "Vec::is_empty")]
27 pub expected_exports: Vec<String>,
28}
29
30#[derive(Serialize, Deserialize, Clone, Debug, Default)]
31pub struct QappDaemonConfig {
32 #[serde(default, skip_serializing_if = "String::is_empty")]
33 pub health_path: String,
34 #[serde(default, skip_serializing_if = "String::is_empty")]
35 pub query_path: String,
36 #[serde(default, skip_serializing_if = "String::is_empty")]
37 pub websocket_path: String,
38 #[serde(default)]
39 pub requires_token: bool,
40}
41
42#[derive(Serialize, Deserialize, Clone, Debug, Default)]
43pub struct QappChatIntegrationConfig {
44 #[serde(default, skip_serializing_if = "String::is_empty")]
45 pub intent: String,
46 #[serde(default, skip_serializing_if = "String::is_empty")]
47 pub payload_version: String,
48 #[serde(default)]
49 pub supports_launch_from_chat: bool,
50 #[serde(default)]
51 pub supports_return_to_chat: bool,
52}
53
54#[derive(Serialize, Deserialize, Clone, Debug, Default)]
55pub struct QappRepresentationContract {
56 #[serde(default, skip_serializing_if = "Vec::is_empty")]
57 pub accepts: Vec<String>,
58}
59
60#[derive(Serialize, Deserialize, Clone, Debug, Default)]
62pub struct QappPinnModelConfig {
63 #[serde(default, skip_serializing_if = "String::is_empty")]
64 pub model_name: String,
65 #[serde(default, skip_serializing_if = "String::is_empty")]
66 pub model_path: String,
67 #[serde(default, skip_serializing_if = "String::is_empty")]
68 pub model_type: String,
69 #[serde(default, skip_serializing_if = "String::is_empty")]
71 pub quantization_bits: String,
72 #[serde(default)]
74 pub compression_ratio: f32,
75 #[serde(default, skip_serializing_if = "String::is_empty")]
77 pub smx_version: String,
78 #[serde(default, skip_serializing_if = "String::is_empty")]
80 pub physics_domain: String,
81 #[serde(default)]
83 pub input_dim: usize,
84 #[serde(default)]
86 pub output_dim: usize,
87 #[serde(default)]
89 pub uses_ternary_quantization: bool,
90 #[serde(default)]
92 pub memory_requirement_mb: u32,
93 #[serde(default)]
95 pub vram_requirement_mb: Option<u32>,
96 #[serde(default, skip_serializing_if = "Vec::is_empty")]
98 pub supported_operations: Vec<String>,
99 #[serde(default, skip_serializing_if = "Option::is_none")]
101 pub training_metadata: Option<QappPinnTrainingMetadata>,
102 #[serde(default, skip_serializing_if = "Option::is_none")]
104 pub performance_metrics: Option<QappPinnPerformanceMetrics>,
105}
106
107#[derive(Serialize, Deserialize, Clone, Debug, Default)]
109pub struct QappPinnTrainingMetadata {
110 #[serde(default)]
111 pub epochs: u32,
112 #[serde(default)]
113 pub final_loss: f64,
114 #[serde(default)]
115 pub convergence_rate: f64,
116 #[serde(default)]
117 pub validation_accuracy: f64,
118 #[serde(default, skip_serializing_if = "String::is_empty")]
119 pub training_dataset: String,
120 #[serde(default, skip_serializing_if = "String::is_empty")]
121 pub training_date: String,
122}
123
124#[derive(Serialize, Deserialize, Clone, Debug, Default)]
126pub struct QappPinnPerformanceMetrics {
127 #[serde(default)]
129 pub inference_speedup: f32,
130 #[serde(default)]
132 pub memory_savings: f32,
133 #[serde(default)]
135 pub quantization_error: f64,
136 #[serde(default)]
138 pub sparsity_ratio: f32,
139 #[serde(default)]
141 pub avg_inference_time_ms: f32,
142 #[serde(default)]
144 pub throughput_ops_per_sec: f64,
145}
146
147#[derive(Serialize, Deserialize, Clone, Debug, Default)]
148pub struct QappHostExtension {
149 #[serde(default, skip_serializing_if = "String::is_empty")]
150 pub app_id: String,
151 #[serde(default, skip_serializing_if = "String::is_empty")]
152 pub display_name: String,
153 #[serde(default, skip_serializing_if = "String::is_empty")]
154 pub category: String,
155 #[serde(default, skip_serializing_if = "Vec::is_empty")]
156 pub launch_modes: Vec<String>,
157 #[serde(default, skip_serializing_if = "String::is_empty")]
158 pub preferred_launch_mode: String,
159 #[serde(default)]
160 pub supports_offline: bool,
161 #[serde(default, skip_serializing_if = "Vec::is_empty")]
162 pub requires: Vec<QappLaunchRequirement>,
163 #[serde(default, skip_serializing_if = "Vec::is_empty")]
164 pub required_ontologies: Vec<String>,
165 #[serde(default, skip_serializing_if = "Vec::is_empty")]
166 pub optional_remote_endpoints: Vec<String>,
167 #[serde(default, skip_serializing_if = "String::is_empty")]
168 pub max_sensitivity_clearance: String,
169 #[serde(default, skip_serializing_if = "Vec::is_empty")]
170 pub required_models: Vec<String>,
171 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
172 pub entrypoints: HashMap<String, String>,
173 #[serde(default, skip_serializing_if = "Option::is_none")]
174 pub wasm: Option<QappWasmConfig>,
175 #[serde(default, skip_serializing_if = "Option::is_none")]
176 pub local_daemon: Option<QappDaemonConfig>,
177 #[serde(default, skip_serializing_if = "Option::is_none")]
178 pub chat_integration: Option<QappChatIntegrationConfig>,
179 #[serde(default, skip_serializing_if = "Option::is_none")]
180 pub representation_contract: Option<QappRepresentationContract>,
181 #[serde(default, skip_serializing_if = "Option::is_none")]
182 pub pinn_model: Option<QappPinnModelConfig>,
183 #[serde(default, skip_serializing_if = "Vec::is_empty")]
184 pub ui_surfaces: Vec<String>,
185 #[serde(default, skip_serializing_if = "Vec::is_empty")]
186 pub notes: Vec<String>,
187 #[serde(default, skip_serializing_if = "Vec::is_empty")]
189 pub pages: Vec<QappPage>,
190 #[serde(default, skip_serializing_if = "Vec::is_empty")]
192 pub export_targets: Vec<String>,
193 #[serde(default, skip_serializing_if = "Option::is_none")]
195 pub did_git: Option<String>,
196}
197
198#[derive(Serialize, Deserialize, Clone, Debug, Default)]
200pub struct QappPage {
201 pub name: String,
202 pub path: String,
203 #[serde(default, skip_serializing_if = "String::is_empty")]
204 pub title: String,
205 #[serde(default, skip_serializing_if = "String::is_empty")]
206 pub icon: String,
207}
208
209#[derive(Serialize, Deserialize, Clone, Debug)]
211pub struct QappPackageManifest {
212 pub name: String,
213 pub version: String,
214 pub required_shapes: Vec<String>,
215 #[serde(default, skip_serializing_if = "Option::is_none")]
216 pub dev_port: Option<u16>,
217 #[serde(default, skip_serializing_if = "Option::is_none")]
218 pub x_qualia: Option<QappHostExtension>,
219}
220
221#[derive(Serialize, Deserialize, Clone, Debug)]
222pub struct RegisteredQapp {
223 pub did: String,
224 pub manifest: QappPackageManifest,
225 pub target: QappTarget,
226}
227
228#[derive(Serialize, Deserialize, Clone, Default, Debug)]
229pub struct QappRegistryState {
230 pub installed_qapps: HashMap<String, RegisteredQapp>,
231}
232
233impl QappRegistryState {
234 pub fn new() -> Self {
235 Self {
236 installed_qapps: HashMap::new(),
237 }
238 }
239}