Class: Scryer::AiFixSuggester
- Inherits:
-
Object
- Object
- Scryer::AiFixSuggester
- Defined in:
- lib/scryer/ai_fix_suggester.rb
Overview
Optional, opt-in enrichment of a finding's suggested_fix using an
external LLM. Every rule already ships a generic, human-reviewable
suggested fix (see Rule#finding) — this replaces that generic text with
one written against the finding's actual code snippet, when the host
app has configured an LLM client.
Scryer stays entirely provider-agnostic here: client is any object
(or bare Proc/lambda) responding to #call(prompt) — or #complete(prompt)
— that returns the model's reply as a String. Claude, OpenAI, a local
Ollama server, a Bedrock/Vertex-backed client, a fake in a test: all of
them work identically, and nothing in this class knows or cares which
one is in use. Scryer::AiClient (ai_client.rb) is a small ready-made
adapter for wiring up any JSON/HTTP chat endpoint; using it is optional.
Off by default: with no client configured (the default), .enhance!/ .enhance_result! are no-ops, and nothing here ever runs — this feature makes no network calls unless Scryer.configuration.ai_client is set. Nothing is ever auto-applied to source files; this only changes the text of a finding's suggested_fix, same as every other rule's fix text — still just something for a human to read and act on.
Class Method Summary collapse
-
.enhance!(finding, client: Scryer.configuration.ai_client) ⇒ Object
Enhances a single Finding in place and returns it.
-
.enhance_many!(findings, client: Scryer.configuration.ai_client, concurrency: 4) ⇒ Object
Same as enhance_result! but for a plain array of findings — used for Scryer::DependencyAudit::Finding objects, which aren't attached to a Scanner::Result.
-
.enhance_result!(result, client: Scryer.configuration.ai_client, concurrency: 4) ⇒ Object
Enhances every security/performance/style finding on a Scanner::Result in place.
Class Method Details
.enhance!(finding, client: Scryer.configuration.ai_client) ⇒ Object
Enhances a single Finding in place and returns it. Any failure (client raises, times out, returns nothing usable) is swallowed and the finding's original suggested_fix is left as-is — an LLM call failing should never break a scan.
28 29 30 31 32 33 34 35 36 |
# File 'lib/scryer/ai_fix_suggester.rb', line 28 def enhance!(finding, client: Scryer.configuration.ai_client) return finding unless client reply = call_client(client, prompt_for(finding)) finding.suggested_fix = reply.strip unless blank?(reply) finding rescue StandardError finding end |
.enhance_many!(findings, client: Scryer.configuration.ai_client, concurrency: 4) ⇒ Object
Same as enhance_result! but for a plain array of findings — used for Scryer::DependencyAudit::Finding objects, which aren't attached to a Scanner::Result. Works on any mix of Finding/DependencyAudit::Finding (prompt_for below dispatches on which one it got). No-op if no client is configured.
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/scryer/ai_fix_suggester.rb', line 55 def enhance_many!(findings, client: Scryer.configuration.ai_client, concurrency: 4) return findings unless client queue = Queue.new findings.each { |f| queue << f } workers = Array.new([concurrency, findings.size].min) do Thread.new do loop do finding = begin queue.pop(true) rescue ThreadError nil end break unless finding enhance!(finding, client: client) end end end workers.each(&:join) findings end |
.enhance_result!(result, client: Scryer.configuration.ai_client, concurrency: 4) ⇒ Object
Enhances every security/performance/style finding on a Scanner::Result in place. Runs across a small thread pool (network-bound work, same pattern as DependencyAudit.vulnerable_gems) so a large finding count doesn't mean one-request-at-a-time. No-op if no client is configured — callers don't need to check first.
44 45 46 47 48 |
# File 'lib/scryer/ai_fix_suggester.rb', line 44 def enhance_result!(result, client: Scryer.configuration.ai_client, concurrency: 4) enhance_many!(result.security_findings + result.performance_findings + result.style_findings, client: client, concurrency: concurrency) result end |