AiSuggestionEngine.java
package org.egothor.methodatlas.ai;
import java.util.List;
/**
* High-level AI orchestration contract for security classification of parsed
* test classes.
*
* <p>
* This interface defines the provider-agnostic entry point used by
* {@code MethodAtlasApp} to request AI-generated security tagging
* suggestions for a single parsed JUnit test class.
* Implementations coordinate taxonomy selection, provider resolution, request
* submission, response normalization, and conversion into the application's
* internal result model.
* </p>
*
* <h2>Responsibilities</h2>
*
* <ul>
* <li>accepting a fully qualified class name and corresponding class
* source</li>
* <li>submitting the class for AI-based security analysis</li>
* <li>normalizing provider-specific responses into
* {@link AiClassSuggestion}</li>
* <li>surfacing failures through {@link AiSuggestionException}</li>
* </ul>
*
* <p>
* The interface intentionally hides provider-specific protocol details so that
* the rest of the application can depend on a stable abstraction independent of
* the selected AI backend.
* </p>
*
* @see AiClassSuggestion
* @see AiProviderClient
*/
@SuppressWarnings("PMD.ImplicitFunctionalInterface")
public interface AiSuggestionEngine {
/**
* Constructs an {@code AiSuggestionEngine} configured for the active AI
* provider. Callers receive an instance through this static factory
* rather than naming the implementation class directly, which keeps the
* concrete type out of caller imports and makes the impl substitutable
* (for example, by a stub in unit tests or by a future alternative
* implementation).
*
* <p>
* The returned engine is stateless beyond its configuration and is safe
* to share across threads.
* </p>
*
* @param options AI runtime configuration; must not be {@code null}
* @return a configured engine ready to handle
* {@link #suggestForClass(String, String, String, java.util.List)}
* calls
* @throws AiSuggestionException if the engine cannot be initialised for
* the supplied options
*/
static AiSuggestionEngine create(AiOptions options) throws AiSuggestionException {
return new AiSuggestionEngineImpl(options);
}
/**
* Constructs an {@code AiSuggestionEngine} that notifies
* {@code rateLimitListener} before each rate-limit pause. The listener is
* the GUI's hook into long-running calls and is supplied by the Swing
* worker that orchestrates analysis.
*
* @param options AI runtime configuration; must not be {@code null}
* @param rateLimitListener callback invoked before each HTTP 429
* pause; must not be {@code null}
* @return a configured engine
* @throws AiSuggestionException if the engine cannot be initialised
* @see RateLimitListener
*/
static AiSuggestionEngine create(AiOptions options, RateLimitListener rateLimitListener)
throws AiSuggestionException {
return new AiSuggestionEngineImpl(options, rateLimitListener);
}
/**
* Requests AI-generated security classification for a single parsed test class.
*
* <p>
* The supplied source code is analyzed in the context of the configured
* taxonomy and AI provider. The returned result may contain both class-level
* and method-level suggestions, including security relevance, display name
* proposals, taxonomy tags, and optional explanatory rationale.
* </p>
*
* <p>
* The method expects the complete source of the class being analyzed, rather
* than a single method fragment, so that the AI engine can evaluate test intent
* using full class context.
* </p>
*
* <p>
* The {@code fileStem} parameter is a dot-separated identifier derived from the
* source file's path relative to the scan root (e.g.
* {@code module-a.src.test.java.com.acme.FooTest}). Automated provider
* implementations ignore it; the {@link ManualConsumeEngine} uses it to locate
* the operator-saved response file ({@code <fileStem>.response.txt}).
* </p>
*
* @param fileStem dot-separated path stem identifying the source file;
* used by file-based engines to locate response files
* @param fqcn fully qualified class name of the parsed test class
* @param classSource complete source code of the class to analyze
* @param targetMethods deterministically extracted JUnit test methods that must
* be classified
* @return normalized AI classification result for the class and its methods
*
* @throws AiSuggestionException if analysis fails due to provider communication
* errors, invalid responses, provider
* unavailability, or normalization failures
*
* @see AiClassSuggestion
* @see AiMethodSuggestion
* @see ManualConsumeEngine
*/
AiClassSuggestion suggestForClass(String fileStem, String fqcn, String classSource,
List<PromptBuilder.TargetMethod> targetMethods) throws AiSuggestionException;
/**
* Registers an optional callback that the engine will notify after each
* successful AI round-trip.
*
* <p>
* The default implementation is a no-op so that engines that have no
* meaningful response data to surface (for example the manual-consume
* engine, which receives operator-saved files rather than live API
* responses) silently ignore the registration. Engines that do back live
* provider calls override this method to forward each call to the
* registered listener.
* </p>
*
* @param listener listener to notify after each successful AI round-trip,
* or {@code null} to clear a previously registered listener
*/
default void setResponseListener(AiResponseListener listener) {
// no-op by default
}
}