feat: integrate Grok2 API as preset provider for image/video generation
Some checks failed
Build & Push Docker Image / build-and-push (push) Has been cancelled
Some checks failed
Build & Push Docker Image / build-and-push (push) Has been cancelled
- 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
This commit is contained in:
165
GROK2_SETUP.md
Normal file
165
GROK2_SETUP.md
Normal file
@@ -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 视频接口
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
"connect": "Connect",
|
"connect": "Connect",
|
||||||
"compatibilityLayerOpenAI": "OpenAI Compatible Layer",
|
"compatibilityLayerOpenAI": "OpenAI Compatible Layer",
|
||||||
"compatibilityLayerGemini": "Gemini Compatible Layer",
|
"compatibilityLayerGemini": "Gemini Compatible Layer",
|
||||||
|
"compatibilityLayerGrok2": "Grok2 API",
|
||||||
"show": "Show",
|
"show": "Show",
|
||||||
"hide": "Hide",
|
"hide": "Hide",
|
||||||
"capability": "Models",
|
"capability": "Models",
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
"connect": "连接",
|
"connect": "连接",
|
||||||
"compatibilityLayerOpenAI": "OpenAI 兼容层",
|
"compatibilityLayerOpenAI": "OpenAI 兼容层",
|
||||||
"compatibilityLayerGemini": "Gemini 兼容层",
|
"compatibilityLayerGemini": "Gemini 兼容层",
|
||||||
|
"compatibilityLayerGrok2": "Grok2 API",
|
||||||
"show": "显示",
|
"show": "显示",
|
||||||
"hide": "隐藏",
|
"hide": "隐藏",
|
||||||
"capability": "能力",
|
"capability": "能力",
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ const MODEL_TYPES: readonly ProviderCardModelType[] = ['llm', 'image', 'video',
|
|||||||
|
|
||||||
export function getAddableModelTypesForProvider(providerId: string): ProviderCardModelType[] {
|
export function getAddableModelTypesForProvider(providerId: string): ProviderCardModelType[] {
|
||||||
const providerKey = getProviderKey(providerId)
|
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']
|
return ['llm', 'image', 'video', 'audio']
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,12 +72,12 @@ export function shouldShowOpenAICompatVideoHint(
|
|||||||
providerId: string,
|
providerId: string,
|
||||||
type: ProviderCardModelType | null,
|
type: ProviderCardModelType | null,
|
||||||
): boolean {
|
): boolean {
|
||||||
return getProviderKey(providerId) === 'openai-compatible' && type === 'video'
|
return (getProviderKey(providerId) === 'openai-compatible' || getProviderKey(providerId) === 'grok2') && type === 'video'
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldShowDefaultTabs(providerId: string): boolean {
|
function shouldShowDefaultTabs(providerId: string): boolean {
|
||||||
const providerKey = getProviderKey(providerId)
|
const providerKey = getProviderKey(providerId)
|
||||||
return providerKey === 'openai-compatible' || providerKey === 'gemini-compatible'
|
return providerKey === 'openai-compatible' || providerKey === 'gemini-compatible' || providerKey === 'grok2'
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getVisibleModelTypesForProvider(
|
export function getVisibleModelTypesForProvider(
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export function getCompatibilityLayerBadgeLabel(
|
|||||||
const providerKey = getProviderKey(providerId)
|
const providerKey = getProviderKey(providerId)
|
||||||
if (providerKey === 'openai-compatible') return t('compatibilityLayerOpenAI')
|
if (providerKey === 'openai-compatible') return t('compatibilityLayerOpenAI')
|
||||||
if (providerKey === 'gemini-compatible') return t('compatibilityLayerGemini')
|
if (providerKey === 'gemini-compatible') return t('compatibilityLayerGemini')
|
||||||
|
if (providerKey === 'grok2') return t('compatibilityLayerGrok2')
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ export function buildProviderConnectionPayload(params: {
|
|||||||
const compatibleBaseUrl = params.baseUrl?.trim()
|
const compatibleBaseUrl = params.baseUrl?.trim()
|
||||||
const llmModel = params.llmModel?.trim()
|
const llmModel = params.llmModel?.trim()
|
||||||
const isCompatibleProvider =
|
const isCompatibleProvider =
|
||||||
params.providerKey === 'openai-compatible' || params.providerKey === 'gemini-compatible'
|
params.providerKey === 'openai-compatible' || params.providerKey === 'gemini-compatible' || params.providerKey === 'grok2'
|
||||||
|
|
||||||
if (isCompatibleProvider && compatibleBaseUrl) {
|
if (isCompatibleProvider && compatibleBaseUrl) {
|
||||||
return {
|
return {
|
||||||
@@ -386,7 +386,7 @@ export function useProviderCardState({
|
|||||||
(presetProvider) => presetProvider.id === provider.id,
|
(presetProvider) => presetProvider.id === provider.id,
|
||||||
)
|
)
|
||||||
const showBaseUrlEdit =
|
const showBaseUrlEdit =
|
||||||
['gemini-compatible', 'openai-compatible'].includes(providerKey) &&
|
['gemini-compatible', 'openai-compatible', 'grok2'].includes(providerKey) &&
|
||||||
Boolean(onUpdateBaseUrl)
|
Boolean(onUpdateBaseUrl)
|
||||||
const tutorial = getProviderTutorial(provider.id)
|
const tutorial = getProviderTutorial(provider.id)
|
||||||
|
|
||||||
|
|||||||
@@ -58,5 +58,5 @@ export type ProviderCardTranslator = (
|
|||||||
export const VERIFIABLE_PROVIDER_KEYS = new Set([
|
export const VERIFIABLE_PROVIDER_KEYS = new Set([
|
||||||
'ark', 'google', 'openrouter', 'minimax', 'fal', 'vidu',
|
'ark', 'google', 'openrouter', 'minimax', 'fal', 'vidu',
|
||||||
'bailian', 'siliconflow',
|
'bailian', 'siliconflow',
|
||||||
'openai-compatible', 'gemini-compatible',
|
'openai-compatible', 'gemini-compatible', 'grok2',
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -182,6 +182,10 @@ export const PRESET_MODELS: PresetModel[] = [
|
|||||||
{ modelId: 'viduq1', name: 'Vidu Q1', type: 'video', provider: 'vidu' },
|
{ modelId: 'viduq1', name: 'Vidu Q1', type: 'video', provider: 'vidu' },
|
||||||
{ modelId: 'viduq1-classic', name: 'Vidu Q1 Classic', 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' },
|
{ 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<string>([
|
const PRESET_COMING_SOON_MODEL_KEYS = new Set<string>([
|
||||||
@@ -205,6 +209,7 @@ export const PRESET_PROVIDERS: Omit<Provider, 'apiKey' | 'hasApiKey'>[] = [
|
|||||||
{ id: 'minimax', name: 'MiniMax Hailuo', baseUrl: 'https://api.minimaxi.com/v1' },
|
{ id: 'minimax', name: 'MiniMax Hailuo', baseUrl: 'https://api.minimaxi.com/v1' },
|
||||||
{ id: 'vidu', name: 'Vidu' },
|
{ id: 'vidu', name: 'Vidu' },
|
||||||
{ id: 'fal', name: 'FAL' },
|
{ id: 'fal', name: 'FAL' },
|
||||||
|
{ id: 'grok2', name: 'Grok2 API', apiMode: 'openai-official', gatewayRoute: 'openai-compat' },
|
||||||
]
|
]
|
||||||
|
|
||||||
const ZH_PROVIDER_NAME_MAP: Record<string, string> = {
|
const ZH_PROVIDER_NAME_MAP: Record<string, string> = {
|
||||||
@@ -213,6 +218,7 @@ const ZH_PROVIDER_NAME_MAP: Record<string, string> = {
|
|||||||
vidu: '生数科技 Vidu',
|
vidu: '生数科技 Vidu',
|
||||||
bailian: '阿里云百炼',
|
bailian: '阿里云百炼',
|
||||||
siliconflow: '硅基流动',
|
siliconflow: '硅基流动',
|
||||||
|
grok2: 'Grok2 API',
|
||||||
}
|
}
|
||||||
|
|
||||||
function isZhLocale(locale?: string): boolean {
|
function isZhLocale(locale?: string): boolean {
|
||||||
|
|||||||
@@ -190,6 +190,7 @@ const OPTIONAL_PRICING_PROVIDER_KEYS = new Set([
|
|||||||
'gemini-compatible',
|
'gemini-compatible',
|
||||||
'bailian',
|
'bailian',
|
||||||
'siliconflow',
|
'siliconflow',
|
||||||
|
'grok2',
|
||||||
])
|
])
|
||||||
const OFFICIAL_ONLY_PROVIDER_KEYS = new Set(['bailian', 'siliconflow'])
|
const OFFICIAL_ONLY_PROVIDER_KEYS = new Set(['bailian', 'siliconflow'])
|
||||||
const RETIRED_PROVIDER_KEYS = new Set(['qwen'])
|
const RETIRED_PROVIDER_KEYS = new Set(['qwen'])
|
||||||
@@ -472,6 +473,7 @@ function resolveProviderGatewayRoute(
|
|||||||
const providerKey = getProviderKey(providerId)
|
const providerKey = getProviderKey(providerId)
|
||||||
const isOpenAICompatibleProvider = providerKey === 'openai-compatible'
|
const isOpenAICompatibleProvider = providerKey === 'openai-compatible'
|
||||||
const isGeminiCompatibleProvider = providerKey === 'gemini-compatible'
|
const isGeminiCompatibleProvider = providerKey === 'gemini-compatible'
|
||||||
|
const isGrok2Provider = providerKey === 'grok2'
|
||||||
|
|
||||||
if (rawGatewayRoute !== undefined && !isGatewayRoute(rawGatewayRoute)) {
|
if (rawGatewayRoute !== undefined && !isGatewayRoute(rawGatewayRoute)) {
|
||||||
throw new ApiError('INVALID_PARAMS', {
|
throw new ApiError('INVALID_PARAMS', {
|
||||||
@@ -479,7 +481,7 @@ function resolveProviderGatewayRoute(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isOpenAICompatibleProvider) {
|
if (isOpenAICompatibleProvider || isGrok2Provider) {
|
||||||
if (rawGatewayRoute === 'official') {
|
if (rawGatewayRoute === 'official') {
|
||||||
throw new ApiError('INVALID_PARAMS', {
|
throw new ApiError('INVALID_PARAMS', {
|
||||||
code: 'PROVIDER_GATEWAY_ROUTE_INVALID',
|
code: 'PROVIDER_GATEWAY_ROUTE_INVALID',
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ type OpenAIImageGenerateSize =
|
|||||||
| '512x512'
|
| '512x512'
|
||||||
| '1792x1024'
|
| '1792x1024'
|
||||||
| '1024x1792'
|
| '1024x1792'
|
||||||
|
| '1280x720'
|
||||||
|
| '720x1280'
|
||||||
|
|
||||||
const OPENAI_IMAGE_OPTION_KEYS = new Set([
|
const OPENAI_IMAGE_OPTION_KEYS = new Set([
|
||||||
'provider',
|
'provider',
|
||||||
@@ -81,6 +83,8 @@ function normalizeOpenAIImageSize(value: string | undefined): OpenAIImageGenerat
|
|||||||
|| value === '512x512'
|
|| value === '512x512'
|
||||||
|| value === '1792x1024'
|
|| value === '1792x1024'
|
||||||
|| value === '1024x1792'
|
|| value === '1024x1792'
|
||||||
|
|| value === '1280x720'
|
||||||
|
|| value === '720x1280'
|
||||||
) {
|
) {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { ModelGatewayRoute } from './types'
|
|||||||
|
|
||||||
const COMPATIBLE_PROVIDER_KEYS = new Set([
|
const COMPATIBLE_PROVIDER_KEYS = new Set([
|
||||||
'openai-compatible',
|
'openai-compatible',
|
||||||
|
'grok2',
|
||||||
])
|
])
|
||||||
const OFFICIAL_ONLY_PROVIDER_KEYS = new Set([
|
const OFFICIAL_ONLY_PROVIDER_KEYS = new Set([
|
||||||
'bailian',
|
'bailian',
|
||||||
|
|||||||
Reference in New Issue
Block a user