Skip to content
Open
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
2 changes: 1 addition & 1 deletion src-tauri/src/models/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pub struct QuotaProtectionConfig {
/// Reserved quota percentage (1-99)
pub threshold_percentage: u32,

/// List of monitored models (e.g. gemini-3-flash, gemini-3-pro-high, claude-sonnet-4-5)
/// List of monitored models (e.g. gemini-3-flash, gemini-3-pro-high, gemini-3.1-pro-high, claude-sonnet-4-5)
#[serde(default = "default_monitored_models")]
pub monitored_models: Vec<String>,
}
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/src/modules/tray.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ pub fn update_tray_menus(app: &tauri::AppHandle) {
// Use strict matching, consistent with frontend
for m in q.models {
let name = m.name.to_lowercase();
if name == "gemini-3-pro-high" { gemini_high = m.percentage; }
if name == "gemini-3.1-pro-high" || name == "gemini-3-pro-high" { gemini_high = m.percentage; }
if name == "gemini-3-pro-image" { gemini_image = m.percentage; }
if name == "claude-sonnet-4-5" { claude = m.percentage; }
}
Expand Down
37 changes: 30 additions & 7 deletions src-tauri/src/proxy/common/model_mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,16 @@ static CLAUDE_TO_GEMINI: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|
// Gemini 协议映射表
m.insert("gemini-2.5-flash-lite", "gemini-2.5-flash");
m.insert("gemini-2.5-flash-thinking", "gemini-2.5-flash-thinking");
m.insert("gemini-3-pro-low", "gemini-3-pro-preview");
m.insert("gemini-3-pro-high", "gemini-3-pro-preview");
m.insert("gemini-3-pro-preview", "gemini-3-pro-preview");
m.insert("gemini-3-pro", "gemini-3-pro-preview"); // 统一映射到 preview
// [Migrate] Gemini 3 Pro High/Low -> Gemini 3.1 Pro High/Low
// Keep 3.0 aliases for backward compatibility.
m.insert("gemini-3.1-pro-low", "gemini-3.1-pro-preview");
m.insert("gemini-3.1-pro-high", "gemini-3.1-pro-preview");
m.insert("gemini-3.1-pro-preview", "gemini-3.1-pro-preview");
m.insert("gemini-3.1-pro", "gemini-3.1-pro-preview");
m.insert("gemini-3-pro-low", "gemini-3.1-pro-preview");
m.insert("gemini-3-pro-high", "gemini-3.1-pro-preview");
m.insert("gemini-3-pro-preview", "gemini-3.1-pro-preview");
m.insert("gemini-3-pro", "gemini-3.1-pro-preview");
m.insert("gemini-2.5-flash", "gemini-2.5-flash");
m.insert("gemini-3-flash", "gemini-3-flash");
m.insert("gemini-3-pro-image", "gemini-3-pro-image");
Expand Down Expand Up @@ -136,7 +142,7 @@ pub async fn get_all_dynamic_models(
}

// 5. 确保包含常用的 Gemini/画画模型 ID
model_ids.insert("gemini-3-pro-low".to_string());
model_ids.insert("gemini-3.1-pro-low".to_string());

// [NEW] Issue #247: Dynamically generate all Image Gen Combinations
let base = "gemini-3-pro-image";
Expand All @@ -156,8 +162,8 @@ pub async fn get_all_dynamic_models(
model_ids.insert("gemini-2.5-flash".to_string());
// gemini-2.5-pro removed
model_ids.insert("gemini-3-flash".to_string());
model_ids.insert("gemini-3-pro-high".to_string());
model_ids.insert("gemini-3-pro-low".to_string());
model_ids.insert("gemini-3.1-pro-high".to_string());
model_ids.insert("gemini-3.1-pro-low".to_string());


let mut sorted_ids: Vec<_> = model_ids.into_iter().collect();
Expand Down Expand Up @@ -319,6 +325,23 @@ mod tests {
map_claude_model_to_gemini("unknown-model"),
"unknown-model"
);
// [Migrate] Gemini 3 Pro High/Low should route to Gemini 3.1 Pro
assert_eq!(
map_claude_model_to_gemini("gemini-3-pro-high"),
"gemini-3.1-pro-preview"
);
assert_eq!(
map_claude_model_to_gemini("gemini-3-pro-low"),
"gemini-3.1-pro-preview"
);
assert_eq!(
map_claude_model_to_gemini("gemini-3.1-pro-high"),
"gemini-3.1-pro-preview"
);
assert_eq!(
map_claude_model_to_gemini("gemini-3.1-pro-low"),
"gemini-3.1-pro-preview"
);

// Test Normalization (Opus 4.6 now merged into "claude" group)
assert_eq!(normalize_to_standard_id("claude-opus-4-6-thinking"), Some("claude".to_string()));
Expand Down
8 changes: 6 additions & 2 deletions src-tauri/src/proxy/mappers/claude/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,8 @@ pub fn transform_claude_request_in(
let target_model_supports_thinking = mapped_model.contains("-thinking")
|| mapped_model.starts_with("claude-")
|| mapped_model.contains("gemini-2.0-pro")
|| mapped_model.contains("gemini-3-pro");
|| mapped_model.contains("gemini-3-pro")
|| mapped_model.contains("gemini-3.1-pro");

if is_thinking_enabled && !target_model_supports_thinking {
tracing::warn!(
Expand Down Expand Up @@ -687,7 +688,10 @@ fn should_enable_thinking_by_default(model: &str) -> bool {
// [FIX #1557] Enable thinking by default for Gemini Pro models (gemini-3-pro, gemini-2.0-pro)
// These models prioritize reasoning but clients might not send thinking config for them
// unless they have "-thinking" suffix (which they don't in Antigravity mapping)
if model_lower.contains("gemini-2.0-pro") || model_lower.contains("gemini-3-pro") {
if model_lower.contains("gemini-2.0-pro")
|| model_lower.contains("gemini-3-pro")
|| model_lower.contains("gemini-3.1-pro")
{
tracing::debug!(
"[Thinking-Mode] Auto-enabling thinking for Gemini Pro model: {}",
model
Expand Down
4 changes: 3 additions & 1 deletion src-tauri/src/proxy/mappers/common_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ pub fn resolve_request_config(
|| mapped_model.starts_with("gemini-2.5-flash-")
|| mapped_model.starts_with("gemini-2.0-flash")
|| mapped_model.starts_with("gemini-3-")
|| mapped_model.starts_with("gemini-3.")
|| mapped_model.contains("claude-3-5-sonnet")
|| mapped_model.contains("claude-3-opus")
|| mapped_model.contains("claude-sonnet")
Expand All @@ -108,7 +109,8 @@ pub fn resolve_request_config(

// [FIX] Map logic aliases back to physical model names for upstream compatibility
final_model = match final_model.as_str() {
"gemini-3-pro-preview" => "gemini-3-pro-high".to_string(), // Preview maps back to High
"gemini-3-pro-preview" => "gemini-3.1-pro-high".to_string(), // 3.0 preview redirects to 3.1 High
"gemini-3.1-pro-preview" => "gemini-3.1-pro-high".to_string(),
"gemini-3-pro-image-preview" => "gemini-3-pro-image".to_string(),
"gemini-3-flash-preview" => "gemini-3-flash".to_string(),
_ => final_model,
Expand Down
4 changes: 2 additions & 2 deletions src-tauri/src/proxy/mappers/gemini/wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ pub fn wrap_request(
let is_preview = lower_model.contains("preview");
let should_inject = lower_model.contains("thinking")
|| (lower_model.contains("gemini-2.0-pro") && !is_preview)
|| (lower_model.contains("gemini-3-pro") && !is_preview);
|| (lower_model.contains("gemini-3-pro") && !is_preview)
|| (lower_model.contains("gemini-3.1-pro") && !is_preview);

if should_inject {
// Scope for borrowing inner_request/gen_config
Expand Down Expand Up @@ -904,4 +905,3 @@ mod tests {
assert_eq!(image_config_2["aspectRatio"], "1:1");
assert_eq!(image_config_2["imageSize"], "1K");
}

2 changes: 1 addition & 1 deletion src-tauri/src/proxy/mappers/openai/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub fn transform_openai_request(
mapped_model_lower.contains("-thinking")
|| mapped_model_lower.contains("gemini-2.0-pro")
|| mapped_model_lower.contains("gemini-3-pro")
|| mapped_model_lower.contains("gemini-3.1-pro")
)
&& !mapped_model_lower.contains("claude");
let is_claude_thinking = mapped_model_lower.ends_with("-thinking");
Expand Down Expand Up @@ -1081,4 +1082,3 @@ mod tests {
crate::proxy::config::update_image_thinking_mode(Some("enabled".to_string()));
}
}

18 changes: 10 additions & 8 deletions src-tauri/src/proxy/opencode_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,10 @@ fn build_model_catalog() -> Vec<ModelDef> {
reasoning: true,
variant_type: Some(VariantType::ClaudeThinking),
},
// Gemini 3 Pro models
// Gemini 3.1 Pro models
ModelDef {
id: "gemini-3-pro-high",
name: "Gemini 3 Pro High",
id: "gemini-3.1-pro-high",
name: "Gemini 3.1 Pro High",
context_limit: 1_048_576,
output_limit: 65_535,
input_modalities: &["text", "image", "pdf"],
Expand All @@ -103,8 +103,8 @@ fn build_model_catalog() -> Vec<ModelDef> {
variant_type: Some(VariantType::Gemini3Pro),
},
ModelDef {
id: "gemini-3-pro-low",
name: "Gemini 3 Pro Low",
id: "gemini-3.1-pro-low",
name: "Gemini 3.1 Pro Low",
context_limit: 1_048_576,
output_limit: 65_535,
input_modalities: &["text", "image", "pdf"],
Expand Down Expand Up @@ -1355,7 +1355,7 @@ mod tests {

// Should have all catalog models
assert!(models.contains_key("claude-sonnet-4-5"), "should have claude-sonnet-4-5");
assert!(models.contains_key("gemini-3-pro-high"), "should have gemini-3-pro-high");
assert!(models.contains_key("gemini-3.1-pro-high"), "should have gemini-3.1-pro-high");
assert!(models.contains_key("gemini-2.5-pro"), "should have gemini-2.5-pro");

// Check model structure
Expand All @@ -1368,7 +1368,7 @@ mod tests {
#[test]
fn test_sync_with_filtered_models() {
let config = serde_json::json!({});
let models_to_sync = &["claude-sonnet-4-5", "gemini-3-pro-high"];
let models_to_sync = &["claude-sonnet-4-5", "gemini-3.1-pro-high"];

let result = apply_sync_to_config(config, "http://localhost:3000", "test-api-key", Some(models_to_sync));

Expand All @@ -1377,7 +1377,7 @@ mod tests {
let models = ag.get("models").unwrap().as_object().unwrap();

assert!(models.contains_key("claude-sonnet-4-5"));
assert!(models.contains_key("gemini-3-pro-high"));
assert!(models.contains_key("gemini-3.1-pro-high"));
assert!(!models.contains_key("gemini-2.5-pro"), "should not have unselected models");
}

Expand Down Expand Up @@ -1611,6 +1611,8 @@ const ANTIGRAVITY_MODEL_IDS: &[&str] = &[
"claude-sonnet-4-5",
"claude-sonnet-4-5-thinking",
"claude-opus-4-5-thinking",
"gemini-3.1-pro-high",
"gemini-3.1-pro-low",
"gemini-3-pro-high",
"gemini-3-pro-low",
"gemini-3-flash",
Expand Down
13 changes: 9 additions & 4 deletions src/components/accounts/AccountRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ function AccountRow({ account, selected, onSelect, isCurrent, isRefreshing, isSw
const { t } = useTranslation();
// [重构] 按组聚合查找逻辑,优先显示组内配额最低的型号以与锁定状态(🔒)对齐
const geminiProModel = account.quota?.models
.filter(m => m.name.toLowerCase() === 'gemini-3-pro-high' || m.name.toLowerCase() === 'gemini-3-pro-low')
.filter(m =>
m.name.toLowerCase() === 'gemini-3-pro-high'
|| m.name.toLowerCase() === 'gemini-3-pro-low'
|| m.name.toLowerCase() === 'gemini-3.1-pro-high'
|| m.name.toLowerCase() === 'gemini-3.1-pro-low'
)
.sort((a, b) => (a.percentage || 0) - (b.percentage || 0))[0];

const geminiFlashModel = account.quota?.models.find(m => m.name.toLowerCase() === 'gemini-3-flash');
Expand Down Expand Up @@ -171,9 +176,9 @@ function AccountRow({ account, selected, onSelect, isCurrent, isRefreshing, isSw
/>
)}
<div className="relative z-10 w-full flex items-center text-[10px] font-mono leading-none">
<span className="w-[64px] text-gray-500 dark:text-gray-400 font-bold pr-1 flex items-center gap-1" title="Gemini 3 Pro">
{account.protected_models?.includes('gemini-3-pro-high') && <Lock className="w-2.5 h-2.5 text-rose-500 shrink-0 z-10" />}
<span className="truncate">G3 Pro</span>
<span className="w-[64px] text-gray-500 dark:text-gray-400 font-bold pr-1 flex items-center gap-1" title="Gemini 3.1 Pro">
{(account.protected_models?.includes('gemini-3-pro-high') || account.protected_models?.includes('gemini-3.1-pro-high')) && <Lock className="w-2.5 h-2.5 text-rose-500 shrink-0 z-10" />}
<span className="truncate">G3.1 Pro</span>
</span>
<div className="flex-1 flex justify-center">
{geminiProModel?.reset_time ? (
Expand Down
19 changes: 18 additions & 1 deletion src/components/accounts/AccountTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ const MODEL_GROUPS = {
'claude'
],
GEMINI_PRO: [
'gemini-3.1-pro-high',
'gemini-3.1-pro-low',
'gemini-3.1-pro-preview',
'gemini-3-pro-high',
'gemini-3-pro-low',
'gemini-3-pro-preview'
Expand All @@ -139,6 +142,19 @@ const MODEL_GROUPS = {
]
};

const MODEL_ID_ALIASES: Record<string, string[]> = {
'gemini-3-pro-high': ['gemini-3-pro-high', 'gemini-3.1-pro-high'],
'gemini-3-pro-low': ['gemini-3-pro-low', 'gemini-3.1-pro-low'],
'gemini-3-pro-preview': ['gemini-3-pro-preview', 'gemini-3.1-pro-preview'],
'gemini-3.1-pro-high': ['gemini-3.1-pro-high', 'gemini-3-pro-high'],
'gemini-3.1-pro-low': ['gemini-3.1-pro-low', 'gemini-3-pro-low'],
'gemini-3.1-pro-preview': ['gemini-3.1-pro-preview', 'gemini-3-pro-preview'],
};

function getModelAliases(modelId: string): string[] {
return MODEL_ID_ALIASES[modelId] || [modelId];
}

function isModelProtected(protectedModels: string[] | undefined, modelName: string): boolean {
if (!protectedModels || protectedModels.length === 0) return false;
const lowerName = modelName.toLowerCase();
Expand Down Expand Up @@ -336,11 +352,12 @@ function AccountRowContent({
})
: pinnedModels.filter(modelId => MODEL_CONFIG[modelId]).map(modelId => {
const config = MODEL_CONFIG[modelId];
const aliases = getModelAliases(modelId);
return {
id: modelId,
label: config.shortLabel || config.label,
protectedKey: config.protectedKey,
data: account.quota?.models.find(m => m.name.toLowerCase() === modelId)
data: account.quota?.models.find(m => aliases.includes(m.name.toLowerCase()))
};
})
).filter(m => m.id !== 'claude-sonnet-4-5-thinking' && m.id !== 'claude-opus-4-5-thinking')
Expand Down
9 changes: 8 additions & 1 deletion src/components/dashboard/BestAccounts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@ function BestAccounts({ accounts, currentAccountId, onSwitch }: BestAccountsProp
const geminiSorted = accounts
.filter(a => a.id !== currentAccountId)
.map(a => {
const proQuota = a.quota?.models.find(m => m.name.toLowerCase() === 'gemini-3-pro-high')?.percentage || 0;
const proQuota = (a.quota?.models || [])
.filter(m =>
m.name.toLowerCase() === 'gemini-3-pro-high'
|| m.name.toLowerCase() === 'gemini-3-pro-low'
|| m.name.toLowerCase() === 'gemini-3.1-pro-high'
|| m.name.toLowerCase() === 'gemini-3.1-pro-low'
)
.reduce((best, model) => Math.max(best, model.percentage || 0), 0);
const flashQuota = a.quota?.models.find(m => m.name.toLowerCase() === 'gemini-3-flash')?.percentage || 0;
// 综合评分:Pro 权重更高 (70%),Flash 权重 30%
return {
Expand Down
11 changes: 8 additions & 3 deletions src/components/dashboard/CurrentAccount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ function CurrentAccount({ account, onSwitch }: CurrentAccountProps) {
}

const geminiProModel = account.quota?.models
.filter(m => m.name.toLowerCase() === 'gemini-3-pro-high' || m.name.toLowerCase() === 'gemini-3-pro-low')
.filter(m =>
m.name.toLowerCase() === 'gemini-3-pro-high'
|| m.name.toLowerCase() === 'gemini-3-pro-low'
|| m.name.toLowerCase() === 'gemini-3.1-pro-high'
|| m.name.toLowerCase() === 'gemini-3.1-pro-low'
)
.sort((a, b) => (a.percentage || 0) - (b.percentage || 0))[0];

const geminiFlashModel = account.quota?.models.find(m => m.name.toLowerCase() === 'gemini-3-flash');
Expand Down Expand Up @@ -94,8 +99,8 @@ function CurrentAccount({ account, onSwitch }: CurrentAccountProps) {
<div className="space-y-1.5">
<div className="flex justify-between items-baseline">
<span className="text-xs font-medium text-gray-600 dark:text-gray-400 flex items-center gap-1">
{account.protected_models?.includes('gemini-3-pro-high') && <Lock className="w-2.5 h-2.5 text-rose-500" />}
Gemini 3 Pro
{(account.protected_models?.includes('gemini-3-pro-high') || account.protected_models?.includes('gemini-3.1-pro-high')) && <Lock className="w-2.5 h-2.5 text-rose-500" />}
Gemini 3.1 Pro
</span>
<div className="flex items-center gap-2">
<span className="text-[10px] text-gray-400 dark:text-gray-500" title={`${t('accounts.reset_time')}: ${new Date(geminiProModel.reset_time).toLocaleString()}`}>
Expand Down
9 changes: 7 additions & 2 deletions src/components/layout/MiniView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,12 @@ export default function MiniView() {

// Extract specific models to match AccountRow.tsx
const geminiProModel = currentAccount?.quota?.models
.filter(m => m.name.toLowerCase() === 'gemini-3-pro-high' || m.name.toLowerCase() === 'gemini-3-pro-low')
.filter(m =>
m.name.toLowerCase() === 'gemini-3-pro-high'
|| m.name.toLowerCase() === 'gemini-3-pro-low'
|| m.name.toLowerCase() === 'gemini-3.1-pro-high'
|| m.name.toLowerCase() === 'gemini-3.1-pro-low'
)
.sort((a, b) => (a.percentage || 0) - (b.percentage || 0))[0];

const geminiFlashModel = currentAccount?.quota?.models.find(m => m.name.toLowerCase() === 'gemini-3-flash');
Expand Down Expand Up @@ -271,7 +276,7 @@ export default function MiniView() {
{/* Models List */}
<AnimatePresence mode='popLayout'>
<div className="space-y-4 !mt-0">
{renderModelRow(geminiProModel, 'Gemini 3 Pro', 'emerald')}
{renderModelRow(geminiProModel, 'Gemini 3.1 Pro', 'emerald')}
{renderModelRow(geminiFlashModel, 'Gemini 3 Flash', 'emerald')}
{renderModelRow(claudeModel, t('common.claude_series', 'Claude 系列'), 'cyan')}

Expand Down
2 changes: 1 addition & 1 deletion src/components/settings/PinnedQuotaModels.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const PinnedQuotaModels = ({ config, onChange }: PinnedQuotaModelsProps) => {
};

const modelOptions = [
{ id: 'gemini-3-pro-high', label: 'G3 Pro', desc: 'Gemini 3 Pro High' },
{ id: 'gemini-3-pro-high', label: 'G3.1 Pro', desc: 'Gemini 3.1 Pro High' },
{ id: 'gemini-3-flash', label: 'G3 Flash', desc: 'Gemini 3 Flash' },
{ id: 'gemini-3-pro-image', label: 'G3 Image', desc: 'Gemini 3 Pro Image' },
{ id: 'claude-opus-4-6-thinking', label: 'Claude 4.6 TK', desc: 'Claude 4.6 Opus Thinking' }
Expand Down
2 changes: 1 addition & 1 deletion src/components/settings/QuotaProtection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const QuotaProtection = ({ config, onChange }: QuotaProtectionProps) => {

const monitoredModelsOptions = [
{ id: 'gemini-3-flash', label: 'Gemini 3 Flash' },
{ id: 'gemini-3-pro-high', label: 'Gemini 3 Pro High' },
{ id: 'gemini-3-pro-high', label: 'Gemini 3.1 Pro High' },
{ id: 'claude-opus-4-6-thinking', label: 'Claude 4.6 TK' },
{ id: 'gemini-3-pro-image', label: 'Gemini 3 Pro Image' }
];
Expand Down
2 changes: 1 addition & 1 deletion src/components/settings/SmartWarmup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const SmartWarmup: React.FC<SmartWarmupProps> = ({ config, onChange }) => {

const warmupModelsOptions = [
{ id: 'gemini-3-flash', label: 'Gemini 3 Flash' },
{ id: 'gemini-3-pro-high', label: 'Gemini 3 Pro High' },
{ id: 'gemini-3-pro-high', label: 'Gemini 3.1 Pro High' },
{ id: 'claude-opus-4-6-thinking', label: 'Claude 4.6 TK' },
{ id: 'gemini-3-pro-image', label: 'Gemini 3 Pro Image' }
];
Expand Down
Loading