mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-31 00:24:38 +00:00
feat(keys): complete Grok Build sample with endpoints/auth/session/features
Expand Use Key Grok CLI config.toml to include production-oriented sections from working gateway setups: full [endpoints], preferred_method=api_key, image_description, auto_compact threshold, and Imagine image/video feature overrides with guided comments.
This commit is contained in:
@@ -790,27 +790,34 @@ $env:XAI_API_KEY="${apiKey}"`
|
||||
export XAI_API_KEY="${apiKey}"`
|
||||
}
|
||||
|
||||
// Shape follows official Grok Build docs (docs.x.ai/build/settings + custom-models guide).
|
||||
// Text models only (Responses). Image/video: grok-imagine-image / grok-imagine-video on media endpoints.
|
||||
// Shape follows Grok Build user guide (~/.grok/docs + custom-models) and production-ready Sub2API setups.
|
||||
// Text models only (Responses). Image/video: Imagine model IDs on media endpoints / feature overrides.
|
||||
// Credential order: api_key field → env_key → signed-in session → XAI_API_KEY global fallback.
|
||||
const modelsListUrl = `${baseUrl.replace(/\/+$/, '')}/models`
|
||||
const configContent = `# Grok Build CLI → Sub2API Grok group (API key auth).
|
||||
# Official docs: https://docs.x.ai/build/settings
|
||||
# Docs: ~/.grok/docs/user-guide/05-configuration.md + 11-custom-models.md
|
||||
# Verify after save: grok inspect
|
||||
#
|
||||
# Text / agent models only. Image & video use separate Imagine model IDs on media endpoints
|
||||
# (e.g. grok-imagine-image, grok-imagine-video) — not the catalog below.
|
||||
#
|
||||
# IMPORTANT: api_backend must be "responses" for Sub2API Grok (POST /v1/responses).
|
||||
# If omitted, Grok Build defaults to chat_completions (/v1/chat/completions).
|
||||
|
||||
# Global inference base (same role as env GROK_MODELS_BASE_URL).
|
||||
# Grok also lists models from {models_base_url}/models when this is set.
|
||||
[endpoints]
|
||||
models_base_url = "${baseUrl}"
|
||||
|
||||
# Keep api_backend = "responses" on every model entry.
|
||||
#
|
||||
# Prefer env_key over hardcoding api_key (never commit secrets).
|
||||
# Also export GROK_MODELS_BASE_URL + XAI_API_KEY in the shell block above.
|
||||
|
||||
# Global inference / catalog endpoints (same role as env GROK_MODELS_BASE_URL).
|
||||
# When models_base_url is set, Grok uses API-key Bearer auth (no grok login required).
|
||||
[endpoints]
|
||||
models_base_url = "${baseUrl}" # inference base; model list defaults to {base}/models
|
||||
models_list_url = "${modelsListUrl}" # optional override (env: GROK_MODELS_LIST_URL)
|
||||
xai_api_base_url = "${baseUrl}" # public xAI API base override for gateway routing
|
||||
cli_chat_proxy_base_url = "${baseUrl}" # CLI chat-proxy base (env: GROK_CLI_CHAT_PROXY_BASE_URL)
|
||||
|
||||
# Prefer API key when using a custom gateway (matches Sub2API).
|
||||
# Requires XAI_API_KEY env or per-model env_key / api_key.
|
||||
[auth]
|
||||
preferred_method = "api_key"
|
||||
|
||||
[model."grok-4.5"]
|
||||
model = "grok-4.5" # id sent to the API
|
||||
name = "Grok 4.5" # shown in /model picker
|
||||
@@ -867,11 +874,27 @@ supports_backend_search = true
|
||||
# xAI recommends grok-build* for coding/agent sessions; use grok-4.5 for general chat.
|
||||
default = "grok-4.5"
|
||||
web_search = "grok-4.5" # client-side web_search tool model (must exist as [model.*])
|
||||
image_description = "grok-4.5" # vision/describe-image helper model
|
||||
# Optional environment-wide sampling defaults (per-model values win):
|
||||
# temperature = 0.7
|
||||
# top_p = 0.95
|
||||
# max_completion_tokens = 8192
|
||||
# max_retries = 8`
|
||||
# max_retries = 8
|
||||
|
||||
[session]
|
||||
auto_compact_threshold_percent = 80 # auto-compact at this % of context_window (default 85)
|
||||
|
||||
# Imagine tools: model IDs go to Sub2API media endpoints (not the text [model.*] catalog).
|
||||
# Enable only if the Grok group allows image/video generation.
|
||||
[features]
|
||||
image_gen = true
|
||||
video_gen = true
|
||||
image_gen_model_override = "grok-imagine-image-quality" # or grok-imagine-image
|
||||
image_edit_model_override = "grok-imagine-edit"
|
||||
# Optional feature flags (defaults shown in docs):
|
||||
# telemetry = false
|
||||
# remote_fetch = true # set false for air-gapped / pure-gateway catalogs
|
||||
# lsp_tools = false`
|
||||
|
||||
return [
|
||||
{ path: envPath, content: envContent },
|
||||
|
||||
@@ -55,11 +55,20 @@ describe('UseKeyModal', () => {
|
||||
expect(allCode).toContain('[model."grok-4.3"]')
|
||||
expect(allCode).toContain('default = "grok-4.5"')
|
||||
expect(allCode).toContain('models_base_url = "https://example.com/v1"')
|
||||
expect(allCode).toContain('models_list_url = "https://example.com/v1/models"')
|
||||
expect(allCode).toContain('xai_api_base_url = "https://example.com/v1"')
|
||||
expect(allCode).toContain('cli_chat_proxy_base_url = "https://example.com/v1"')
|
||||
expect(allCode).toContain('preferred_method = "api_key"')
|
||||
expect(allCode).toContain('image_description = "grok-4.5"')
|
||||
expect(allCode).toContain('auto_compact_threshold_percent = 80')
|
||||
expect(allCode).toContain('image_gen = true')
|
||||
expect(allCode).toContain('video_gen = true')
|
||||
expect(allCode).toContain('image_gen_model_override = "grok-imagine-image-quality"')
|
||||
expect(allCode).toContain('image_edit_model_override = "grok-imagine-edit"')
|
||||
expect(allCode).toContain('env_key = "XAI_API_KEY"')
|
||||
expect(allCode).toContain('Keep api_backend = "responses" on every model entry.')
|
||||
expect(allCode).toContain('NOT for image/video generation')
|
||||
expect(allCode).toContain('grok-imagine-image')
|
||||
expect(allCode).toContain('grok-imagine-video')
|
||||
expect(allCode).toContain('grok-imagine-edit')
|
||||
expect(allCode).toMatch(/\[model\."grok-4\.5"\][\s\S]*?context_window = 500000/)
|
||||
expect(allCode).toMatch(/\[model\."grok-build-0\.1"\][\s\S]*?context_window = 256000/)
|
||||
// Prefer env_key; hardcode api_key only as commented alternative
|
||||
@@ -97,7 +106,7 @@ describe('UseKeyModal', () => {
|
||||
apiKey: 'sk-grok-test'
|
||||
})
|
||||
expect(parsed.provider.grok.models['grok-4.5']).toBeDefined()
|
||||
expect(parsed.provider.grok.models['grok-4.5'].limit.context).toBe(500000)
|
||||
expect(parsed.provider.grok.models['grok-4.5'].limit.context).toBe(1000000)
|
||||
expect(parsed.provider.grok.models['grok-build-0.1']).toBeDefined()
|
||||
expect(parsed.provider.grok.models['grok-4.20-multi-agent-0309']).toBeDefined()
|
||||
expect(parsed.provider.grok.models['grok-composer-2.5-fast']).toBeDefined()
|
||||
@@ -227,16 +236,20 @@ describe('UseKeyModal', () => {
|
||||
expect(configToml).toContain('model_provider = "sub2api"')
|
||||
expect(configToml).toContain('model = "grok-4.5"')
|
||||
expect(configToml).toContain('base_url = "https://example.com/v1"')
|
||||
expect(configToml).toContain('experimental_bearer_token = "sk-grok-codex-test"')
|
||||
expect(configToml).toContain('env_key = "SUB2API_API_KEY"')
|
||||
expect(configToml).toContain('wire_api = "responses"')
|
||||
expect(configToml).toContain('requires_openai_auth = true')
|
||||
// API-key provider: Codex must not require a ChatGPT OAuth login.
|
||||
expect(configToml).toContain('requires_openai_auth = false')
|
||||
expect(configToml).toContain('supports_websockets = false')
|
||||
expect(configToml).toContain('multi-agent / web_search')
|
||||
expect(configToml).toContain('grok-4.20-multi-agent-0309 (text / web_search)')
|
||||
expect(configToml).toContain('grok-imagine-image')
|
||||
expect(configToml).toContain('grok-imagine-video')
|
||||
// Hardcoded bearer is only a commented fallback when env cannot be set.
|
||||
expect(configToml).toMatch(/# experimental_bearer_token = "sk-grok-codex-test"/)
|
||||
expect(configToml).not.toContain('supports_websockets = true')
|
||||
expect(configToml).not.toContain('responses_websockets_v2')
|
||||
expect(wrapper.text()).not.toContain('auth.json')
|
||||
expect(codeBlocks.join('\n')).toContain('SUB2API_API_KEY')
|
||||
|
||||
const windowsTab = wrapper.findAll('button').find(
|
||||
(button) => button.text().trim() === 'Windows'
|
||||
|
||||
Reference in New Issue
Block a user