1import * as GCP from "@/GCP";
2import * as Effect from "effect/Effect";
3import { serveProbes } from "../../bindingHost.ts";
4import { location } from "../common.ts";
5
6export const Ships = GCP.Speech.CustomClass("BindShips", {
7 location,
8 items: [{ value: "sloop" }, { value: "schooner" }],
9});
10
11export const Hints = GCP.Speech.PhraseSet("BindHints", {
12 location,
13 phrases: [{ value: "weather" }],
14});
15
16/** ~0.1 s of 16 kHz LINEAR16 silence (3000 zero bytes, base64). */
17const SILENCE = "A".repeat(4000);
18
19/**
20 * Effect-native Cloud Run service exercising every Speech-to-Text binding
21 * as its own runtime service account. Deployed from
22 * {@link ../Bindings.test.ts}.
23 */
24export default class SpeechBindingsHost extends GCP.Function<SpeechBindingsHost>()(
25 "SpeechBindingsHost",
26 { main: import.meta.url, invokerIamDisabled: true },
27 Effect.gen(function* () {
28 const getCustomClass = yield* GCP.Speech.GetCustomClass(Ships);
29 const getPhraseSet = yield* GCP.Speech.GetPhraseSet(Hints);
30 const recognize = yield* GCP.Speech.Recognize(Hints);
31
32 return {
33 fetch: serveProbes({
34 getCustomClass: getCustomClass().pipe(
35 Effect.map((customClass) => ({
36 name: customClass.name,
37 items: (customClass.items ?? []).map((item) => item.value),
38 })),
39 ),
40 getPhraseSet: getPhraseSet().pipe(
41 Effect.map((phraseSet) => ({
42 name: phraseSet.name,
43 phrases: (phraseSet.phrases ?? []).map((phrase) => phrase.value),
44 })),
45 ),
46 recognize: recognize({
47 body: {
48 config: {
49 languageCode: "en-US",
50 encoding: "LINEAR16",
51 sampleRateHertz: 16000,
52 },
53 audio: { content: SILENCE },
54 },
55 }).pipe(
56 Effect.map((response) => ({
57 results: response.results ?? [],
58 totalBilledTime: response.totalBilledTime,
59 })),
60 ),
61 }),
62 };
63 }).pipe(
64 Effect.provide(GCP.Speech.GetCustomClassHttp),
65 Effect.provide(GCP.Speech.GetPhraseSetHttp),
66 Effect.provide(GCP.Speech.RecognizeHttp),
67 ),
68) {}