1use axum::{
9 extract::{Path, State},
10 http::StatusCode,
11 response::Json,
12 routing::post,
13 Router,
14};
15use mockforge_core::{
16 ai_studio::{
17 api_critique::{ApiCritiqueEngine, CritiqueRequest},
18 artifact_freezer::{ArtifactFreezer, FreezeMetadata, FreezeRequest},
19 behavioral_simulator::{BehavioralSimulator, CreateAgentRequest, SimulateBehaviorRequest},
20 config::DeterministicModeConfig,
21 system_generator::{SystemGenerationRequest, SystemGenerator},
22 },
23 intelligent_behavior::IntelligentBehaviorConfig,
24};
25use serde::{Deserialize, Serialize};
26use std::collections::HashMap;
27use std::sync::Arc;
28use tokio::sync::{Mutex, RwLock};
29use tracing::{error, info, warn};
30
31#[derive(Clone)]
33pub struct AiStudioState {
34 pub critique_engine: Arc<ApiCritiqueEngine>,
36 pub system_generator: Arc<SystemGenerator>,
38 pub behavioral_simulator: Arc<Mutex<BehavioralSimulator>>,
40 pub artifact_freezer: Arc<ArtifactFreezer>,
42 pub config: IntelligentBehaviorConfig,
44 pub deterministic_config: Option<DeterministicModeConfig>,
46 pub workspace_id: Option<String>,
48 pub system_storage: Arc<RwLock<HashMap<String, mockforge_core::ai_studio::system_generator::GeneratedSystem>>>,
50}
51
52impl AiStudioState {
53 pub fn new(config: IntelligentBehaviorConfig) -> Self {
55 let critique_engine = Arc::new(ApiCritiqueEngine::new(config.clone()));
56 let system_generator = Arc::new(SystemGenerator::new(config.clone()));
57 let behavioral_simulator = Arc::new(Mutex::new(BehavioralSimulator::new(config.clone())));
58 let artifact_freezer = Arc::new(ArtifactFreezer::new());
59 Self {
60 critique_engine,
61 system_generator,
62 behavioral_simulator,
63 artifact_freezer,
64 config,
65 deterministic_config: None,
66 workspace_id: None,
67 system_storage: Arc::new(RwLock::new(HashMap::new())),
68 }
69 }
70
71 pub fn with_workspace(
73 mut self,
74 workspace_id: String,
75 deterministic_config: Option<DeterministicModeConfig>,
76 ) -> Self {
77 self.workspace_id = Some(workspace_id);
78 self.deterministic_config = deterministic_config;
79 self
80 }
81}
82
83#[derive(Debug, Deserialize, Serialize)]
85pub struct ApiCritiqueRequest {
86 pub schema: serde_json::Value,
88
89 pub schema_type: String,
91
92 #[serde(default)]
95 pub focus_areas: Vec<String>,
96
97 pub workspace_id: Option<String>,
99}
100
101#[derive(Debug, Serialize)]
103pub struct ApiCritiqueResponse {
104 pub critique: mockforge_core::ai_studio::api_critique::ApiCritique,
106
107 #[serde(skip_serializing_if = "Option::is_none")]
109 pub artifact_id: Option<String>,
110
111 pub frozen: bool,
113}
114
115pub async fn api_critique_handler(
119 State(state): State<AiStudioState>,
120 Json(request): Json<ApiCritiqueRequest>,
121) -> std::result::Result<Json<ApiCritiqueResponse>, StatusCode> {
122 info!("API critique request received for schema type: {}", request.schema_type);
123
124 let critique_request = CritiqueRequest {
126 schema: request.schema,
127 schema_type: request.schema_type,
128 focus_areas: request.focus_areas,
129 workspace_id: request.workspace_id.or_else(|| state.workspace_id.clone()),
130 };
131
132 let critique = match state.critique_engine.analyze(&critique_request).await {
134 Ok(c) => c,
135 Err(e) => {
136 error!("Failed to generate API critique: {}", e);
137 return Err(StatusCode::INTERNAL_SERVER_ERROR);
138 }
139 };
140
141 let critique_json = match serde_json::to_value(&critique) {
143 Ok(v) => v,
144 Err(e) => {
145 error!("Failed to serialize critique: {}", e);
146 return Err(StatusCode::INTERNAL_SERVER_ERROR);
147 }
148 };
149
150 let freeze_request = FreezeRequest {
152 artifact_type: "api_critique".to_string(),
153 content: critique_json,
154 format: "json".to_string(),
155 path: None,
156 metadata: Some(FreezeMetadata {
157 llm_provider: Some(state.config.behavior_model.llm_provider.clone()),
158 llm_model: Some(state.config.behavior_model.model.clone()),
159 llm_version: None,
160 prompt_hash: None,
161 output_hash: None,
162 original_prompt: None,
163 }),
164 };
165
166 let frozen_artifact = match state.artifact_freezer.freeze(&freeze_request).await {
167 Ok(a) => a,
168 Err(e) => {
169 error!("Failed to freeze critique artifact: {}", e);
170 return Err(StatusCode::INTERNAL_SERVER_ERROR);
171 }
172 };
173
174 info!(
175 "API critique completed. Artifact path: {}, Tokens used: {:?}, Cost: ${:.4}",
176 frozen_artifact.path,
177 critique.tokens_used,
178 critique.cost_usd.unwrap_or(0.0)
179 );
180
181 Ok(Json(ApiCritiqueResponse {
182 critique,
183 artifact_id: Some(frozen_artifact.path),
184 frozen: true,
185 }))
186}
187
188#[derive(Debug, Deserialize)]
190pub struct SystemGenerationHttpRequest {
191 pub description: String,
193
194 #[serde(default)]
196 pub output_formats: Vec<String>,
197
198 pub workspace_id: Option<String>,
200
201 pub system_id: Option<String>,
203}
204
205#[derive(Debug, Serialize)]
207pub struct SystemGenerationResponse {
208 pub system: mockforge_core::ai_studio::system_generator::GeneratedSystem,
210}
211
212#[derive(Debug, Deserialize)]
214pub struct ApplySystemRequest {
215 #[serde(default)]
217 pub artifact_ids: Option<Vec<String>>,
218}
219
220#[derive(Debug, Serialize)]
222pub struct ApplySystemResponse {
223 pub applied: mockforge_core::ai_studio::system_generator::AppliedSystem,
225}
226
227#[derive(Debug, Deserialize)]
229pub struct FreezeArtifactsRequest {
230 pub artifact_ids: Vec<String>,
232}
233
234#[derive(Debug, Serialize)]
236pub struct FreezeArtifactsResponse {
237 pub frozen_paths: Vec<String>,
239}
240
241pub async fn generate_system_handler(
245 State(state): State<AiStudioState>,
246 Json(request): Json<SystemGenerationHttpRequest>,
247) -> std::result::Result<Json<SystemGenerationResponse>, StatusCode> {
248 info!("System generation request received");
249
250 let generation_request = SystemGenerationRequest {
251 description: request.description,
252 output_formats: request.output_formats,
253 workspace_id: request.workspace_id.or_else(|| state.workspace_id.clone()),
254 system_id: request.system_id,
255 };
256
257 let system = match state
258 .system_generator
259 .generate(&generation_request, state.deterministic_config.as_ref())
260 .await
261 {
262 Ok(s) => s,
263 Err(e) => {
264 error!("Failed to generate system: {}", e);
265 return Err(StatusCode::INTERNAL_SERVER_ERROR);
266 }
267 };
268
269 {
271 let mut storage = state.system_storage.write().await;
272 storage.insert(system.system_id.clone(), system.clone());
273 }
274
275 info!(
276 "System generation completed. System ID: {}, Version: {}, Status: {}, Tokens: {:?}, Cost: ${:.4}",
277 system.system_id,
278 system.version,
279 system.status,
280 system.tokens_used,
281 system.cost_usd.unwrap_or(0.0)
282 );
283
284 Ok(Json(SystemGenerationResponse { system }))
285}
286
287pub async fn apply_system_handler(
291 State(state): State<AiStudioState>,
292 Path(system_id): Path<String>,
293 Json(request): Json<ApplySystemRequest>,
294) -> std::result::Result<Json<ApplySystemResponse>, StatusCode> {
295 info!("Apply system request received for system: {}", system_id);
296
297 let system = {
299 let storage = state.system_storage.read().await;
300 storage.get(&system_id).cloned()
301 };
302
303 let system = match system {
304 Some(s) => s,
305 None => {
306 warn!("System not found: {}", system_id);
307 return Err(StatusCode::NOT_FOUND);
308 }
309 };
310
311 let applied = match state
313 .system_generator
314 .apply_system_design(
315 &system,
316 state.deterministic_config.as_ref(),
317 request.artifact_ids.clone(),
318 )
319 .await
320 {
321 Ok(a) => a,
322 Err(e) => {
323 error!("Failed to apply system design: {}", e);
324 return Err(StatusCode::INTERNAL_SERVER_ERROR);
325 }
326 };
327
328 if applied.frozen {
330 let mut storage = state.system_storage.write().await;
331 if let Some(stored_system) = storage.get_mut(&system_id) {
332 stored_system.status = "frozen".to_string();
333 }
334 }
335
336 info!(
337 "System design applied. System ID: {}, Applied artifacts: {}, Frozen: {}",
338 applied.system_id,
339 applied.applied_artifacts.len(),
340 applied.frozen
341 );
342
343 Ok(Json(ApplySystemResponse { applied }))
344}
345
346pub async fn freeze_artifacts_handler(
350 State(state): State<AiStudioState>,
351 Path(system_id): Path<String>,
352 Json(request): Json<FreezeArtifactsRequest>,
353) -> std::result::Result<Json<FreezeArtifactsResponse>, StatusCode> {
354 info!(
355 "Freeze artifacts request received for system: {}, artifacts: {:?}",
356 system_id, request.artifact_ids
357 );
358
359 let system = {
361 let storage = state.system_storage.read().await;
362 storage.get(&system_id).cloned()
363 };
364
365 let system = match system {
366 Some(s) => s,
367 None => {
368 warn!("System not found: {}", system_id);
369 return Err(StatusCode::NOT_FOUND);
370 }
371 };
372
373 let frozen_paths = match state
375 .system_generator
376 .freeze_artifacts(&system, request.artifact_ids.clone())
377 .await
378 {
379 Ok(paths) => paths,
380 Err(e) => {
381 error!("Failed to freeze artifacts: {}", e);
382 return Err(StatusCode::INTERNAL_SERVER_ERROR);
383 }
384 };
385
386 if !frozen_paths.is_empty() {
388 let mut storage = state.system_storage.write().await;
389 if let Some(stored_system) = storage.get_mut(&system_id) {
390 let all_frozen = stored_system
392 .artifacts
393 .values()
394 .all(|artifact| request.artifact_ids.contains(&artifact.artifact_id));
395 if all_frozen {
396 stored_system.status = "frozen".to_string();
397 }
398 }
399 }
400
401 info!(
402 "Artifacts frozen. System ID: {}, Frozen paths: {}",
403 system_id,
404 frozen_paths.len()
405 );
406
407 Ok(Json(FreezeArtifactsResponse {
408 frozen_paths,
409 }))
410}
411
412#[derive(Debug, Deserialize)]
414pub struct CreateAgentHttpRequest {
415 pub persona_id: Option<String>,
417
418 pub behavior_policy: Option<String>,
420
421 pub generate_persona: bool,
423
424 pub workspace_id: Option<String>,
426}
427
428#[derive(Debug, Serialize)]
430pub struct CreateAgentResponse {
431 pub agent: mockforge_core::ai_studio::behavioral_simulator::NarrativeAgent,
433}
434
435#[derive(Debug, Deserialize)]
437pub struct SimulateBehaviorHttpRequest {
438 pub agent_id: Option<String>,
440
441 pub persona_id: Option<String>,
443
444 pub current_state: mockforge_core::ai_studio::behavioral_simulator::AppState,
446
447 pub trigger_event: Option<String>,
449
450 pub workspace_id: Option<String>,
452}
453
454#[axum::debug_handler]
458pub async fn create_agent_handler(
459 State(state): State<AiStudioState>,
460 Json(request): Json<CreateAgentHttpRequest>,
461) -> std::result::Result<Json<CreateAgentResponse>, StatusCode> {
462 info!("Create agent request received");
463
464 let create_request = CreateAgentRequest {
465 persona_id: request.persona_id,
466 behavior_policy: request.behavior_policy,
467 generate_persona: request.generate_persona,
468 workspace_id: request.workspace_id.or_else(|| state.workspace_id.clone()),
469 };
470
471 let mut simulator = state.behavioral_simulator.lock().await;
472 let agent = match simulator.create_agent(&create_request).await {
473 Ok(a) => a,
474 Err(e) => {
475 error!("Failed to create agent: {}", e);
476 return Err(StatusCode::INTERNAL_SERVER_ERROR);
477 }
478 };
479
480 info!("Agent created: {}", agent.agent_id);
481
482 Ok(Json(CreateAgentResponse { agent }))
483}
484
485#[axum::debug_handler]
489pub async fn simulate_behavior_handler(
490 State(state): State<AiStudioState>,
491 Json(request): Json<SimulateBehaviorHttpRequest>,
492) -> std::result::Result<
493 Json<mockforge_core::ai_studio::behavioral_simulator::SimulateBehaviorResponse>,
494 StatusCode,
495> {
496 info!("Simulate behavior request received");
497
498 let simulate_request = SimulateBehaviorRequest {
499 agent_id: request.agent_id,
500 persona_id: request.persona_id,
501 current_state: request.current_state,
502 trigger_event: request.trigger_event,
503 workspace_id: request.workspace_id.or_else(|| state.workspace_id.clone()),
504 };
505
506 let mut simulator = state.behavioral_simulator.lock().await;
507 let response = match simulator.simulate_behavior(&simulate_request).await {
508 Ok(r) => r,
509 Err(e) => {
510 error!("Failed to simulate behavior: {}", e);
511 return Err(StatusCode::INTERNAL_SERVER_ERROR);
512 }
513 };
514
515 info!(
516 "Behavior simulation completed. Intention: {:?}, Tokens: {:?}, Cost: ${:.4}",
517 response.intention,
518 response.tokens_used,
519 response.cost_usd.unwrap_or(0.0)
520 );
521
522 Ok(Json(response))
523}
524
525pub fn ai_studio_router(state: AiStudioState) -> Router {
527 let mut router = Router::new()
528 .route("/api-critique", post(api_critique_handler))
529 .route("/generate-system", post(generate_system_handler))
530 .route("/system/{system_id}/apply", post(apply_system_handler))
531 .route("/system/{system_id}/freeze", post(freeze_artifacts_handler))
532 .route("/simulate-behavior/create-agent", post(create_agent_handler))
533 .route("/simulate-behavior", post(simulate_behavior_handler));
534 router.with_state(state)
535}
536
537#[cfg(test)]
538mod tests {
539 use super::*;
540 use mockforge_core::intelligent_behavior::config::BehaviorModelConfig;
541
542 fn create_test_state() -> AiStudioState {
543 let config = IntelligentBehaviorConfig {
544 behavior_model: BehaviorModelConfig {
545 llm_provider: "ollama".to_string(),
546 model: "llama2".to_string(),
547 api_endpoint: Some("http://localhost:11434/api/chat".to_string()),
548 api_key: None,
549 temperature: 0.7,
550 max_tokens: 2000,
551 rules: mockforge_core::intelligent_behavior::types::BehaviorRules::default(),
552 },
553 ..Default::default()
554 };
555 AiStudioState::new(config)
556 }
557
558 #[test]
559 fn test_ai_studio_state_creation() {
560 let state = create_test_state();
561 assert!(true);
563 }
564
565 #[test]
566 fn test_api_critique_request_serialization() {
567 let request = ApiCritiqueRequest {
568 schema: serde_json::json!({"openapi": "3.0.0"}),
569 schema_type: "openapi".to_string(),
570 focus_areas: vec!["anti-patterns".to_string()],
571 workspace_id: None,
572 };
573
574 let json = serde_json::to_string(&request).unwrap();
575 assert!(json.contains("openapi"));
576 assert!(json.contains("anti-patterns"));
577 }
578}