θΏ™ζ˜―indexlocζδΎ›ηš„ζœεŠ‘οΌŒδΈθ¦θΎ“ε…₯任何密码
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { useTranslation } from "react-i18next";

let timeout;
const SILENCE_INTERVAL = 3_200; // wait in seconds of silence before closing.
export default function SpeechToText({ sendCommand }) {
export default function SpeechToText({ sendSTTCommand }) {
const {
transcript,
listening,
Expand Down Expand Up @@ -40,7 +40,7 @@ export default function SpeechToText({ sendCommand }) {
function endSTTSession() {
SpeechRecognition.stopListening();
if (transcript.length > 0) {
sendCommand(transcript, true);
sendSTTCommand(transcript);
}

resetTranscript();
Expand Down Expand Up @@ -83,7 +83,6 @@ export default function SpeechToText({ sendCommand }) {

useEffect(() => {
if (transcript?.length > 0 && listening) {
sendCommand(transcript, false);
clearTimeout(timeout);
timeout = setTimeout(() => {
endSTTSession();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import AttachmentManager from "./Attachments";
import AttachItem from "./AttachItem";
import { PASTE_ATTACHMENT_EVENT } from "../DnDWrapper";
import useTextSize from "@/hooks/useTextSize";
import System from "@/models/system";
import { useTranslation } from "react-i18next";

export const PROMPT_INPUT_EVENT = "set_prompt_input";
Expand All @@ -39,6 +40,7 @@ export default function PromptInput({
const undoStack = useRef([]);
const redoStack = useRef([]);
const { textSizeClass } = useTextSize();
const [speechToTextAutoSubmit, setSpeechToTextAutoSubmit] = useState(true);

/**
* To prevent too many re-renders we remotely listen for updates from the parent
Expand All @@ -62,6 +64,14 @@ export default function PromptInput({
resetTextAreaHeight();
}, [isStreaming]);

useEffect(() => {
async function getSettings() {
const _settings = await System.keys();
setSpeechToTextAutoSubmit(_settings?.SpeechToTextAutoSubmit === "true");
}
getSettings();
}, []);

/**
* Save the current state before changes
* @param {number} adjustment
Expand Down Expand Up @@ -202,26 +212,36 @@ export default function PromptInput({

const pasteText = e.clipboardData.getData("text/plain");
if (pasteText) {
const textarea = textareaRef.current;
const start = textarea.selectionStart;
const end = textarea.selectionEnd;
const newPromptInput =
promptInput.substring(0, start) +
pasteText +
promptInput.substring(end);
setPromptInput(newPromptInput);
onChange({ target: { value: newPromptInput } });

// Set the cursor position after the pasted text
// we need to use setTimeout to prevent the cursor from being set to the end of the text
setTimeout(() => {
textarea.selectionStart = textarea.selectionEnd =
start + pasteText.length;
}, 0);
addToInputPrompt(pasteText);
}
return;
}

function addToInputPrompt(text) {
const textarea = textareaRef.current;
const start = textarea.selectionStart;
const end = textarea.selectionEnd;
const newPromptInput =
promptInput.substring(0, start) + text + promptInput.substring(end);
setPromptInput(newPromptInput);
onChange({ target: { value: newPromptInput } });

// Set the cursor position after the pasted text
// we need to use setTimeout to prevent the cursor from being set to the end of the text
setTimeout(() => {
textarea.selectionStart = textarea.selectionEnd =
start + text.length;
}, 0);
}

function sendSTTCommand(text) {
if (speechToTextAutoSubmit) {
sendCommand(text, true);
} else {
addToInputPrompt(text);
}
}

function handleChange(e) {
debouncedSaveState(-1);
onChange(e);
Expand Down Expand Up @@ -312,7 +332,7 @@ export default function PromptInput({
<TextSizeButton />
</div>
<div className="flex gap-x-2">
<SpeechToText sendCommand={sendCommand} />
<SpeechToText sendSTTCommand={sendSTTCommand} />
</div>
</div>
</div>
Expand Down
22 changes: 21 additions & 1 deletion frontend/src/pages/GeneralSettings/AudioPreference/stt.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,14 @@ export default function SpeechToTextProvider({ settings }) {
);
const [searchMenuOpen, setSearchMenuOpen] = useState(false);
const searchInputRef = useRef(null);
const [autoSubmit, setAutoSubmit] = useState(
settings?.SpeechToTextAutoSubmit === "true"
);

const handleSubmit = async (e) => {
e.preventDefault();
const form = e.target;
const data = { SpeechToTextProvider: selectedProvider };
const data = { SpeechToTextProvider: selectedProvider, SpeechToTextAutoSubmit: String(autoSubmit) };
const formData = new FormData(form);

for (var [key, value] of formData.entries()) data[key] = value;
Expand All @@ -54,6 +57,11 @@ export default function SpeechToTextProvider({ settings }) {
setHasChanges(true);
};

const updateAutoSubmitChoice = () => {
setAutoSubmit(!autoSubmit);
setHasChanges(true);
}

const handleXButton = () => {
if (searchQuery.length > 0) {
setSearchQuery("");
Expand Down Expand Up @@ -185,6 +193,18 @@ export default function SpeechToTextProvider({ settings }) {
(provider) => provider.value === selectedProvider
)?.options(settings)}
</div>
<div className="text-base font-bold text-white mt-6 mb-4">Autosubmit</div>
<div>
<p className="text-xs leading-[18px] font-base text-white text-opacity-60">
<input
onChange={updateAutoSubmitChoice}
type="checkbox"
checked={autoSubmit}
/>&nbsp;
Here you can specify whether you want your speech to text prompt to be submitted automatically after 3s of silence
or if it should be submitted manually
</p>
</div>
</div>
</form>
);
Expand Down
2 changes: 2 additions & 0 deletions server/models/systemSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@ const SystemSettings = {
// TTS/STT Selection Settings & Configs
// - Currently the only 3rd party is OpenAI or the native browser-built in
// --------------------------------------------------------
SpeechToTextAutoSubmit: process.env.STT_AUTO_SUBMIT || "true",

TextToSpeechProvider: process.env.TTS_PROVIDER || "native",
TTSOpenAIKey: !!process.env.TTS_OPEN_AI_KEY,
TTSOpenAIVoiceModel: process.env.TTS_OPEN_AI_VOICE_MODEL,
Expand Down
13 changes: 13 additions & 0 deletions server/utils/helpers/updateENV.js
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,11 @@ const KEY_MAPPING = {
},

// TTS/STT Integration ENVS
SpeechToTextAutoSubmit: {
envKey: "STT_AUTO_SUBMIT",
checks: [validBooleanString],
},

TextToSpeechProvider: {
envKey: "TTS_PROVIDER",
checks: [supportedTTSProvider],
Expand Down Expand Up @@ -683,6 +688,14 @@ function validOllamaLLMBasePath(input = "") {
}
}

function validBooleanString(input = "") {
const validSelection = [
"true",
"false",
].includes(input);
return validSelection ? null : `${input} is not a valid boolean flag.`;
}

function supportedTTSProvider(input = "") {
const validSelection = [
"native",
Expand Down