From 7ec38730e9bbe1ecfbe6c21538b018502616fad9 Mon Sep 17 00:00:00 2001 From: shiyue Date: Wed, 11 Mar 2026 14:29:58 +0000 Subject: [PATCH] feat: integrate Grok2 API as preset provider for image/video generation - Add grok2 to PRESET_PROVIDERS with openai-compat gateway route - Add grok-imagine-1.0 (image) and grok-imagine-1.0-video (video) preset models - Extend OpenAIImageGenerateSize to support 1280x720 and 720x1280 resolutions - Add grok2 to COMPATIBLE_PROVIDER_KEYS in router - Add grok2 to VERIFIABLE_PROVIDER_KEYS for test-connection UI - Show Base URL input and model tabs for grok2 provider - Add i18n labels for Grok2 API compatibility layer badge - Add GROK2_SETUP.md with configuration documentation --- GROK2_SETUP.md | 165 ++++++++++++++++++ messages/en/apiConfig.json | 1 + messages/zh/apiConfig.json | 1 + .../provider-card/ProviderAdvancedFields.tsx | 6 +- .../provider-card/ProviderCardShell.tsx | 1 + .../hooks/useProviderCardState.ts | 4 +- .../api-config/provider-card/types.ts | 2 +- .../profile/components/api-config/types.ts | 6 + src/app/api/user/api-config/route.ts | 4 +- src/lib/model-gateway/openai-compat/image.ts | 4 + src/lib/model-gateway/router.ts | 1 + 11 files changed, 188 insertions(+), 7 deletions(-) create mode 100644 GROK2_SETUP.md diff --git a/GROK2_SETUP.md b/GROK2_SETUP.md new file mode 100644 index 0000000..0460318 --- /dev/null +++ b/GROK2_SETUP.md @@ -0,0 +1,165 @@ +# Grok2 API 集成说明 + +## 概述 + +本项目已集成 Grok2 API 支持,可以使用 Grok2 进行图像和视频生成。 + +## 配置步骤 + +### 1. 启动服务 + +确保 Docker 镜像已构建并启动: + +```bash +cd /opt/stacks/waoo +docker-compose up -d +``` + +### 2. 配置 Grok2 Provider + +1. 访问 http://localhost:13000 +2. 登录后进入 **个人设置 > API 配置** +3. 找到 **Grok2 API** 提供商 +4. 点击 **配置** 按钮 +5. 填写以下信息: + - **Base URL**: `http://45.128.210.183:8048` + - **API Key**: (如果需要的话填写你的 API Key) + +### 3. 配置模型 + +在 Grok2 API 提供商下,已预置以下模型: + +#### 图像生成模型 +- **模型 ID**: `grok-imagine-1.0` +- **名称**: Grok Imagine 1.0 +- **支持尺寸**: + - 1280x720 + - 720x1280 + - 1792x1024 + - 1024x1792 + - 1024x1024 + +#### 视频生成模型 +- **模型 ID**: `grok-imagine-1.0-video` +- **名称**: Grok Imagine 1.0 Video +- **支持时长**: 6-30 秒 +- **支持质量**: standard (480p), high (720p) + +### 4. 配置视频模型模板(重要) + +由于 Grok2 的视频接口与标准 OpenAI 格式不同,需要为视频模型配置模板: + +1. 在 **Grok Imagine 1.0 Video** 模型卡片中,点击 **高级设置** +2. 找到 **兼容媒体模板** 选项 +3. 点击 **AI 助手** 或 **手动配置** +4. 使用以下模板配置: + +```json +{ + "version": 1, + "mediaType": "video", + "mode": "sync", + "create": { + "method": "POST", + "path": "/v1/videos", + "contentType": "application/json", + "bodyTemplate": { + "model": "{{model}}", + "prompt": "{{prompt}}", + "image_reference": "{{image}}", + "seconds": "{{duration}}", + "quality": "{{quality}}", + "size": "{{size}}" + } + }, + "response": { + "outputUrlPath": "data[0].url" + } +} +``` + +5. 点击 **验证模板** 确保配置正确 +6. 保存配置 + +### 5. 设置默认模型(可选) + +在 **默认模型配置** 区域,可以将 Grok2 模型设置为默认: + +- **角色模型**: Grok Imagine 1.0 +- **场景模型**: Grok Imagine 1.0 +- **分镜模型**: Grok Imagine 1.0 +- **视频模型**: Grok Imagine 1.0 Video + +## 使用说明 + +### 图像生成 + +1. 进入项目的资产库或分镜页面 +2. 选择使用 Grok2 图像模型 +3. 输入提示词 +4. 选择尺寸(支持 Grok2 特有的 1280x720 和 720x1280) +5. 点击生成 + +### 视频生成 + +1. 准备一张起始图片 +2. 在视频生成界面选择 Grok Imagine 1.0 Video 模型 +3. 输入提示词(可选) +4. 设置时长(6-30 秒) +5. 选择质量(standard 或 high) +6. 点击生成 + +## 注意事项 + +1. **图像生成**:Grok2 图像生成使用标准 OpenAI 兼容接口,可以直接使用 +2. **视频生成**:必须配置模板才能正常工作,因为 Grok2 的视频接口路径和参数与标准格式不同 +3. **API 地址**:确保 `http://45.128.210.183:8048` 可以从 Docker 容器内访问 +4. **网络问题**:如果遇到连接问题,检查防火墙和网络配置 + +## 故障排查 + +### 图像生成失败 + +1. 检查 Base URL 是否正确 +2. 检查 API Key 是否有效(如果需要) +3. 查看浏览器控制台或服务器日志 + +### 视频生成失败 + +1. 确认已正确配置视频模型模板 +2. 检查模板中的字段映射是否正确 +3. 确认 Grok2 API 服务正常运行 +4. 查看详细错误信息 + +### 测试连接 + +在配置 Provider 后,可以点击 **测试连接** 按钮验证配置是否正确。 + +## 技术细节 + +### 实现方式 + +- Grok2 被实现为一个预设 Provider,使用 OpenAI 兼容网关 +- 图像生成通过扩展尺寸白名单支持 Grok2 特有尺寸 +- 视频生成通过模板系统映射 Grok2 的自定义接口格式 + +### 路由配置 + +- Provider Key: `grok2` +- Gateway Route: `openai-compat` +- API Mode: `openai-official` + +### 字段映射 + +视频生成字段映射: +- `duration` → `seconds` +- `image` → `image_reference` +- `quality` → `quality` (standard/high) +- `size` → `size` (宽高比) + +## 更新日志 + +- 2026-03-11: 初始集成 Grok2 API 支持 + - 添加图像生成支持(1280x720, 720x1280 尺寸) + - 添加视频生成支持(6-30 秒,standard/high 质量) + - 配置模板系统适配 Grok2 视频接口 diff --git a/messages/en/apiConfig.json b/messages/en/apiConfig.json index 516324c..535cb7a 100644 --- a/messages/en/apiConfig.json +++ b/messages/en/apiConfig.json @@ -9,6 +9,7 @@ "connect": "Connect", "compatibilityLayerOpenAI": "OpenAI Compatible Layer", "compatibilityLayerGemini": "Gemini Compatible Layer", + "compatibilityLayerGrok2": "Grok2 API", "show": "Show", "hide": "Hide", "capability": "Models", diff --git a/messages/zh/apiConfig.json b/messages/zh/apiConfig.json index 6f92cd7..a0379b5 100644 --- a/messages/zh/apiConfig.json +++ b/messages/zh/apiConfig.json @@ -9,6 +9,7 @@ "connect": "连接", "compatibilityLayerOpenAI": "OpenAI 兼容层", "compatibilityLayerGemini": "Gemini 兼容层", + "compatibilityLayerGrok2": "Grok2 API", "show": "显示", "hide": "隐藏", "capability": "能力", diff --git a/src/app/[locale]/profile/components/api-config/provider-card/ProviderAdvancedFields.tsx b/src/app/[locale]/profile/components/api-config/provider-card/ProviderAdvancedFields.tsx index 8e7d7d4..19bcca6 100644 --- a/src/app/[locale]/profile/components/api-config/provider-card/ProviderAdvancedFields.tsx +++ b/src/app/[locale]/profile/components/api-config/provider-card/ProviderAdvancedFields.tsx @@ -64,7 +64,7 @@ const MODEL_TYPES: readonly ProviderCardModelType[] = ['llm', 'image', 'video', export function getAddableModelTypesForProvider(providerId: string): ProviderCardModelType[] { const providerKey = getProviderKey(providerId) - if (providerKey === 'openai-compatible') return ['llm', 'image', 'video'] + if (providerKey === 'openai-compatible' || providerKey === 'grok2') return ['image', 'video'] return ['llm', 'image', 'video', 'audio'] } @@ -72,12 +72,12 @@ export function shouldShowOpenAICompatVideoHint( providerId: string, type: ProviderCardModelType | null, ): boolean { - return getProviderKey(providerId) === 'openai-compatible' && type === 'video' + return (getProviderKey(providerId) === 'openai-compatible' || getProviderKey(providerId) === 'grok2') && type === 'video' } function shouldShowDefaultTabs(providerId: string): boolean { const providerKey = getProviderKey(providerId) - return providerKey === 'openai-compatible' || providerKey === 'gemini-compatible' + return providerKey === 'openai-compatible' || providerKey === 'gemini-compatible' || providerKey === 'grok2' } export function getVisibleModelTypesForProvider( diff --git a/src/app/[locale]/profile/components/api-config/provider-card/ProviderCardShell.tsx b/src/app/[locale]/profile/components/api-config/provider-card/ProviderCardShell.tsx index 0acbfea..9fb903b 100644 --- a/src/app/[locale]/profile/components/api-config/provider-card/ProviderCardShell.tsx +++ b/src/app/[locale]/profile/components/api-config/provider-card/ProviderCardShell.tsx @@ -27,6 +27,7 @@ export function getCompatibilityLayerBadgeLabel( const providerKey = getProviderKey(providerId) if (providerKey === 'openai-compatible') return t('compatibilityLayerOpenAI') if (providerKey === 'gemini-compatible') return t('compatibilityLayerGemini') + if (providerKey === 'grok2') return t('compatibilityLayerGrok2') return null } diff --git a/src/app/[locale]/profile/components/api-config/provider-card/hooks/useProviderCardState.ts b/src/app/[locale]/profile/components/api-config/provider-card/hooks/useProviderCardState.ts index 665af4b..ab4529d 100644 --- a/src/app/[locale]/profile/components/api-config/provider-card/hooks/useProviderCardState.ts +++ b/src/app/[locale]/profile/components/api-config/provider-card/hooks/useProviderCardState.ts @@ -176,7 +176,7 @@ export function buildProviderConnectionPayload(params: { const compatibleBaseUrl = params.baseUrl?.trim() const llmModel = params.llmModel?.trim() const isCompatibleProvider = - params.providerKey === 'openai-compatible' || params.providerKey === 'gemini-compatible' + params.providerKey === 'openai-compatible' || params.providerKey === 'gemini-compatible' || params.providerKey === 'grok2' if (isCompatibleProvider && compatibleBaseUrl) { return { @@ -386,7 +386,7 @@ export function useProviderCardState({ (presetProvider) => presetProvider.id === provider.id, ) const showBaseUrlEdit = - ['gemini-compatible', 'openai-compatible'].includes(providerKey) && + ['gemini-compatible', 'openai-compatible', 'grok2'].includes(providerKey) && Boolean(onUpdateBaseUrl) const tutorial = getProviderTutorial(provider.id) diff --git a/src/app/[locale]/profile/components/api-config/provider-card/types.ts b/src/app/[locale]/profile/components/api-config/provider-card/types.ts index 2f08c67..ecb90e0 100644 --- a/src/app/[locale]/profile/components/api-config/provider-card/types.ts +++ b/src/app/[locale]/profile/components/api-config/provider-card/types.ts @@ -58,5 +58,5 @@ export type ProviderCardTranslator = ( export const VERIFIABLE_PROVIDER_KEYS = new Set([ 'ark', 'google', 'openrouter', 'minimax', 'fal', 'vidu', 'bailian', 'siliconflow', - 'openai-compatible', 'gemini-compatible', + 'openai-compatible', 'gemini-compatible', 'grok2', ]) diff --git a/src/app/[locale]/profile/components/api-config/types.ts b/src/app/[locale]/profile/components/api-config/types.ts index 0e84635..4af426b 100644 --- a/src/app/[locale]/profile/components/api-config/types.ts +++ b/src/app/[locale]/profile/components/api-config/types.ts @@ -182,6 +182,10 @@ export const PRESET_MODELS: PresetModel[] = [ { modelId: 'viduq1', name: 'Vidu Q1', type: 'video', provider: 'vidu' }, { modelId: 'viduq1-classic', name: 'Vidu Q1 Classic', type: 'video', provider: 'vidu' }, { modelId: 'vidu2.0', name: 'Vidu 2.0', type: 'video', provider: 'vidu' }, + + // Grok2 模型 + { modelId: 'grok-imagine-1.0', name: 'Grok Imagine 1.0', type: 'image', provider: 'grok2' }, + { modelId: 'grok-imagine-1.0-video', name: 'Grok Imagine 1.0 Video', type: 'video', provider: 'grok2' }, ] const PRESET_COMING_SOON_MODEL_KEYS = new Set([ @@ -205,6 +209,7 @@ export const PRESET_PROVIDERS: Omit[] = [ { id: 'minimax', name: 'MiniMax Hailuo', baseUrl: 'https://api.minimaxi.com/v1' }, { id: 'vidu', name: 'Vidu' }, { id: 'fal', name: 'FAL' }, + { id: 'grok2', name: 'Grok2 API', apiMode: 'openai-official', gatewayRoute: 'openai-compat' }, ] const ZH_PROVIDER_NAME_MAP: Record = { @@ -213,6 +218,7 @@ const ZH_PROVIDER_NAME_MAP: Record = { vidu: '生数科技 Vidu', bailian: '阿里云百炼', siliconflow: '硅基流动', + grok2: 'Grok2 API', } function isZhLocale(locale?: string): boolean { diff --git a/src/app/api/user/api-config/route.ts b/src/app/api/user/api-config/route.ts index e5ff793..624fc27 100644 --- a/src/app/api/user/api-config/route.ts +++ b/src/app/api/user/api-config/route.ts @@ -190,6 +190,7 @@ const OPTIONAL_PRICING_PROVIDER_KEYS = new Set([ 'gemini-compatible', 'bailian', 'siliconflow', + 'grok2', ]) const OFFICIAL_ONLY_PROVIDER_KEYS = new Set(['bailian', 'siliconflow']) const RETIRED_PROVIDER_KEYS = new Set(['qwen']) @@ -472,6 +473,7 @@ function resolveProviderGatewayRoute( const providerKey = getProviderKey(providerId) const isOpenAICompatibleProvider = providerKey === 'openai-compatible' const isGeminiCompatibleProvider = providerKey === 'gemini-compatible' + const isGrok2Provider = providerKey === 'grok2' if (rawGatewayRoute !== undefined && !isGatewayRoute(rawGatewayRoute)) { throw new ApiError('INVALID_PARAMS', { @@ -479,7 +481,7 @@ function resolveProviderGatewayRoute( }) } - if (isOpenAICompatibleProvider) { + if (isOpenAICompatibleProvider || isGrok2Provider) { if (rawGatewayRoute === 'official') { throw new ApiError('INVALID_PARAMS', { code: 'PROVIDER_GATEWAY_ROUTE_INVALID', diff --git a/src/lib/model-gateway/openai-compat/image.ts b/src/lib/model-gateway/openai-compat/image.ts index bd96489..3cfd299 100644 --- a/src/lib/model-gateway/openai-compat/image.ts +++ b/src/lib/model-gateway/openai-compat/image.ts @@ -19,6 +19,8 @@ type OpenAIImageGenerateSize = | '512x512' | '1792x1024' | '1024x1792' + | '1280x720' + | '720x1280' const OPENAI_IMAGE_OPTION_KEYS = new Set([ 'provider', @@ -81,6 +83,8 @@ function normalizeOpenAIImageSize(value: string | undefined): OpenAIImageGenerat || value === '512x512' || value === '1792x1024' || value === '1024x1792' + || value === '1280x720' + || value === '720x1280' ) { return value } diff --git a/src/lib/model-gateway/router.ts b/src/lib/model-gateway/router.ts index bdd5e7e..21fba7e 100644 --- a/src/lib/model-gateway/router.ts +++ b/src/lib/model-gateway/router.ts @@ -3,6 +3,7 @@ import type { ModelGatewayRoute } from './types' const COMPATIBLE_PROVIDER_KEYS = new Set([ 'openai-compatible', + 'grok2', ]) const OFFICIAL_ONLY_PROVIDER_KEYS = new Set([ 'bailian',