mirror of
https://github.com/lobehub/lobehub.git
synced 2026-03-27 13:29:15 +07:00
🐛 fix(model-runtime): prune unsupported xAI reasoning penalties (#13066)
This commit is contained in:
@@ -22,9 +22,61 @@ describe('LobeXAI - custom features', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
instance = new LobeXAI({ apiKey: 'test_api_key' });
|
||||
vi.spyOn(instance['client'].chat.completions, 'create').mockResolvedValue(
|
||||
new ReadableStream() as any,
|
||||
);
|
||||
vi.spyOn(instance['client'].responses, 'create').mockResolvedValue(new ReadableStream() as any);
|
||||
});
|
||||
|
||||
describe('chatCompletion.handlePayload', () => {
|
||||
it('should remove unsupported penalty parameters for reasoning models', async () => {
|
||||
await instance.chat({
|
||||
apiMode: 'chatCompletion',
|
||||
frequency_penalty: 0.4,
|
||||
messages: [{ content: 'Hello', role: 'user' }],
|
||||
model: 'grok-4',
|
||||
presence_penalty: 0.6,
|
||||
} as any);
|
||||
|
||||
const createCall = (instance['client'].chat.completions.create as Mock).mock.calls[0][0];
|
||||
|
||||
expect(createCall.frequency_penalty).toBeUndefined();
|
||||
expect(createCall.presence_penalty).toBeUndefined();
|
||||
expect(createCall.stream).toBe(true);
|
||||
});
|
||||
|
||||
it('should remove unsupported penalty parameters for grok-4.1 reasoning variants', async () => {
|
||||
await instance.chat({
|
||||
apiMode: 'chatCompletion',
|
||||
frequency_penalty: 0.4,
|
||||
messages: [{ content: 'Hello', role: 'user' }],
|
||||
model: 'grok-4-1-fast-reasoning',
|
||||
presence_penalty: 0.6,
|
||||
} as any);
|
||||
|
||||
const createCall = (instance['client'].chat.completions.create as Mock).mock.calls[0][0];
|
||||
|
||||
expect(createCall.frequency_penalty).toBeUndefined();
|
||||
expect(createCall.presence_penalty).toBeUndefined();
|
||||
expect(createCall.stream).toBe(true);
|
||||
});
|
||||
|
||||
it('should preserve penalty parameters for non-reasoning models', async () => {
|
||||
await instance.chat({
|
||||
apiMode: 'chatCompletion',
|
||||
frequency_penalty: 0.4,
|
||||
messages: [{ content: 'Hello', role: 'user' }],
|
||||
model: 'grok-4-fast-non-reasoning',
|
||||
presence_penalty: 0.6,
|
||||
} as any);
|
||||
|
||||
const createCall = (instance['client'].chat.completions.create as Mock).mock.calls[0][0];
|
||||
|
||||
expect(createCall.frequency_penalty).toBe(0.4);
|
||||
expect(createCall.presence_penalty).toBe(0.6);
|
||||
});
|
||||
});
|
||||
|
||||
describe('responses.handlePayload', () => {
|
||||
it('should add web_search and x_search tools when enabledSearch is true', async () => {
|
||||
await instance.chat({
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ModelProvider } from 'model-bank';
|
||||
import { LOBE_DEFAULT_MODEL_LIST, ModelProvider } from 'model-bank';
|
||||
|
||||
import { createOpenAICompatibleRuntime } from '../../core/openaiCompatibleFactory';
|
||||
import type { ChatStreamPayload } from '../../types';
|
||||
import { MODEL_LIST_CONFIGS, processModelList } from '../../utils/modelParse';
|
||||
import { createXAIImage } from './createImage';
|
||||
|
||||
@@ -8,9 +9,38 @@ export interface XAIModelCard {
|
||||
id: string;
|
||||
}
|
||||
|
||||
const xaiReasoningModels = new Set(
|
||||
LOBE_DEFAULT_MODEL_LIST.filter(
|
||||
(model) =>
|
||||
model.providerId === ModelProvider.XAI &&
|
||||
model.type === 'chat' &&
|
||||
!!model.abilities?.reasoning,
|
||||
).map((model) => model.id),
|
||||
);
|
||||
|
||||
const isXAIReasoningModel = (model: string) => xaiReasoningModels.has(model);
|
||||
|
||||
const pruneUnsupportedReasoningParameters = (payload: ChatStreamPayload) => {
|
||||
if (!isXAIReasoningModel(payload.model)) return payload;
|
||||
|
||||
return {
|
||||
...payload,
|
||||
// xAI reasoning models reject these parameters:
|
||||
// https://docs.x.ai/developers/model-capabilities/text/reasoning
|
||||
frequency_penalty: undefined,
|
||||
presence_penalty: undefined,
|
||||
stop: undefined,
|
||||
} as ChatStreamPayload;
|
||||
};
|
||||
|
||||
export const LobeXAI = createOpenAICompatibleRuntime({
|
||||
baseURL: 'https://api.x.ai/v1',
|
||||
chatCompletion: {
|
||||
handlePayload: (payload) =>
|
||||
({
|
||||
...pruneUnsupportedReasoningParameters(payload),
|
||||
stream: payload.stream ?? true,
|
||||
}) as any,
|
||||
useResponse: true,
|
||||
},
|
||||
createImage: createXAIImage,
|
||||
@@ -27,7 +57,7 @@ export const LobeXAI = createOpenAICompatibleRuntime({
|
||||
provider: ModelProvider.XAI,
|
||||
responses: {
|
||||
handlePayload: (payload) => {
|
||||
const { enabledSearch, tools, ...rest } = payload;
|
||||
const { enabledSearch, tools, ...rest } = pruneUnsupportedReasoningParameters(payload);
|
||||
|
||||
const xaiTools = enabledSearch
|
||||
? [...(tools || []), { type: 'web_search' }, { type: 'x_search' }]
|
||||
|
||||
Reference in New Issue
Block a user