1import * as Context from "effect/Context";
2import * as Effect from "effect/Effect";
3import * as Layer from "effect/Layer";
4import type { ProviderInstanceId } from "@t3tools/contracts";
5import type { ProviderTextGeneration } from "@t3tools/provider-core/server/textGeneration";
6import { TextGenerationError } from "@t3tools/contracts";
7
8import * as ProviderInstanceRegistry from "../provider/ProviderInstanceRegistry.ts";
9import type { ProviderInstance } from "@t3tools/provider-core/server/driver";
10import * as SourceControlProviderRegistry from "../sourceControl/SourceControlProviderRegistry.ts";
11import * as ThreadTitleLinks from "./ThreadTitleLinks.ts";
12
13export type {
14 BranchNameGenerationInput,
15 BranchNameGenerationResult,
16 CommitMessageGenerationInput,
17 CommitMessageGenerationResult,
18 PrContentGenerationInput,
19 PrContentGenerationResult,
20 ReviewGuideGenerationInput,
21 ReviewGuideGenerationResult,
22 ThreadTitleGenerationInput,
23 ThreadTitleGenerationResult,
24} from "@t3tools/provider-core/server/textGeneration";
25
26/**
27 * TextGeneration - Service tag for commit and change request text generation.
28 */
29export class TextGeneration extends Context.Service<TextGeneration, ProviderTextGeneration>()(
30 "t3/textGeneration/TextGeneration",
31) {}
32
33type TextGenerationOp =
34 | "generateCommitMessage"
35 | "generatePrContent"
36 | "generateBranchName"
37 | "generateThreadTitle"
38 | "generateReviewGuide";
39
40const resolveInstance = (
41 registry: ProviderInstanceRegistry.ProviderInstanceRegistry["Service"],
42 operation: TextGenerationOp,
43 instanceId: ProviderInstanceId,
44): Effect.Effect<ProviderInstance["textGeneration"], TextGenerationError> =>
45 registry.getInstance(instanceId).pipe(
46 Effect.flatMap((instance) =>
47 instance
48 ? Effect.succeed(instance.textGeneration)
49 : Effect.fail(
50 new TextGenerationError({
51 operation,
52 detail: `No provider instance registered for id '${instanceId}'.`,
53 }),
54 ),
55 ),
56 );
57
58/** @public Service construction is part of the canonical Effect module API. */
59export const make = Effect.gen(function* () {
60 const registry = yield* ProviderInstanceRegistry.ProviderInstanceRegistry;
61 const sourceControl = yield* SourceControlProviderRegistry.SourceControlProviderRegistry;
62 return TextGeneration.of({
63 generateCommitMessage: (input) =>
64 resolveInstance(registry, "generateCommitMessage", input.modelSelection.instanceId).pipe(
65 Effect.flatMap((textGeneration) => textGeneration.generateCommitMessage(input)),
66 ),
67 generatePrContent: (input) =>
68 resolveInstance(registry, "generatePrContent", input.modelSelection.instanceId).pipe(
69 Effect.flatMap((textGeneration) => textGeneration.generatePrContent(input)),
70 ),
71 generateBranchName: (input) =>
72 resolveInstance(registry, "generateBranchName", input.modelSelection.instanceId).pipe(
73 Effect.flatMap((textGeneration) => textGeneration.generateBranchName(input)),
74 ),
75 generateReviewGuide: (input) =>
76 resolveInstance(registry, "generateReviewGuide", input.modelSelection.instanceId).pipe(
77 Effect.flatMap((textGeneration) => textGeneration.generateReviewGuide(input)),
78 ),
79 generateThreadTitle: (input) =>
80 resolveInstance(registry, "generateThreadTitle", input.modelSelection.instanceId).pipe(
81 Effect.flatMap((textGeneration) =>
82 Effect.gen(function* () {
83 const linkedContext =
84 input.linkedContext ??
85 (yield* ThreadTitleLinks.resolveThreadTitleLinks(input).pipe(
86 Effect.provideService(
87 SourceControlProviderRegistry.SourceControlProviderRegistry,
88 sourceControl,
89 ),
90 ));
91 return yield* textGeneration.generateThreadTitle({ ...input, linkedContext });
92 }),
93 ),
94 ),
95 });
96});
97
98export const layer = Layer.effect(TextGeneration, make);