Mercurial Hosting > lang
changeset 12:2d4b3f003ec2
tts
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Thu, 24 Jul 2025 22:14:49 -0600 |
parents | 003a90ce72d7 |
children | 65bd7e245c63 |
files | .hgignore src/chat.js src/lib/ai/claude/Chat.luan src/private/Config_sample.luan src/site.js src/tts.mp3.luan |
diffstat | 6 files changed, 63 insertions(+), 10 deletions(-) [+] |
line wrap: on
line diff
diff -r 003a90ce72d7 -r 2d4b3f003ec2 .hgignore --- a/.hgignore Tue Jul 22 15:30:10 2025 -0600 +++ b/.hgignore Thu Jul 24 22:14:49 2025 -0600 @@ -5,3 +5,4 @@ private/Config.luan mine/ .DS_Store +test/
diff -r 003a90ce72d7 -r 2d4b3f003ec2 src/chat.js --- a/src/chat.js Tue Jul 22 15:30:10 2025 -0600 +++ b/src/chat.js Thu Jul 24 22:14:49 2025 -0600 @@ -52,6 +52,11 @@ scroll.scrollTo(0,scroll.scrollHeight); }); */ + let audios = document.querySelectorAll('audio'); + if( audios.length >= 1 ) { + let audio = audios[audios.length-1]; + audio.play(); + } } const isMobile = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;
diff -r 003a90ce72d7 -r 2d4b3f003ec2 src/lib/ai/claude/Chat.luan --- a/src/lib/ai/claude/Chat.luan Tue Jul 22 15:30:10 2025 -0600 +++ b/src/lib/ai/claude/Chat.luan Thu Jul 24 22:14:49 2025 -0600 @@ -96,16 +96,7 @@ messages[#messages+1] = { role = "assistant" content = [[ -- 1 -- 2 -- 3 -- 4 -- 5 -- 6 -- 7 -- 8 -- 9 -- 10 +uhuh ]] } if true then
diff -r 003a90ce72d7 -r 2d4b3f003ec2 src/private/Config_sample.luan --- a/src/private/Config_sample.luan Tue Jul 22 15:30:10 2025 -0600 +++ b/src/private/Config_sample.luan Thu Jul 24 22:14:49 2025 -0600 @@ -8,4 +8,8 @@ claude = { key = "sk-xxx" } + azure_tts = { + key = "xxx" + region = "eastus" + } }
diff -r 003a90ce72d7 -r 2d4b3f003ec2 src/site.js --- a/src/site.js Tue Jul 22 15:30:10 2025 -0600 +++ b/src/site.js Thu Jul 24 22:14:49 2025 -0600 @@ -119,7 +119,11 @@ window.scrollTo( 0, lastY ); } + // requires markdown-it + +let mdDiv = document.createElement('div'); + function handleMarkdown() { let converter = window.markdownit({html: true}); let divs = document.querySelectorAll('[markdown]'); @@ -127,6 +131,15 @@ let text = div.textContent; text = text.replace(/\{([^|}]+)\|([^|}]+)\}/g, '<ruby>$1<rt>$2</rt></ruby>'); text = converter.render(text); + if( div.getAttribute('role')==='assistant' ) { + mdDiv.innerHTML = text; + let rts = mdDiv.querySelectorAll('rt'); + for( let rt of rts ) { + rt.remove(); + } + //console.log(mdDiv.textContent); + text += `\n<p><audio controls preload=none src="/tts.mp3?text=${encodeURIComponent(mdDiv.textContent)}"></p>\n`; + } div.innerHTML = text; div.removeAttribute('markdown'); }
diff -r 003a90ce72d7 -r 2d4b3f003ec2 src/tts.mp3.luan --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/tts.mp3.luan Thu Jul 24 22:14:49 2025 -0600 @@ -0,0 +1,39 @@ +local Luan = require "luan:Luan.luan" +local error = Luan.error +local Parsers = require "luan:Parsers.luan" +local xml_encode = Parsers.xml_encode or error() +local Io = require "luan:Io.luan" +local uri = Io.uri or error() +local Http = require "luan:http/Http.luan" +local Config = require "site:/private/Config.luan" + + +local region = Config.azure_tts.region or error() +local url = "https://"..region..".tts.speech.microsoft.com/cognitiveservices/v1" +local headers = { + ["Ocp-Apim-Subscription-Key"] = Config.azure_tts.key or error() + ["Content-Type"] = "application/ssml+xml" + ["X-Microsoft-OutputFormat"] = "audio-16khz-128kbitrate-mono-mp3" +} + +local function text_to_speech(lang,text) + local xml = `%> +<speak version='1.0' xml:lang='<%=lang%>'> + <voice name='en-US-BrandonMultilingualNeural'> +<%= xml_encode(text) %> + </voice> +</speak> +<% ` + local options = { + method = "POST" + headers = headers + content = xml + } + return uri(url,options) +end + +return function() + local text = Http.request.parameters.text or error() + local input = text_to_speech("ja-JP",text) + Http.response.binary_writer().write_from(input) +end