Skip to content

Commit 639d2e0

Browse files
Update demo json_invoke
V2.41
1 parent 437dbbd commit 639d2e0

8 files changed

Lines changed: 193 additions & 388 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ if(LLM_INVOKE_CPP_BUILD_EXAMPLES)
156156
endif()
157157

158158
add_executable(json_invoke_demo examples/json_invoke/json_invoke_demo.cpp)
159-
target_link_libraries(json_invoke_demo PRIVATE llm_invoke_cpp::json_invoke)
159+
target_link_libraries(json_invoke_demo PRIVATE llm_invoke_cpp::json_invoke examples_fs_tools)
160160

161161
add_executable(json_stateful_demo examples/json_stateful/json_stateful_demo.cpp)
162162
target_link_libraries(json_stateful_demo PRIVATE llm_invoke_cpp::json_session_invoke)

SCHEMA_TRAITS.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -446,8 +446,8 @@ In this repository, keep `schema_traits<T>` next to related type support code.
446446

447447
Examples:
448448

449-
1. `examples/json_invoke/person_support.hpp` holds both `json_traits<Person>` and `schema_traits<Person>`.
450-
2. Enum mappings live in `examples/json_invoke/priority_support.hpp`.
449+
1. `tests/json_invoke_tests.cpp` co-locates `json_traits<Person>` and `schema_traits<Person>` for snapshot coverage.
450+
2. The same file co-locates enum mappings via `enum_traits<Priority>`.
451451

452452
That organization is easier for both humans and LLMs to follow than scattering traits across many unrelated files.
453453

examples/json_invoke/README.md

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
# JSON Invoke Demo
22

3-
This example demonstrates the `json_invoke` library: a JSON-based function invocation system that bridges C++ functions with LLM-style tool calling.
3+
This example demonstrates the `json_invoke` library: register native C++ functions once, then invoke them through JSON payloads (including LLM-style tool call format).
44

5-
Functions are registered and invoked via JSON payloads, supporting:
6-
- Standard JSON request format
7-
- LLM-compatible tool call format (matching Claude, ChatGPT conventions)
8-
- Complex types (structs, arrays, nested objects)
9-
- Custom type serialization
5+
The scenario is the same filesystem workflow used across the examples: write a file, read it back, inspect metadata, list a directory, and search text.
106

117
## Build
128

@@ -29,8 +25,9 @@ Linux/macOS:
2925

3026
## Features Demonstrated
3127

32-
- Invoke functions by JSON request with typed parameters
33-
- Support for LLM tool-call request format with JSON-encoded arguments
34-
- Serialize and deserialize complex C++ types to/from JSON
35-
- Invoke functions and get results back as C++ types or JSON
36-
- Process nested objects and arrays in function arguments
28+
- Register free functions as JSON-callable tools
29+
- Invoke with standard JSON request format (`name` + `args`)
30+
- Invoke with LLM-compatible payload (`type=function`, JSON string `arguments`)
31+
- Read simple results as typed C++ values (`std::string`)
32+
- Read the same tool results as generic JSON
33+
- Export all tool schemas for host-side inspection

examples/json_invoke/json_invoke_demo.cpp

Lines changed: 59 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -1,195 +1,89 @@
11
#include <iostream>
2-
#include <optional>
32
#include <string>
4-
#include "person_support.hpp"
5-
#include "priority_support.hpp"
3+
4+
#include <filesystem_tools.hpp>
65
#include <json_invoke/json_invoke.hpp>
76

7+
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(NumberedLine, line_number, content);
8+
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(FileEntry, name, type, size_bytes, last_modified);
9+
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SearchMatch, path, line_number, line);
10+
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(FileInfo, path, type, size_bytes, last_modified, exists);
11+
812
int main()
913
{
1014
json_invoke::JsonInvokeAdapterThreadSafe adapter;
1115

12-
adapter.registerFunction("add", json_invoke::readOnly([](int x, int y) { return x + y; }), "Add two integers.");
13-
adapter.registerFunction("getPerson", json_invoke::readOnly(getPerson), "Construct one person.");
14-
adapter.registerFunction(
15-
"describePerson",
16-
json_invoke::readOnly(&Person::describe),
17-
json_invoke::FunctionMetadata{{"person"}, "Call Person::describe() for one person."});
1816
adapter.registerFunction(
19-
"countPeopleAtLeastAge",
20-
json_invoke::readOnly(countPeopleAtLeastAge),
21-
json_invoke::FunctionMetadata{{"people", "minimum_age"}, "Count how many people in a JSON roster meet a minimum age."});
17+
"write_file",
18+
json_invoke::mutating(writeFile),
19+
json_invoke::FunctionMetadata{{"path", "content", "overwrite"}, "Write text content to a file."});
2220
adapter.registerFunction(
23-
"countPeopleAtLeastAgeByTeam",
24-
json_invoke::readOnly(countPeopleAtLeastAgeByTeam),
25-
json_invoke::FunctionMetadata{
26-
{"team_rosters", "minimum_age"},
27-
"Count how many people in each team roster meet a minimum age."});
21+
"read_file",
22+
json_invoke::readOnly(readFile),
23+
json_invoke::FunctionMetadata{{"path"}, "Read the full text content of a file."});
2824
adapter.registerFunction(
29-
"oldestPersonByQueue",
30-
json_invoke::readOnly(oldestPersonByQueue),
31-
json_invoke::FunctionMetadata{
32-
{"queue_rosters"},
33-
"Find the oldest person in each support queue."});
25+
"get_file_info",
26+
json_invoke::readOnly(getFileInfo),
27+
json_invoke::FunctionMetadata{{"path"}, "Return metadata for one path."});
3428
adapter.registerFunction(
35-
"displayNickname",
36-
json_invoke::readOnly([](std::optional<std::string> nickname) {
37-
return nickname.value_or("anonymous");
38-
}),
39-
json_invoke::FunctionMetadata{{"nickname"}, "Return the nickname or a default when omitted."});
29+
"list_directory",
30+
json_invoke::readOnly(listDirectory),
31+
json_invoke::FunctionMetadata{{"path"}, "List immediate directory entries."});
4032
adapter.registerFunction(
41-
"recommendIncidentPriority",
42-
json_invoke::readOnly(recommendIncidentPriority),
43-
json_invoke::FunctionMetadata{
44-
{"requested_priority", "customer_blocked", "production_impact", "affected_users"},
45-
"Recommend an incident priority from request urgency and customer impact."});
46-
47-
//#1
48-
std::cout << "--- Invoke add as int ---" << std::endl;
49-
int sum = adapter.invoke({
50-
{"name", "add"},
51-
{"args", {8, 13}},
52-
});
53-
std::cout << sum << std::endl;
33+
"search_files",
34+
json_invoke::readOnly(searchFiles),
35+
json_invoke::FunctionMetadata{{"directory", "pattern", "recursive"}, "Search text files for a literal pattern."});
5436

55-
//#2
56-
std::cout << "\n--- Invoke LLM-style payload as int ---" << std::endl;
57-
json_invoke::json llm_tool_call_request = {
58-
{"type", "function"},
59-
{"function", {
60-
{"name", "add"},
61-
{"arguments", R"({"arg0":21,"arg1":9})"},
37+
std::cout << "--- 1) write_file (JSON request -> typed return) ---" << std::endl;
38+
std::string write_result = adapter.invoke({
39+
{"name", "write_file"},
40+
{"args", {
41+
{"path", "build/_json_invoke_scratch.txt"},
42+
{"content", "alpha\nbeta\ngamma\n"},
43+
{"overwrite", true},
6244
}},
63-
};
64-
int llm_sum = adapter.invoke(llm_tool_call_request);
65-
std::cout << llm_sum << std::endl;
66-
67-
//#3
68-
std::cout << "\n--- Invoke getPerson get Person object ---" << std::endl;
69-
Person person = adapter.invoke({
70-
{"name", "getPerson"},
7145
});
72-
std::cout << person.describe() << std::endl;
46+
std::cout << write_result << std::endl;
7347

74-
//#4
75-
std::cout << "\n--- Invoke describePerson get std::string ---" << std::endl;
76-
std::string description = adapter.invoke({
77-
{"name", "describePerson"},
78-
{"args", {person}},
48+
std::cout << "\n--- 2) read_file (JSON request -> typed string) ---" << std::endl;
49+
std::string content = adapter.invoke({
50+
{"name", "read_file"},
51+
{"args", {{"path", "build/_json_invoke_scratch.txt"}}},
7952
});
80-
std::cout << description << std::endl;
53+
std::cout << content;
8154

82-
//#5
83-
std::cout << "\n--- Invoke getPerson get Person as Json ---" << std::endl;
84-
json_invoke::json personJson = adapter.invoke({
85-
{"name", "getPerson"},
55+
std::cout << "\n--- 3) get_file_info (as JSON) ---" << std::endl;
56+
json_invoke::json info_json = adapter.invoke({
57+
{"name", "get_file_info"},
58+
{"args", {{"path", "build/_json_invoke_scratch.txt"}}},
8659
});
87-
std::cout << personJson.dump(2) << std::endl;
88-
89-
//#6
90-
std::cout << "\n--- Invoke describePerson using json object ---" << std::endl;
91-
json_invoke::json descriptionRequest = {
92-
{"name", "describePerson"},
93-
{"args", {personJson}},
94-
};
95-
json_invoke::json descriptionJson = adapter.invoke(descriptionRequest);
96-
std::cout << descriptionJson.dump(2) << std::endl;
97-
98-
std::cout << "\n--- Count people from array of JSON objects ---" << std::endl;
99-
json_invoke::json rosterRequest = {
100-
{"name", "countPeopleAtLeastAge"},
101-
{"args", {
102-
{"people", json_invoke::json::array({
103-
personJson,
104-
json_invoke::json{{"name", "Bob"}, {"age", 17}},
105-
json_invoke::json{{"name", "Cara"}, {"age", 41}},
106-
})},
107-
{"minimum_age", 21},
108-
}},
109-
};
110-
int adultCount = adapter.invoke(rosterRequest);
111-
std::cout << adultCount << std::endl;
60+
std::cout << info_json.dump(2) << std::endl;
11261

113-
std::cout << "\n--- Count people by team from dictionary of rosters ---" << std::endl;
114-
json_invoke::json teamRosterRequest = {
115-
{"name", "countPeopleAtLeastAgeByTeam"},
116-
{"args", {
117-
{"team_rosters", {
118-
{"support", json_invoke::json::array({
119-
personJson,
120-
json_invoke::json{{"name", "Bob"}, {"age", 17}},
121-
})},
122-
{"platform", json_invoke::json::array({
123-
json_invoke::json{{"name", "Cara"}, {"age", 41}},
124-
json_invoke::json{{"name", "Dylan"}, {"age", 29}},
125-
json_invoke::json{{"name", "Eva"}, {"age", 18}},
126-
})},
127-
}},
128-
{"minimum_age", 21},
62+
std::cout << "\n--- 4) list_directory (LLM-style payload) ---" << std::endl;
63+
json_invoke::json llm_tool_call_request = {
64+
{"type", "function"},
65+
{"function", {
66+
{"name", "list_directory"},
67+
{"arguments", R"({"path":"build"})"},
12968
}},
13069
};
131-
json_invoke::json teamCounts = adapter.invoke(teamRosterRequest);
132-
std::cout << teamCounts.dump(2) << std::endl;
70+
json_invoke::json entries_json = adapter.invoke(llm_tool_call_request);
71+
std::cout << entries_json.dump(2) << std::endl;
13372

134-
std::cout << "\n--- Find oldest person by queue from unordered dictionary ---" << std::endl;
135-
json_invoke::json queueRequest = {
136-
{"name", "oldestPersonByQueue"},
73+
std::cout << "\n--- 5) search_files (as JSON) ---" << std::endl;
74+
json_invoke::json matches_json = adapter.invoke({
75+
{"name", "search_files"},
13776
{"args", {
138-
{"queue_rosters", {
139-
{"billing", json_invoke::json::array({
140-
json_invoke::json{{"name", "Mia"}, {"age", 26}},
141-
json_invoke::json{{"name", "Noah"}, {"age", 31}},
142-
})},
143-
{"vip", json_invoke::json::array({
144-
personJson,
145-
json_invoke::json{{"name", "Olivia"}, {"age", 45}},
146-
})},
147-
}},
77+
{"directory", "build"},
78+
{"pattern", "beta"},
79+
{"recursive", false},
14880
}},
149-
};
150-
json_invoke::json oldestByQueue = adapter.invoke(queueRequest);
151-
std::cout << oldestByQueue.dump(2) << std::endl;
152-
153-
std::cout << "\n--- Invoke displayNickname without optional arg ---" << std::endl;
154-
std::string nickname = adapter.invoke({
155-
{"name", "displayNickname"},
156-
{"args", json_invoke::json::object()},
157-
});
158-
std::cout << nickname << std::endl;
159-
160-
std::cout << "\n--- Invoke displayNickname with optional arg ---" << std::endl;
161-
std::string nicknameWithArg = adapter.invoke({
162-
{"name", "displayNickname"},
163-
{"args", {"tom"}},
16481
});
165-
std::cout << nicknameWithArg << std::endl;
166-
167-
std::cout << "\n--- Recommend incident priority from named JSON args ---" << std::endl;
168-
json_invoke::json recommendedPriorityJson = adapter.invoke({
169-
{"name", "recommendIncidentPriority"},
170-
{"args", {
171-
{"requested_priority", "low"},
172-
{"customer_blocked", true},
173-
{"production_impact", false},
174-
{"affected_users", 120},
175-
}},
176-
});
177-
std::cout << recommendedPriorityJson.dump(2) << std::endl;
178-
179-
std::cout << "\n--- Recommend incident priority as typed enum ---" << std::endl;
180-
Priority recommendedPriority = adapter.invoke({
181-
{"name", "recommendIncidentPriority"},
182-
{"args", {
183-
{"requested_priority", "normal"},
184-
{"customer_blocked", true},
185-
{"production_impact", true},
186-
{"affected_users", 320},
187-
}},
188-
});
189-
std::cout << func_registry::enum_name(recommendedPriority) << std::endl;
190-
191-
//std::cout << "\n--- Tool summaries JSON ---" << std::endl;
192-
//std::cout << adapter.getAllToolSummariesJson().dump(2) << std::endl;
82+
std::cout << "matches: " << matches_json.size() << std::endl;
83+
if (!matches_json.empty())
84+
{
85+
std::cout << "first match: " << matches_json.front().dump() << std::endl;
86+
}
19387

19488
std::cout << "\n--- Tool schemas JSON ---" << std::endl;
19589
std::cout << adapter.getAllToolSchemasJson().dump(2) << std::endl;

examples/json_invoke/person.hpp

Lines changed: 0 additions & 31 deletions
This file was deleted.

0 commit comments

Comments
 (0)