Skip to main content

fix_engine/
context.rs

1//! Fix context trait for framework/ruleset-specific LLM prompt customization.
2//!
3//! The fix engine is generic — it works with any Konveyor analysis output.
4//! But LLM-assisted fixes benefit from framework-specific guidance (prompt
5//! constraints, priority ordering, system prompts). This module defines the
6//! trait that framework crates implement, plus a generic fallback.
7
8/// Trait that framework/ruleset-specific crates implement to provide
9/// LLM prompt context for the fix engine.
10///
11/// Implementations are registered in a [`FixContextRegistry`](super::registry::FixContextRegistry)
12/// and looked up by ruleset name at runtime.
13pub trait FixContext: Send + Sync {
14    /// Unique name that matches the `RuleSet.name` field from Konveyor
15    /// analysis output. Used for registry lookup.
16    fn ruleset_name(&self) -> &str;
17
18    /// Human-readable description of the migration (e.g., "PatternFly v5 to v6").
19    /// Used in LLM system prompts and prompt preambles.
20    fn migration_description(&self) -> &str;
21
22    /// Framework-specific constraints to include in LLM prompts.
23    /// Each string is a single constraint line (will be prefixed with "- ").
24    fn llm_constraints(&self) -> &[String];
25
26    /// Warning text about revert pitfalls for batch mode context sections.
27    /// Shown when a batch prompt includes previously-applied fixes to warn
28    /// the LLM about framework-specific patterns it should not undo.
29    ///
30    /// Returns `None` if no revert warnings are needed.
31    fn revert_warnings(&self) -> Option<&str> {
32        None
33    }
34
35    /// Determine the processing priority of a fix request within a batch.
36    /// Lower number = processed first. This ensures structural migration
37    /// rules come before informational/review-only rules.
38    ///
39    /// Default: all rules have equal priority (3).
40    fn fix_priority(&self, _rule_id: &str) -> u8 {
41        3
42    }
43
44    /// Examples of change types for LLM prompt context.
45    ///
46    /// Used in batch prompts to describe the kinds of changes the LLM should
47    /// look for. Should be a parenthetical list of language-specific actions.
48    ///
49    /// Default: generic description suitable for any language.
50    fn change_type_examples(&self) -> &str {
51        "add/remove/rename identifiers, update references"
52    }
53
54    /// Optional verification instructions appended to the LLM batch prompt.
55    ///
56    /// Framework-specific checks the LLM should perform after applying fixes.
57    /// Returns `None` if no verification instructions are needed.
58    fn verification_prompt(&self) -> Option<&str> {
59        None
60    }
61
62    /// System prompt for the OpenAI-compatible LLM client.
63    ///
64    /// Default implementation builds a generic prompt from `migration_description()`.
65    fn llm_system_prompt(&self) -> String {
66        format!(
67            "You are a {} assistant. \
68             Given a code snippet and a migration message, output ONLY the corrected \
69             code for the affected lines. Output in this exact format:\n\n\
70             ```fix\n\
71             LINE:<line_number>\n\
72             OLD:<exact old text on that line>\n\
73             NEW:<replacement text>\n\
74             ```\n\n\
75             You may output multiple fix blocks. Do not include any explanation outside \
76             the fix blocks. Only output fixes for lines that need to change.",
77            self.migration_description()
78        )
79    }
80}
81
82/// Generic fallback context with no framework-specific guidance.
83///
84/// Used when no registered `FixContext` matches the ruleset name.
85/// Provides reasonable defaults that work for any Konveyor analysis output.
86pub struct GenericFixContext;
87
88impl FixContext for GenericFixContext {
89    fn ruleset_name(&self) -> &str {
90        ""
91    }
92
93    fn migration_description(&self) -> &str {
94        "code migration"
95    }
96
97    fn llm_constraints(&self) -> &[String] {
98        &[]
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    #[test]
107    fn test_generic_context_defaults() {
108        let ctx = GenericFixContext;
109        assert_eq!(ctx.ruleset_name(), "");
110        assert_eq!(ctx.migration_description(), "code migration");
111        assert!(ctx.llm_constraints().is_empty());
112        assert!(ctx.revert_warnings().is_none());
113        assert_eq!(ctx.fix_priority("any-rule"), 3);
114    }
115
116    #[test]
117    fn test_generic_context_system_prompt() {
118        let ctx = GenericFixContext;
119        let prompt = ctx.llm_system_prompt();
120        assert!(prompt.contains("code migration"));
121        assert!(prompt.contains("```fix"));
122        assert!(prompt.contains("LINE:"));
123    }
124}