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).
|
/// Siivoa mallin tuottama vastaus (prefill-yhteensopiva).
|
||||||
fn strip_markdown_wrapper(text: &str) -> String {
|
fn strip_markdown_wrapper(text: &str) -> String {
|
||||||
let mut result = text.trim().to_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') {
|
if let Some(nl) = result.find('\n') {
|
||||||
let first = result[..nl].trim();
|
let first = result[..nl].trim().to_lowercase();
|
||||||
let is_lang = !first.is_empty()
|
if LANG_TAGS.contains(&first.as_str()) {
|
||||||
&& first.len() <= 20
|
result = result[nl + 1..].to_string();
|
||||||
&& 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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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();
|
let lower = result.trim().to_lowercase();
|
||||||
for prefix in &["sure!", "here is", "here's", "certainly!", "below is"] {
|
for prefix in &["sure!", "here is", "here's", "certainly!", "below is"] {
|
||||||
if lower.starts_with(prefix) {
|
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();
|
let mut lines: Vec<&str> = result.trim().lines().collect();
|
||||||
while !lines.is_empty() {
|
while !lines.is_empty() {
|
||||||
let first = lines[0].trim();
|
let first = lines[0].trim();
|
||||||
|
|||||||
@@ -27,30 +27,37 @@ struct CachedModel {
|
|||||||
is_3b: bool,
|
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.
|
/// Siivoa mallin tuottama vastaus.
|
||||||
/// Prefill-tekniikan vuoksi malli tuottaa: "rust\nfn main() {...}\n```"
|
/// Prefill-tekniikan vuoksi malli tuottaa: "rust\nfn main() {...}\n```"
|
||||||
/// eli kielitunniste alussa + sulkeva ``` lopussa. Molemmat poistetaan.
|
/// eli kielitunniste alussa + sulkeva ``` lopussa. Molemmat poistetaan.
|
||||||
fn strip_markdown_wrapper(text: &str) -> String {
|
fn strip_markdown_wrapper(text: &str) -> String {
|
||||||
let mut result = text.trim().to_string();
|
let mut result = text.trim().to_string();
|
||||||
|
|
||||||
// 1. Poistetaan kielitunniste ensimmäiseltä riviltä (rust, python, javascript jne.)
|
// 1. Poistetaan kielitunniste ensimmäiseltä riviltä — VAIN jos se on tunnettu kieli
|
||||||
// Tunnistetaan: rivi jossa vain yksi lyhyt sana ilman välilyöntejä tai erikoismerkkejä
|
|
||||||
if let Some(first_newline) = result.find('\n') {
|
if let Some(first_newline) = result.find('\n') {
|
||||||
let first_line = result[..first_newline].trim();
|
let first_line = result[..first_newline].trim().to_lowercase();
|
||||||
let is_lang_tag = !first_line.is_empty()
|
if LANG_TAGS.contains(&first_line.as_str()) {
|
||||||
&& first_line.len() <= 20
|
|
||||||
&& first_line.chars().all(|c| c.is_alphanumeric() || c == '+' || c == '#');
|
|
||||||
if is_lang_tag {
|
|
||||||
result = result[first_newline + 1..].to_string();
|
result = result[first_newline + 1..].to_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Poistetaan sulkeva ``` lopusta
|
// 2. Poistetaan sulkeva ``` VAIN jos se on omalla rivillään lopussa
|
||||||
if let Some(pos) = result.rfind("```") {
|
let trimmed = result.trim_end();
|
||||||
// Varmistetaan ettei poisteta koodin sisällä olevaa ```
|
if trimmed.ends_with("```") {
|
||||||
let after = result[pos + 3..].trim();
|
let before = &trimmed[..trimmed.len() - 3];
|
||||||
if after.is_empty() {
|
// Varmistetaan: edellinen merkki on rivinvaihto tai alku (eli ``` on oma rivinsä)
|
||||||
result = result[..pos].to_string();
|
if before.is_empty() || before.ends_with('\n') {
|
||||||
|
result = before.trim_end().to_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user