Skip to content

Commit 8da42d8

Browse files
committed
Implement configurable session management in HAPPE
1 parent 38e2a77 commit 8da42d8

13 files changed

Lines changed: 979 additions & 108 deletions

File tree

TASKS.md

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,3 +189,138 @@ This task list breaks down the implementation steps outlined in `PLANNING.md`.
189189
- [ ] **Configuration:** Refine configuration loading (e.g., use environment variables, config files).
190190
- [ ] **Logging:** Improve logging messages for clarity and debugging.
191191
- [ ] **Version Control:** Commit changes frequently with clear messages.
192+
193+
## Phase 9: Enhance IDA with Broker LLM (Direct Integration)
194+
195+
This phase implements the plan to integrate a "broker" LLM directly within IDA
196+
to refine memory retrieval results before sending them to HAPPE.
197+
198+
### Sub-Phase 1: Prerequisites and Setup
199+
200+
- [x] **Broker LLM Configuration Loading:**
201+
- [x] Verify `MemoryBrokerConfig` fields (`provider`, `api_key`, `model_name`, `base_url`) are loaded correctly into `IdaConfig`.
202+
- [x] Add loaded `IdaConfig` to shared `DaemonState` struct.
203+
- [x] Update `DaemonState` instantiation in `ida/src/bin/ida-daemon.rs`.
204+
- [x] Ensure `handle_message` in `ida/src/ipc_server.rs` can access the `IdaConfig`.
205+
- [x] **Ensure MCP Access Reuse in IDA:**
206+
- [x] Identify where `MemoryStore` is initialized in `ida/src/bin/ida-daemon.rs`.
207+
- [x] Store the `Arc<dyn McpHostInterface + Send + Sync>` used for `MemoryStore` in the `DaemonState`.
208+
- [x] Update `DaemonState` definition and instantiation.
209+
- [x] **Integrate Broker LLM Client into IDA:**
210+
- [x] **Configuration (`core/src/config.rs`):**
211+
- [x] Modify `MemoryBrokerConfig` struct (add `provider`, `base_url`, etc.).
212+
- [x] Update corresponding `IdaConfig` in `ida/src/config.rs` and its `From` impl.
213+
- [x] Update default config generation (`install` or `tools`) for `[ida.memory_broker]`.
214+
- [x] **Implementation (IDA Crate):**
215+
- [x] Add dependencies (`reqwest`, `serde`, `serde_json`, `async-trait`) to `ida/Cargo.toml`.
216+
- [x] Create `ida/src/llm_clients.rs`.
217+
- [x] Define `trait LLMClient { async fn generate(&self, prompt: &str) -> Result<String>; }`.
218+
- [x] Implement the trait for supported providers (e.g., `GeminiClient`, `OllamaClient`).
219+
- [x] Create factory function `create_llm_client(config: &CoreMemoryBrokerConfig) -> Result<Option<Arc<dyn LLMClient>>>`.
220+
- [x] Add `Option<Arc<dyn LLMClient>>` field to `DaemonState`.
221+
- [x] Initialize the client in `ida/src/bin/ida-daemon.rs` using the factory.
222+
223+
### Sub-Phase 2: Core Logic Implementation in IDA
224+
225+
- [x] **Modify `ida::memory_mcp_client::retrieve_memories`:**
226+
- [x] Change signature to accept `broker_llm_client: &Option<Arc<dyn LLMClient>>` and `conversation_context: Option<String>`.
227+
- [x] After semantic search, if results exist and client exists:
228+
- [x] Construct broker prompt (query, context, candidates, instructions).
229+
- [x] Call `client.generate(&broker_prompt).await`.
230+
- [x] Parse response (e.g., comma-separated keys).
231+
- [x] Filter semantic search results based on broker response.
232+
- [x] Implement fallback logic on broker error.
233+
- [x] Update call site in `ida/src/ipc_server.rs` (`handle_message`) to pass the client and context.
234+
235+
### Sub-Phase 3: Integrating Conversation Context (IPC Changes)
236+
237+
- [x] **Modify IPC Message (`ipc/src/internal_messages.rs`):**
238+
- [x] Add `conversation_context: Option<String>` to `GetMemoriesRequest`.
239+
- [x] **Update HAPPE Client (`happe/src/ida_client.rs`):**
240+
- [x] Update `get_memories` signature and request construction.
241+
- [x] **Update HAPPE Orchestrator (`happe/src/coordinator.rs` or similar):**
242+
- [x] Gather context and pass it to `ida_client::get_memories`.
243+
- [x] **Update IDA Server (`ida/src/ipc_server.rs`):**
244+
- [x] Extract context and pass it to `memory_mcp_client::retrieve_memories`.
245+
246+
### Sub-Phase 4: Testing and Refinement
247+
248+
- [ ] **Testing:** Unit, Integration, End-to-End tests.
249+
- [ ] **Refinement:** Prompt engineering, latency analysis, fallback logic, configuration.
250+
251+
## Phase 10: Implement Configurable Session Management in HAPPE
252+
253+
This phase adds a stateful session management system to HAPPE, allowing it to maintain conversation history across multiple requests within a session. This is crucial for providing proper context to `IDA` for memory retrieval.
254+
255+
### Sub-Phase 1: Define Core Session Store Trait & In-Memory Adapter
256+
257+
- [x] **Module Setup:**
258+
- [x] Create directory `happe/src/session/`.
259+
- [x] Create directory `happe/src/session/adapters/`.
260+
- [x] Create file `happe/src/session/mod.rs`.
261+
- [x] Create file `happe/src/session/store.rs`.
262+
- [x] Create file `happe/src/session/adapters/in_memory.rs`.
263+
- [x] Declare `session` module in `happe/src/lib.rs` (`pub mod session;`).
264+
- [x] **Define `SessionStore` Trait (`happe/src/session/store.rs`):**
265+
- [x] Define `#[async_trait] pub trait SessionStore: Send + Sync`.
266+
- [x] Add methods for session management:
267+
- [x] `create_session`
268+
- [x] `get_session`
269+
- [x] `save_session`
270+
- [x] `delete_session`
271+
- [x] `cleanup_expired_sessions`
272+
- [x] **Implement `InMemorySessionStore` (`happe/src/session/adapters/in_memory.rs`):**
273+
- [x] Define `struct InMemorySessionStore`.
274+
- [x] Add thread-safe storage using `Arc<RwLock<HashMap<String, Session>>>`.
275+
- [x] Implement `SessionStore` trait.
276+
- [x] Add expiration and cleanup logic.
277+
- [x] **Dependencies (`happe/Cargo.toml`):**
278+
- [x] Add `async-trait = "0.1"`.
279+
- [x] Add `uuid = { version = "1", features = ["v4"] }` (for session ID generation).
280+
- [x] Ensure `tokio` features include `sync`.
281+
282+
### Sub-Phase 2: Integrate Session Store into HAPPE State
283+
284+
- [x] **State (`happe/src/http_server.rs`, `happe/src/ipc_server.rs`):**
285+
- [x] Add `session_store: SessionStoreRef` to `AppState` struct (`http_server.rs`).
286+
- [x] Add `session_store: SessionStoreRef` to `IpcServerState` struct (`ipc_server.rs`).
287+
- [x] Initialize session store in HTTP and IPC servers.
288+
- [x] Add session cleanup task to the IPC server.
289+
290+
### Sub-Phase 3: Modify Coordinator & IPC Handler
291+
292+
- [x] **Coordinator (`happe/src/coordinator.rs`):**
293+
- [x] Modify `process_query` signature to accept `session: &Session`.
294+
- [x] Add helper function `get_conversation_history` to extract history from the session.
295+
- [x] Add helper function `update_session_history` to store turns in the session.
296+
- [x] **IPC Handler (`happe/src/ipc_server.rs`):**
297+
- [x] Modify `handle_connection`:
298+
- [x] Get or create session for the request.
299+
- [x] Pass session to `coordinator::process_query`.
300+
- [x] Update session with new conversation turn.
301+
- [x] Save session back to the store.
302+
- [x] **HTTP Handler (`happe/src/http_server.rs`):**
303+
- [x] Modify `handle_query`:
304+
- [x] Extract session ID from request or create a new one.
305+
- [x] Get or create session.
306+
- [x] Pass session to `coordinator::process_query`.
307+
- [x] Update and save session.
308+
309+
### Sub-Phase 4: Update IPC Request & Client (`@cli`)
310+
311+
- [x] **IPC Request (`ipc/src/happe_request/types.rs` or `mod.rs`):**
312+
- [x] Add `session_id: Option<String>` field to `HappeQueryRequest` struct.
313+
- [x] Add `session_id: Option<String>` field to `HappeQueryResponse` struct.
314+
- [x] **CLI (`@cli` Crate):**
315+
- [x] Identify where the main interaction loop/HAPPE client logic resides (e.g., `cli/src/app.rs` or `cli/src/happe_client.rs`).
316+
- [x] On CLI startup, generate a persistent `session_id` for the duration of the run (e.g., `let session_id = uuid::Uuid::new_v4().to_string();`).
317+
- [x] Modify the code that creates and sends `HappeQueryRequest` to include this `session_id`.
318+
319+
### Sub-Phase 5: Testing & Refinement
320+
321+
- [x] **Unit Tests:**
322+
- [x] Add tests for the `Session` struct in `store.rs`.
323+
- [x] Add tests for `InMemorySessionStore`.
324+
- [ ] **Integration Tests:** Test IPC handler with session history.
325+
- [ ] **End-to-End Tests (`@cli` -> `happe-daemon`):** Verify context is maintained across multiple turns within a single CLI run.
326+
- [ ] **Refinement:** Assess performance, history pruning, error handling, security of session IDs.

cli/src/app.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::output::print_happe_response;
1111
/// Runs a single query mode, sending one prompt to the HAPPE daemon and displaying the response
1212
pub async fn run_single_query(prompt: String, happe_client: &HappeClient) -> Result<()> {
1313
info!("Running single query: {}", prompt);
14+
info!("Using session ID: {}", happe_client.session_id());
1415

1516
// Display a spinner while waiting for response
1617
let spinner = ProgressBar::new_spinner();
@@ -27,13 +28,18 @@ pub async fn run_single_query(prompt: String, happe_client: &HappeClient) -> Res
2728
match happe_client.send_query(prompt).await {
2829
Ok(response) => {
2930
spinner.finish_and_clear();
30-
31+
3132
if let Some(error) = response.error {
3233
error!("HAPPE error: {}", error);
3334
println!("Error: {}", error);
3435
return Ok(());
3536
}
36-
37+
38+
// Log session ID if one was returned
39+
if let Some(session_id) = &response.session_id {
40+
debug!("Response session ID: {}", session_id);
41+
}
42+
3743
print_happe_response(&response.response);
3844
}
3945
Err(e) => {
@@ -49,6 +55,7 @@ pub async fn run_single_query(prompt: String, happe_client: &HappeClient) -> Res
4955
/// Runs an interactive chat session with the HAPPE daemon
5056
pub async fn run_interactive_chat(happe_client: &HappeClient) -> Result<()> {
5157
println!("Starting interactive chat session with HAPPE daemon.");
58+
println!("Session ID: {}", happe_client.session_id().blue());
5259
println!("Type 'exit' or 'quit' to end the session.");
5360
println!();
5461

@@ -89,13 +96,19 @@ pub async fn run_interactive_chat(happe_client: &HappeClient) -> Result<()> {
8996
match happe_client.send_query(input.to_string()).await {
9097
Ok(response) => {
9198
spinner.finish_and_clear();
92-
99+
93100
if let Some(error) = response.error {
94101
error!("HAPPE error: {}", error);
95102
println!("Error: {}", error);
96103
continue;
97104
}
98-
105+
106+
// Log session ID if one was returned
107+
if let Some(session_id) = &response.session_id {
108+
debug!("Response session ID: {}", session_id);
109+
// We don't need to update our session ID as the client will track it
110+
}
111+
99112
print_happe_response(&response.response);
100113
}
101114
Err(e) => {

0 commit comments

Comments
 (0)