Merge pull request #23 from southseact-3d/codex/analyze-tool-call-request-counting-issue
Cap Chutes provider tool orchestration to a single SDK step
This commit is contained in:
@@ -2,6 +2,7 @@ import { Installation } from "@/installation"
|
||||
import { Provider } from "@/provider/provider"
|
||||
import { Log } from "@/util/log"
|
||||
import {
|
||||
stepCountIs,
|
||||
streamText,
|
||||
wrapLanguageModel,
|
||||
type ModelMessage,
|
||||
@@ -116,6 +117,8 @@ export namespace LLM {
|
||||
providerOptions: ProviderTransform.providerOptions(input.model, params.options),
|
||||
activeTools: Object.keys(tools).filter((x) => x !== "invalid"),
|
||||
tools,
|
||||
// Chutes accounts each provider round-trip as a separate request, so keep SDK orchestration to one step.
|
||||
stopWhen: input.model.providerID === "chutes" ? stepCountIs(1) : undefined,
|
||||
maxOutputTokens,
|
||||
abortSignal: input.abort,
|
||||
headers: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test"
|
||||
import path from "path"
|
||||
import type { ModelMessage } from "ai"
|
||||
import { type ModelMessage, jsonSchema, tool } from "ai"
|
||||
import { LLM } from "../../src/session/llm"
|
||||
import { Global } from "../../src/global"
|
||||
import { Instance } from "../../src/project/instance"
|
||||
@@ -579,6 +579,131 @@ describe("session.llm.stream", () => {
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
test("limits chutes tool runs to a single SDK step", async () => {
|
||||
const server = state.server
|
||||
if (!server) throw new Error("Server not initialized")
|
||||
|
||||
const providerID = "chutes"
|
||||
const modelID = "NousResearch/Hermes-4.3-36B"
|
||||
const fixture = await loadFixture(providerID, modelID)
|
||||
const model = fixture.model
|
||||
|
||||
const request = waitRequest(
|
||||
"/chat/completions",
|
||||
createEventResponse(
|
||||
[
|
||||
{
|
||||
id: "chatcmpl-1",
|
||||
object: "chat.completion.chunk",
|
||||
choices: [{ delta: { role: "assistant" } }],
|
||||
},
|
||||
{
|
||||
id: "chatcmpl-1",
|
||||
object: "chat.completion.chunk",
|
||||
choices: [
|
||||
{
|
||||
delta: {
|
||||
tool_calls: [
|
||||
{
|
||||
index: 0,
|
||||
id: "call_1",
|
||||
type: "function",
|
||||
function: {
|
||||
name: "echo",
|
||||
arguments: '{"value":"hello"}',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "chatcmpl-1",
|
||||
object: "chat.completion.chunk",
|
||||
choices: [{ delta: {}, finish_reason: "tool_calls" }],
|
||||
},
|
||||
],
|
||||
true,
|
||||
),
|
||||
)
|
||||
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
await Bun.write(
|
||||
path.join(dir, "opencode.json"),
|
||||
JSON.stringify({
|
||||
$schema: "https://opencode.ai/config.json",
|
||||
enabled_providers: [providerID],
|
||||
provider: {
|
||||
[providerID]: {
|
||||
options: {
|
||||
apiKey: "test-chutes-key",
|
||||
baseURL: `${server.url.origin}/v1`,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const resolved = await Provider.getModel(providerID, model.id)
|
||||
const sessionID = "session-test-5"
|
||||
const agent = {
|
||||
name: "test",
|
||||
mode: "primary",
|
||||
options: {},
|
||||
permission: [{ permission: "*", pattern: "*", action: "allow" }],
|
||||
temperature: 0.4,
|
||||
} satisfies Agent.Info
|
||||
|
||||
const user = {
|
||||
id: "user-5",
|
||||
sessionID,
|
||||
role: "user",
|
||||
time: { created: Date.now() },
|
||||
agent: agent.name,
|
||||
model: { providerID, modelID: resolved.id },
|
||||
} satisfies MessageV2.User
|
||||
|
||||
const stream = await LLM.stream({
|
||||
user,
|
||||
sessionID,
|
||||
model: resolved,
|
||||
agent,
|
||||
system: ["You are a helpful assistant."],
|
||||
abort: new AbortController().signal,
|
||||
messages: [{ role: "user", content: "Use echo" }],
|
||||
tools: {
|
||||
echo: tool({
|
||||
inputSchema: jsonSchema({
|
||||
type: "object",
|
||||
properties: {
|
||||
value: { type: "string" },
|
||||
},
|
||||
required: ["value"],
|
||||
additionalProperties: false,
|
||||
}),
|
||||
execute: async () => "ok",
|
||||
}),
|
||||
},
|
||||
})
|
||||
|
||||
for await (const _ of stream.fullStream) {
|
||||
}
|
||||
|
||||
const capture = await request
|
||||
expect(capture.url.pathname.endsWith("/chat/completions")).toBe(true)
|
||||
expect(state.queue.length).toBe(0)
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("sends Google API payload for Gemini models", async () => {
|
||||
const server = state.server
|
||||
if (!server) {
|
||||
|
||||
Reference in New Issue
Block a user