Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/types/src/providers/friendli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export const friendliModels = {
outputPrice: 4.4,
cacheWritesPrice: 0,
cacheReadsPrice: 0.26,
supportsReasoningEffort: ["minimal", "low", "medium", "high", "xhigh", "max"],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does GLM offer a way to disable reasoning effort completely using 'disable' as a key?

@Lee-Si-Yoon Lee-Si-Yoon Jul 19, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, verified that it throws 422 when disable or none is given

Endpoint: https://api.friendli.ai/serverless/v1/chat/completions
Model: zai-org/GLM-5.2

{"message":"invalid JSON payload: unknown enum value: 'disable'"}

{"message":"invalid JSON payload: unknown enum value: 'none'"}

To disable thinking it has to be done via chat_template_kwargs: { enable_thinking: true } option

reasoningEffort: "high",
description:
"GLM-5.2 is Zhipu's flagship model with a 1M context window and 128k max output, served via Friendli Model APIs. It delivers top-tier long-context reasoning, coding, and agentic performance for extended engineering sessions.",
},
Expand All @@ -33,6 +35,8 @@ export const friendliModels = {
outputPrice: 4.4,
cacheWritesPrice: 0,
cacheReadsPrice: 0.26,
supportsReasoningEffort: ["minimal", "low", "medium", "high", "xhigh", "max"],
reasoningEffort: "high",
description:
"GLM-5.1 is Zhipu's most capable model with a 200k context window and 128k max output, served via Friendli Model APIs. It delivers top-tier reasoning, coding, and agentic performance.",
},
Expand Down
222 changes: 221 additions & 1 deletion src/api/providers/__tests__/friendli.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,16 @@ describe("FriendliHandler", () => {
},
])(
"should expose newly added model $modelId",
({ modelId, contextWindow, maxTokens, supportsMaxTokens, inputPrice, outputPrice, cacheWritesPrice, cacheReadsPrice }) => {
({
modelId,
contextWindow,
maxTokens,
supportsMaxTokens,
inputPrice,
outputPrice,
cacheWritesPrice,
cacheReadsPrice,
}) => {
expect(friendliModels[modelId]).toBeDefined()
const info = friendliModels[modelId] as import("@roo-code/types").ModelInfo
expect(info.maxTokens).toBe(maxTokens)
Expand Down Expand Up @@ -394,3 +403,214 @@ describe("Friendli model max output tokens (clamping behavior)", () => {
expect(result).toBe(80_000)
})
})

describe("FriendliHandler — Friendli-specific reasoning params", () => {
beforeEach(() => {
vi.clearAllMocks()
})

it("should include reasoning_effort, chat_template_kwargs, parse_reasoning for GLM-5.2 with reasoning enabled", async () => {
const handler = new FriendliHandler({
apiModelId: "zai-org/GLM-5.2",
friendliApiKey: "test-key",
enableReasoningEffort: true,
reasoningEffort: "high",
})

mockCreate.mockImplementationOnce(() => ({
[Symbol.asyncIterator]: () => ({
async next() {
return { done: true }
},
}),
}))

await handler.createMessage("system", []).next()

expect(mockCreate).toHaveBeenCalledWith(
expect.objectContaining({
model: "zai-org/GLM-5.2",
reasoning_effort: "high",
chat_template_kwargs: { enable_thinking: true },
parse_reasoning: true,
include_reasoning: true,
}),
undefined,
)
})

it("should send enable_thinking: false when enableReasoningEffort is false on controllable model", async () => {
const handler = new FriendliHandler({
apiModelId: "zai-org/GLM-5.2",
friendliApiKey: "test-...ey",
enableReasoningEffort: false,
})

mockCreate.mockImplementationOnce(() => ({
[Symbol.asyncIterator]: () => ({
async next() {
return { done: true }
},
}),
}))

await handler.createMessage("system", []).next()

const callArgs = mockCreate.mock.calls[0][0] as any
expect(callArgs.reasoning_effort).toBeUndefined()
expect(callArgs.chat_template_kwargs).toEqual({ enable_thinking: false })
expect(callArgs.parse_reasoning).toBeUndefined()
expect(callArgs.include_reasoning).toBeUndefined()
})

it("should send enable_thinking: false when reasoningEffort is none on controllable model", async () => {
const handler = new FriendliHandler({
apiModelId: "zai-org/GLM-5.2",
friendliApiKey: "test-...ey",
enableReasoningEffort: true,
reasoningEffort: "none",
})

mockCreate.mockImplementationOnce(() => ({
[Symbol.asyncIterator]: () => ({
async next() {
return { done: true }
},
}),
}))

await handler.createMessage("system", []).next()

const callArgs = mockCreate.mock.calls[0][0] as any
expect(callArgs.reasoning_effort).toBeUndefined()
expect(callArgs.chat_template_kwargs).toEqual({ enable_thinking: false })
expect(callArgs.parse_reasoning).toBeUndefined()
})

it("should send enable_thinking: false when reasoningEffort is disable on controllable model", async () => {
const handler = new FriendliHandler({
apiModelId: "zai-org/GLM-5.2",
friendliApiKey: "test-...ey",
enableReasoningEffort: true,
reasoningEffort: "disable",
})

mockCreate.mockImplementationOnce(() => ({
[Symbol.asyncIterator]: () => ({
async next() {
return { done: true }
},
}),
}))

await handler.createMessage("system", []).next()

const callArgs = mockCreate.mock.calls[0][0] as any
expect(callArgs.reasoning_effort).toBeUndefined()
expect(callArgs.chat_template_kwargs).toEqual({ enable_thinking: false })
expect(callArgs.parse_reasoning).toBeUndefined()
})

it("should use model default reasoningEffort when no explicit settings are provided", async () => {
const handler = new FriendliHandler({
apiModelId: "zai-org/GLM-5.2",
friendliApiKey: "test-...ey",
// No enableReasoningEffort or reasoningEffort — model default "high" kicks in
})

mockCreate.mockImplementationOnce(() => ({
[Symbol.asyncIterator]: () => ({
async next() {
return { done: true }
},
}),
}))

await handler.createMessage("system", []).next()

const callArgs = mockCreate.mock.calls[0][0] as any
expect(callArgs.reasoning_effort).toBe("high")
expect(callArgs.chat_template_kwargs).toEqual({ enable_thinking: true })
expect(callArgs.parse_reasoning).toBe(true)
expect(callArgs.include_reasoning).toBe(true)
})

it("should not include any reasoning params for non-reasoning DeepSeek-V3.2", async () => {
const handler = new FriendliHandler({
apiModelId: "deepseek-ai/DeepSeek-V3.2",
friendliApiKey: "test-key",
enableReasoningEffort: true,
reasoningEffort: "high",
})

mockCreate.mockImplementationOnce(() => ({
[Symbol.asyncIterator]: () => ({
async next() {
return { done: true }
},
}),
}))

await handler.createMessage("system", []).next()

const callArgs = mockCreate.mock.calls[0][0] as any
expect(callArgs.reasoning_effort).toBeUndefined()
expect(callArgs.chat_template_kwargs).toBeUndefined()
expect(callArgs.parse_reasoning).toBeUndefined()
})

it("should handle delta.reasoning_content from parse_reasoning=true stream", async () => {
const handler = new FriendliHandler({
apiModelId: "zai-org/GLM-5.2",
friendliApiKey: "test-key",
enableReasoningEffort: true,
reasoningEffort: "high",
})

mockCreate.mockImplementationOnce(async () => ({
[Symbol.asyncIterator]: async function* () {
yield {
choices: [{ delta: { reasoning_content: "Let me think..." } }],
usage: null,
}
yield {
choices: [{ delta: { content: "The answer is 42" } }],
usage: null,
}
yield {
choices: [{ delta: {} }],
usage: { prompt_tokens: 10, completion_tokens: 20, total_tokens: 30 },
}
},
}))

const stream = handler.createMessage("system", [])
const chunks = []
for await (const chunk of stream) {
chunks.push(chunk)
}

expect(chunks).toContainEqual({ type: "reasoning", text: "Let me think..." })
expect(chunks).toContainEqual({ type: "text", text: "The answer is 42" })
})

it("completePrompt should include reasoning params when enabled", async () => {
const handler = new FriendliHandler({
apiModelId: "zai-org/GLM-5.2",
friendliApiKey: "test-key",
enableReasoningEffort: true,
reasoningEffort: "medium",
})

mockCreate.mockResolvedValueOnce({
choices: [{ message: { content: "test result" } }],
})

await handler.completePrompt("test")

const callArgs = mockCreate.mock.calls[0][0] as any
expect(callArgs.reasoning_effort).toBe("medium")
expect(callArgs.chat_template_kwargs).toEqual({ enable_thinking: true })
expect(callArgs.parse_reasoning).toBe(true)
})
})
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Loading
Loading