mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-10-06 02:47:56 +00:00
Merge 91e27664a2 into eafed9705c
This commit is contained in:
commit
c10281f09b
2 changed files with 140 additions and 22 deletions
|
|
@ -89,19 +89,19 @@ describe("QwenCodeHandler Native Tools", () => {
|
||||||
})
|
})
|
||||||
await stream.next()
|
await stream.next()
|
||||||
|
|
||||||
expect(mockCreate).toHaveBeenCalledWith(
|
const callArgs = mockCreate.mock.calls[0][0]
|
||||||
expect.objectContaining({
|
expect(callArgs.tools).toEqual(
|
||||||
tools: expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
type: "function",
|
type: "function",
|
||||||
function: expect.objectContaining({
|
function: expect.objectContaining({
|
||||||
name: "test_tool",
|
name: "test_tool",
|
||||||
}),
|
|
||||||
}),
|
}),
|
||||||
]),
|
}),
|
||||||
parallel_tool_calls: true,
|
]),
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
// DashScope does not support parallel_tool_calls
|
||||||
|
expect(callArgs).not.toHaveProperty("parallel_tool_calls")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should include tool_choice when provided", async () => {
|
it("should include tool_choice when provided", async () => {
|
||||||
|
|
@ -145,7 +145,8 @@ describe("QwenCodeHandler Native Tools", () => {
|
||||||
const callArgs = mockCreate.mock.calls[mockCreate.mock.calls.length - 1][0]
|
const callArgs = mockCreate.mock.calls[mockCreate.mock.calls.length - 1][0]
|
||||||
expect(callArgs).toHaveProperty("tools")
|
expect(callArgs).toHaveProperty("tools")
|
||||||
expect(callArgs).toHaveProperty("tool_choice")
|
expect(callArgs).toHaveProperty("tool_choice")
|
||||||
expect(callArgs).toHaveProperty("parallel_tool_calls", true)
|
// DashScope does not support parallel_tool_calls
|
||||||
|
expect(callArgs).not.toHaveProperty("parallel_tool_calls")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should yield tool_call_partial chunks during streaming", async () => {
|
it("should yield tool_call_partial chunks during streaming", async () => {
|
||||||
|
|
@ -215,7 +216,7 @@ describe("QwenCodeHandler Native Tools", () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should set parallel_tool_calls based on metadata", async () => {
|
it("should not include parallel_tool_calls even when metadata provides it (DashScope unsupported)", async () => {
|
||||||
mockCreate.mockImplementationOnce(() => ({
|
mockCreate.mockImplementationOnce(() => ({
|
||||||
[Symbol.asyncIterator]: async function* () {
|
[Symbol.asyncIterator]: async function* () {
|
||||||
yield {
|
yield {
|
||||||
|
|
@ -231,11 +232,9 @@ describe("QwenCodeHandler Native Tools", () => {
|
||||||
})
|
})
|
||||||
await stream.next()
|
await stream.next()
|
||||||
|
|
||||||
expect(mockCreate).toHaveBeenCalledWith(
|
const callArgs = mockCreate.mock.calls[0][0]
|
||||||
expect.objectContaining({
|
// DashScope does not support parallel_tool_calls - should never be sent
|
||||||
parallel_tool_calls: true,
|
expect(callArgs).not.toHaveProperty("parallel_tool_calls")
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should yield tool_call_end events when finish_reason is tool_calls", async () => {
|
it("should yield tool_call_end events when finish_reason is tool_calls", async () => {
|
||||||
|
|
@ -370,4 +369,98 @@ describe("QwenCodeHandler Native Tools", () => {
|
||||||
expect(endChunks).toHaveLength(1)
|
expect(endChunks).toHaveLength(1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("DashScope API Compatibility", () => {
|
||||||
|
it("should use max_tokens instead of max_completion_tokens", async () => {
|
||||||
|
mockCreate.mockImplementationOnce(() => ({
|
||||||
|
[Symbol.asyncIterator]: async function* () {
|
||||||
|
yield {
|
||||||
|
choices: [{ delta: { content: "Test response" } }],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
const stream = handler.createMessage("test prompt", [], {
|
||||||
|
taskId: "test-task-id",
|
||||||
|
})
|
||||||
|
await stream.next()
|
||||||
|
|
||||||
|
const callArgs = mockCreate.mock.calls[0][0]
|
||||||
|
expect(callArgs).toHaveProperty("max_tokens")
|
||||||
|
expect(callArgs).not.toHaveProperty("max_completion_tokens")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should not include stream_options", async () => {
|
||||||
|
mockCreate.mockImplementationOnce(() => ({
|
||||||
|
[Symbol.asyncIterator]: async function* () {
|
||||||
|
yield {
|
||||||
|
choices: [{ delta: { content: "Test response" } }],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
const stream = handler.createMessage("test prompt", [], {
|
||||||
|
taskId: "test-task-id",
|
||||||
|
})
|
||||||
|
await stream.next()
|
||||||
|
|
||||||
|
const callArgs = mockCreate.mock.calls[0][0]
|
||||||
|
expect(callArgs).not.toHaveProperty("stream_options")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should not include parallel_tool_calls", async () => {
|
||||||
|
mockCreate.mockImplementationOnce(() => ({
|
||||||
|
[Symbol.asyncIterator]: async function* () {
|
||||||
|
yield {
|
||||||
|
choices: [{ delta: { content: "Test response" } }],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
const stream = handler.createMessage("test prompt", [], {
|
||||||
|
taskId: "test-task-id",
|
||||||
|
tools: testTools,
|
||||||
|
})
|
||||||
|
await stream.next()
|
||||||
|
|
||||||
|
const callArgs = mockCreate.mock.calls[0][0]
|
||||||
|
expect(callArgs).not.toHaveProperty("parallel_tool_calls")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should not set strict: true on tool definitions", async () => {
|
||||||
|
mockCreate.mockImplementationOnce(() => ({
|
||||||
|
[Symbol.asyncIterator]: async function* () {
|
||||||
|
yield {
|
||||||
|
choices: [{ delta: { content: "Test response" } }],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
const stream = handler.createMessage("test prompt", [], {
|
||||||
|
taskId: "test-task-id",
|
||||||
|
tools: testTools,
|
||||||
|
})
|
||||||
|
await stream.next()
|
||||||
|
|
||||||
|
const callArgs = mockCreate.mock.calls[0][0]
|
||||||
|
const tools = callArgs.tools
|
||||||
|
expect(tools).toBeDefined()
|
||||||
|
for (const tool of tools) {
|
||||||
|
// DashScope does not support strict mode
|
||||||
|
expect(tool.function).not.toHaveProperty("strict")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should use max_tokens in completePrompt", async () => {
|
||||||
|
mockCreate.mockImplementationOnce(() => ({
|
||||||
|
choices: [{ message: { content: "response" } }],
|
||||||
|
}))
|
||||||
|
|
||||||
|
await handler.completePrompt("test prompt")
|
||||||
|
|
||||||
|
const callArgs = mockCreate.mock.calls[0][0]
|
||||||
|
expect(callArgs).toHaveProperty("max_tokens")
|
||||||
|
expect(callArgs).not.toHaveProperty("max_completion_tokens")
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -219,16 +219,19 @@ export class QwenCodeHandler extends BaseProvider implements SingleCompletionHan
|
||||||
|
|
||||||
const convertedMessages = [systemMessage, ...convertToOpenAiMessages(messages)]
|
const convertedMessages = [systemMessage, ...convertToOpenAiMessages(messages)]
|
||||||
|
|
||||||
|
// DashScope's OpenAI-compatible API does not support several OpenAI-specific
|
||||||
|
// parameters. Using them causes a 400 Bad Request error. Specifically:
|
||||||
|
// - max_completion_tokens -> use max_tokens instead
|
||||||
|
// - parallel_tool_calls -> not supported
|
||||||
|
// - stream_options -> not supported
|
||||||
const requestOptions: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming = {
|
const requestOptions: OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming = {
|
||||||
model: model.id,
|
model: model.id,
|
||||||
temperature: 0,
|
temperature: 0,
|
||||||
messages: convertedMessages,
|
messages: convertedMessages,
|
||||||
stream: true,
|
stream: true,
|
||||||
stream_options: { include_usage: true },
|
max_tokens: model.info.maxTokens,
|
||||||
max_completion_tokens: model.info.maxTokens,
|
|
||||||
tools: this.convertToolsForOpenAI(metadata?.tools),
|
tools: this.convertToolsForOpenAI(metadata?.tools),
|
||||||
tool_choice: metadata?.tool_choice,
|
tool_choice: metadata?.tool_choice,
|
||||||
parallel_tool_calls: metadata?.parallelToolCalls ?? true,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const stream = await this.callApiWithRetry(() => client.chat.completions.create(requestOptions))
|
const stream = await this.callApiWithRetry(() => client.chat.completions.create(requestOptions))
|
||||||
|
|
@ -321,6 +324,28 @@ export class QwenCodeHandler extends BaseProvider implements SingleCompletionHan
|
||||||
return { id, info }
|
return { id, info }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override to skip strict mode for DashScope compatibility.
|
||||||
|
* DashScope's OpenAI-compatible API does not support OpenAI's strict mode
|
||||||
|
* on tool definitions (strict: true, additionalProperties: false enforcement).
|
||||||
|
* Sending these causes a 400 Bad Request error.
|
||||||
|
*/
|
||||||
|
protected override convertToolsForOpenAI(tools: any[] | undefined): any[] | undefined {
|
||||||
|
if (!tools) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return tools
|
||||||
|
.filter((tool) => tool.type === "function")
|
||||||
|
.map((tool) => ({
|
||||||
|
...tool,
|
||||||
|
function: {
|
||||||
|
...tool.function,
|
||||||
|
// Do not set strict: true - DashScope does not support it
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
async completePrompt(prompt: string): Promise<string> {
|
async completePrompt(prompt: string): Promise<string> {
|
||||||
await this.ensureAuthenticated()
|
await this.ensureAuthenticated()
|
||||||
const client = this.ensureClient()
|
const client = this.ensureClient()
|
||||||
|
|
@ -329,7 +354,7 @@ export class QwenCodeHandler extends BaseProvider implements SingleCompletionHan
|
||||||
const requestOptions: OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming = {
|
const requestOptions: OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming = {
|
||||||
model: model.id,
|
model: model.id,
|
||||||
messages: [{ role: "user", content: prompt }],
|
messages: [{ role: "user", content: prompt }],
|
||||||
max_completion_tokens: model.info.maxTokens,
|
max_tokens: model.info.maxTokens,
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await this.callApiWithRetry(() => client.chat.completions.create(requestOptions))
|
const response = await this.callApiWithRetry(() => client.chat.completions.create(requestOptions))
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue