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