Add shellgen core and backends modules
This commit is contained in:
128
app/shellgen/core/generator.py
Normal file
128
app/shellgen/core/generator.py
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
"""Command generation logic."""
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from .prompts import PromptTemplates
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class GenerationResult:
|
||||||
|
"""Result of command generation."""
|
||||||
|
|
||||||
|
command: str
|
||||||
|
explanation: str
|
||||||
|
confidence: float
|
||||||
|
|
||||||
|
|
||||||
|
class CommandGenerator:
|
||||||
|
"""Generate shell commands from natural language descriptions."""
|
||||||
|
|
||||||
|
def __init__(self, backend):
|
||||||
|
"""Initialize the command generator.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
backend: LLM backend instance for generating commands.
|
||||||
|
"""
|
||||||
|
self.backend = backend
|
||||||
|
self.templates = PromptTemplates()
|
||||||
|
|
||||||
|
def generate(
|
||||||
|
self,
|
||||||
|
description: str,
|
||||||
|
shell: str = "bash",
|
||||||
|
context: Optional[str] = None,
|
||||||
|
) -> GenerationResult:
|
||||||
|
"""Generate a shell command from natural language.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
description: Natural language description of what to do.
|
||||||
|
shell: Target shell (bash or zsh).
|
||||||
|
context: Optional context (current directory, files, etc.).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
GenerationResult with command, explanation, and confidence.
|
||||||
|
"""
|
||||||
|
prompt = self.templates.generate_prompt(description, shell, context)
|
||||||
|
|
||||||
|
response = self.backend.generate(prompt)
|
||||||
|
|
||||||
|
command = self._extract_command(response)
|
||||||
|
explanation = self._extract_explanation(response)
|
||||||
|
confidence = self._estimate_confidence(response)
|
||||||
|
|
||||||
|
return GenerationResult(
|
||||||
|
command=command.strip(),
|
||||||
|
explanation=explanation.strip(),
|
||||||
|
confidence=confidence,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _extract_command(self, response: str) -> str:
|
||||||
|
"""Extract the shell command from LLM response."""
|
||||||
|
lines = response.strip().split("\n")
|
||||||
|
command = ""
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
line = line.strip()
|
||||||
|
if line.startswith("```"):
|
||||||
|
continue
|
||||||
|
if line.startswith("Command:") or line.startswith("```"):
|
||||||
|
if "```" in line:
|
||||||
|
parts = line.split("```")
|
||||||
|
for part in parts:
|
||||||
|
if part.strip() and not part.strip().startswith("Command"):
|
||||||
|
command = part.strip()
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
command = line.replace("Command:", "").strip()
|
||||||
|
elif line and not line.startswith("#") and not line.startswith("//"):
|
||||||
|
if command:
|
||||||
|
command += " " + line
|
||||||
|
else:
|
||||||
|
command = line
|
||||||
|
|
||||||
|
if not command:
|
||||||
|
for line in lines:
|
||||||
|
if line.startswith("$") or line.startswith("!"):
|
||||||
|
command = line[1:].strip()
|
||||||
|
break
|
||||||
|
|
||||||
|
return command if command else response.strip()
|
||||||
|
|
||||||
|
def _extract_explanation(self, response: str) -> str:
|
||||||
|
"""Extract the explanation from LLM response."""
|
||||||
|
lines = response.strip().split("\n")
|
||||||
|
explanation = []
|
||||||
|
in_explanation = False
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
line_lower = line.lower().strip()
|
||||||
|
if "explanation" in line_lower or "what this does" in line_lower:
|
||||||
|
in_explanation = True
|
||||||
|
continue
|
||||||
|
if line.startswith("```"):
|
||||||
|
in_explanation = False
|
||||||
|
continue
|
||||||
|
if in_explanation:
|
||||||
|
explanation.append(line)
|
||||||
|
elif line.startswith("#") or line.startswith("//"):
|
||||||
|
explanation.append(line.lstrip("#/").strip())
|
||||||
|
|
||||||
|
return " ".join(explanation) if explanation else "Generated command"
|
||||||
|
|
||||||
|
def _estimate_confidence(self, response: str) -> float:
|
||||||
|
"""Estimate confidence score based on response characteristics."""
|
||||||
|
confidence = 0.5
|
||||||
|
|
||||||
|
if "```" in response:
|
||||||
|
confidence += 0.2
|
||||||
|
if len(response) > 50:
|
||||||
|
confidence += 0.1
|
||||||
|
if "sure" in response.lower() or "certain" in response.lower():
|
||||||
|
confidence += 0.1
|
||||||
|
if "?" in response:
|
||||||
|
confidence -= 0.2
|
||||||
|
if "not sure" in response.lower() or "uncertain" in response.lower():
|
||||||
|
confidence -= 0.3
|
||||||
|
|
||||||
|
return max(0.0, min(1.0, confidence))
|
||||||
Reference in New Issue
Block a user