Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions apps/server/src/git/Layers/CodexTextGeneration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
TextGeneration,
} from "../Services/TextGeneration.ts";

const CODEX_MODEL = "gpt-5.3-codex";
const DEFAULT_CODEX_MODEL = "gpt-5.3-codex";
const CODEX_REASONING_EFFORT = "low";
const CODEX_TIMEOUT_MS = 180_000;

Expand Down Expand Up @@ -187,13 +187,15 @@ const makeCodexTextGeneration = Effect.gen(function* () {
outputSchemaJson,
imagePaths = [],
cleanupPaths = [],
model,
}: {
operation: "generateCommitMessage" | "generatePrContent" | "generateBranchName";
cwd: string;
prompt: string;
outputSchemaJson: S;
imagePaths?: ReadonlyArray<string>;
cleanupPaths?: ReadonlyArray<string>;
model?: string;
}): Effect.Effect<S["Type"], TextGenerationError, S["DecodingServices"]> =>
Effect.gen(function* () {
const schemaPath = yield* writeTempFile(
Expand All @@ -212,7 +214,7 @@ const makeCodexTextGeneration = Effect.gen(function* () {
"-s",
"read-only",
"--model",
CODEX_MODEL,
model ?? DEFAULT_CODEX_MODEL,
"--config",
`model_reasoning_effort="${CODEX_REASONING_EFFORT}"`,
"--output-schema",
Expand Down Expand Up @@ -353,6 +355,7 @@ const makeCodexTextGeneration = Effect.gen(function* () {
cwd: input.cwd,
prompt,
outputSchemaJson,
...(input.model ? { model: input.model } : {}),
}).pipe(
Effect.map(
(generated) =>
Expand Down Expand Up @@ -398,6 +401,7 @@ const makeCodexTextGeneration = Effect.gen(function* () {
title: Schema.String,
body: Schema.String,
}),
...(input.model ? { model: input.model } : {}),
}).pipe(
Effect.map(
(generated) =>
Expand Down Expand Up @@ -449,6 +453,7 @@ const makeCodexTextGeneration = Effect.gen(function* () {
branch: Schema.String,
}),
imagePaths,
...(input.model ? { model: input.model } : {}),
});

return {
Expand Down
14 changes: 12 additions & 2 deletions apps/server/src/git/Layers/GitManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,8 @@ export const makeGitManager = Effect.gen(function* () {
/** When true, also produce a semantic feature branch name. */
includeBranch?: boolean;
filePaths?: readonly string[];
/** Optional model to use for commit message generation. */
model?: string;
}) =>
Effect.gen(function* () {
const context = yield* gitCore.prepareCommitContext(input.cwd, input.filePaths);
Expand All @@ -665,6 +667,7 @@ export const makeGitManager = Effect.gen(function* () {
stagedSummary: limitContext(context.stagedSummary, 8_000),
stagedPatch: limitContext(context.stagedPatch, 50_000),
...(input.includeBranch ? { includeBranch: true } : {}),
...(input.model ? { model: input.model } : {}),
})
.pipe(Effect.map((result) => sanitizeCommitMessage(result)));

Expand All @@ -682,6 +685,7 @@ export const makeGitManager = Effect.gen(function* () {
commitMessage?: string,
preResolvedSuggestion?: CommitAndBranchSuggestion,
filePaths?: readonly string[],
model?: string,
) =>
Effect.gen(function* () {
const suggestion =
Expand All @@ -691,6 +695,7 @@ export const makeGitManager = Effect.gen(function* () {
branch,
...(commitMessage ? { commitMessage } : {}),
...(filePaths ? { filePaths } : {}),
...(model ? { model } : {}),
}));
if (!suggestion) {
return { status: "skipped_no_changes" as const };
Expand All @@ -704,7 +709,7 @@ export const makeGitManager = Effect.gen(function* () {
};
});

const runPrStep = (cwd: string, fallbackBranch: string | null) =>
const runPrStep = (cwd: string, fallbackBranch: string | null, model?: string) =>
Effect.gen(function* () {
const details = yield* gitCore.statusDetails(cwd);
const branch = details.branch ?? fallbackBranch;
Expand Down Expand Up @@ -748,6 +753,7 @@ export const makeGitManager = Effect.gen(function* () {
commitSummary: limitContext(rangeContext.commitSummary, 20_000),
diffSummary: limitContext(rangeContext.diffSummary, 20_000),
diffPatch: limitContext(rangeContext.diffPatch, 60_000),
...(model ? { model } : {}),
});

const bodyFile = path.join(tempDir, `t3code-pr-body-${process.pid}-${randomUUID()}.md`);
Expand Down Expand Up @@ -972,6 +978,7 @@ export const makeGitManager = Effect.gen(function* () {
branch: string | null,
commitMessage?: string,
filePaths?: readonly string[],
model?: string,
) =>
Effect.gen(function* () {
const suggestion = yield* resolveCommitAndBranchSuggestion({
Expand All @@ -980,6 +987,7 @@ export const makeGitManager = Effect.gen(function* () {
...(commitMessage ? { commitMessage } : {}),
...(filePaths ? { filePaths } : {}),
includeBranch: true,
...(model ? { model } : {}),
});
if (!suggestion) {
return yield* gitManagerError(
Expand Down Expand Up @@ -1028,6 +1036,7 @@ export const makeGitManager = Effect.gen(function* () {
initialStatus.branch,
input.commitMessage,
input.filePaths,
input.model,
);
branchStep = result.branchStep;
commitMessageForStep = result.resolvedCommitMessage;
Expand All @@ -1044,14 +1053,15 @@ export const makeGitManager = Effect.gen(function* () {
commitMessageForStep,
preResolvedCommitSuggestion,
input.filePaths,
input.model,
);

const push = wantsPush
? yield* gitCore.pushCurrentBranch(input.cwd, currentBranch)
: { status: "skipped_not_requested" as const };

const pr = wantsPr
? yield* runPrStep(input.cwd, currentBranch)
? yield* runPrStep(input.cwd, currentBranch, input.model)
: { status: "skipped_not_requested" as const };

return {
Expand Down
6 changes: 6 additions & 0 deletions apps/server/src/git/Services/TextGeneration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export interface CommitMessageGenerationInput {
stagedPatch: string;
/** When true, the model also returns a semantic branch name for the change. */
includeBranch?: boolean;
/** Optional model to use for generation. Falls back to default if not provided. */
model?: string;
}

export interface CommitMessageGenerationResult {
Expand All @@ -35,6 +37,8 @@ export interface PrContentGenerationInput {
commitSummary: string;
diffSummary: string;
diffPatch: string;
/** Optional model to use for generation. Falls back to default if not provided. */
model?: string;
}

export interface PrContentGenerationResult {
Expand All @@ -46,6 +50,8 @@ export interface BranchNameGenerationInput {
cwd: string;
message: string;
attachments?: ReadonlyArray<ChatAttachment> | undefined;
/** Optional model to use for generation. Falls back to default if not provided. */
model?: string;
}

export interface BranchNameGenerationResult {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ const make = Effect.gen(function* () {
readonly messageId: string;
readonly messageText: string;
readonly attachments?: ReadonlyArray<ChatAttachment>;
readonly model?: string;
}) {
if (!input.branch || !input.worktreePath) {
return;
Expand All @@ -391,6 +392,7 @@ const make = Effect.gen(function* () {
cwd,
message: input.messageText,
...(attachments.length > 0 ? { attachments } : {}),
...(input.model ? { model: input.model } : {}),
})
.pipe(
Effect.catch((error) =>
Expand Down Expand Up @@ -459,6 +461,7 @@ const make = Effect.gen(function* () {
messageId: message.id,
messageText: message.text,
...(message.attachments !== undefined ? { attachments: message.attachments } : {}),
...(event.payload.model !== undefined ? { model: event.payload.model } : {}),
}).pipe(Effect.forkScoped);

yield* sendTurnForThread({
Expand Down
1 change: 1 addition & 0 deletions apps/web/src/components/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3241,6 +3241,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
diffToggleShortcutLabel={diffPanelShortcutLabel}
gitCwd={gitCwd}
diffOpen={diffOpen}
selectedModel={selectedModel}
onRunProjectScript={(script) => {
void runProjectScript(script);
}}
Expand Down
11 changes: 9 additions & 2 deletions apps/web/src/components/GitActionsControl.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { GitStackedAction, GitStatusResult, ThreadId } from "@t3tools/contracts";
import type { GitStackedAction, GitStatusResult, ModelSlug, ThreadId } from "@t3tools/contracts";
import { useIsMutating, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useCallback, useEffect, useMemo, useState } from "react";
import { ChevronDownIcon, CloudUploadIcon, GitCommitIcon, InfoIcon } from "lucide-react";
Expand Down Expand Up @@ -48,6 +48,7 @@ import { readNativeApi } from "~/nativeApi";
interface GitActionsControlProps {
gitCwd: string | null;
activeThreadId: ThreadId | null;
selectedModel?: ModelSlug | undefined;
}

interface PendingDefaultBranchAction {
Expand Down Expand Up @@ -153,7 +154,11 @@ function GitQuickActionIcon({ quickAction }: { quickAction: GitQuickAction }) {
return <InfoIcon className={iconClassName} />;
}

export default function GitActionsControl({ gitCwd, activeThreadId }: GitActionsControlProps) {
export default function GitActionsControl({
gitCwd,
activeThreadId,
selectedModel,
}: GitActionsControlProps) {
const threadToastData = useMemo(
() => (activeThreadId ? { threadId: activeThreadId } : undefined),
[activeThreadId],
Expand Down Expand Up @@ -351,6 +356,7 @@ export default function GitActionsControl({ gitCwd, activeThreadId }: GitActions
...(commitMessage ? { commitMessage } : {}),
...(featureBranch ? { featureBranch } : {}),
...(filePaths ? { filePaths } : {}),
...(selectedModel ? { model: selectedModel } : {}),
});

try {
Expand Down Expand Up @@ -447,6 +453,7 @@ export default function GitActionsControl({ gitCwd, activeThreadId }: GitActions
setPendingDefaultBranchAction,
threadToastData,
gitStatusForActions,
selectedModel,
],
);

Expand Down
11 changes: 10 additions & 1 deletion apps/web/src/components/chat/ChatHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
type ProjectScript,
type ResolvedKeybindingsConfig,
type ThreadId,
type ModelSlug,
} from "@t3tools/contracts";
import { memo } from "react";
import GitActionsControl from "../GitActionsControl";
Expand All @@ -27,6 +28,7 @@ interface ChatHeaderProps {
diffToggleShortcutLabel: string | null;
gitCwd: string | null;
diffOpen: boolean;
selectedModel: ModelSlug | undefined;
onRunProjectScript: (script: ProjectScript) => void;
onAddProjectScript: (input: NewProjectScriptInput) => Promise<void>;
onUpdateProjectScript: (scriptId: string, input: NewProjectScriptInput) => Promise<void>;
Expand All @@ -47,6 +49,7 @@ export const ChatHeader = memo(function ChatHeader({
diffToggleShortcutLabel,
gitCwd,
diffOpen,
selectedModel,
onRunProjectScript,
onAddProjectScript,
onUpdateProjectScript,
Expand Down Expand Up @@ -93,7 +96,13 @@ export const ChatHeader = memo(function ChatHeader({
openInCwd={openInCwd}
/>
)}
{activeProjectName && <GitActionsControl gitCwd={gitCwd} activeThreadId={activeThreadId} />}
{activeProjectName && (
<GitActionsControl
gitCwd={gitCwd}
activeThreadId={activeThreadId}
selectedModel={selectedModel}
/>
)}
<Tooltip>
<TooltipTrigger
render={
Expand Down
3 changes: 3 additions & 0 deletions apps/web/src/lib/gitReactQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,13 @@ export function gitRunStackedActionMutationOptions(input: {
commitMessage,
featureBranch,
filePaths,
model,
}: {
action: GitStackedAction;
commitMessage?: string;
featureBranch?: boolean;
filePaths?: string[];
model?: string;
}) => {
const api = ensureNativeApi();
if (!input.cwd) throw new Error("Git action is unavailable.");
Expand All @@ -134,6 +136,7 @@ export function gitRunStackedActionMutationOptions(input: {
...(commitMessage ? { commitMessage } : {}),
...(featureBranch ? { featureBranch } : {}),
...(filePaths ? { filePaths } : {}),
...(model ? { model } : {}),
});
},
onSettled: async () => {
Expand Down
1 change: 1 addition & 0 deletions packages/contracts/src/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export const GitRunStackedActionInput = Schema.Struct({
filePaths: Schema.optional(
Schema.Array(TrimmedNonEmptyStringSchema).check(Schema.isMinLength(1)),
),
model: Schema.optional(Schema.String),
});
export type GitRunStackedActionInput = typeof GitRunStackedActionInput.Type;

Expand Down