Files
openclaw/src/utils/provider-utils.ts
Gustavo Madeira Santana 560ea25294 Matrix: restore ordered progress delivery with explicit streaming modes (#59266)
Merged via squash.

Prepared head SHA: 523623b7e1
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-04-01 19:35:03 -04:00

91 lines
2.6 KiB
TypeScript

import type { OpenClawConfig } from "../config/config.js";
import { resolveProviderReasoningOutputModeWithPlugin } from "../plugins/provider-runtime.js";
import type { ProviderRuntimeModel } from "../plugins/types.js";
/**
* Utility functions for provider-specific logic and capabilities.
*/
export function resolveReasoningOutputMode(params: {
provider: string | undefined | null;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
modelId?: string;
modelApi?: string | null;
model?: ProviderRuntimeModel;
}): "native" | "tagged" {
const provider = params.provider?.trim();
if (!provider) {
return "native";
}
const pluginMode = resolveProviderReasoningOutputModeWithPlugin({
provider,
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
context: {
config: params.config,
workspaceDir: params.workspaceDir,
env: params.env,
provider,
modelId: params.modelId,
modelApi: params.modelApi,
model: params.model,
},
});
if (pluginMode) {
return pluginMode;
}
const normalized = provider.toLowerCase();
// Check for exact matches or known prefixes/substrings for reasoning providers.
// Note: Ollama is intentionally excluded - its OpenAI-compatible endpoint
// handles reasoning natively via the `reasoning` field in streaming chunks,
// so tag-based enforcement is unnecessary and causes all output to be
// discarded as "(no output)" (#2279).
// Note: MiniMax is also intentionally excluded. In production it does not
// reliably wrap user-visible output in <final> tags, so forcing tag
// enforcement suppresses normal assistant replies.
if (
normalized === "google" ||
normalized === "google-gemini-cli" ||
normalized === "google-generative-ai"
) {
return "tagged";
}
return "native";
}
/**
* Returns true if the provider requires reasoning to be wrapped in tags
* (e.g. <think> and <final>) in the text stream, rather than using native
* API fields for reasoning/thinking.
*/
export function isReasoningTagProvider(
provider: string | undefined | null,
options?: {
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
modelId?: string;
modelApi?: string | null;
model?: ProviderRuntimeModel;
},
): boolean {
return (
resolveReasoningOutputMode({
provider,
config: options?.config,
workspaceDir: options?.workspaceDir,
env: options?.env,
modelId: options?.modelId,
modelApi: options?.modelApi,
model: options?.model,
}) === "tagged"
);
}