Skip to content

Commit 4613af0

Browse files
author
FreeSynergy
committed
feat(help-sidebar): wire AiHelpSource to POST /ai/chat REST endpoint
1 parent 8ffb215 commit 4613af0

2 files changed

Lines changed: 44 additions & 9 deletions

File tree

crates/fs-gui-workspace/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ fs-session = { workspace = true }
2525
fs-db-desktop = { workspace = true }
2626
fs-help = { workspace = true }
2727
serde = { workspace = true }
28+
serde_json = { workspace = true }
29+
reqwest = { workspace = true }
2830
tokio = { workspace = true }
2931
tracing = { workspace = true }
3032
chrono = { workspace = true }

crates/fs-gui-workspace/src/help_sidebar.rs

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
// No direct network access from this module.
1717

1818
use fs_help::{HelpSystem, HelpTopic};
19+
use serde_json;
1920

2021
use crate::sidebar_state::{SidebarMode, SidebarState};
2122

@@ -84,18 +85,49 @@ impl HelpSource for LocalHelpTopicSource {
8485

8586
// ── AiHelpSource ──────────────────────────────────────────────────────────────
8687

87-
/// Resolves help via AI capability stub (gRPC to fs-ai when present).
88+
/// Resolves help via the running fs-ai REST service (`POST /ai/chat`).
8889
///
89-
/// In this phase the implementation is a stub that returns a canned response.
90-
/// Full gRPC wiring happens when fs-ai is registered in fs-registry (Phase 6).
90+
/// The endpoint is read from the `FS_AI_ENDPOINT` environment variable
91+
/// (e.g. `http://localhost:8080`). Falls back to `HelpContent::None`
92+
/// when the service is unreachable or not configured.
9193
pub struct AiHelpSource;
9294

95+
impl AiHelpSource {
96+
fn endpoint() -> Option<String> {
97+
std::env::var("FS_AI_ENDPOINT").ok()
98+
}
99+
}
100+
93101
impl HelpSource for AiHelpSource {
94102
fn resolve(&self, context: &str) -> HelpContent {
95-
// Stub: returns a placeholder response indicating AI is available.
96-
// Real impl: gRPC call to fs-ai service → stream response.
97-
let response = format!("AI help for '{context}' — connect fs-ai for live responses.");
98-
HelpContent::AiResponse(response)
103+
let Some(base) = Self::endpoint() else {
104+
return HelpContent::None;
105+
};
106+
107+
let url = format!("{base}/ai/chat");
108+
let body = serde_json::json!({
109+
"question": format!("Help me with the context: {context}"),
110+
"context": context
111+
});
112+
113+
let client = reqwest::blocking::Client::new();
114+
let result = client
115+
.post(&url)
116+
.json(&body)
117+
.send()
118+
.and_then(reqwest::blocking::Response::error_for_status)
119+
.and_then(reqwest::blocking::Response::json::<serde_json::Value>);
120+
121+
match result {
122+
Ok(json) => {
123+
let answer = json["answer"]
124+
.as_str()
125+
.unwrap_or("(no response)")
126+
.to_owned();
127+
HelpContent::AiResponse(answer)
128+
}
129+
Err(_) => HelpContent::None,
130+
}
99131
}
100132

101133
fn source_name(&self) -> &'static str {
@@ -225,9 +257,10 @@ mod tests {
225257
}
226258

227259
#[test]
228-
fn ai_help_source_returns_response() {
260+
fn ai_help_source_returns_none_without_endpoint() {
261+
// Without FS_AI_ENDPOINT set the source gracefully returns None.
229262
let src = AiHelpSource;
230-
assert!(matches!(src.resolve("browser"), HelpContent::AiResponse(_)));
263+
assert!(matches!(src.resolve("browser"), HelpContent::None));
231264
}
232265

233266
#[test]

0 commit comments

Comments
 (0)