| author | |
| committer | |
| log | 579a69eb7756b2169d26299d36f936838db39caa |
| tree | 594f6b85cfdb645425b60063c03580bf64fb0d41 |
| parent | 9ee8c4f5430a221d6569ba9c2f984d8501ec4fe5 |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Julius Marminge <julius0216@outlook.com>11 files changed, 118 insertions(+), 9 deletions(-)
apps/server/src/git/Layers/CodexTextGeneration.ts+7-2| ... | ... | @@ -3,6 +3,7 @@ import { randomUUID } from "node:crypto"; |
| 3 | 3 | import { Effect, FileSystem, Layer, Option, Path, Schema, Stream } from "effect"; |
| 4 | 4 | import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; |
| 5 | 5 | |
| 6 | import { DEFAULT_GIT_TEXT_GENERATION_MODEL } from "@t3tools/contracts"; | |
| 6 | 7 | import { sanitizeBranchFragment, sanitizeFeatureBranchName } from "@t3tools/shared/git"; |
| 7 | 8 | |
| 8 | 9 | import { resolveAttachmentPath } from "../../attachmentStore.ts"; |
| ... | ... | @@ -17,7 +18,6 @@ import { |
| 17 | 18 | TextGeneration, |
| 18 | 19 | } from "../Services/TextGeneration.ts"; |
| 19 | 20 | |
| 20 | const CODEX_MODEL = "gpt-5.3-codex"; | |
| 21 | 21 | const CODEX_REASONING_EFFORT = "low"; |
| 22 | 22 | const CODEX_TIMEOUT_MS = 180_000; |
| 23 | 23 | |
| ... | ... | @@ -187,6 +187,7 @@ const makeCodexTextGeneration = Effect.gen(function* () { |
| 187 | 187 | outputSchemaJson, |
| 188 | 188 | imagePaths = [], |
| 189 | 189 | cleanupPaths = [], |
| 190 | model, | |
| 190 | 191 | }: { |
| 191 | 192 | operation: "generateCommitMessage" | "generatePrContent" | "generateBranchName"; |
| 192 | 193 | cwd: string; |
| ... | ... | @@ -194,6 +195,7 @@ const makeCodexTextGeneration = Effect.gen(function* () { |
| 194 | 195 | outputSchemaJson: S; |
| 195 | 196 | imagePaths?: ReadonlyArray<string>; |
| 196 | 197 | cleanupPaths?: ReadonlyArray<string>; |
| 198 | model?: string; | |
| 197 | 199 | }): Effect.Effect<S["Type"], TextGenerationError, S["DecodingServices"]> => |
| 198 | 200 | Effect.gen(function* () { |
| 199 | 201 | const schemaPath = yield* writeTempFile( |
| ... | ... | @@ -212,7 +214,7 @@ const makeCodexTextGeneration = Effect.gen(function* () { |
| 212 | 214 | "-s", |
| 213 | 215 | "read-only", |
| 214 | 216 | "--model", |
| 215 | CODEX_MODEL, | |
| 217 | model ?? DEFAULT_GIT_TEXT_GENERATION_MODEL, | |
| 216 | 218 | "--config", |
| 217 | 219 | `model_reasoning_effort="${CODEX_REASONING_EFFORT}"`, |
| 218 | 220 | "--output-schema", |
| ... | ... | @@ -353,6 +355,7 @@ const makeCodexTextGeneration = Effect.gen(function* () { |
| 353 | 355 | cwd: input.cwd, |
| 354 | 356 | prompt, |
| 355 | 357 | outputSchemaJson, |
| 358 | ...(input.model ? { model: input.model } : {}), | |
| 356 | 359 | }).pipe( |
| 357 | 360 | Effect.map( |
| 358 | 361 | (generated) => |
| ... | ... | @@ -398,6 +401,7 @@ const makeCodexTextGeneration = Effect.gen(function* () { |
| 398 | 401 | title: Schema.String, |
| 399 | 402 | body: Schema.String, |
| 400 | 403 | }), |
| 404 | ...(input.model ? { model: input.model } : {}), | |
| 401 | 405 | }).pipe( |
| 402 | 406 | Effect.map( |
| 403 | 407 | (generated) => |
| ... | ... | @@ -449,6 +453,7 @@ const makeCodexTextGeneration = Effect.gen(function* () { |
| 449 | 453 | branch: Schema.String, |
| 450 | 454 | }), |
| 451 | 455 | imagePaths, |
| 456 | ...(input.model ? { model: input.model } : {}), | |
| 452 | 457 | }); |
| 453 | 458 | |
| 454 | 459 | return { |
apps/server/src/git/Layers/GitManager.ts+11-2| ... | ... | @@ -639,6 +639,7 @@ export const makeGitManager = Effect.gen(function* () { |
| 639 | 639 | /** When true, also produce a semantic feature branch name. */ |
| 640 | 640 | includeBranch?: boolean; |
| 641 | 641 | filePaths?: readonly string[]; |
| 642 | model?: string; | |
| 642 | 643 | }) => |
| 643 | 644 | Effect.gen(function* () { |
| 644 | 645 | const context = yield* gitCore.prepareCommitContext(input.cwd, input.filePaths); |
| ... | ... | @@ -665,6 +666,7 @@ export const makeGitManager = Effect.gen(function* () { |
| 665 | 666 | stagedSummary: limitContext(context.stagedSummary, 8_000), |
| 666 | 667 | stagedPatch: limitContext(context.stagedPatch, 50_000), |
| 667 | 668 | ...(input.includeBranch ? { includeBranch: true } : {}), |
| 669 | ...(input.model ? { model: input.model } : {}), | |
| 668 | 670 | }) |
| 669 | 671 | .pipe(Effect.map((result) => sanitizeCommitMessage(result))); |
| 670 | 672 | |
| ... | ... | @@ -682,6 +684,7 @@ export const makeGitManager = Effect.gen(function* () { |
| 682 | 684 | commitMessage?: string, |
| 683 | 685 | preResolvedSuggestion?: CommitAndBranchSuggestion, |
| 684 | 686 | filePaths?: readonly string[], |
| 687 | model?: string, | |
| 685 | 688 | ) => |
| 686 | 689 | Effect.gen(function* () { |
| 687 | 690 | const suggestion = |
| ... | ... | @@ -691,6 +694,7 @@ export const makeGitManager = Effect.gen(function* () { |
| 691 | 694 | branch, |
| 692 | 695 | ...(commitMessage ? { commitMessage } : {}), |
| 693 | 696 | ...(filePaths ? { filePaths } : {}), |
| 697 | ...(model ? { model } : {}), | |
| 694 | 698 | })); |
| 695 | 699 | if (!suggestion) { |
| 696 | 700 | return { status: "skipped_no_changes" as const }; |
| ... | ... | @@ -704,7 +708,7 @@ export const makeGitManager = Effect.gen(function* () { |
| 704 | 708 | }; |
| 705 | 709 | }); |
| 706 | 710 | |
| 707 | const runPrStep = (cwd: string, fallbackBranch: string | null) => | |
| 711 | const runPrStep = (cwd: string, fallbackBranch: string | null, model?: string) => | |
| 708 | 712 | Effect.gen(function* () { |
| 709 | 713 | const details = yield* gitCore.statusDetails(cwd); |
| 710 | 714 | const branch = details.branch ?? fallbackBranch; |
| ... | ... | @@ -748,6 +752,7 @@ export const makeGitManager = Effect.gen(function* () { |
| 748 | 752 | commitSummary: limitContext(rangeContext.commitSummary, 20_000), |
| 749 | 753 | diffSummary: limitContext(rangeContext.diffSummary, 20_000), |
| 750 | 754 | diffPatch: limitContext(rangeContext.diffPatch, 60_000), |
| 755 | ...(model ? { model } : {}), | |
| 751 | 756 | }); |
| 752 | 757 | |
| 753 | 758 | const bodyFile = path.join(tempDir, `t3code-pr-body-${process.pid}-${randomUUID()}.md`); |
| ... | ... | @@ -972,6 +977,7 @@ export const makeGitManager = Effect.gen(function* () { |
| 972 | 977 | branch: string | null, |
| 973 | 978 | commitMessage?: string, |
| 974 | 979 | filePaths?: readonly string[], |
| 980 | model?: string, | |
| 975 | 981 | ) => |
| 976 | 982 | Effect.gen(function* () { |
| 977 | 983 | const suggestion = yield* resolveCommitAndBranchSuggestion({ |
| ... | ... | @@ -980,6 +986,7 @@ export const makeGitManager = Effect.gen(function* () { |
| 980 | 986 | ...(commitMessage ? { commitMessage } : {}), |
| 981 | 987 | ...(filePaths ? { filePaths } : {}), |
| 982 | 988 | includeBranch: true, |
| 989 | ...(model ? { model } : {}), | |
| 983 | 990 | }); |
| 984 | 991 | if (!suggestion) { |
| 985 | 992 | return yield* gitManagerError( |
| ... | ... | @@ -1028,6 +1035,7 @@ export const makeGitManager = Effect.gen(function* () { |
| 1028 | 1035 | initialStatus.branch, |
| 1029 | 1036 | input.commitMessage, |
| 1030 | 1037 | input.filePaths, |
| 1038 | input.textGenerationModel, | |
| 1031 | 1039 | ); |
| 1032 | 1040 | branchStep = result.branchStep; |
| 1033 | 1041 | commitMessageForStep = result.resolvedCommitMessage; |
| ... | ... | @@ -1044,6 +1052,7 @@ export const makeGitManager = Effect.gen(function* () { |
| 1044 | 1052 | commitMessageForStep, |
| 1045 | 1053 | preResolvedCommitSuggestion, |
| 1046 | 1054 | input.filePaths, |
| 1055 | input.textGenerationModel, | |
| 1047 | 1056 | ); |
| 1048 | 1057 | |
| 1049 | 1058 | const push = wantsPush |
| ... | ... | @@ -1051,7 +1060,7 @@ export const makeGitManager = Effect.gen(function* () { |
| 1051 | 1060 | : { status: "skipped_not_requested" as const }; |
| 1052 | 1061 | |
| 1053 | 1062 | const pr = wantsPr |
| 1054 | ? yield* runPrStep(input.cwd, currentBranch) | |
| 1063 | ? yield* runPrStep(input.cwd, currentBranch, input.textGenerationModel) | |
| 1055 | 1064 | : { status: "skipped_not_requested" as const }; |
| 1056 | 1065 | |
| 1057 | 1066 | return { |
apps/server/src/git/Services/TextGeneration.ts+6| ... | ... | @@ -19,6 +19,8 @@ export interface CommitMessageGenerationInput { |
| 19 | 19 | stagedPatch: string; |
| 20 | 20 | /** When true, the model also returns a semantic branch name for the change. */ |
| 21 | 21 | includeBranch?: boolean; |
| 22 | /** Model to use for generation. Defaults to gpt-5.4-mini if not specified. */ | |
| 23 | model?: string; | |
| 22 | 24 | } |
| 23 | 25 | |
| 24 | 26 | export interface CommitMessageGenerationResult { |
| ... | ... | @@ -35,6 +37,8 @@ export interface PrContentGenerationInput { |
| 35 | 37 | commitSummary: string; |
| 36 | 38 | diffSummary: string; |
| 37 | 39 | diffPatch: string; |
| 40 | /** Model to use for generation. Defaults to gpt-5.4-mini if not specified. */ | |
| 41 | model?: string; | |
| 38 | 42 | } |
| 39 | 43 | |
| 40 | 44 | export interface PrContentGenerationResult { |
| ... | ... | @@ -46,6 +50,8 @@ export interface BranchNameGenerationInput { |
| 46 | 50 | cwd: string; |
| 47 | 51 | message: string; |
| 48 | 52 | attachments?: ReadonlyArray<ChatAttachment> | undefined; |
| 53 | /** Model to use for generation. Defaults to gpt-5.4-mini if not specified. */ | |
| 54 | model?: string; | |
| 49 | 55 | } |
| 50 | 56 | |
| 51 | 57 | export interface BranchNameGenerationResult { |
apps/server/src/orchestration/Layers/ProviderCommandReactor.ts+2| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | import { |
| 2 | 2 | type ChatAttachment, |
| 3 | 3 | CommandId, |
| 4 | DEFAULT_GIT_TEXT_GENERATION_MODEL, | |
| 4 | 5 | EventId, |
| 5 | 6 | type OrchestrationEvent, |
| 6 | 7 | type ProviderModelOptions, |
| ... | ... | @@ -391,6 +392,7 @@ const make = Effect.gen(function* () { |
| 391 | 392 | cwd, |
| 392 | 393 | message: input.messageText, |
| 393 | 394 | ...(attachments.length > 0 ? { attachments } : {}), |
| 395 | model: DEFAULT_GIT_TEXT_GENERATION_MODEL, | |
| 394 | 396 | }) |
| 395 | 397 | .pipe( |
| 396 | 398 | Effect.catch((error) => |
apps/web/src/appSettings.test.ts+1| ... | ... | @@ -28,6 +28,7 @@ describe("getAppModelOptions", () => { |
| 28 | 28 | |
| 29 | 29 | expect(options.map((option) => option.slug)).toEqual([ |
| 30 | 30 | "gpt-5.4", |
| 31 | "gpt-5.4-mini", | |
| 31 | 32 | "gpt-5.3-codex", |
| 32 | 33 | "gpt-5.3-codex-spark", |
| 33 | 34 | "gpt-5.2-codex", |
apps/web/src/appSettings.ts+2-1| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | import { useCallback } from "react"; |
| 2 | 2 | import { Option, Schema } from "effect"; |
| 3 | import { type ProviderKind } from "@t3tools/contracts"; | |
| 3 | import { TrimmedNonEmptyString, type ProviderKind } from "@t3tools/contracts"; | |
| 4 | 4 | import { getDefaultModel, getModelOptions, normalizeModelSlug } from "@t3tools/shared/model"; |
| 5 | 5 | import { useLocalStorage } from "./hooks/useLocalStorage"; |
| 6 | 6 | |
| ... | ... | @@ -34,6 +34,7 @@ const AppSettingsSchema = Schema.Struct({ |
| 34 | 34 | customCodexModels: Schema.Array(Schema.String).pipe( |
| 35 | 35 | Schema.withConstructorDefault(() => Option.some([])), |
| 36 | 36 | ), |
| 37 | textGenerationModel: Schema.optional(TrimmedNonEmptyString), | |
| 37 | 38 | }); |
| 38 | 39 | export type AppSettings = typeof AppSettingsSchema.Type; |
| 39 | 40 | export interface AppModelOption { |
apps/web/src/components/GitActionsControl.tsx+7-1| ... | ... | @@ -15,6 +15,7 @@ import { |
| 15 | 15 | resolveQuickAction, |
| 16 | 16 | summarizeGitResult, |
| 17 | 17 | } from "./GitActionsControl.logic"; |
| 18 | import { useAppSettings } from "~/appSettings"; | |
| 18 | 19 | import { Button } from "~/components/ui/button"; |
| 19 | 20 | import { Checkbox } from "~/components/ui/checkbox"; |
| 20 | 21 | import { |
| ... | ... | @@ -154,6 +155,7 @@ function GitQuickActionIcon({ quickAction }: { quickAction: GitQuickAction }) { |
| 154 | 155 | } |
| 155 | 156 | |
| 156 | 157 | export default function GitActionsControl({ gitCwd, activeThreadId }: GitActionsControlProps) { |
| 158 | const { settings } = useAppSettings(); | |
| 157 | 159 | const threadToastData = useMemo( |
| 158 | 160 | () => (activeThreadId ? { threadId: activeThreadId } : undefined), |
| 159 | 161 | [activeThreadId], |
| ... | ... | @@ -191,7 +193,11 @@ export default function GitActionsControl({ gitCwd, activeThreadId }: GitActions |
| 191 | 193 | const initMutation = useMutation(gitInitMutationOptions({ cwd: gitCwd, queryClient })); |
| 192 | 194 | |
| 193 | 195 | const runImmediateGitActionMutation = useMutation( |
| 194 | gitRunStackedActionMutationOptions({ cwd: gitCwd, queryClient }), | |
| 196 | gitRunStackedActionMutationOptions({ | |
| 197 | cwd: gitCwd, | |
| 198 | queryClient, | |
| 199 | model: settings.textGenerationModel ?? null, | |
| 200 | }), | |
| 195 | 201 | ); |
| 196 | 202 | const pullMutation = useMutation(gitPullMutationOptions({ cwd: gitCwd, queryClient })); |
| 197 | 203 |
apps/web/src/lib/gitReactQuery.ts+2| ... | ... | @@ -112,6 +112,7 @@ export function gitCheckoutMutationOptions(input: { |
| 112 | 112 | export function gitRunStackedActionMutationOptions(input: { |
| 113 | 113 | cwd: string | null; |
| 114 | 114 | queryClient: QueryClient; |
| 115 | model?: string | null; | |
| 115 | 116 | }) { |
| 116 | 117 | return mutationOptions({ |
| 117 | 118 | mutationKey: gitMutationKeys.runStackedAction(input.cwd), |
| ... | ... | @@ -134,6 +135,7 @@ export function gitRunStackedActionMutationOptions(input: { |
| 134 | 135 | ...(commitMessage ? { commitMessage } : {}), |
| 135 | 136 | ...(featureBranch ? { featureBranch } : {}), |
| 136 | 137 | ...(filePaths ? { filePaths } : {}), |
| 138 | ...(input.model ? { model: input.model } : {}), | |
| 137 | 139 | }); |
| 138 | 140 | }, |
| 139 | 141 | onSettled: async () => { |
apps/web/src/routes/_chat.settings.tsx+72-2| ... | ... | @@ -1,9 +1,9 @@ |
| 1 | 1 | import { createFileRoute } from "@tanstack/react-router"; |
| 2 | 2 | import { useQuery } from "@tanstack/react-query"; |
| 3 | 3 | import { useCallback, useState } from "react"; |
| 4 | import { type ProviderKind } from "@t3tools/contracts"; | |
| 4 | import { type ProviderKind, DEFAULT_GIT_TEXT_GENERATION_MODEL } from "@t3tools/contracts"; | |
| 5 | 5 | import { getModelOptions, normalizeModelSlug } from "@t3tools/shared/model"; |
| 6 | import { MAX_CUSTOM_MODEL_LENGTH, useAppSettings } from "../appSettings"; | |
| 6 | import { getAppModelOptions, MAX_CUSTOM_MODEL_LENGTH, useAppSettings } from "../appSettings"; | |
| 7 | 7 | import { resolveAndPersistPreferredEditor } from "../editorPreferences"; |
| 8 | 8 | import { isElectron } from "../env"; |
| 9 | 9 | import { useTheme } from "../hooks/useTheme"; |
| ... | ... | @@ -112,6 +112,17 @@ function SettingsRouteView() { |
| 112 | 112 | const keybindingsConfigPath = serverConfigQuery.data?.keybindingsConfigPath ?? null; |
| 113 | 113 | const availableEditors = serverConfigQuery.data?.availableEditors; |
| 114 | 114 | |
| 115 | const gitTextGenerationModelOptions = getAppModelOptions( | |
| 116 | "codex", | |
| 117 | settings.customCodexModels, | |
| 118 | settings.textGenerationModel, | |
| 119 | ); | |
| 120 | const selectedGitTextGenerationModelLabel = | |
| 121 | gitTextGenerationModelOptions.find( | |
| 122 | (option) => | |
| 123 | option.slug === (settings.textGenerationModel ?? DEFAULT_GIT_TEXT_GENERATION_MODEL), | |
| 124 | )?.name ?? settings.textGenerationModel; | |
| 125 | ||
| 115 | 126 | const openKeybindingsFile = useCallback(() => { |
| 116 | 127 | if (!keybindingsConfigPath) return; |
| 117 | 128 | setOpenKeybindingsError(null); |
| ... | ... | @@ -502,6 +513,65 @@ function SettingsRouteView() { |
| 502 | 513 | </div> |
| 503 | 514 | </section> |
| 504 | 515 | |
| 516 | <section className="rounded-2xl border border-border bg-card p-5"> | |
| 517 | <div className="mb-4"> | |
| 518 | <h2 className="text-sm font-medium text-foreground">Git</h2> | |
| 519 | <p className="mt-1 text-xs text-muted-foreground"> | |
| 520 | Configure the model used for generating commit messages, PR titles, and branch | |
| 521 | names. | |
| 522 | </p> | |
| 523 | </div> | |
| 524 | ||
| 525 | <div className="flex flex-col gap-4 rounded-lg border border-border bg-background px-3 py-3 sm:flex-row sm:items-center sm:justify-between"> | |
| 526 | <div className="min-w-0 flex-1"> | |
| 527 | <p className="text-sm font-medium text-foreground">Text generation model</p> | |
| 528 | <p className="text-xs text-muted-foreground"> | |
| 529 | Model used for auto-generated git content. | |
| 530 | </p> | |
| 531 | </div> | |
| 532 | <Select | |
| 533 | value={settings.textGenerationModel ?? DEFAULT_GIT_TEXT_GENERATION_MODEL} | |
| 534 | onValueChange={(value) => { | |
| 535 | if (value) { | |
| 536 | updateSettings({ | |
| 537 | textGenerationModel: value, | |
| 538 | }); | |
| 539 | } | |
| 540 | }} | |
| 541 | > | |
| 542 | <SelectTrigger | |
| 543 | className="w-full shrink-0 sm:w-48" | |
| 544 | aria-label="Git text generation model" | |
| 545 | > | |
| 546 | <SelectValue>{selectedGitTextGenerationModelLabel}</SelectValue> | |
| 547 | </SelectTrigger> | |
| 548 | <SelectPopup align="end"> | |
| 549 | {gitTextGenerationModelOptions.map((option) => ( | |
| 550 | <SelectItem key={option.slug} value={option.slug}> | |
| 551 | {option.name} | |
| 552 | </SelectItem> | |
| 553 | ))} | |
| 554 | </SelectPopup> | |
| 555 | </Select> | |
| 556 | </div> | |
| 557 | ||
| 558 | {settings.textGenerationModel !== defaults.textGenerationModel ? ( | |
| 559 | <div className="mt-3 flex justify-end"> | |
| 560 | <Button | |
| 561 | size="xs" | |
| 562 | variant="outline" | |
| 563 | onClick={() => | |
| 564 | updateSettings({ | |
| 565 | textGenerationModel: defaults.textGenerationModel, | |
| 566 | }) | |
| 567 | } | |
| 568 | > | |
| 569 | Restore default | |
| 570 | </Button> | |
| 571 | </div> | |
| 572 | ) : null} | |
| 573 | </section> | |
| 574 | ||
| 505 | 575 | <section className="rounded-2xl border border-border bg-card p-5"> |
| 506 | 576 | <div className="mb-4"> |
| 507 | 577 | <h2 className="text-sm font-medium text-foreground">Threads</h2> |
packages/contracts/src/git.ts+5-1| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | import { Schema } from "effect"; | |
| 1 | import { Option, Schema } from "effect"; | |
| 2 | 2 | import { NonNegativeInt, PositiveInt, TrimmedNonEmptyString } from "./baseSchemas"; |
| 3 | import { DEFAULT_GIT_TEXT_GENERATION_MODEL } from "./model"; | |
| 3 | 4 | |
| 4 | 5 | const TrimmedNonEmptyStringSchema = TrimmedNonEmptyString; |
| 5 | 6 | |
| ... | ... | @@ -64,6 +65,9 @@ export const GitRunStackedActionInput = Schema.Struct({ |
| 64 | 65 | filePaths: Schema.optional( |
| 65 | 66 | Schema.Array(TrimmedNonEmptyStringSchema).check(Schema.isMinLength(1)), |
| 66 | 67 | ), |
| 68 | textGenerationModel: Schema.optional(TrimmedNonEmptyStringSchema).pipe( | |
| 69 | Schema.withConstructorDefault(() => Option.some(DEFAULT_GIT_TEXT_GENERATION_MODEL)), | |
| 70 | ), | |
| 67 | 71 | }); |
| 68 | 72 | export type GitRunStackedActionInput = typeof GitRunStackedActionInput.Type; |
| 69 | 73 |
packages/contracts/src/model.ts+3| ... | ... | @@ -23,6 +23,7 @@ type ModelOption = { |
| 23 | 23 | export const MODEL_OPTIONS_BY_PROVIDER = { |
| 24 | 24 | codex: [ |
| 25 | 25 | { slug: "gpt-5.4", name: "GPT-5.4" }, |
| 26 | { slug: "gpt-5.4-mini", name: "GPT-5.4 Mini" }, | |
| 26 | 27 | { slug: "gpt-5.3-codex", name: "GPT-5.3 Codex" }, |
| 27 | 28 | { slug: "gpt-5.3-codex-spark", name: "GPT-5.3 Codex Spark" }, |
| 28 | 29 | { slug: "gpt-5.2-codex", name: "GPT-5.2 Codex" }, |
| ... | ... | @@ -38,6 +39,8 @@ export const DEFAULT_MODEL_BY_PROVIDER = { |
| 38 | 39 | codex: "gpt-5.4", |
| 39 | 40 | } as const satisfies Record<ProviderKind, ModelSlug>; |
| 40 | 41 | |
| 42 | export const DEFAULT_GIT_TEXT_GENERATION_MODEL = "gpt-5.4-mini" as const; | |
| 43 | ||
| 41 | 44 | export const MODEL_SLUG_ALIASES_BY_PROVIDER = { |
| 42 | 45 | codex: { |
| 43 | 46 | "5.4": "gpt-5.4", |