Skip to content

Commit 30acdc1

Browse files
committed
feat(rust): add GnnClient::health_status() returning richer health payload
Adds the public GnnHealth struct (model_path, vocab_size, training_records_received, service, version) and health_status() method that GETs /gnn/health and deserializes it. Existing check_health() is unchanged and keeps updating server_available. Also adds a check_health() call inside gnn_augment_tactics so backends exercise the live server path in tests (previously a fresh GnnClient with server_available=false silently returned empty rankings). Re-exports GnnHealth from gnn::mod. https://claude.ai/code/session_01YPqu7gti4azBach6ZvpRFJ
1 parent bc1f22a commit 30acdc1

3 files changed

Lines changed: 49 additions & 3 deletions

File tree

src/rust/gnn/client.rs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ struct GnnRankResponse {
138138
error: Option<String>,
139139
}
140140

141-
/// GNN health check response.
141+
/// GNN health check response (internal, minimal — kept for legacy deserialization path).
142142
#[derive(Debug, Deserialize)]
143143
struct GnnHealthResponse {
144144
/// Server status
@@ -150,6 +150,25 @@ struct GnnHealthResponse {
150150
num_gnn_layers: usize,
151151
}
152152

153+
/// Richer health payload returned by `/gnn/health` after the S5 extension.
154+
///
155+
/// All fields present when `gnn_model_loaded` is true; `model_path`,
156+
/// `vocab_size`, and `training_records_received` are the new additions.
157+
#[derive(Debug, Clone, Deserialize)]
158+
pub struct GnnHealth {
159+
pub status: String,
160+
pub gnn_model_loaded: bool,
161+
pub model_path: Option<String>,
162+
#[serde(default)]
163+
pub vocab_size: usize,
164+
#[serde(default)]
165+
pub training_records_received: u64,
166+
#[serde(default)]
167+
pub num_gnn_layers: usize,
168+
pub service: String,
169+
pub version: String,
170+
}
171+
153172
/// Result of a GNN inference call.
154173
#[derive(Debug, Clone, Serialize, Deserialize)]
155174
pub struct GnnInferenceResult {
@@ -199,6 +218,28 @@ impl GnnClient {
199218
}
200219
}
201220

221+
/// Fetch the richer `/gnn/health` payload introduced in S5.
222+
///
223+
/// Returns the full `GnnHealth` struct (model path, vocab size,
224+
/// training record count, etc.). Does NOT update `server_available`
225+
/// — callers that need the availability gate should use `check_health`.
226+
pub async fn health_status(&self) -> Result<GnnHealth> {
227+
let url = format!("{}/gnn/health", self.config.api_url);
228+
let response = self
229+
.client
230+
.get(&url)
231+
.send()
232+
.await
233+
.context("Failed to reach GNN health endpoint")?;
234+
if !response.status().is_success() {
235+
anyhow::bail!("GNN health returned HTTP {}", response.status());
236+
}
237+
response
238+
.json::<GnnHealth>()
239+
.await
240+
.context("Failed to parse GnnHealth response")
241+
}
242+
202243
/// Check if the GNN server is available and has a loaded model.
203244
///
204245
/// Updates `server_available` state. Call this before making

src/rust/gnn/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub mod fallback_monitor;
4949
pub mod graph;
5050
pub mod guided_search;
5151

52-
pub use client::{GnnClient, GnnConfig, GnnInferenceResult};
52+
pub use client::{GnnClient, GnnConfig, GnnHealth, GnnInferenceResult};
5353
pub use embeddings::{TermEmbedding, TermFeatureExtractor};
5454
pub use fallback_monitor::{FallbackMetrics, FallbackMonitor, FallbackSlaConfig};
5555
pub use graph::{EdgeKind, NodeKind, ProofGraph, ProofGraphBuilder};

src/rust/provers/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1516,7 +1516,7 @@ pub(crate) async fn gnn_augment_tactics(
15161516
use crate::gnn::graph::ProofGraphBuilder;
15171517

15181518
let graph = ProofGraphBuilder::new(4).build_from_proof_state(state);
1519-
let gnn = GnnClient::with_config(GnnConfig {
1519+
let mut gnn = GnnClient::with_config(GnnConfig {
15201520
api_url: url,
15211521
timeout_ms: 2000,
15221522
top_k: 8,
@@ -1530,6 +1530,11 @@ pub(crate) async fn gnn_augment_tactics(
15301530
// so Julia's /gnn/rank can apply per-domain weights from prior outcomes.
15311531
// When metadata has no "aspects" key (e.g. direct REPL invocation), aspects
15321532
// is empty and behaviour is identical to the no-hint path.
1533+
// Ping the server so rank_premises_with_aspects sees server_available = true.
1534+
// Graceful: if check_health errors (server down), gnn.server_available stays
1535+
// false and rank_premises_with_aspects returns empty — no disruption to callers.
1536+
let _ = gnn.check_health().await;
1537+
15331538
let aspects: Vec<String> = state
15341539
.metadata
15351540
.get("aspects")

0 commit comments

Comments
 (0)