Skip to content

Commit 8f85bac

Browse files
Copilotphrocker
andcommitted
Add AI-powered tooltip system with codebase search and LLM integration (#282)
* Initial plan * Add tooltip controller with DTOs, service, and tests - Created TooltipController with /describe and /chat endpoints - Added DTOs for tooltip requests and responses - Implemented TooltipService for AI-powered tooltips - Added CodebaseIndexingService for indexing docs and code - Created comprehensive unit tests - All tests passing Co-authored-by: phrocker <[email protected]> * Add admin indexing endpoint and comprehensive documentation - Added /admin/index endpoint for manual codebase indexing - Added permission check for admin-only indexing trigger - Created comprehensive TOOLTIP_SYSTEM.md documentation - Documented API endpoints, configuration, architecture, and usage - Included troubleshooting guide and future enhancements Co-authored-by: phrocker <[email protected]> * Fix TooltipControllerTest to include CodebaseIndexingService mock - Added missing CodebaseIndexingService mock in test setup - Updated constructor call with all 5 required parameters - All 7 tests passing successfully Co-authored-by: phrocker <[email protected]> * Address code review feedback - Made LLM model configurable via application properties - Added comprehensive documentation for deprecated method usage - Improved glob pattern matching documentation - Fixed Lombok annotation imports to use short form - Updated documentation with new configuration option Co-authored-by: phrocker <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: phrocker <[email protected]>
1 parent edd49f2 commit 8f85bac

File tree

9 files changed

+1598
-0
lines changed

9 files changed

+1598
-0
lines changed
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
package io.sentrius.sso.controllers.api;
2+
3+
import io.sentrius.sso.core.annotations.LimitAccess;
4+
import io.sentrius.sso.core.config.SystemOptions;
5+
import io.sentrius.sso.core.controllers.BaseController;
6+
import io.sentrius.sso.core.dto.tooltip.TooltipChatRequest;
7+
import io.sentrius.sso.core.dto.tooltip.TooltipChatResponse;
8+
import io.sentrius.sso.core.dto.tooltip.TooltipDescribeRequest;
9+
import io.sentrius.sso.core.dto.tooltip.TooltipDescribeResponse;
10+
import io.sentrius.sso.core.dto.ztat.TokenDTO;
11+
import io.sentrius.sso.core.model.security.enums.ApplicationAccessEnum;
12+
import io.sentrius.sso.core.model.security.enums.SSHAccessEnum;
13+
import io.sentrius.sso.core.model.users.User;
14+
import io.sentrius.sso.core.model.verbs.Endpoint;
15+
import io.sentrius.sso.core.services.ErrorOutputService;
16+
import io.sentrius.sso.core.services.UserService;
17+
import io.sentrius.sso.core.services.tooltip.CodebaseIndexingService;
18+
import io.sentrius.sso.core.services.tooltip.TooltipService;
19+
import io.sentrius.sso.core.utils.AccessUtil;
20+
import jakarta.servlet.http.HttpServletRequest;
21+
import jakarta.servlet.http.HttpServletResponse;
22+
import lombok.extern.slf4j.Slf4j;
23+
import org.springframework.http.HttpStatus;
24+
import org.springframework.http.ResponseEntity;
25+
import org.springframework.web.bind.annotation.*;
26+
27+
import java.util.HashMap;
28+
import java.util.Map;
29+
30+
/**
31+
* REST Controller for AI-powered tooltip and contextual help features.
32+
* Provides endpoints for getting descriptions of UI elements and chat-based assistance.
33+
*/
34+
@Slf4j
35+
@RestController
36+
@RequestMapping("/api/v1/tooltip")
37+
public class TooltipController extends BaseController {
38+
39+
private final TooltipService tooltipService;
40+
private final CodebaseIndexingService indexingService;
41+
42+
public TooltipController(
43+
UserService userService,
44+
SystemOptions systemOptions,
45+
ErrorOutputService errorOutputService,
46+
TooltipService tooltipService,
47+
CodebaseIndexingService indexingService) {
48+
super(userService, systemOptions, errorOutputService);
49+
this.tooltipService = tooltipService;
50+
this.indexingService = indexingService;
51+
}
52+
53+
/**
54+
* Get AI-powered description for a UI element.
55+
* Searches indexed codebase and documentation to provide contextual tooltips.
56+
*
57+
* @param request Element context information from the frontend
58+
* @param httpRequest HTTP request for authentication
59+
* @param httpResponse HTTP response
60+
* @return AI-generated description of the element
61+
*/
62+
@PostMapping("/describe")
63+
@Endpoint(description = "Get AI-powered description for a UI element")
64+
@LimitAccess(applicationAccess = ApplicationAccessEnum.CAN_LOG_IN)
65+
public ResponseEntity<TooltipDescribeResponse> describe(
66+
@RequestBody TooltipDescribeRequest request,
67+
HttpServletRequest httpRequest,
68+
HttpServletResponse httpResponse) {
69+
70+
try {
71+
User operatingUser = getOperatingUser(httpRequest, httpResponse);
72+
log.info("Tooltip describe request from user: {}", operatingUser.getUserId());
73+
74+
// Validate request
75+
if (request.getContext() == null) {
76+
return ResponseEntity.badRequest()
77+
.body(TooltipDescribeResponse.builder()
78+
.description("Invalid request: context is required")
79+
.error("Context is required")
80+
.success(false)
81+
.build());
82+
}
83+
84+
// Build token DTO for LLM service
85+
TokenDTO tokenDTO = TokenDTO.builder().build();
86+
// Token will be populated from security context by LLM service
87+
88+
// Generate description
89+
TooltipDescribeResponse response = tooltipService.describeElement(request, tokenDTO);
90+
91+
return ResponseEntity.ok(response);
92+
93+
} catch (Exception e) {
94+
log.error("Error processing tooltip describe request", e);
95+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
96+
.body(TooltipDescribeResponse.builder()
97+
.description("An error occurred while generating the description.")
98+
.error(e.getMessage())
99+
.success(false)
100+
.build());
101+
}
102+
}
103+
104+
/**
105+
* Chat endpoint for conversational assistance about UI elements and features.
106+
*
107+
* @param request Chat message and optional element context
108+
* @param httpRequest HTTP request for authentication
109+
* @param httpResponse HTTP response
110+
* @return AI-generated chat response
111+
*/
112+
@PostMapping("/chat")
113+
@Endpoint(description = "Chat with AI assistant about UI elements and features")
114+
@LimitAccess(applicationAccess = ApplicationAccessEnum.CAN_LOG_IN)
115+
public ResponseEntity<TooltipChatResponse> chat(
116+
@RequestBody TooltipChatRequest request,
117+
HttpServletRequest httpRequest,
118+
HttpServletResponse httpResponse) {
119+
120+
try {
121+
User operatingUser = getOperatingUser(httpRequest, httpResponse);
122+
log.info("Tooltip chat request from user: {}", operatingUser.getUserId());
123+
124+
// Validate request
125+
if (request.getMessage() == null || request.getMessage().trim().isEmpty()) {
126+
return ResponseEntity.badRequest()
127+
.body(TooltipChatResponse.builder()
128+
.response("Invalid request: message is required")
129+
.error("Message is required")
130+
.success(false)
131+
.build());
132+
}
133+
134+
// Build token DTO for LLM service
135+
TokenDTO tokenDTO = TokenDTO.builder().build();
136+
// Token will be populated from security context by LLM service
137+
138+
// Generate chat response
139+
TooltipChatResponse response = tooltipService.chat(request, tokenDTO);
140+
141+
return ResponseEntity.ok(response);
142+
143+
} catch (Exception e) {
144+
log.error("Error processing tooltip chat request", e);
145+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
146+
.body(TooltipChatResponse.builder()
147+
.response("An error occurred while processing your message.")
148+
.error(e.getMessage())
149+
.success(false)
150+
.build());
151+
}
152+
}
153+
154+
/**
155+
* Trigger manual indexing of codebase and documentation.
156+
* Requires admin/system management permissions.
157+
*
158+
* @param httpRequest HTTP request for authentication
159+
* @param httpResponse HTTP response
160+
* @return Indexing result with statistics
161+
*/
162+
@PostMapping("/admin/index")
163+
@Endpoint(description = "Trigger manual indexing of codebase and documentation")
164+
@LimitAccess(applicationAccess = ApplicationAccessEnum.CAN_LOG_IN)
165+
public ResponseEntity<Map<String, Object>> triggerIndexing(
166+
HttpServletRequest httpRequest,
167+
HttpServletResponse httpResponse) {
168+
169+
try {
170+
User operatingUser = getOperatingUser(httpRequest, httpResponse);
171+
172+
// Check if user has admin/system permissions
173+
if (!AccessUtil.canAccess(operatingUser, SSHAccessEnum.CAN_MANAGE_SYSTEMS)) {
174+
log.warn("Non-admin user {} attempted to trigger indexing", operatingUser.getUserId());
175+
return ResponseEntity.status(HttpStatus.FORBIDDEN)
176+
.body(Map.of(
177+
"success", false,
178+
"error", "Admin privileges required to trigger indexing"
179+
));
180+
}
181+
182+
log.info("Indexing triggered by admin user: {}", operatingUser.getUserId());
183+
184+
// Run indexing
185+
CodebaseIndexingService.IndexingResult result = indexingService.indexCodebase();
186+
187+
// Build response
188+
Map<String, Object> response = new HashMap<>();
189+
response.put("success", result.isSuccess());
190+
response.put("message", result.getMessage());
191+
response.put("totalFiles", result.getTotalFiles());
192+
response.put("successCount", result.getSuccessCount());
193+
response.put("errorCount", result.getErrorCount());
194+
195+
if (result.getErrors() != null && !result.getErrors().isEmpty()) {
196+
response.put("errors", result.getErrors());
197+
}
198+
199+
return ResponseEntity.ok(response);
200+
201+
} catch (Exception e) {
202+
log.error("Error triggering indexing", e);
203+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
204+
.body(Map.of(
205+
"success", false,
206+
"error", "Failed to trigger indexing: " + e.getMessage()
207+
));
208+
}
209+
}
210+
}

0 commit comments

Comments
 (0)