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