mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fix(llama-cpp): recover plaintext tool calls (#116736)
Co-authored-by: Peter Steinberger <steipete@macos.shared>
This commit is contained in:
committed by
GitHub
parent
1f578f0e65
commit
0e1304d0de
@@ -364,6 +364,100 @@ describe("llama.cpp inference provider", () => {
|
||||
expect(mocks.llama.createGrammarForJsonSchema).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
format: "Harmony",
|
||||
text: '<|channel|>commentary to=weather code<|message|>{"city":"Paris"}<|call|>',
|
||||
},
|
||||
{
|
||||
format: "bracketed",
|
||||
text: '[weather]\n{"city":"Paris"}\n[END_TOOL_REQUEST]',
|
||||
},
|
||||
])("promotes $format plaintext tool calls into native tool events", async ({ text }) => {
|
||||
mocks.generateResponse.mockImplementationOnce(async (_history, options) => {
|
||||
options.onTextChunk(text.slice(0, 12));
|
||||
options.onTextChunk(text.slice(12));
|
||||
return {
|
||||
response: text,
|
||||
functionCalls: undefined,
|
||||
metadata: { stopReason: "eogToken" },
|
||||
};
|
||||
});
|
||||
|
||||
const stream = await createLlamaCppStreamFn({})(model, {
|
||||
messages: [{ role: "user", content: "Weather?", timestamp: 1 }],
|
||||
tools: [
|
||||
{
|
||||
name: "weather",
|
||||
description: "Get weather",
|
||||
parameters: { type: "object", properties: { city: { type: "string" } } },
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const events = await collectEvents(stream);
|
||||
|
||||
expect(events.map((event) => event.type)).toEqual([
|
||||
"start",
|
||||
"toolcall_start",
|
||||
"toolcall_delta",
|
||||
"toolcall_end",
|
||||
"done",
|
||||
]);
|
||||
expect(events.at(-1)).toMatchObject({
|
||||
type: "done",
|
||||
reason: "toolUse",
|
||||
message: {
|
||||
stopReason: "toolUse",
|
||||
content: [
|
||||
{
|
||||
type: "toolCall",
|
||||
name: "weather",
|
||||
arguments: { city: "Paris" },
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves plaintext calls for tools that are not registered", async () => {
|
||||
const text = '[tool:calendar] {"city":"Paris"}';
|
||||
mocks.generateResponse.mockImplementationOnce(async (_history, options) => {
|
||||
options.onTextChunk(text);
|
||||
return {
|
||||
response: text,
|
||||
functionCalls: undefined,
|
||||
metadata: { stopReason: "eogToken" },
|
||||
};
|
||||
});
|
||||
|
||||
const stream = await createLlamaCppStreamFn({})(model, {
|
||||
messages: [{ role: "user", content: "Weather?", timestamp: 1 }],
|
||||
tools: [
|
||||
{
|
||||
name: "weather",
|
||||
description: "Get weather",
|
||||
parameters: { type: "object", properties: { city: { type: "string" } } },
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const events = await collectEvents(stream);
|
||||
|
||||
expect(events.map((event) => event.type)).toEqual([
|
||||
"start",
|
||||
"text_start",
|
||||
"text_delta",
|
||||
"text_end",
|
||||
"done",
|
||||
]);
|
||||
expect(events.at(-1)).toMatchObject({
|
||||
type: "done",
|
||||
reason: "stop",
|
||||
message: { content: [{ type: "text", text }] },
|
||||
});
|
||||
});
|
||||
|
||||
it("lets tools win when responseFormat is also present", async () => {
|
||||
const stream = await createLlamaCppStreamFn({})(
|
||||
model,
|
||||
|
||||
@@ -17,6 +17,7 @@ import type {
|
||||
} from "openclaw/plugin-sdk/llm";
|
||||
import { createAssistantMessageEventStream } from "openclaw/plugin-sdk/llm";
|
||||
import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-model-shared";
|
||||
import { createPlainTextToolCallCompatWrapper } from "openclaw/plugin-sdk/provider-stream-shared";
|
||||
import {
|
||||
DEFAULT_LLAMA_CPP_CONTEXT_SIZE,
|
||||
resolveLlamaCppModelCacheDir,
|
||||
@@ -293,7 +294,7 @@ async function clearLlamaCppInferenceCacheForTests(): Promise<void> {
|
||||
}
|
||||
|
||||
export function createLlamaCppStreamFn(params: { providerConfig?: ModelProviderConfig }): StreamFn {
|
||||
return (model, context, options) => {
|
||||
return createPlainTextToolCallCompatWrapper((model, context, options) => {
|
||||
const stream = createAssistantMessageEventStream();
|
||||
let streamedText = "";
|
||||
let generationAborted = false;
|
||||
@@ -453,7 +454,7 @@ export function createLlamaCppStreamFn(params: { providerConfig?: ModelProviderC
|
||||
queueMicrotask(() => void serialize(run));
|
||||
}
|
||||
return stream;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (process.env.VITEST || process.env.NODE_ENV === "test") {
|
||||
|
||||
Reference in New Issue
Block a user