strip_markdown_wrapper robustimmaksi: whitelist-kielitunnisteet + tarkempi ```-poisto
Edelliset heuristiikat olivat hauraita:
- Kielitunniste tunnistettiin "lyhyt alphanumeerinen rivi" → osui koodiin (i, 42)
- rfind("```") poisti koodin sisäisiä backtickejä
Korjaukset:
- Kielitunniste poistetaan VAIN jos se on tunnettu (LANG_TAGS whitelist, 50+ kieltä)
- Sulkeva ``` poistetaan VAIN jos se on omalla rivillään tiedoston lopussa
(ends_with tarkistus + edeltävä rivinvaihto)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -234,27 +234,37 @@ impl LlmEngine {
|
||||
}
|
||||
}
|
||||
|
||||
const LANG_TAGS: &[&str] = &[
|
||||
"python", "py", "rust", "rs", "javascript", "js", "typescript", "ts",
|
||||
"java", "kotlin", "scala", "go", "ruby", "rb", "php", "swift",
|
||||
"c", "cpp", "c++", "c#", "csharp", "r", "sql", "bash", "sh", "zsh",
|
||||
"html", "css", "json", "yaml", "yml", "toml", "xml", "markdown", "md",
|
||||
"lua", "perl", "dart", "elixir", "haskell", "hs", "ocaml", "zig",
|
||||
"plaintext", "text", "txt",
|
||||
];
|
||||
|
||||
/// Siivoa mallin tuottama vastaus (prefill-yhteensopiva).
|
||||
fn strip_markdown_wrapper(text: &str) -> String {
|
||||
let mut result = text.trim().to_string();
|
||||
|
||||
// Poistetaan kielitunniste ensimmäiseltä riviltä
|
||||
// 1. Kielitunniste — VAIN tunnettu kieli
|
||||
if let Some(nl) = result.find('\n') {
|
||||
let first = result[..nl].trim();
|
||||
let is_lang = !first.is_empty()
|
||||
&& first.len() <= 20
|
||||
&& first.chars().all(|c| c.is_alphanumeric() || c == '+' || c == '#');
|
||||
if is_lang { result = result[nl + 1..].to_string(); }
|
||||
}
|
||||
|
||||
// Poistetaan sulkeva ``` lopusta
|
||||
if let Some(pos) = result.rfind("```") {
|
||||
if result[pos + 3..].trim().is_empty() {
|
||||
result = result[..pos].to_string();
|
||||
let first = result[..nl].trim().to_lowercase();
|
||||
if LANG_TAGS.contains(&first.as_str()) {
|
||||
result = result[nl + 1..].to_string();
|
||||
}
|
||||
}
|
||||
|
||||
// Poistetaan johdantolauseet
|
||||
// 2. Sulkeva ``` — VAIN omalla rivillään lopussa
|
||||
let trimmed = result.trim_end();
|
||||
if trimmed.ends_with("```") {
|
||||
let before = &trimmed[..trimmed.len() - 3];
|
||||
if before.is_empty() || before.ends_with('\n') {
|
||||
result = before.trim_end().to_string();
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Johdantolauseet
|
||||
let lower = result.trim().to_lowercase();
|
||||
for prefix in &["sure!", "here is", "here's", "certainly!", "below is"] {
|
||||
if lower.starts_with(prefix) {
|
||||
@@ -263,7 +273,7 @@ fn strip_markdown_wrapper(text: &str) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
// Poistetaan selityskommentit alusta
|
||||
// 4. Selityskommentit alusta
|
||||
let mut lines: Vec<&str> = result.trim().lines().collect();
|
||||
while !lines.is_empty() {
|
||||
let first = lines[0].trim();
|
||||
|
||||
@@ -27,30 +27,37 @@ struct CachedModel {
|
||||
is_3b: bool,
|
||||
}
|
||||
|
||||
/// Tunnetut kielitunnisteet joita malli voi tuottaa prefill-backtickien jälkeen.
|
||||
const LANG_TAGS: &[&str] = &[
|
||||
"python", "py", "rust", "rs", "javascript", "js", "typescript", "ts",
|
||||
"java", "kotlin", "scala", "go", "ruby", "rb", "php", "swift",
|
||||
"c", "cpp", "c++", "c#", "csharp", "r", "sql", "bash", "sh", "zsh",
|
||||
"html", "css", "json", "yaml", "yml", "toml", "xml", "markdown", "md",
|
||||
"lua", "perl", "dart", "elixir", "haskell", "hs", "ocaml", "zig",
|
||||
"plaintext", "text", "txt",
|
||||
];
|
||||
|
||||
/// Siivoa mallin tuottama vastaus.
|
||||
/// Prefill-tekniikan vuoksi malli tuottaa: "rust\nfn main() {...}\n```"
|
||||
/// eli kielitunniste alussa + sulkeva ``` lopussa. Molemmat poistetaan.
|
||||
fn strip_markdown_wrapper(text: &str) -> String {
|
||||
let mut result = text.trim().to_string();
|
||||
|
||||
// 1. Poistetaan kielitunniste ensimmäiseltä riviltä (rust, python, javascript jne.)
|
||||
// Tunnistetaan: rivi jossa vain yksi lyhyt sana ilman välilyöntejä tai erikoismerkkejä
|
||||
// 1. Poistetaan kielitunniste ensimmäiseltä riviltä — VAIN jos se on tunnettu kieli
|
||||
if let Some(first_newline) = result.find('\n') {
|
||||
let first_line = result[..first_newline].trim();
|
||||
let is_lang_tag = !first_line.is_empty()
|
||||
&& first_line.len() <= 20
|
||||
&& first_line.chars().all(|c| c.is_alphanumeric() || c == '+' || c == '#');
|
||||
if is_lang_tag {
|
||||
let first_line = result[..first_newline].trim().to_lowercase();
|
||||
if LANG_TAGS.contains(&first_line.as_str()) {
|
||||
result = result[first_newline + 1..].to_string();
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Poistetaan sulkeva ``` lopusta
|
||||
if let Some(pos) = result.rfind("```") {
|
||||
// Varmistetaan ettei poisteta koodin sisällä olevaa ```
|
||||
let after = result[pos + 3..].trim();
|
||||
if after.is_empty() {
|
||||
result = result[..pos].to_string();
|
||||
// 2. Poistetaan sulkeva ``` VAIN jos se on omalla rivillään lopussa
|
||||
let trimmed = result.trim_end();
|
||||
if trimmed.ends_with("```") {
|
||||
let before = &trimmed[..trimmed.len() - 3];
|
||||
// Varmistetaan: edellinen merkki on rivinvaihto tai alku (eli ``` on oma rivinsä)
|
||||
if before.is_empty() || before.ends_with('\n') {
|
||||
result = before.trim_end().to_string();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user