Autoparser - complete refactoring of parser architecture (#18675)
* Autoparser - full single commit squish * Final pre-merge changes: minor fixes, Kimi 2.5 model parser
This commit is contained in:
committed by
GitHub
parent
34df42f7be
commit
566059a26b
@@ -729,6 +729,10 @@ export class SchemaConverter {
|
||||
return this._addRule(ruleName, out.join(''));
|
||||
} else if ((schemaType === 'object') || (Object.keys(schema).length === 0)) {
|
||||
return this._addRule(ruleName, this._addPrimitive('object', PRIMITIVE_RULES['object']));
|
||||
} else if (schemaType === undefined && typeof schema === 'object' && !Array.isArray(schema) && schema !== null) {
|
||||
// No type constraint and no recognized structural keywords (e.g. {"description": "..."}).
|
||||
// Per JSON Schema semantics this is equivalent to {} and accepts any value.
|
||||
return this._addRule(ruleName, this._addPrimitive('value', PRIMITIVE_RULES['value']));
|
||||
} else {
|
||||
if (!(schemaType in PRIMITIVE_RULES)) {
|
||||
throw new Error(`Unrecognized schema: ${JSON.stringify(schema)}`);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#include "server-common.h"
|
||||
#include "server-task.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "llama.h"
|
||||
#include "chat.h"
|
||||
#include "common.h"
|
||||
#include "json-schema-to-grammar.h"
|
||||
#include "llama.h"
|
||||
#include "sampling.h"
|
||||
#include "speculative.h"
|
||||
#include "json-schema-to-grammar.h"
|
||||
#include "server-common.h"
|
||||
|
||||
using json = nlohmann::ordered_json;
|
||||
|
||||
@@ -157,7 +157,8 @@ json task_params::to_json(bool only_metrics) const {
|
||||
common_chat_msg task_result_state::update_chat_msg(
|
||||
const std::string & text_added,
|
||||
bool is_partial,
|
||||
std::vector<common_chat_msg_diff> & diffs) {
|
||||
std::vector<common_chat_msg_diff> & diffs,
|
||||
bool filter_tool_calls) {
|
||||
generated_text += text_added;
|
||||
auto msg_prv_copy = chat_msg;
|
||||
SRV_DBG("Parsing chat message: %s\n", generated_text.c_str());
|
||||
@@ -168,7 +169,64 @@ common_chat_msg task_result_state::update_chat_msg(
|
||||
if (!new_msg.empty()) {
|
||||
new_msg.set_tool_call_ids(generated_tool_call_ids, gen_tool_call_id);
|
||||
chat_msg = new_msg;
|
||||
diffs = common_chat_msg_diff::compute_diffs(msg_prv_copy, new_msg.empty() ? msg_prv_copy : new_msg);
|
||||
auto all_diffs = common_chat_msg_diff::compute_diffs(msg_prv_copy, chat_msg);
|
||||
|
||||
if (!filter_tool_calls) {
|
||||
diffs = std::move(all_diffs);
|
||||
} else {
|
||||
for (auto & d : all_diffs) {
|
||||
// If this is a new type of delta, flush all currently pending tool call names
|
||||
for (size_t i = 0; i < chat_msg.tool_calls.size(); ++i) {
|
||||
if (sent_tool_call_names.count(i) || chat_msg.tool_calls[i].name.empty()) {
|
||||
continue;
|
||||
}
|
||||
if (d.tool_call_index != i || !d.tool_call_delta.arguments.empty()) {
|
||||
common_chat_msg_diff header;
|
||||
header.tool_call_index = i;
|
||||
header.tool_call_delta.id = chat_msg.tool_calls[i].id;
|
||||
header.tool_call_delta.name = chat_msg.tool_calls[i].name;
|
||||
diffs.push_back(std::move(header));
|
||||
sent_tool_call_names.insert(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (d.tool_call_index == std::string::npos) {
|
||||
diffs.push_back(std::move(d));
|
||||
} else {
|
||||
size_t i = d.tool_call_index;
|
||||
if (sent_tool_call_names.count(i)) {
|
||||
if (!d.tool_call_delta.arguments.empty()) {
|
||||
d.tool_call_delta.name = "";
|
||||
d.tool_call_delta.id = "";
|
||||
diffs.push_back(std::move(d));
|
||||
}
|
||||
} else {
|
||||
// Not sent yet.
|
||||
if (!d.tool_call_delta.arguments.empty() || !is_partial) {
|
||||
d.tool_call_delta.name = chat_msg.tool_calls[i].name;
|
||||
d.tool_call_delta.id = chat_msg.tool_calls[i].id;
|
||||
diffs.push_back(std::move(d));
|
||||
sent_tool_call_names.insert(i);
|
||||
} else {
|
||||
// Suppress
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Final check at EOF
|
||||
if (!is_partial) {
|
||||
for (size_t i = 0; i < chat_msg.tool_calls.size(); ++i) {
|
||||
if (!sent_tool_call_names.count(i) && !chat_msg.tool_calls[i].name.empty()) {
|
||||
common_chat_msg_diff header;
|
||||
header.tool_call_index = i;
|
||||
header.tool_call_delta.id = chat_msg.tool_calls[i].id;
|
||||
header.tool_call_delta.name = chat_msg.tool_calls[i].name;
|
||||
diffs.push_back(std::move(header));
|
||||
sent_tool_call_names.insert(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return chat_msg;
|
||||
}
|
||||
|
||||
@@ -98,6 +98,7 @@ struct task_result_state {
|
||||
common_chat_msg chat_msg;
|
||||
std::string generated_text; // append new chunks of generated text here
|
||||
std::vector<std::string> generated_tool_call_ids;
|
||||
std::unordered_set<size_t> sent_tool_call_names;
|
||||
|
||||
// for OpenAI Responses and Anthropic streaming API:
|
||||
// track output item / content block state across chunks
|
||||
@@ -120,7 +121,8 @@ struct task_result_state {
|
||||
common_chat_msg update_chat_msg(
|
||||
const std::string & text_added,
|
||||
bool is_partial,
|
||||
std::vector<common_chat_msg_diff> & diffs);
|
||||
std::vector<common_chat_msg_diff> & diffs,
|
||||
bool filter_tool_calls = false);
|
||||
};
|
||||
|
||||
struct server_task {
|
||||
|
||||
@@ -100,18 +100,19 @@ def do_test_completion_with_required_tool_tiny(server: ServerProcess, tool: dict
|
||||
assert choice["message"].get("content") in (None, ""), f'Expected no content in {choice["message"]}'
|
||||
# assert len(tool_call.get("id", "")) > 0, f'Expected non empty tool call id in {tool_call}'
|
||||
expected_function_name = "python" if tool["type"] == "code_interpreter" else tool["function"]["name"]
|
||||
assert expected_function_name == tool_call["function"]["name"]
|
||||
assert expected_function_name == tool_call["function"]["name"], f'Expected tool name to be {tool_call["function"]["name"]} in {choice["message"]}'
|
||||
actual_arguments = tool_call["function"]["arguments"]
|
||||
assert isinstance(actual_arguments, str)
|
||||
assert isinstance(actual_arguments, dict) or isinstance(actual_arguments, str), f'Expected arguments to be a dict or str, got: {actual_arguments}'
|
||||
if argument_key is not None:
|
||||
actual_arguments = json.loads(actual_arguments)
|
||||
assert argument_key in actual_arguments, f"tool arguments: {json.dumps(actual_arguments)}, expected: {argument_key}"
|
||||
if (isinstance(actual_arguments, str)):
|
||||
actual_arguments = json.loads(actual_arguments)
|
||||
assert argument_key in actual_arguments, f"tool arguments: {actual_arguments}, expected: {argument_key}"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("stream", [CompletionMode.NORMAL, CompletionMode.STREAMED])
|
||||
@pytest.mark.parametrize("template_name,tool,argument_key", [
|
||||
("google-gemma-2-2b-it", TEST_TOOL, "success"),
|
||||
("google-gemma-2-2b-it", TEST_TOOL, "success"),
|
||||
("Qwen3-Coder", TEST_TOOL, "success"),
|
||||
("Qwen3-Coder", TEST_TOOL, "success"),
|
||||
("meta-llama-Llama-3.3-70B-Instruct", TEST_TOOL, "success"),
|
||||
("meta-llama-Llama-3.3-70B-Instruct", TEST_TOOL, "success"),
|
||||
("meta-llama-Llama-3.3-70B-Instruct", PYTHON_TOOL, "code"),
|
||||
|
||||
Reference in New Issue
Block a user