mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-01 08:13:37 +00:00
feature/codex-cli
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import type { ModelAlias, ThinkingLevel } from '@/store/app-store';
|
||||
import type { ModelProvider } from '@automaker/types';
|
||||
import { CURSOR_MODEL_MAP } from '@automaker/types';
|
||||
import type { ModelAlias } from '@/store/app-store';
|
||||
import type { ModelProvider, ThinkingLevel, ReasoningEffort } from '@automaker/types';
|
||||
import { CURSOR_MODEL_MAP, CODEX_MODEL_MAP } from '@automaker/types';
|
||||
import { Brain, Zap, Scale, Cpu, Rocket, Sparkles } from 'lucide-react';
|
||||
|
||||
export type ModelOption = {
|
||||
@@ -51,9 +51,64 @@ export const CURSOR_MODELS: ModelOption[] = Object.entries(CURSOR_MODEL_MAP).map
|
||||
);
|
||||
|
||||
/**
|
||||
* All available models (Claude + Cursor)
|
||||
* Codex/OpenAI models
|
||||
* Official models from https://developers.openai.com/codex/models/
|
||||
*/
|
||||
export const ALL_MODELS: ModelOption[] = [...CLAUDE_MODELS, ...CURSOR_MODELS];
|
||||
export const CODEX_MODELS: ModelOption[] = [
|
||||
{
|
||||
id: CODEX_MODEL_MAP.gpt52Codex,
|
||||
label: 'GPT-5.2-Codex',
|
||||
description: 'Most advanced agentic coding model (default for ChatGPT users).',
|
||||
badge: 'Premium',
|
||||
provider: 'codex',
|
||||
hasThinking: true,
|
||||
},
|
||||
{
|
||||
id: CODEX_MODEL_MAP.gpt5Codex,
|
||||
label: 'GPT-5-Codex',
|
||||
description: 'Purpose-built for Codex CLI (default for CLI users).',
|
||||
badge: 'Balanced',
|
||||
provider: 'codex',
|
||||
hasThinking: true,
|
||||
},
|
||||
{
|
||||
id: CODEX_MODEL_MAP.gpt5CodexMini,
|
||||
label: 'GPT-5-Codex-Mini',
|
||||
description: 'Faster workflows for code Q&A and editing.',
|
||||
badge: 'Speed',
|
||||
provider: 'codex',
|
||||
hasThinking: false,
|
||||
},
|
||||
{
|
||||
id: CODEX_MODEL_MAP.codex1,
|
||||
label: 'Codex-1',
|
||||
description: 'o3-based model optimized for software engineering.',
|
||||
badge: 'Premium',
|
||||
provider: 'codex',
|
||||
hasThinking: true,
|
||||
},
|
||||
{
|
||||
id: CODEX_MODEL_MAP.codexMiniLatest,
|
||||
label: 'Codex-Mini-Latest',
|
||||
description: 'o4-mini-based model for faster workflows.',
|
||||
badge: 'Balanced',
|
||||
provider: 'codex',
|
||||
hasThinking: false,
|
||||
},
|
||||
{
|
||||
id: CODEX_MODEL_MAP.gpt5,
|
||||
label: 'GPT-5',
|
||||
description: 'GPT-5 base flagship model.',
|
||||
badge: 'Balanced',
|
||||
provider: 'codex',
|
||||
hasThinking: true,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* All available models (Claude + Cursor + Codex)
|
||||
*/
|
||||
export const ALL_MODELS: ModelOption[] = [...CLAUDE_MODELS, ...CURSOR_MODELS, ...CODEX_MODELS];
|
||||
|
||||
export const THINKING_LEVELS: ThinkingLevel[] = ['none', 'low', 'medium', 'high', 'ultrathink'];
|
||||
|
||||
@@ -65,6 +120,28 @@ export const THINKING_LEVEL_LABELS: Record<ThinkingLevel, string> = {
|
||||
ultrathink: 'Ultra',
|
||||
};
|
||||
|
||||
/**
|
||||
* Reasoning effort levels for Codex/OpenAI models
|
||||
* All models support reasoning effort levels
|
||||
*/
|
||||
export const REASONING_EFFORT_LEVELS: ReasoningEffort[] = [
|
||||
'none',
|
||||
'minimal',
|
||||
'low',
|
||||
'medium',
|
||||
'high',
|
||||
'xhigh',
|
||||
];
|
||||
|
||||
export const REASONING_EFFORT_LABELS: Record<ReasoningEffort, string> = {
|
||||
none: 'None',
|
||||
minimal: 'Min',
|
||||
low: 'Low',
|
||||
medium: 'Med',
|
||||
high: 'High',
|
||||
xhigh: 'XHigh',
|
||||
};
|
||||
|
||||
// Profile icon mapping
|
||||
export const PROFILE_ICONS: Record<string, React.ComponentType<{ className?: string }>> = {
|
||||
Brain,
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Brain, Bot, Terminal, AlertTriangle } from 'lucide-react';
|
||||
import { Brain, AlertTriangle } from 'lucide-react';
|
||||
import { AnthropicIcon, CursorIcon, OpenAIIcon } from '@/components/ui/provider-icon';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { ModelAlias } from '@/store/app-store';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
import { useSetupStore } from '@/store/setup-store';
|
||||
import { getModelProvider, PROVIDER_PREFIXES, stripProviderPrefix } from '@automaker/types';
|
||||
import type { ModelProvider } from '@automaker/types';
|
||||
import { CLAUDE_MODELS, CURSOR_MODELS, ModelOption } from './model-constants';
|
||||
import { CLAUDE_MODELS, CURSOR_MODELS, CODEX_MODELS, ModelOption } from './model-constants';
|
||||
|
||||
interface ModelSelectorProps {
|
||||
selectedModel: string; // Can be ModelAlias or "cursor-{id}"
|
||||
@@ -21,13 +22,16 @@ export function ModelSelector({
|
||||
testIdPrefix = 'model-select',
|
||||
}: ModelSelectorProps) {
|
||||
const { enabledCursorModels, cursorDefaultModel } = useAppStore();
|
||||
const { cursorCliStatus } = useSetupStore();
|
||||
const { cursorCliStatus, codexCliStatus } = useSetupStore();
|
||||
|
||||
const selectedProvider = getModelProvider(selectedModel);
|
||||
|
||||
// Check if Cursor CLI is available
|
||||
const isCursorAvailable = cursorCliStatus?.installed && cursorCliStatus?.auth?.authenticated;
|
||||
|
||||
// Check if Codex CLI is available
|
||||
const isCodexAvailable = codexCliStatus?.installed && codexCliStatus?.auth?.authenticated;
|
||||
|
||||
// Filter Cursor models based on enabled models from global settings
|
||||
const filteredCursorModels = CURSOR_MODELS.filter((model) => {
|
||||
// Extract the cursor model ID from the prefixed ID (e.g., "cursor-auto" -> "auto")
|
||||
@@ -39,6 +43,9 @@ export function ModelSelector({
|
||||
if (provider === 'cursor' && selectedProvider !== 'cursor') {
|
||||
// Switch to Cursor's default model (from global settings)
|
||||
onModelSelect(`${PROVIDER_PREFIXES.cursor}${cursorDefaultModel}`);
|
||||
} else if (provider === 'codex' && selectedProvider !== 'codex') {
|
||||
// Switch to Codex's default model (gpt-5.2)
|
||||
onModelSelect('gpt-5.2');
|
||||
} else if (provider === 'claude' && selectedProvider !== 'claude') {
|
||||
// Switch to Claude's default model
|
||||
onModelSelect('sonnet');
|
||||
@@ -62,7 +69,7 @@ export function ModelSelector({
|
||||
)}
|
||||
data-testid={`${testIdPrefix}-provider-claude`}
|
||||
>
|
||||
<Bot className="w-4 h-4" />
|
||||
<AnthropicIcon className="w-4 h-4" />
|
||||
Claude
|
||||
</button>
|
||||
<button
|
||||
@@ -76,9 +83,23 @@ export function ModelSelector({
|
||||
)}
|
||||
data-testid={`${testIdPrefix}-provider-cursor`}
|
||||
>
|
||||
<Terminal className="w-4 h-4" />
|
||||
<CursorIcon className="w-4 h-4" />
|
||||
Cursor CLI
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleProviderChange('codex')}
|
||||
className={cn(
|
||||
'flex-1 px-3 py-2 rounded-md border text-sm font-medium transition-colors flex items-center justify-center gap-2',
|
||||
selectedProvider === 'codex'
|
||||
? 'bg-primary text-primary-foreground border-primary'
|
||||
: 'bg-background hover:bg-accent border-border'
|
||||
)}
|
||||
data-testid={`${testIdPrefix}-provider-codex`}
|
||||
>
|
||||
<OpenAIIcon className="w-4 h-4" />
|
||||
Codex CLI
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -136,7 +157,7 @@ export function ModelSelector({
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="flex items-center gap-2">
|
||||
<Terminal className="w-4 h-4 text-primary" />
|
||||
<CursorIcon className="w-4 h-4 text-primary" />
|
||||
Cursor Model
|
||||
</Label>
|
||||
<span className="text-[11px] px-2 py-0.5 rounded-full border border-amber-500/40 text-amber-600 dark:text-amber-400">
|
||||
@@ -188,6 +209,67 @@ export function ModelSelector({
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Codex Models */}
|
||||
{selectedProvider === 'codex' && (
|
||||
<div className="space-y-3">
|
||||
{/* Warning when Codex CLI is not available */}
|
||||
{!isCodexAvailable && (
|
||||
<div className="flex items-start gap-2 p-3 rounded-lg bg-amber-500/10 border border-amber-500/20">
|
||||
<AlertTriangle className="w-4 h-4 text-amber-400 mt-0.5 shrink-0" />
|
||||
<div className="text-sm text-amber-400">
|
||||
Codex CLI is not installed or authenticated. Configure it in Settings → AI
|
||||
Providers.
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="flex items-center gap-2">
|
||||
<OpenAIIcon className="w-4 h-4 text-primary" />
|
||||
Codex Model
|
||||
</Label>
|
||||
<span className="text-[11px] px-2 py-0.5 rounded-full border border-emerald-500/40 text-emerald-600 dark:text-emerald-400">
|
||||
CLI
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
{CODEX_MODELS.map((option) => {
|
||||
const isSelected = selectedModel === option.id;
|
||||
return (
|
||||
<button
|
||||
key={option.id}
|
||||
type="button"
|
||||
onClick={() => onModelSelect(option.id)}
|
||||
title={option.description}
|
||||
className={cn(
|
||||
'w-full px-3 py-2 rounded-md border text-sm font-medium transition-colors flex items-center justify-between',
|
||||
isSelected
|
||||
? 'bg-primary text-primary-foreground border-primary'
|
||||
: 'bg-background hover:bg-accent border-border'
|
||||
)}
|
||||
data-testid={`${testIdPrefix}-${option.id}`}
|
||||
>
|
||||
<span>{option.label}</span>
|
||||
{option.badge && (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={cn(
|
||||
'text-xs',
|
||||
isSelected
|
||||
? 'border-primary-foreground/50 text-primary-foreground'
|
||||
: 'border-muted-foreground/50 text-muted-foreground'
|
||||
)}
|
||||
>
|
||||
{option.badge}
|
||||
</Badge>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user