Skip to content
Source
Sign inSign up
Open History to see the last commit

postProcessing.ts

65 lines2.7 KB

1import {
2 DEFAULT_SPEECH_POST_PROCESSING_PROMPT,
3 type EnvironmentSpeechPostProcessingRequest,
4} from "@t3tools/contracts";
5import * as Context from "effect/Context";
6import * as Effect from "effect/Effect";
7import * as FileSystem from "effect/FileSystem";
8import * as Layer from "effect/Layer";
9import { resolveSpeechPostProcessingModelSelection } from "@t3tools/shared/serverSettings";
10​
11import * as ServerSettings from "../serverSettings.ts";
12import * as TextGeneration from "../textGeneration/TextGeneration.ts";
13import { buildTranscriptionPostProcessingPrompt } from "../textGeneration/TranscriptionPostProcessing.ts";
14import { applySpeechAliases } from "./customWords.ts";
15import { SpeechOperationError } from "./SpeechService.ts";
16​
17export class SpeechPostProcessing extends Context.Service<
18 SpeechPostProcessing,
19 {
20 readonly process: (
21 input: EnvironmentSpeechPostProcessingRequest,
22 ) => Effect.Effect<{ readonly text: string }, SpeechOperationError>;
23 }
24>()("t3/speech/postProcessing/SpeechPostProcessing") {}
25​
26const make = Effect.gen(function* () {
27 const settingsService = yield* ServerSettings.ServerSettingsService;
28 const fileSystem = yield* FileSystem.FileSystem;
29 const textGeneration = yield* TextGeneration.TextGeneration;
30 return SpeechPostProcessing.of({
31 process: Effect.fn("SpeechPostProcessing.process")(
32 function* (input) {
33 if (input.transcript.trim().length === 0) return { text: input.transcript };
34 const settings = yield* settingsService.getSettings;
35 const { mode, customInstructions } = input.options.speechPostProcessingPrompt;
36 const instructions =
37 (mode === "custom" ? customInstructions.trim() : "") ||
38 DEFAULT_SPEECH_POST_PROCESSING_PROMPT;
39 // Providers run in a working directory; cleanup must not read a checkout.
40 const cwd = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-voice-cleanup-" });
41 const generated = yield* textGeneration.generateTranscriptionPostProcessing({
42 cwd,
43 prompt: buildTranscriptionPostProcessingPrompt(
44 instructions,
45 input.transcript,
46 input.draft,
47 input.options.speechCorrectionWord,
48 ),
49 modelSelection: resolveSpeechPostProcessingModelSelection(settings),
50 });
51 const text = generated.transcription.trim();
52 return {
53 text:
54 text.length > 0
55 ? applySpeechAliases(text, input.options.speechCustomWords)
56 : input.transcript,
57 };
58 },
59 Effect.scoped,
60 Effect.mapError((cause) => new SpeechOperationError({ operation: "post-processing", cause })),
61 ),
62 });
63});
64​
65export const layer = Layer.effect(SpeechPostProcessing, make);