Skip to content

Commit 6f860dd

Browse files
Update exception error
V2.22
1 parent e065464 commit 6f860dd

9 files changed

Lines changed: 100 additions & 64 deletions

File tree

include/json_invoke/json_error.hpp

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,43 @@
66

77
namespace json_invoke {
88

9+
enum class ErrorCategory {
10+
// Request structure is malformed: missing fields, wrong types, wrong arg count
11+
InvalidRequest,
12+
// Named function does not exist in the registry
13+
FunctionNotFound,
14+
// A C++ type has no registered JSON converter
15+
UnsupportedType,
16+
// A registered converter threw during JSON <-> C++ conversion
17+
ConversionFailed,
18+
// The registered function itself threw during execution
19+
CallFailed,
20+
// Task was cancelled before it ran
21+
TaskCancelled,
22+
// Task exceeded its deadline while waiting in the queue
23+
TaskTimeout,
24+
// Task completed but its result was discarded after cancellation
25+
TaskResultDiscarded,
26+
// The scheduler was shut down while the task was pending
27+
SchedulerStopped,
28+
// The LLM response JSON does not conform to the expected envelope
29+
InvalidResponse,
30+
};
31+
932
class JsonInvokeError : public std::runtime_error {
1033
public:
11-
JsonInvokeError(std::string code, std::string message)
12-
: std::runtime_error(std::move(message)), code_(std::move(code))
34+
JsonInvokeError(ErrorCategory category, std::string code, std::string message)
35+
: std::runtime_error(std::move(message))
36+
, category_(category)
37+
, code_(std::move(code))
1338
{
1439
}
1540

16-
const std::string& code() const noexcept
17-
{
18-
return code_;
19-
}
41+
ErrorCategory category() const noexcept { return category_; }
42+
const std::string& code() const noexcept { return code_; }
2043

2144
private:
45+
ErrorCategory category_;
2246
std::string code_;
2347
};
2448

include/json_invoke/json_introspection.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include <nlohmann/json.hpp>
88

9+
#include <json_invoke/json_error.hpp>
910
#include "../tool_meta/tool_introspection.hpp"
1011

1112
namespace json_invoke {
@@ -185,7 +186,7 @@ json getToolSchemaJson(const Registry& registry, const std::string& name)
185186
}
186187
catch (const std::exception& e)
187188
{
188-
throw JsonInvokeError("function_not_found", e.what());
189+
throw JsonInvokeError(ErrorCategory::FunctionNotFound, "function_not_found", e.what());
189190
}
190191
}
191192

include/json_invoke/json_invoke.hpp

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -315,10 +315,10 @@ inline void ensureSuccessfulResponse(const json& response)
315315
{
316316
const std::string code = error_it->value("code", "unknown_error");
317317
const std::string message = error_it->value("message", "request failed");
318-
throw JsonInvokeError(code, message);
318+
throw JsonInvokeError(ErrorCategory::InvalidResponse, code, message);
319319
}
320320

321-
throw JsonInvokeError("invalid_response", "response is missing a successful result state");
321+
throw JsonInvokeError(ErrorCategory::InvalidResponse, "invalid_response", "response is missing a successful result state");
322322
}
323323

324324
inline const json& responseValue(const json& response)
@@ -328,7 +328,7 @@ inline const json& responseValue(const json& response)
328328
auto value_it = response.find("value");
329329
if (value_it == response.end())
330330
{
331-
throw JsonInvokeError("invalid_response", "successful response does not contain a 'value' field");
331+
throw JsonInvokeError(ErrorCategory::InvalidResponse, "invalid_response", "successful response does not contain a 'value' field");
332332
}
333333

334334
return *value_it;
@@ -375,7 +375,7 @@ class JsonInvokeResult {
375375
}
376376
catch (const std::exception& e)
377377
{
378-
throw JsonInvokeError("conversion_failed", "failed to convert response value: " + std::string(e.what()));
378+
throw JsonInvokeError(ErrorCategory::ConversionFailed, "conversion_failed", "failed to convert response value: " + std::string(e.what()));
379379
}
380380
}
381381

@@ -451,7 +451,7 @@ class JsonTypeRegistry {
451451
{
452452
if (!value.is_null())
453453
{
454-
throw JsonInvokeError("conversion_failed", "void type expects null JSON");
454+
throw JsonInvokeError(ErrorCategory::ConversionFailed, "conversion_failed", "void type expects null JSON");
455455
}
456456
return std::any{};
457457
}
@@ -461,6 +461,7 @@ class JsonTypeRegistry {
461461
if (it == from_json_.end())
462462
{
463463
throw JsonInvokeError(
464+
ErrorCategory::UnsupportedType,
464465
"unsupported_type",
465466
"JSON input conversion is not registered for C++ type '" + std::string(expected_type.name()) + "'");
466467
}
@@ -476,6 +477,7 @@ class JsonTypeRegistry {
476477
catch (const std::exception& e)
477478
{
478479
throw JsonInvokeError(
480+
ErrorCategory::ConversionFailed,
479481
"conversion_failed",
480482
"JSON input conversion failed for C++ type '" + std::string(expected_type.name()) + "': " + e.what());
481483
}
@@ -493,6 +495,7 @@ class JsonTypeRegistry {
493495
if (it == to_json_.end())
494496
{
495497
throw JsonInvokeError(
498+
ErrorCategory::UnsupportedType,
496499
"unsupported_type",
497500
"JSON output conversion is not registered for C++ type '" + std::string(actual_type.name()) + "'");
498501
}
@@ -508,6 +511,7 @@ class JsonTypeRegistry {
508511
catch (const std::exception& e)
509512
{
510513
throw JsonInvokeError(
514+
ErrorCategory::ConversionFailed,
511515
"conversion_failed",
512516
"JSON output conversion failed for C++ type '" + std::string(actual_type.name()) + "': " + e.what());
513517
}
@@ -946,7 +950,7 @@ class BasicJsonInvokeAdapter {
946950
{
947951
if (!request.is_object())
948952
{
949-
throw JsonInvokeError("invalid_request", "request must be a JSON object");
953+
throw JsonInvokeError(ErrorCategory::InvalidRequest, "invalid_request", "request must be a JSON object");
950954
}
951955
return request;
952956
}
@@ -965,7 +969,7 @@ class BasicJsonInvokeAdapter {
965969

966970
if (!it->is_string())
967971
{
968-
throw JsonInvokeError("invalid_request", "field '" + std::string(field) + "' must be a string");
972+
throw JsonInvokeError(ErrorCategory::InvalidRequest, "invalid_request", "field '" + std::string(field) + "' must be a string");
969973
}
970974

971975
return it->get<std::string>();
@@ -976,24 +980,24 @@ class BasicJsonInvokeAdapter {
976980
{
977981
if (!function_it->is_object())
978982
{
979-
throw JsonInvokeError("invalid_request", "field 'function' must be an object");
983+
throw JsonInvokeError(ErrorCategory::InvalidRequest, "invalid_request", "field 'function' must be an object");
980984
}
981985

982986
auto name_it = function_it->find("name");
983987
if (name_it == function_it->end())
984988
{
985-
throw JsonInvokeError("invalid_request", "field 'function.name' is required");
989+
throw JsonInvokeError(ErrorCategory::InvalidRequest, "invalid_request", "field 'function.name' is required");
986990
}
987991

988992
if (!name_it->is_string())
989993
{
990-
throw JsonInvokeError("invalid_request", "field 'function.name' must be a string");
994+
throw JsonInvokeError(ErrorCategory::InvalidRequest, "invalid_request", "field 'function.name' must be a string");
991995
}
992996

993997
return name_it->get<std::string>();
994998
}
995999

996-
throw JsonInvokeError("invalid_request", "request must contain a string field 'name'");
1000+
throw JsonInvokeError(ErrorCategory::InvalidRequest, "invalid_request", "request must contain a string field 'name'");
9971001
}
9981002

9991003
static json parseArgumentsString(std::string_view raw)
@@ -1004,7 +1008,7 @@ class BasicJsonInvokeAdapter {
10041008
}
10051009
catch (const json::parse_error& e)
10061010
{
1007-
throw JsonInvokeError("invalid_request", "tool arguments string is not valid JSON: " + std::string(e.what()));
1011+
throw JsonInvokeError(ErrorCategory::InvalidRequest, "invalid_request", "tool arguments string is not valid JSON: " + std::string(e.what()));
10081012
}
10091013
}
10101014

@@ -1037,7 +1041,7 @@ class BasicJsonInvokeAdapter {
10371041
{
10381042
if (!function_it->is_object())
10391043
{
1040-
throw JsonInvokeError("invalid_request", "field 'function' must be an object");
1044+
throw JsonInvokeError(ErrorCategory::InvalidRequest, "invalid_request", "field 'function' must be an object");
10411045
}
10421046

10431047
auto function_args_it = function_it->find("arguments");
@@ -1064,7 +1068,7 @@ class BasicJsonInvokeAdapter {
10641068
}
10651069
catch (const std::exception& e)
10661070
{
1067-
throw JsonInvokeError("function_not_found", e.what());
1071+
throw JsonInvokeError(ErrorCategory::FunctionNotFound, "function_not_found", e.what());
10681072
}
10691073
}
10701074

@@ -1082,7 +1086,7 @@ class BasicJsonInvokeAdapter {
10821086
}
10831087
catch (const std::exception& e)
10841088
{
1085-
throw JsonInvokeError("call_failed", e.what());
1089+
throw JsonInvokeError(ErrorCategory::CallFailed, "call_failed", e.what());
10861090
}
10871091
}
10881092

@@ -1093,6 +1097,7 @@ class BasicJsonInvokeAdapter {
10931097
if (!json_type_registry_.canRead(info.arg_types[index]))
10941098
{
10951099
throw JsonInvokeError(
1100+
ErrorCategory::UnsupportedType,
10961101
"unsupported_type",
10971102
"argument " + std::to_string(index) + " ('" + argumentLabel(info, index) + "') uses unsupported C++ type '" +
10981103
std::string(info.arg_types[index].name()) + "' for JSON input conversion");
@@ -1102,6 +1107,7 @@ class BasicJsonInvokeAdapter {
11021107
if (!json_type_registry_.canWrite(info.ret_type))
11031108
{
11041109
throw JsonInvokeError(
1110+
ErrorCategory::UnsupportedType,
11051111
"unsupported_type",
11061112
"return type '" + std::string(info.ret_type.name()) + "' is not registered for JSON output conversion");
11071113
}
@@ -1124,14 +1130,15 @@ class BasicJsonInvokeAdapter {
11241130
return {};
11251131
}
11261132

1127-
throw JsonInvokeError("invalid_request", "field 'args' must be an array or object");
1133+
throw JsonInvokeError(ErrorCategory::InvalidRequest, "invalid_request", "field 'args' must be an array or object");
11281134
}
11291135

11301136
std::vector<std::any> packPositionalArgs(const FunctionInfo& info, const json& args) const
11311137
{
11321138
if (args.size() > info.arg_types.size())
11331139
{
11341140
throw JsonInvokeError(
1141+
ErrorCategory::InvalidRequest,
11351142
"invalid_request",
11361143
"argument count mismatch: expected " + std::to_string(info.arg_types.size()) +
11371144
", got " + std::to_string(args.size()));
@@ -1142,6 +1149,7 @@ class BasicJsonInvokeAdapter {
11421149
if (isArgumentRequired(info, index))
11431150
{
11441151
throw JsonInvokeError(
1152+
ErrorCategory::InvalidRequest,
11451153
"invalid_request",
11461154
"missing JSON argument for parameter '" + argumentLabel(info, index) + "'");
11471155
}
@@ -1166,7 +1174,7 @@ class BasicJsonInvokeAdapter {
11661174
{
11671175
if (!args.is_object())
11681176
{
1169-
throw JsonInvokeError("invalid_request", "named JSON arguments must be a JSON object");
1177+
throw JsonInvokeError(ErrorCategory::InvalidRequest, "invalid_request", "named JSON arguments must be a JSON object");
11701178
}
11711179

11721180
for (auto it = args.begin(); it != args.end(); ++it)
@@ -1183,7 +1191,7 @@ class BasicJsonInvokeAdapter {
11831191

11841192
if (!known)
11851193
{
1186-
throw JsonInvokeError("invalid_request", "unexpected JSON argument '" + it.key() + "'");
1194+
throw JsonInvokeError(ErrorCategory::InvalidRequest, "invalid_request", "unexpected JSON argument '" + it.key() + "'");
11871195
}
11881196
}
11891197

@@ -1199,6 +1207,7 @@ class BasicJsonInvokeAdapter {
11991207
if (isArgumentRequired(info, index))
12001208
{
12011209
throw JsonInvokeError(
1210+
ErrorCategory::InvalidRequest,
12021211
"invalid_request",
12031212
"missing JSON argument for parameter '" + parameter_name + "'");
12041213
}
@@ -1225,7 +1234,7 @@ class BasicJsonInvokeAdapter {
12251234
}
12261235
catch (const JsonInvokeError& e)
12271236
{
1228-
throw JsonInvokeError(e.code(), "argument " + std::to_string(index) + " ('" + label + "'): " + e.what());
1237+
throw JsonInvokeError(e.category(), e.code(), "argument " + std::to_string(index) + " ('" + label + "'): " + e.what());
12291238
}
12301239
}
12311240

include/json_session_invoke/json_session_invoke.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
namespace json_session_invoke {
1919

2020
using json_invoke::JsonInvokeError;
21+
using json_invoke::ErrorCategory;
2122
using json_invoke::JsonInvokeResult;
2223
using json_invoke::json;
2324

include/json_session_invoke/session_objects.hpp

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ class BasicStatefulObjectStore {
317317
{
318318
if (object_id.empty())
319319
{
320-
throw json_invoke::JsonInvokeError("invalid_object", "object handle is missing 'object_id'");
320+
throw json_invoke::JsonInvokeError(json_invoke::ErrorCategory::InvalidRequest, "invalid_object", "object handle is missing 'object_id'");
321321
}
322322
}
323323

@@ -437,7 +437,7 @@ class BasicStatefulObjectStore {
437437
const auto it = objects_.find(object_id);
438438
if (it == objects_.end())
439439
{
440-
throw json_invoke::JsonInvokeError("invalid_object", "object not found: " + object_id);
440+
throw json_invoke::JsonInvokeError(json_invoke::ErrorCategory::InvalidRequest, "invalid_object", "object not found: " + object_id);
441441
}
442442

443443
return it->second;
@@ -452,13 +452,15 @@ class BasicStatefulObjectStore {
452452
if (stored_object.cpp_type != expected_cpp_type)
453453
{
454454
throw json_invoke::JsonInvokeError(
455+
json_invoke::ErrorCategory::InvalidRequest,
455456
"object_type_mismatch",
456457
"object '" + handle.object_id + "' is not compatible with the requested C++ type");
457458
}
458459

459460
if (!expected_object_type_name.empty() && stored_object.object_type_name != expected_object_type_name)
460461
{
461462
throw json_invoke::JsonInvokeError(
463+
json_invoke::ErrorCategory::InvalidRequest,
462464
"object_type_mismatch",
463465
"object '" + handle.object_id + "' has type '" + stored_object.object_type_name + "', expected '" +
464466
std::string(expected_object_type_name) + "'");
@@ -467,6 +469,7 @@ class BasicStatefulObjectStore {
467469
if (!handle.object_type.empty() && stored_object.object_type_name != handle.object_type)
468470
{
469471
throw json_invoke::JsonInvokeError(
472+
json_invoke::ErrorCategory::InvalidRequest,
470473
"object_type_mismatch",
471474
"object '" + handle.object_id + "' has type '" + stored_object.object_type_name + "', not '" +
472475
handle.object_type + "'");
@@ -496,36 +499,36 @@ struct json_traits<json_session_invoke::ObjectHandle> {
496499
const std::string object_id = value.get<std::string>();
497500
if (object_id.empty())
498501
{
499-
throw JsonInvokeError("conversion_failed", "object handle string must not be empty");
502+
throw JsonInvokeError(json_invoke::ErrorCategory::ConversionFailed, "conversion_failed", "object handle string must not be empty");
500503
}
501504

502505
return json_session_invoke::ObjectHandle{object_id, {}};
503506
}
504507

505508
if (!value.is_object())
506509
{
507-
throw JsonInvokeError("conversion_failed", "object handle expects a string or object value");
510+
throw JsonInvokeError(json_invoke::ErrorCategory::ConversionFailed, "conversion_failed", "object handle expects a string or object value");
508511
}
509512

510513
const auto object_id_it = value.find("object_id");
511514
if (object_id_it == value.end() || !object_id_it->is_string())
512515
{
513-
throw JsonInvokeError("conversion_failed", "object handle requires a string 'object_id'");
516+
throw JsonInvokeError(json_invoke::ErrorCategory::ConversionFailed, "conversion_failed", "object handle requires a string 'object_id'");
514517
}
515518

516519
json_session_invoke::ObjectHandle handle;
517520
handle.object_id = object_id_it->get<std::string>();
518521
if (handle.object_id.empty())
519522
{
520-
throw JsonInvokeError("conversion_failed", "object handle 'object_id' must not be empty");
523+
throw JsonInvokeError(json_invoke::ErrorCategory::ConversionFailed, "conversion_failed", "object handle 'object_id' must not be empty");
521524
}
522525

523526
const auto object_type_it = value.find("object_type");
524527
if (object_type_it != value.end() && !object_type_it->is_null())
525528
{
526529
if (!object_type_it->is_string())
527530
{
528-
throw JsonInvokeError("conversion_failed", "object handle 'object_type' must be a string when provided");
531+
throw JsonInvokeError(json_invoke::ErrorCategory::ConversionFailed, "conversion_failed", "object handle 'object_type' must be a string when provided");
529532
}
530533

531534
handle.object_type = object_type_it->get<std::string>();

0 commit comments

Comments
 (0)