Add supportsUsageInStreaming compat flag for OpenAI-compatible providers

Renamed from supportsStreamOptions to clarify this controls stream_options: { include_usage: true }.
Defaults to true (no behavioral change for existing providers).
Providers like gatewayz.ai that reject this parameter can set supportsUsageInStreaming: false in model config.

Based on #596 by @XesGaDeus
This commit is contained in:
Mario Zechner 2026-01-10 00:34:06 +01:00
parent 732d46123b
commit 52ce113754
14 changed files with 8 additions and 9 deletions

View file

@ -2,8 +2,8 @@
## [Unreleased] ## [Unreleased]
### Fixed ### Added
- Fixed 422 error with non-standard providers (gatewayz.ai, chutes.ai, etc.) by adding `supportsStreamOptions` compatibility flag and conditionally sending `stream_options` parameter. - Added `supportsUsageInStreaming` compatibility flag for OpenAI-compatible providers that reject `stream_options: { include_usage: true }`. Defaults to `true`. Set to `false` in model config for providers like gatewayz.ai. ([#596](https://github.com/badlogic/pi-mono/pull/596) by [@XesGaDeus](https://github.com/XesGaDeus))
## [0.42.0] - 2026-01-09 ## [0.42.0] - 2026-01-09

View file

@ -369,7 +369,7 @@ function buildParams(model: Model<"openai-completions">, context: Context, optio
stream: true, stream: true,
}; };
if (compat.supportsStreamOptions) { if (compat.supportsUsageInStreaming !== false) {
(params as any).stream_options = { include_usage: true }; (params as any).stream_options = { include_usage: true };
} }
@ -644,8 +644,7 @@ function detectCompatFromUrl(baseUrl: string): Required<OpenAICompat> {
baseUrl.includes("cerebras.ai") || baseUrl.includes("cerebras.ai") ||
baseUrl.includes("api.x.ai") || baseUrl.includes("api.x.ai") ||
baseUrl.includes("mistral.ai") || baseUrl.includes("mistral.ai") ||
baseUrl.includes("chutes.ai") || baseUrl.includes("chutes.ai");
baseUrl.includes("gatewayz.ai");
const useMaxTokens = baseUrl.includes("mistral.ai") || baseUrl.includes("chutes.ai"); const useMaxTokens = baseUrl.includes("mistral.ai") || baseUrl.includes("chutes.ai");
@ -657,7 +656,7 @@ function detectCompatFromUrl(baseUrl: string): Required<OpenAICompat> {
supportsStore: !isNonStandard, supportsStore: !isNonStandard,
supportsDeveloperRole: !isNonStandard, supportsDeveloperRole: !isNonStandard,
supportsReasoningEffort: !isGrok, supportsReasoningEffort: !isGrok,
supportsStreamOptions: !isNonStandard, supportsUsageInStreaming: true,
maxTokensField: useMaxTokens ? "max_tokens" : "max_completion_tokens", maxTokensField: useMaxTokens ? "max_tokens" : "max_completion_tokens",
requiresToolResultName: isMistral, requiresToolResultName: isMistral,
requiresAssistantAfterToolResult: false, // Mistral no longer requires this as of Dec 2024 requiresAssistantAfterToolResult: false, // Mistral no longer requires this as of Dec 2024
@ -678,7 +677,7 @@ function getCompat(model: Model<"openai-completions">): Required<OpenAICompat> {
supportsStore: model.compat.supportsStore ?? detected.supportsStore, supportsStore: model.compat.supportsStore ?? detected.supportsStore,
supportsDeveloperRole: model.compat.supportsDeveloperRole ?? detected.supportsDeveloperRole, supportsDeveloperRole: model.compat.supportsDeveloperRole ?? detected.supportsDeveloperRole,
supportsReasoningEffort: model.compat.supportsReasoningEffort ?? detected.supportsReasoningEffort, supportsReasoningEffort: model.compat.supportsReasoningEffort ?? detected.supportsReasoningEffort,
supportsStreamOptions: model.compat.supportsStreamOptions ?? detected.supportsStreamOptions, supportsUsageInStreaming: model.compat.supportsUsageInStreaming ?? detected.supportsUsageInStreaming,
maxTokensField: model.compat.maxTokensField ?? detected.maxTokensField, maxTokensField: model.compat.maxTokensField ?? detected.maxTokensField,
requiresToolResultName: model.compat.requiresToolResultName ?? detected.requiresToolResultName, requiresToolResultName: model.compat.requiresToolResultName ?? detected.requiresToolResultName,
requiresAssistantAfterToolResult: requiresAssistantAfterToolResult:

View file

@ -207,8 +207,8 @@ export interface OpenAICompat {
supportsDeveloperRole?: boolean; supportsDeveloperRole?: boolean;
/** Whether the provider supports `reasoning_effort`. Default: auto-detected from URL. */ /** Whether the provider supports `reasoning_effort`. Default: auto-detected from URL. */
supportsReasoningEffort?: boolean; supportsReasoningEffort?: boolean;
/** Whether the provider supports `stream_options`. Default: auto-detected from URL. */ /** Whether the provider supports `stream_options: { include_usage: true }` for token usage in streaming responses. Default: true. */
supportsStreamOptions?: boolean; supportsUsageInStreaming?: boolean;
/** Which field to use for max tokens. Default: auto-detected from URL. */ /** Which field to use for max tokens. Default: auto-detected from URL. */
maxTokensField?: "max_completion_tokens" | "max_tokens"; maxTokensField?: "max_completion_tokens" | "max_tokens";
/** Whether tool results require the `name` field. Default: auto-detected from URL. */ /** Whether tool results require the `name` field. Default: auto-detected from URL. */

Binary file not shown.

After

Width:  |  Height:  |  Size: 446 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 810 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 MiB

After

Width:  |  Height:  |  Size: 1 MiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 MiB

After

Width:  |  Height:  |  Size: 2 MiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 321 B

After

Width:  |  Height:  |  Size: 321 B

Before After
Before After