diff --git a/crates/gateway/src/v1/mod.rs b/crates/gateway/src/v1/mod.rs index c455418..119d48a 100644 --- a/crates/gateway/src/v1/mod.rs +++ b/crates/gateway/src/v1/mod.rs @@ -438,6 +438,46 @@ async fn chat( }); } + // Phase 40 part 2 — fire-and-forget /event to observer at :3800. + // Same ring-buffer that scrum + scenario events land in, so any + // tool-routed-through-our-gateway (Pi, Archon, openai SDK clients) + // shows up alongside scrum_master events for KB consolidation + + // pathway-memory + bug-fingerprint compounding. Best-effort: + // observer being down doesn't block the chat response. + { + let provider = used_provider.clone(); + let model = resp.model.clone(); + let prompt_tokens = resp.usage.prompt_tokens; + let completion_tokens = resp.usage.completion_tokens; + let success = true; + tokio::spawn(async move { + let body = serde_json::json!({ + "endpoint": "/v1/chat", + "source": "v1.chat", + "event_kind": "chat_completion", + "input_summary": format!( + "{} {} prompt={}t", + provider, model, prompt_tokens + ), + "output_summary": format!( + "completion={}t {}ms", + completion_tokens, latency_ms + ), + "success": success, + "duration_ms": latency_ms, + }); + let client = reqwest::Client::builder() + .timeout(std::time::Duration::from_secs(2)) + .build() + .unwrap_or_else(|_| reqwest::Client::new()); + let _ = client + .post("http://localhost:3800/event") + .json(&body) + .send() + .await; + }); + } + // Phase 40: per-provider usage tracking { let mut u = state.usage.write().await;