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