feat: integrate thinking level support across various components

- Enhanced multiple server and UI components to include an optional thinking level parameter, improving the configurability of model interactions.
- Updated request handlers and services to manage and pass the thinking level, ensuring consistent data handling across the application.
- Refactored UI components to display and manage the selected model along with its thinking level, enhancing user experience and clarity.
- Adjusted the Electron API and HTTP client to support the new thinking level parameter in requests, ensuring seamless integration.

This update significantly improves the application's ability to adapt reasoning capabilities based on user-defined thinking levels, enhancing overall performance and user satisfaction.
This commit is contained in:
Shirone
2026-01-02 17:52:03 +01:00
parent 69f3ba9724
commit 2b942a6cb1
17 changed files with 233 additions and 254 deletions

View File

@@ -323,7 +323,8 @@ export function AddFeatureDialog({
const result = await api.enhancePrompt?.enhance(
newFeature.description,
enhancementMode,
enhancementOverride.effectiveModel
enhancementOverride.effectiveModel, // API accepts string, extract from PhaseModelEntry
enhancementOverride.effectiveModelEntry.thinkingLevel // Pass thinking level
);
if (result?.success && result.enhancedText) {
@@ -512,7 +513,7 @@ export function AddFeatureDialog({
</Button>
<ModelOverrideTrigger
currentModel={enhancementOverride.effectiveModel}
currentModelEntry={enhancementOverride.effectiveModelEntry}
onModelChange={enhancementOverride.setOverride}
phase="enhancementModel"
isOverridden={enhancementOverride.isOverridden}

View File

@@ -33,6 +33,16 @@ import type {
import { ModelOverrideTrigger } from '@/components/shared/model-override-trigger';
import { useAppStore } from '@/store/app-store';
/**
* Normalize PhaseModelEntry or string to PhaseModelEntry
*/
function normalizeEntry(entry: PhaseModelEntry | string): PhaseModelEntry {
if (typeof entry === 'string') {
return { model: entry as ModelAlias | CursorModelId };
}
return entry;
}
/**
* Extract model string from PhaseModelEntry or string
*/
@@ -71,7 +81,7 @@ export function BacklogPlanDialog({
const [prompt, setPrompt] = useState('');
const [expandedChanges, setExpandedChanges] = useState<Set<number>>(new Set());
const [selectedChanges, setSelectedChanges] = useState<Set<number>>(new Set());
const [modelOverride, setModelOverride] = useState<ModelAlias | CursorModelId | null>(null);
const [modelOverride, setModelOverride] = useState<PhaseModelEntry | null>(null);
const { phaseModels } = useAppStore();
@@ -105,7 +115,8 @@ export function BacklogPlanDialog({
setIsGeneratingPlan(true);
// Use model override if set, otherwise use global default (extract model string from PhaseModelEntry)
const effectiveModel = modelOverride || extractModel(phaseModels.backlogPlanningModel);
const effectiveModelEntry = modelOverride || normalizeEntry(phaseModels.backlogPlanningModel);
const effectiveModel = effectiveModelEntry.model;
const result = await api.backlogPlan.generate(projectPath, prompt, effectiveModel);
if (!result.success) {
setIsGeneratingPlan(false);
@@ -381,8 +392,9 @@ export function BacklogPlanDialog({
}
};
// Get effective model (override or global default) - extract model string from PhaseModelEntry
const effectiveModel = modelOverride || extractModel(phaseModels.backlogPlanningModel);
// Get effective model entry (override or global default)
const effectiveModelEntry = modelOverride || normalizeEntry(phaseModels.backlogPlanningModel);
const effectiveModel = effectiveModelEntry.model;
return (
<Dialog open={open} onOpenChange={(isOpen) => !isOpen && onClose()}>
@@ -407,7 +419,7 @@ export function BacklogPlanDialog({
<div className="flex items-center gap-2 mr-auto">
<span className="text-xs text-muted-foreground">Model:</span>
<ModelOverrideTrigger
currentModel={effectiveModel}
currentModelEntry={effectiveModelEntry}
onModelChange={setModelOverride}
phase="backlogPlanningModel"
size="sm"

View File

@@ -240,7 +240,8 @@ export function EditFeatureDialog({
const result = await api.enhancePrompt?.enhance(
editingFeature.description,
enhancementMode,
enhancementOverride.effectiveModel
enhancementOverride.effectiveModel, // API accepts string, extract from PhaseModelEntry
enhancementOverride.effectiveModelEntry.thinkingLevel // Pass thinking level
);
if (result?.success && result.enhancedText) {
@@ -392,7 +393,7 @@ export function EditFeatureDialog({
</Button>
<ModelOverrideTrigger
currentModel={enhancementOverride.effectiveModel}
currentModelEntry={enhancementOverride.effectiveModelEntry}
onModelChange={enhancementOverride.setOverride}
phase="enhancementModel"
isOverridden={enhancementOverride.isOverridden}

View File

@@ -31,6 +31,9 @@ export function GitHubIssuesView() {
// Model override for validation
const validationModelOverride = useModelOverride({ phase: 'validationModel' });
// Extract model string for API calls (backward compatibility)
const validationModelString = validationModelOverride.effectiveModel;
const { openIssues, closedIssues, loading, refreshing, error, refresh } = useGithubIssues();
const { validatingIssues, cachedValidations, handleValidateIssue, handleViewCachedValidation } =

View File

@@ -58,6 +58,7 @@ export function IssueDetailPanel({
const getValidationOptions = (forceRevalidate = false) => {
return {
forceRevalidate,
modelEntry: modelOverride.effectiveModelEntry, // Pass the full PhaseModelEntry to preserve thinking level
comments: includeCommentsInAnalysis && comments.length > 0 ? comments : undefined,
linkedPRs: issue.linkedPRs?.map((pr) => ({
number: pr.number,
@@ -119,12 +120,13 @@ export function IssueDetailPanel({
View (stale)
</Button>
<ModelOverrideTrigger
currentModel={modelOverride.effectiveModel}
currentModelEntry={modelOverride.effectiveModelEntry}
onModelChange={modelOverride.setOverride}
phase="validationModel"
isOverridden={modelOverride.isOverridden}
size="sm"
variant="icon"
className="mx-1"
/>
<Button
variant="default"
@@ -141,12 +143,13 @@ export function IssueDetailPanel({
return (
<>
<ModelOverrideTrigger
currentModel={modelOverride.effectiveModel}
currentModelEntry={modelOverride.effectiveModelEntry}
onModelChange={modelOverride.setOverride}
phase="validationModel"
isOverridden={modelOverride.isOverridden}
size="sm"
variant="icon"
className="mr-1"
/>
<Button
variant="default"

View File

@@ -227,12 +227,13 @@ export function useIssueValidation({
issue: GitHubIssue,
options: {
forceRevalidate?: boolean;
model?: string;
model?: string | PhaseModelEntry; // Accept either string (backward compat) or PhaseModelEntry
modelEntry?: PhaseModelEntry; // New preferred way to pass model with thinking level
comments?: GitHubComment[];
linkedPRs?: LinkedPRInfo[];
} = {}
) => {
const { forceRevalidate = false, model, comments, linkedPRs } = options;
const { forceRevalidate = false, model, modelEntry, comments, linkedPRs } = options;
if (!currentProject?.path) {
toast.error('No project selected');
@@ -260,8 +261,20 @@ export function useIssueValidation({
});
// Use provided model override or fall back to phaseModels.validationModel
// Extract model string from PhaseModelEntry (handles both old string format and new object format)
const modelToUse = model || extractModel(phaseModels.validationModel);
// Extract model string and thinking level from PhaseModelEntry (handles both old string format and new object format)
const effectiveModelEntry = modelEntry
? modelEntry
: model
? typeof model === 'string'
? { model: model as ModelAlias | CursorModelId }
: model
: phaseModels.validationModel;
const normalizedEntry =
typeof effectiveModelEntry === 'string'
? { model: effectiveModelEntry as ModelAlias | CursorModelId }
: effectiveModelEntry;
const modelToUse = normalizedEntry.model;
const thinkingLevelToUse = normalizedEntry.thinkingLevel;
try {
const api = getElectronAPI();
@@ -277,7 +290,8 @@ export function useIssueValidation({
const result = await api.github.validateIssue(
currentProject.path,
validationInput,
modelToUse
modelToUse,
thinkingLevelToUse
);
if (!result.success) {

View File

@@ -1,5 +1,5 @@
import type { GitHubIssue, StoredValidation, GitHubComment } from '@/lib/electron';
import type { ModelAlias, CursorModelId, LinkedPRInfo } from '@automaker/types';
import type { ModelAlias, CursorModelId, LinkedPRInfo, PhaseModelEntry } from '@automaker/types';
export interface IssueRowProps {
issue: GitHubIssue;
@@ -36,8 +36,9 @@ export interface IssueDetailPanelProps {
formatDate: (date: string) => string;
/** Model override state */
modelOverride: {
effectiveModelEntry: PhaseModelEntry;
effectiveModel: ModelAlias | CursorModelId;
isOverridden: boolean;
setOverride: (model: ModelAlias | CursorModelId | null) => void;
setOverride: (entry: PhaseModelEntry | null) => void;
};
}