Skip to content

clean_for_speech - post process markdown text fit for reading aloud - #4

Open
factorial wants to merge 1 commit into
PenumbraOS:masterfrom
factorial:markdown-cleanup-post-processor
Open

clean_for_speech - post process markdown text fit for reading aloud#4
factorial wants to merge 1 commit into
PenumbraOS:masterfrom
factorial:markdown-cleanup-post-processor

Conversation

@factorial

Copy link
Copy Markdown
Contributor

Since LLM responses (especially OpenAI) often include markdown that sounds bad when read aloud, post processing function for cleaning up the response for speech.

Comment thread server-rs/src/util/mod.rs
// Matches RFC 3986 scheme followed by `://` and a run of non-whitespace,
// non-quote/angle-bracket characters.
static URL_PATTERN: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r#"\b[a-zA-Z][a-zA-Z0-9+.-]*://[^\s<>"]+"#).expect("valid regex"));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're not validating URLs, I think any schema followed by :// followed by anything up until whitespace can be considered a URL

Comment thread server-rs/Cargo.toml
Comment thread server-rs/src/util/mod.rs
let trimmed = matched.trim_end_matches(TRAILING_PUNCTUATION);
let trailing = &matched[trimmed.len()..];

match reqwest::Url::parse(trimmed).ok() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very LLM. I have no idea why you would do this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, this is leftover from when originally I was leaving the domain behind of any URL returned. OpenAI's web search is very fond of returning exact source URLs. For a minute I thought it'd be nice to say "example.com" but ultimately decided removing them entirely was better. Will clean up.

Comment thread server-rs/src/util/mod.rs

// Characters that are almost always sentence punctuation that may come
// after a URL (e.g. "(see https://example.com).").
const TRAILING_PUNCTUATION: &[char] = &['.', ',', ';', ':', '!', '?', ')', ']', '\'', '"'];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this idea, but I think it's done incorrectly. I would grab the entire URL. If it ends in any of the above characters (probably not \' lol), remove all BUT that last character. We keep the trailing punctuation so it's spoken correctly.

Comment thread server-rs/src/util/mod.rs
}


/// Removes URLs from `text`. Useful for preventing LLM responses being read aloud

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No LLM comments

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was actually mine lol but if you prefer its removal that's OK

let mut list_item_seen: Vec<bool> = Vec::new();
let mut skip = Skip::None;

let push_block_separator = |output: &mut String| {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is overcomplicated LLM garbage. You want to add a sentence terminator (.) after very markdown block; no blocks exist mid-sentence

if is_first {
push_block_separator(&mut output);
} else {
output.push_str("; ");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not a comma? Should be a const


let mut output = String::new();
let mut list_item_seen: Vec<bool> = Vec::new();
let mut skip = Skip::None;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be a stack

@@ -0,0 +1,293 @@
use pulldown_cmark::{Event, Options, Parser, Tag, TagEnd};

enum Skip {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NestedContext. None is not necessary

match event {
Event::Start(tag) => match tag {
Tag::CodeBlock(_) => {
skip = Skip::CodeBlock(String::new());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You enter the code block. You push to the context stack. You do nothing else until you see the codeblock end token. At that point, you pop the stack, and IFF the stack is empty, you write the codeblock substitution string.

Same for all other blocks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants