fix(ai): allow explicit xai-oauth selectors with XAI_API_KEY
Keep hasAuth() dedicated so SuperGrok is not auto-selected from a paid key. Explicit preflight uses hasResolvableAuth() so xai-oauth/grok-4.5 can still borrow XAI_API_KEY.
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
|
||||
### Fixed
|
||||
|
||||
- Stopped treating `XAI_API_KEY` as SuperGrok (`xai-oauth`) sign-in for availability, so paid-key-only setups default to `xai/grok-4.5` instead of the zero-cost SuperGrok catalog path.
|
||||
- Stopped treating `XAI_API_KEY` as SuperGrok (`xai-oauth`) sign-in for availability, so paid-key-only setups default to `xai/grok-4.5` instead of the zero-cost SuperGrok catalog path. Explicit `xai-oauth/…` selectors still accept the paid key via the existing env fallback.
|
||||
- Omitted unsupported `reasoning.summary` on paid xAI Responses requests (`xai/grok-4.5`), matching SuperGrok, so a thinking level no longer serializes `summary: "auto"`.
|
||||
- Omitted presence/frequency penalties on all first-party xAI Responses models, including non-reasoning ids such as `xai/grok-2`.
|
||||
|
||||
|
||||
@@ -2680,8 +2680,10 @@ export class AuthStorage {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if any form of auth is configured for a provider.
|
||||
* Unlike getApiKey(), this doesn't refresh OAuth tokens.
|
||||
* Dedicated auth for default-model availability (picker / `getAvailable`).
|
||||
* Unlike {@link getApiKey}, this does not refresh OAuth tokens, and unlike
|
||||
* {@link hasResolvableAuth} it ignores cross-provider env aliases so
|
||||
* `XAI_API_KEY` does not auto-select SuperGrok (`xai-oauth`).
|
||||
*/
|
||||
hasAuth(provider: string): boolean {
|
||||
if (this.#runtimeOverrides.has(provider)) return true;
|
||||
@@ -2692,6 +2694,18 @@ export class AuthStorage {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a request could resolve a key for this provider, including
|
||||
* cross-provider env aliases (`xai-oauth` borrowing `XAI_API_KEY`).
|
||||
* Use this for explicit model preflight (`xai-oauth/grok-4.5`); use
|
||||
* {@link hasAuth} for auto-availability so the default picker stays on
|
||||
* paid `xai` when only `XAI_API_KEY` is set.
|
||||
*/
|
||||
hasResolvableAuth(provider: string): boolean {
|
||||
if (this.hasAuth(provider)) return true;
|
||||
return Boolean(getEnvApiKey(provider));
|
||||
}
|
||||
|
||||
/**
|
||||
* True iff a dedicated, non-env credential source is configured for this
|
||||
* provider — i.e. anything in the cascade EXCEPT `getEnvApiKey(provider)`.
|
||||
|
||||
@@ -39,6 +39,9 @@ describe("xAI API login wiring", () => {
|
||||
try {
|
||||
expect(storage.hasAuth("xai")).toBe(true);
|
||||
expect(storage.hasAuth("xai-oauth")).toBe(false);
|
||||
expect(storage.hasResolvableAuth("xai")).toBe(true);
|
||||
expect(storage.hasResolvableAuth("xai-oauth")).toBe(true);
|
||||
expect(getEnvApiKey("xai-oauth")).toBe("xai-env-key");
|
||||
expect(storage.getCredentialOrigin("xai")).toEqual({ kind: "env", envVar: "XAI_API_KEY" });
|
||||
expect(storage.getCredentialOrigin("xai-oauth")).toBeUndefined();
|
||||
} finally {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
- Changed the default model for SuperGrok OAuth (`xai-oauth`) from `grok-4.3` to `grok-4.5`.
|
||||
- Included `reasoning.encrypted_content` in Responses `include` for paid xAI and SuperGrok OAuth models.
|
||||
- Replayed encrypted xAI reasoning on follow-up Responses turns for `xai` and `xai-oauth`.
|
||||
- Kept automatic model selection on paid `xai/grok-4.5` when only `XAI_API_KEY` is set, instead of preferring SuperGrok `xai-oauth/grok-4.5`.
|
||||
- Kept automatic model selection on paid `xai/grok-4.5` when only `XAI_API_KEY` is set, instead of preferring SuperGrok `xai-oauth/grok-4.5`. Explicit `xai-oauth/grok-4.5` still works with that paid key.
|
||||
- Stopped sending presence/frequency penalties and stop sequences to xAI reasoning models such as `grok-4.5`, which reject them.
|
||||
|
||||
## [17.3.4] - 2026-08-14
|
||||
|
||||
@@ -1667,17 +1667,22 @@ export class ModelRegistry {
|
||||
*
|
||||
* Side-effect-free and synchronous: a command-backed key (`!cmd`) counts as
|
||||
* configured by its presence alone — the program is NOT executed — and OAuth
|
||||
* tokens are NOT refreshed (`authStorage.hasAuth`). This is what keeps the
|
||||
* tokens are NOT refreshed (`authStorage.hasResolvableAuth`). This is what keeps the
|
||||
* model-switch pre-flight off the event loop's hot path; the real key
|
||||
* (command execution + OAuth refresh) is resolved lazily per request via
|
||||
* {@link ModelRegistry.resolver}.
|
||||
*
|
||||
* Cross-provider env aliases count here (`xai-oauth` can borrow `XAI_API_KEY`)
|
||||
* so an explicit `xai-oauth/…` selector does not fail with "No API key".
|
||||
* Default-model availability still uses {@link AuthStorage.hasAuth}, which
|
||||
* ignores that alias so SuperGrok is not auto-selected from a paid key.
|
||||
*/
|
||||
hasConfiguredAuth(model: Model<Api>): boolean {
|
||||
const keyConfig = this.#customProviderApiKeys.get(model.provider);
|
||||
return (
|
||||
isCommandConfigValue(keyConfig) ||
|
||||
this.#keylessProviders.has(model.provider) ||
|
||||
this.authStorage.hasAuth(model.provider)
|
||||
this.authStorage.hasResolvableAuth(model.provider)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user