feat: 新增模型支持并扩展功能配置
- 添加 Gemini 3.1 Flash Lite 模型到 provider-models 和别名映射 - 扩展 Codex 转换器以支持 reasoning 配置和服务层级传递 - 为 Codex 模型添加 -fast 后缀变体,自动设置优先级服务层级和高推理强度 - 扩展 Grok 模型以支持 -nsfw 后缀变体,并添加账户级 NSFW 设置流程 - 放宽 Gemini 模型思考功能检测条件以包含所有 gemini-3 系列模型
This commit is contained in:
parent
4a1a382dc2
commit
3f3c9e1a45
6 changed files with 124 additions and 18 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
2.10.7
|
2.10.8
|
||||||
|
|
|
||||||
|
|
@ -103,20 +103,19 @@ export class CodexConverter extends BaseConverter {
|
||||||
codexRequest.store = false;
|
codexRequest.store = false;
|
||||||
codexRequest.parallel_tool_calls = true;
|
codexRequest.parallel_tool_calls = true;
|
||||||
codexRequest.include = ['reasoning.encrypted_content'];
|
codexRequest.include = ['reasoning.encrypted_content'];
|
||||||
|
codexRequest.service_tier = responsesRequest.service_tier || 'default';
|
||||||
|
|
||||||
// 删除Codex不支持的字段
|
// 删除Codex不支持的字段
|
||||||
delete codexRequest.max_output_tokens;
|
delete codexRequest.max_output_tokens;
|
||||||
delete codexRequest.max_completion_tokens;
|
delete codexRequest.max_completion_tokens;
|
||||||
delete codexRequest.temperature;
|
delete codexRequest.temperature;
|
||||||
delete codexRequest.top_p;
|
delete codexRequest.top_p;
|
||||||
delete codexRequest.service_tier;
|
|
||||||
delete codexRequest.user;
|
delete codexRequest.user;
|
||||||
delete codexRequest.reasoning;
|
|
||||||
|
|
||||||
// 添加 reasoning 配置
|
// 添加 reasoning 配置
|
||||||
codexRequest.reasoning = {
|
codexRequest.reasoning = {
|
||||||
"effort": "medium",
|
"effort": responsesRequest.reasoning_effort || responsesRequest.reasoning?.effort || "medium",
|
||||||
"summary": "auto"
|
"summary": responsesRequest.reasoning?.summary || "auto"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -155,11 +154,12 @@ export class CodexConverter extends BaseConverter {
|
||||||
store: false,
|
store: false,
|
||||||
metadata: data.metadata || {},
|
metadata: data.metadata || {},
|
||||||
reasoning: {
|
reasoning: {
|
||||||
effort: data.reasoning_effort || 'medium',
|
effort: data.reasoning_effort || data.reasoning?.effort || 'medium',
|
||||||
summary: 'auto'
|
summary: data.reasoning?.summary || 'auto'
|
||||||
},
|
},
|
||||||
parallel_tool_calls: true,
|
parallel_tool_calls: true,
|
||||||
include: ['reasoning.encrypted_content']
|
include: ['reasoning.encrypted_content'],
|
||||||
|
service_tier: data.service_tier || 'default'
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理 OpenAI Responses 特有的 instructions 和 input 字段(如果存在)
|
// 处理 OpenAI Responses 特有的 instructions 和 input 字段(如果存在)
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ const MODEL_ALIAS_MAP = {
|
||||||
'gemini-3-pro-image-preview': 'gemini-3-pro-image',
|
'gemini-3-pro-image-preview': 'gemini-3-pro-image',
|
||||||
'gemini-3-pro-preview': 'gemini-3-pro-high',
|
'gemini-3-pro-preview': 'gemini-3-pro-high',
|
||||||
'gemini-3.1-pro-preview': 'gemini-3.1-pro-high',
|
'gemini-3.1-pro-preview': 'gemini-3.1-pro-high',
|
||||||
|
'gemini-3.1-flash-lite-preview': 'gemini-3.1-flash-lite-preview',
|
||||||
'gemini-3-flash-preview': 'gemini-3-flash',
|
'gemini-3-flash-preview': 'gemini-3-flash',
|
||||||
'gemini-2.5-flash-preview': 'gemini-2.5-flash',
|
'gemini-2.5-flash-preview': 'gemini-2.5-flash',
|
||||||
'gemini-claude-sonnet-4-5': 'claude-sonnet-4-5',
|
'gemini-claude-sonnet-4-5': 'claude-sonnet-4-5',
|
||||||
|
|
@ -77,6 +78,7 @@ const MODEL_NAME_MAP = {
|
||||||
'gemini-3-pro-image': 'gemini-3-pro-image-preview',
|
'gemini-3-pro-image': 'gemini-3-pro-image-preview',
|
||||||
'gemini-3-pro-high': 'gemini-3-pro-preview',
|
'gemini-3-pro-high': 'gemini-3-pro-preview',
|
||||||
'gemini-3.1-pro-high': 'gemini-3.1-pro-preview',
|
'gemini-3.1-pro-high': 'gemini-3.1-pro-preview',
|
||||||
|
'gemini-3.1-flash-lite-preview': 'gemini-3.1-flash-lite-preview',
|
||||||
'gemini-3-flash': 'gemini-3-flash-preview',
|
'gemini-3-flash': 'gemini-3-flash-preview',
|
||||||
'gemini-2.5-flash': 'gemini-2.5-flash-preview',
|
'gemini-2.5-flash': 'gemini-2.5-flash-preview',
|
||||||
'claude-sonnet-4-5': 'gemini-claude-sonnet-4-5',
|
'claude-sonnet-4-5': 'gemini-claude-sonnet-4-5',
|
||||||
|
|
@ -129,8 +131,8 @@ function isImageModel(modelName) {
|
||||||
function modelSupportsThinking(modelName) {
|
function modelSupportsThinking(modelName) {
|
||||||
if (!modelName) return false;
|
if (!modelName) return false;
|
||||||
const name = modelName.toLowerCase();
|
const name = modelName.toLowerCase();
|
||||||
// 支持 thinking 的模型:gemini-3-*, gemini-2.5-*, claude-*-thinking
|
// 支持 thinking 的模型:gemini-3*, gemini-2.5-*, claude-*-thinking
|
||||||
return name.startsWith('gemini-3-') ||
|
return name.startsWith('gemini-3') ||
|
||||||
name.startsWith('gemini-2.5-') ||
|
name.startsWith('gemini-2.5-') ||
|
||||||
name.includes('-thinking');
|
name.includes('-thinking');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,7 @@ const httpsAgent = new https.Agent({
|
||||||
ALPNProtocols: ['http/1.1'], ecdhCurve: 'X25519:P-256:P-384', honorCipherOrder: false, sessionTimeout: 300,
|
ALPNProtocols: ['http/1.1'], ecdhCurve: 'X25519:P-256:P-384', honorCipherOrder: false, sessionTimeout: 300,
|
||||||
});
|
});
|
||||||
|
|
||||||
const GROK_MODELS = getProviderModels(MODEL_PROVIDER.GROK_CUSTOM);
|
const CORE_MODEL_MAPPING = {
|
||||||
const MODEL_MAPPING = {
|
|
||||||
'grok-3': { name: 'grok-3', mode: 'MODEL_MODE_GROK_3' },
|
'grok-3': { name: 'grok-3', mode: 'MODEL_MODE_GROK_3' },
|
||||||
'grok-3-mini': { name: 'grok-3', mode: 'MODEL_MODE_GROK_3_MINI_THINKING' },
|
'grok-3-mini': { name: 'grok-3', mode: 'MODEL_MODE_GROK_3_MINI_THINKING' },
|
||||||
'grok-3-thinking': { name: 'grok-3', mode: 'MODEL_MODE_GROK_3_THINKING' },
|
'grok-3-thinking': { name: 'grok-3', mode: 'MODEL_MODE_GROK_3_THINKING' },
|
||||||
|
|
@ -53,6 +52,24 @@ const MODEL_MAPPING = {
|
||||||
'grok-imagine-1.0-video': { name: 'grok-3', mode: 'MODEL_MODE_FAST' }
|
'grok-imagine-1.0-video': { name: 'grok-3', mode: 'MODEL_MODE_FAST' }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const MODEL_MAPPING = { ...CORE_MODEL_MAPPING };
|
||||||
|
Object.keys(CORE_MODEL_MAPPING).forEach(key => {
|
||||||
|
if (!key.endsWith('-nsfw')) {
|
||||||
|
MODEL_MAPPING[`${key}-nsfw`] = CORE_MODEL_MAPPING[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const GROK_MODELS = Object.keys(MODEL_MAPPING);
|
||||||
|
|
||||||
|
function isGrokNsfwModel(modelId) {
|
||||||
|
return typeof modelId === 'string' && modelId.toLowerCase().endsWith('-nsfw');
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeGrokModelId(modelId) {
|
||||||
|
if (typeof modelId !== 'string') return modelId;
|
||||||
|
return isGrokNsfwModel(modelId) ? modelId.slice(0, -5) : modelId;
|
||||||
|
}
|
||||||
|
|
||||||
export class GrokApiService {
|
export class GrokApiService {
|
||||||
constructor(config) {
|
constructor(config) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
|
@ -63,11 +80,70 @@ export class GrokApiService {
|
||||||
this.baseUrl = config.GROK_BASE_URL || 'https://grok.com';
|
this.baseUrl = config.GROK_BASE_URL || 'https://grok.com';
|
||||||
this.chatApi = `${this.baseUrl}/rest/app-chat/conversations/new`;
|
this.chatApi = `${this.baseUrl}/rest/app-chat/conversations/new`;
|
||||||
this.isInitialized = false;
|
this.isInitialized = false;
|
||||||
|
this.nsfwSetupDone = false;
|
||||||
this.converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.GROK);
|
this.converter = ConverterFactory.getConverter(MODEL_PROTOCOL_PREFIX.GROK);
|
||||||
if (this.converter && this.uuid) this.converter.setUuid(this.uuid);
|
if (this.converter && this.uuid) this.converter.setUuid(this.uuid);
|
||||||
this.lastSyncAt = null;
|
this.lastSyncAt = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async setupNsfw() {
|
||||||
|
if (this.nsfwSetupDone) return;
|
||||||
|
try {
|
||||||
|
await this.acceptTos();
|
||||||
|
await this.setBirthDate();
|
||||||
|
await this.enableNsfwAccount();
|
||||||
|
this.nsfwSetupDone = true;
|
||||||
|
logger.info(`[Grok NSFW] Account-level NSFW setup completed for ${this.uuid}`);
|
||||||
|
} catch (error) {
|
||||||
|
logger.warn(`[Grok NSFW] Failed to setup account-level NSFW: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async acceptTos() {
|
||||||
|
const axiosConfig = { method: 'post', url: `${this.baseUrl}/rest/app-chat/accept-tos`, headers: this.buildHeaders(), data: {}, httpAgent, httpsAgent, timeout: 15000 };
|
||||||
|
configureAxiosProxy(axiosConfig, this.config, MODEL_PROVIDER.GROK_CUSTOM);
|
||||||
|
this._applySidecar(axiosConfig);
|
||||||
|
try { await axios(axiosConfig); } catch (e) { logger.debug(`[Grok TOS] ${e.message}`); }
|
||||||
|
}
|
||||||
|
|
||||||
|
async setBirthDate() {
|
||||||
|
const axiosConfig = { method: 'post', url: `${this.baseUrl}/rest/app-chat/set-birth-date`, headers: this.buildHeaders(), data: { "birthDate": "1990-01-01" }, httpAgent, httpsAgent, timeout: 15000 };
|
||||||
|
configureAxiosProxy(axiosConfig, this.config, MODEL_PROVIDER.GROK_CUSTOM);
|
||||||
|
this._applySidecar(axiosConfig);
|
||||||
|
try { await axios(axiosConfig); } catch (e) { logger.debug(`[Grok Birth] ${e.message}`); }
|
||||||
|
}
|
||||||
|
|
||||||
|
async enableNsfwAccount() {
|
||||||
|
const name = Buffer.from("always_show_nsfw_content");
|
||||||
|
const inner = Buffer.concat([Buffer.from([0x0a, name.length]), name]);
|
||||||
|
const protobuf = Buffer.concat([Buffer.from([0x0a, 0x02, 0x10, 0x01, 0x12, inner.length]), inner]);
|
||||||
|
|
||||||
|
const header = Buffer.alloc(5);
|
||||||
|
header.writeUInt8(0, 0);
|
||||||
|
header.writeUInt32BE(protobuf.length, 1);
|
||||||
|
const payload = Buffer.concat([header, protobuf]);
|
||||||
|
|
||||||
|
const headers = this.buildHeaders();
|
||||||
|
headers['content-type'] = 'application/grpc-web+proto';
|
||||||
|
headers['x-grpc-web'] = '1';
|
||||||
|
headers['x-user-agent'] = 'connect-es/2.1.1';
|
||||||
|
headers['referer'] = `${this.baseUrl}/?_s=data`;
|
||||||
|
|
||||||
|
const axiosConfig = {
|
||||||
|
method: 'post',
|
||||||
|
url: `${this.baseUrl}/auth_mgmt.AuthManagement/UpdateUserFeatureControls`,
|
||||||
|
headers,
|
||||||
|
data: payload,
|
||||||
|
httpAgent,
|
||||||
|
httpsAgent,
|
||||||
|
timeout: 15000,
|
||||||
|
responseType: 'arraybuffer'
|
||||||
|
};
|
||||||
|
configureAxiosProxy(axiosConfig, this.config, MODEL_PROVIDER.GROK_CUSTOM);
|
||||||
|
this._applySidecar(axiosConfig);
|
||||||
|
try { await axios(axiosConfig); } catch (e) { throw e; }
|
||||||
|
}
|
||||||
|
|
||||||
_applySidecar(axiosConfig) {
|
_applySidecar(axiosConfig) {
|
||||||
const sidecar = getTLSSidecar();
|
const sidecar = getTLSSidecar();
|
||||||
if (sidecar.isReady()) {
|
if (sidecar.isReady()) {
|
||||||
|
|
@ -243,7 +319,9 @@ export class GrokApiService {
|
||||||
}
|
}
|
||||||
|
|
||||||
buildPayload(modelId, requestBody) {
|
buildPayload(modelId, requestBody) {
|
||||||
const mapping = MODEL_MAPPING[modelId] || MODEL_MAPPING['grok-3'];
|
const rawModelId = typeof modelId === 'string' ? modelId : '';
|
||||||
|
const normalizedModelId = normalizeGrokModelId(rawModelId);
|
||||||
|
const mapping = MODEL_MAPPING[normalizedModelId] || MODEL_MAPPING['grok-3'];
|
||||||
let message = requestBody.message || "";
|
let message = requestBody.message || "";
|
||||||
let toolOverrides = requestBody.toolOverrides || {};
|
let toolOverrides = requestBody.toolOverrides || {};
|
||||||
let fileAttachments = requestBody.fileAttachments || [];
|
let fileAttachments = requestBody.fileAttachments || [];
|
||||||
|
|
@ -296,12 +374,13 @@ export class GrokApiService {
|
||||||
if (requestBody.videoGenPrompt) message = requestBody.videoGenPrompt;
|
if (requestBody.videoGenPrompt) message = requestBody.videoGenPrompt;
|
||||||
}
|
}
|
||||||
|
|
||||||
const modelLower = modelId.toLowerCase();
|
const modelLower = normalizedModelId.toLowerCase();
|
||||||
const isMediaModel = modelLower.includes('imagine') || modelLower.includes('video') || modelLower.includes('edit');
|
const isMediaModel = modelLower.includes('imagine') || modelLower.includes('video') || modelLower.includes('edit');
|
||||||
|
const isNsfw = isGrokNsfwModel(rawModelId) || requestBody.nsfw === true || requestBody.disableNsfwFilter === true;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"deviceEnvInfo": { "darkModeEnabled": false, "devicePixelRatio": 2, "screenWidth": 2056, "screenHeight": 1329, "viewportWidth": 2056, "viewportHeight": 1083 },
|
"deviceEnvInfo": { "darkModeEnabled": false, "devicePixelRatio": 2, "screenWidth": 2056, "screenHeight": 1329, "viewportWidth": 2056, "viewportHeight": 1083 },
|
||||||
"disableMemory": false, "disableSearch": false, "disableSelfHarmShortCircuit": false, "disableTextFollowUps": false,
|
"disableMemory": false, "disableNsfwFilter": isNsfw, "disableSearch": false, "disableSelfHarmShortCircuit": false, "disableTextFollowUps": false,
|
||||||
"enableImageGeneration": isMediaModel, "enableImageStreaming": isMediaModel, "enableSideBySide": true,
|
"enableImageGeneration": isMediaModel, "enableImageStreaming": isMediaModel, "enableSideBySide": true,
|
||||||
"fileAttachments": fileAttachments, "forceConcise": false, "forceSideBySide": false, "imageAttachments": [], "imageGenerationCount": 2,
|
"fileAttachments": fileAttachments, "forceConcise": false, "forceSideBySide": false, "imageAttachments": [], "imageGenerationCount": 2,
|
||||||
"isAsyncChat": false, "isReasoning": false, "message": message, "modelMode": mapping.mode, "modelName": mapping.name,
|
"isAsyncChat": false, "isReasoning": false, "message": message, "modelMode": mapping.mode, "modelName": mapping.name,
|
||||||
|
|
@ -406,9 +485,14 @@ export class GrokApiService {
|
||||||
getProviderPoolManager().markProviderNeedRefresh(MODEL_PROVIDER.GROK_CUSTOM, { uuid: this.uuid });
|
getProviderPoolManager().markProviderNeedRefresh(MODEL_PROVIDER.GROK_CUSTOM, { uuid: this.uuid });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const rawModel = typeof model === 'string' ? model : '';
|
||||||
|
const normalizedModel = normalizeGrokModelId(rawModel);
|
||||||
|
const modelLower = normalizedModel.toLowerCase();
|
||||||
|
const isNsfw = isGrokNsfwModel(rawModel) || requestBody.nsfw === true || requestBody.disableNsfwFilter === true;
|
||||||
|
if (isNsfw) await this.setupNsfw();
|
||||||
|
|
||||||
this.buildPayload(model, requestBody);
|
this.buildPayload(model, requestBody);
|
||||||
|
|
||||||
const modelLower = model.toLowerCase();
|
|
||||||
const isVideoModel = modelLower.includes('video');
|
const isVideoModel = modelLower.includes('video');
|
||||||
const isImageModel = modelLower.includes('imagine') && !isVideoModel && !modelLower.includes('edit');
|
const isImageModel = modelLower.includes('imagine') && !isVideoModel && !modelLower.includes('edit');
|
||||||
const isImageEditModel = modelLower.includes('edit');
|
const isImageEditModel = modelLower.includes('edit');
|
||||||
|
|
@ -461,7 +545,8 @@ export class GrokApiService {
|
||||||
requestBody.toolOverrides = { ...requestBody.toolOverrides, videoGen: true };
|
requestBody.toolOverrides = { ...requestBody.toolOverrides, videoGen: true };
|
||||||
}
|
}
|
||||||
} else if (isImageModel || isImageEditModel) {
|
} else if (isImageModel || isImageEditModel) {
|
||||||
requestBody.toolOverrides = { ...requestBody.toolOverrides, imageGen: true };
|
const isNsfw = isGrokNsfwModel(rawModel) || requestBody.nsfw === true || requestBody.disableNsfwFilter === true;
|
||||||
|
requestBody.toolOverrides = { ...requestBody.toolOverrides, imageGen: isNsfw ? { disableNsfwFilter: true } : true };
|
||||||
}
|
}
|
||||||
|
|
||||||
let fileAttachments = requestBody.fileAttachments || [];
|
let fileAttachments = requestBody.fileAttachments || [];
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,9 @@ import { MODEL_PROVIDER, formatExpiryLog } from '../../utils/common.js';
|
||||||
import { getProxyConfigForProvider } from '../../utils/proxy-utils.js';
|
import { getProxyConfigForProvider } from '../../utils/proxy-utils.js';
|
||||||
import { getProviderModels } from '../provider-models.js';
|
import { getProviderModels } from '../provider-models.js';
|
||||||
|
|
||||||
const CODEX_MODELS = getProviderModels(MODEL_PROVIDER.CODEX_API);
|
const baseModels = getProviderModels(MODEL_PROVIDER.CODEX_API);
|
||||||
|
const fastModels = baseModels.map(m => `${m}-fast`);
|
||||||
|
const CODEX_MODELS = [...new Set([...baseModels, ...fastModels])];
|
||||||
const CODEX_VERSION = '0.111.0';
|
const CODEX_VERSION = '0.111.0';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -339,9 +341,19 @@ export class CodexApiService {
|
||||||
// 明确会话维度:优先使用 session_id 或 conversation_id,其次 user_id
|
// 明确会话维度:优先使用 session_id 或 conversation_id,其次 user_id
|
||||||
const sessionId = metadata.session_id || metadata.conversation_id || metadata.user_id || 'default';
|
const sessionId = metadata.session_id || metadata.conversation_id || metadata.user_id || 'default';
|
||||||
|
|
||||||
|
// 判断是否为 fast 模型并确定默认值
|
||||||
|
const isFastModel = model.endsWith('-fast');
|
||||||
|
const defaultServiceTier = isFastModel ? 'priority' : 'default';
|
||||||
|
const defaultReasoningEffort = isFastModel ? 'xhigh' : 'medium';
|
||||||
|
|
||||||
const cleanedBody = { ...requestBody };
|
const cleanedBody = { ...requestBody };
|
||||||
delete cleanedBody.metadata;
|
delete cleanedBody.metadata;
|
||||||
|
|
||||||
|
// 如果是 fast 模型,移除后缀再传给上游
|
||||||
|
if (isFastModel) {
|
||||||
|
cleanedBody.model = model.replace('-fast', '');
|
||||||
|
}
|
||||||
|
|
||||||
// 生成会话缓存键
|
// 生成会话缓存键
|
||||||
// 弱化 model 依赖,以提升同会话跨模型的缓存命中率
|
// 弱化 model 依赖,以提升同会话跨模型的缓存命中率
|
||||||
// 仅当 sessionId 为 'default' 时加上 model 前缀,提供基础隔离
|
// 仅当 sessionId 为 'default' 时加上 model 前缀,提供基础隔离
|
||||||
|
|
@ -363,6 +375,11 @@ export class CodexApiService {
|
||||||
// 注意:requestBody 已经去除了 metadata
|
// 注意:requestBody 已经去除了 metadata
|
||||||
return {
|
return {
|
||||||
...cleanedBody,
|
...cleanedBody,
|
||||||
|
service_tier: cleanedBody.service_tier || defaultServiceTier,
|
||||||
|
reasoning: {
|
||||||
|
...cleanedBody.reasoning,
|
||||||
|
effort: cleanedBody.reasoning?.effort || defaultReasoningEffort
|
||||||
|
},
|
||||||
stream,
|
stream,
|
||||||
prompt_cache_key: cache.id
|
prompt_cache_key: cache.id
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,13 @@ export const PROVIDER_MODELS = {
|
||||||
'gemini-3-pro-preview',
|
'gemini-3-pro-preview',
|
||||||
'gemini-3-flash-preview',
|
'gemini-3-flash-preview',
|
||||||
'gemini-3.1-pro-preview',
|
'gemini-3.1-pro-preview',
|
||||||
|
'gemini-3.1-flash-lite-preview',
|
||||||
],
|
],
|
||||||
'gemini-antigravity': [
|
'gemini-antigravity': [
|
||||||
'gemini-2.5-computer-use-preview-10-2025',
|
'gemini-2.5-computer-use-preview-10-2025',
|
||||||
'gemini-3-pro-image-preview',
|
'gemini-3-pro-image-preview',
|
||||||
'gemini-3.1-pro-preview',
|
'gemini-3.1-pro-preview',
|
||||||
|
'gemini-3.1-flash-lite-preview',
|
||||||
'gemini-3-pro-preview',
|
'gemini-3-pro-preview',
|
||||||
'gemini-3-flash-preview',
|
'gemini-3-flash-preview',
|
||||||
'gemini-2.5-flash-preview',
|
'gemini-2.5-flash-preview',
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue