Skip to content

Commit 6bda165

Browse files
committed
Describe the tool_choice mapping as the code does it
OpenAiRequestMapper's class Javadoc listed tool_choice and parallel_tool_calls among the fields forwarded verbatim as raw JSON. Only messages and tools are: tool_choice goes in its string form, a named function becomes "required" with only that tool forwarded, any other object form is ignored and the request runs as "auto", and parallel_tool_calls is read as a boolean. The tool_choice comment in ResponsesApiSupport.toOpenAiChatRequest described the string form only and now says the same. Closes the F109 addendum that named both texts. Documentation only. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VBFFnrhtvXiUeyxjmUc4R2
1 parent c26bb17 commit 6bda165

3 files changed

Lines changed: 13 additions & 8 deletions

File tree

‎BUGHUNT-FOUND.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,6 +1056,7 @@ Repos plus eins. Geprüfte Klassen ohne Fund stehen in [`BUGHUNT-CHECKED.md`](BU
10561056
- **Reproduktion:** `OpenAiRequestMapperTest#toolChoiceNamingAFunctionRequiresAToolCall`, `ResponsesApiSupportTest#toolChoiceNamingAFunctionRequiresAToolCall`
10571057
- **Rot-Grün-Nachweis:** ohne Fix rot, je Weg für sich (2 Fehlschläge, je „Expected: is "required" but: was ""“), mit Fix grün (28/28 und 51/51, skipped 0); Gegenprobe per `git stash` beider Produktionsdateien rot (1 + 1), nach `pop` grün. `OpenAiCompatServerHttpTest` 47/47, `AnthropicApiSupportTest` 22/22, `ResponsesStreamTranslatorTest` 5/5, `OllamaApiSupportTest` 32/32, `LlamaArchitectureTest` 13/13, `spotless:apply` und `spotbugs:check` grün.
10581058
- **Nachtrag (2026-10-18, nur beschrieben):** Zwei Kommentare neben dem Fix sind nicht nachgezogen: die Klassen-Javadoc von `OpenAiRequestMapper` nennt `tool_choice` unter den Feldern, die „verbatim as raw JSON“ weitergehen (galt schon vorher nur für die Textform), und der Kommentar über dem Werkzeugblock in `ResponsesApiSupport.toOpenAiChatRequest` beschreibt nur die Textform. Reine Doku-Abweichung, nach der Nutzerregel ohne Test nicht nachträglich geändert; Vorschlag: `tool_choice` aus der Verbatim-Aufzählung nehmen und „ein benanntes Werkzeug wird zu `required`“ ergänzen.
1059+
- **Nachtrag (Branch-Review 2026-10-20, Funde J2-6, J5-8):** Erledigt. Die Klassen-Javadoc von `OpenAiRequestMapper` nennt als wörtlich weitergereicht nur noch `messages` und `tools`; `tool_choice` geht in der Textform weiter, ein benanntes Werkzeug wird zu `required` mit nur diesem Werkzeug (F227), jede andere Objektform (`allowed_tools`, `custom`) wird ignoriert und läuft als `auto`, `parallel_tool_calls` wird als Boolean gelesen. Der Kommentar über `tool_choice` in `ResponsesApiSupport.toOpenAiChatRequest` sagt dasselbe. Reine Doku, kein Test.
10591060
- **Nachtrag (Branch-Review 2026-10-20):** Mit mehreren Werkzeugen ließ `required` jeden Aufruf zu; seit F227 geht auf beiden Wegen nur das benannte Werkzeug weiter, ein unbekannter oder fehlender Name ist 400.
10601061
- **Status:** gefixt
10611062
- **Commit:** java-llama.cpp `c5823a5`

‎llama/src/main/java/net/ladenthin/llama/server/OpenAiRequestMapper.java‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@
1818
/**
1919
* Pure mapping from an OpenAI {@code /v1/chat/completions} request body to {@link InferenceParameters}.
2020
*
21-
* <p>The structural fields — {@code messages}, {@code tools}, the string form of {@code tool_choice}, and
22-
* {@code parallel_tool_calls} — are forwarded
23-
* <em>verbatim</em> as raw JSON so the full OpenAI shape (assistant {@code tool_calls},
24-
* {@code role:"tool"} results with {@code tool_call_id}, and vision {@code image_url} content parts)
25-
* round-trips untouched into the native chat-template parser. A {@code tool_choice} naming one function
26-
* becomes {@code required} with only that tool forwarded (see {@link #namedToolChoice}). Sampling fields are translated to the
21+
* <p>The structural fields {@code messages} and {@code tools} are forwarded <em>verbatim</em> as raw JSON
22+
* so the full OpenAI shape (assistant {@code tool_calls}, {@code role:"tool"} results with
23+
* {@code tool_call_id}, and vision {@code image_url} content parts) round-trips untouched into the native
24+
* chat-template parser. {@code tool_choice} is forwarded in its string form ({@code auto}, {@code none},
25+
* {@code required}, the only values llama.cpp's chat parser accepts); a {@code tool_choice} naming one
26+
* function becomes {@code required} with only that tool forwarded (see {@link #namedToolChoice}), and any
27+
* other object form ({@code allowed_tools}, {@code custom}) is ignored, so the request runs as
28+
* {@code auto}, which is also what upstream reads from a non-string. {@code parallel_tool_calls} is read as
29+
* a boolean. Sampling fields are translated to the
2730
* matching {@code InferenceParameters.with*} setter, as are the llama.cpp sampling extensions upstream's
2831
* request schema reads ({@code min_p}, {@code repeat_penalty}, {@code grammar}, ...) and the reasoning
2932
* controls upstream's chat

‎llama/src/main/java/net/ladenthin/llama/server/ResponsesApiSupport.java‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,9 @@ static ObjectNode toOpenAiChatRequest(JsonNode request) {
159159
throw new IllegalArgumentException("tool_choice names unknown tool '" + namedTool + "'");
160160
}
161161
// The Responses API uses the same tool_choice + parallel_tool_calls fields as chat; forward
162-
// them so the shared chat core honors them. The mapper consumes the string form of
163-
// tool_choice ("auto"/"none"/"required"), which is what we forward here.
162+
// them so the shared chat core honors them. The string form (auto/none/required) goes as is,
163+
// a named function becomes "required" with only that tool forwarded above, and any other
164+
// object form is dropped, so the request runs as "auto".
164165
if (toolChoice.isTextual()) {
165166
openAi.put("tool_choice", toolChoice.asText());
166167
} else if (namedTool != null) {

0 commit comments

Comments
 (0)