Skip to content
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
6836b3c
Add ToolCallConfig and header-inspection mode for Harmony tool calling
Aug 10, 2026
2cdc513
Add ToolCallConfig, header-inspection mode, and Harmony unit tests
Aug 10, 2026
7566cd3
Fix GCC -Werror=missing-field-initializers in ToolCallConfig::Simple()
Aug 11, 2026
31e7937
Merge main into harmony, resolve chat_session.cc conflicts
Aug 19, 2026
0d0da52
Share byte-preserving tool batch admission and structural JSON grammar
baijumeswani Nov 5, 2026
aa76ad3
Merge main into shared tool admission branch
baijumeswani Nov 7, 2026
bec08f1
Add qualified native Harmony chat and tool calling
baijumeswani Nov 7, 2026
8e31665
Preserve the original Harmony PR history
baijumeswani Nov 7, 2026
bb6be93
Preserve tool body encoding through Responses replay and recovery
baijumeswani Nov 7, 2026
21c6f0e
Keep caller-supplied replay calls on the default encoding
baijumeswani Nov 7, 2026
e81aca6
Support legal Harmony tool history
sayanshaw24 Nov 7, 2026
4b07671
Merge main into Harmony support
sayanshaw24 Nov 8, 2026
f9d2b43
Cover Harmony terminal and replay contracts
sayanshaw24 Nov 8, 2026
b25ac22
Cover direct tool batch admission and object grammar helpers
sayanshaw24 Nov 9, 2026
00a582f
Merge latest main into shared tool admission helpers
sayanshaw24 Nov 9, 2026
9770e63
Merge shared tool admission tests and latest main
sayanshaw24 Nov 9, 2026
c714171
Reject Harmony instruction roles that would be silently omitted
sayanshaw24 Nov 9, 2026
74c5e7f
Reject Harmony retry calls that fail custom input normalization
sayanshaw24 Nov 9, 2026
67a8db7
Validate Harmony recipients before template rendering
sayanshaw24 Nov 9, 2026
29d8b3e
Preserve stored-input body provenance and cover protocol grammars
sayanshaw24 Nov 9, 2026
63f98e0
Distinguish recorded call encodings from unannotated caller history
sayanshaw24 Nov 9, 2026
cb4a9f4
Merge latest main and fix Harmony Windows shadowing diagnostics
sayanshaw24 Nov 10, 2026
a90f5e7
Merge main into native Harmony support
sayanshaw24 Nov 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/instructions/cpp-tool-calling.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@ applyTo: "sdk_v2/cpp/src/inferencing/generative/{chat,toolcalling}/**"
Permit JSON whitespace at structural boundaries explicitly; never use global whitespace ignoring inside strings.
- Reasoning and executable tool boundaries use published token IDs. Added tokens may have `special:false`; quoted
marker spellings are data. Preserve payload ownership once a canonical or explicit raw envelope has opened.
- Automatic structured output must explicitly choose `MESSAGE\n` plus ordinary text or an executable tool boundary.
- Canonical automatic structured output must explicitly choose `MESSAGE\n` plus ordinary text or an executable tool boundary.
Strip text framing incrementally and project it into assistant history. Use whole-turn guidance for automatic
turns, including nonreasoning Engine models; region-only masks cannot enforce this initial mode selection.
- Qualified native Harmony uses assistant channel/recipient headers instead of canonical text framing or arrays.
Decode its structure by token identity, seed the prepared assistant-header continuation explicitly, and authorize
a call only at the actual sampled handoff. Reuse the same original-schema plan and publication/recovery sinks.
Terminal-token completion metadata is not a retained token and must never be added to the Engine sequence ledger.
- Encode transcript/schema/argument data with the independent literal tokenizer. Only template-owned structure,
runtime-owned protocol boundaries, and actual historical call boundaries use ordinary encoding. Encoding
protocol boundary exemplars literally teaches the model to echo ordinary text instead of invoking tools.
Expand Down
13 changes: 12 additions & 1 deletion sdk_v2/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,15 @@ check_cxx_source_compiles(
}"
FOUNDRY_LOCAL_HAS_REQUIRED_NON_GENERATIVE_ORT_GENAI
)
unset(FOUNDRY_LOCAL_OGA_HAS_TERMINAL_TOKEN CACHE)
check_cxx_source_compiles(
"#include <ort_genai_c.h>
int main() {
int32_t token = -1;
return OgaEngineEventGetTerminalTokenId(nullptr, &token) != nullptr;
}"
FOUNDRY_LOCAL_OGA_HAS_TERMINAL_TOKEN
)
unset(CMAKE_REQUIRED_INCLUDES)
unset(CMAKE_REQUIRED_LIBRARIES)
if(NOT FOUNDRY_LOCAL_HAS_REQUIRED_NON_GENERATIVE_ORT_GENAI)
Expand Down Expand Up @@ -262,6 +271,7 @@ set(FOUNDRY_LOCAL_SOURCES
src/inferencing/session/session_manager.cc
src/inferencing/session/tool_registry.cc
src/inferencing/generative/chat/chat_session.cc
src/inferencing/generative/chat/harmony_decoder.cc
src/inferencing/generative/chat/chat_template.cc
src/inferencing/generative/chat/chat_transcript.cc
src/inferencing/generative/chat/media_input.cc
Expand Down Expand Up @@ -394,7 +404,8 @@ list(APPEND FOUNDRY_LOCAL_SOURCES ${FOUNDRY_LOCAL_PUBLIC_HEADERS})
function(foundry_local_configure_target TARGET LINK_SCOPE)
target_compile_options(${TARGET} PRIVATE ${FOUNDRY_LOCAL_COMPILE_OPTIONS})
target_compile_definitions(${TARGET} ${LINK_SCOPE}
FOUNDRY_LOCAL_HAS_SHARED_HOST=$<BOOL:${FOUNDRY_LOCAL_HAS_SHARED_HOST}>)
FOUNDRY_LOCAL_HAS_SHARED_HOST=$<BOOL:${FOUNDRY_LOCAL_HAS_SHARED_HOST}>
FOUNDRY_LOCAL_OGA_HAS_TERMINAL_TOKEN=$<BOOL:${FOUNDRY_LOCAL_OGA_HAS_TERMINAL_TOKEN}>)

target_include_directories(${TARGET}
PUBLIC
Expand Down
39 changes: 38 additions & 1 deletion sdk_v2/cpp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ The unchanged original parameter schema is validated with jsoncons before any
call in the batch is published. Structural guidance alone does not guarantee
schema compliance or that the model chose the user's intended argument value.

Automatic tool turns select their response mode explicitly: ordinary assistant
Canonical automatic tool turns select their response mode explicitly: ordinary assistant
text starts with an internal `MESSAGE` line, while executable calls start with
the model's dedicated tool boundary. The runtime removes the text framing line
incrementally; callers still receive normal content and tool-call events.
Expand Down Expand Up @@ -397,6 +397,43 @@ Recovery also shares implicit classic Generator output limits; it cannot acquire
fresh default allowance or use the retained conversation's full context as an output
budget. Engine text turns remain uncapped when the caller leaves their limit unset.

### Native Harmony

GPT-OSS packages use their qualified native template and assistant channel/recipient
headers, not canonical `MESSAGE` framing or tool-call arrays. Model loading checks
the tokenizer's distinct control/literal encodings, EOS configuration, protected
argument/result slots, reasoning/commentary projection, and generation prefix.
The model must use the Engine backend. An incompatible template, tokenizer, or
runtime is rejected rather than falling back to string-based parsing.

Native support requires matching GenAI headers and libraries exposing
`OgaEngineEventGetTerminalTokenId`, plus the matching CUDA add-on when applicable.
The CMake capability probe checks both compilation and linking. Existing pinned
packages without this API still build for canonical models but cannot load a
qualified GPT-OSS package. Sampled handoff and answer completion are metadata,
not extra retained sequence tokens.

Each generated turn supports analysis, optional visible commentary, then a final
answer or one directed tool call. Calls are validated before publication; JSON
argument bytes are preserved. Custom tools with the admitted stock patch grammar
use native raw bodies; unframed text custom tools retain JSON input-object transport.
Both reuse the synthesized `{"input":"raw text"}` object for schema admission and
deliver unchanged text to callers. Recorded native body encodings survive tool
definition changes during history replay. Tool results are correlated by call ID
and rendered as named, protected JSON values. Source text and literal marker spellings remain data.
Original schemas and custom input formats are supplied as bounded literal prompt
information; schema admission remains owned by the existing `ToolCallPlan`.

GPT-OSS generation is sequential even when the client permits parallel calls.
Native reasoning effort supports `low`, `medium`, and `high`; model-owned reasoning
cannot be disabled with `enable_thinking` or altered with `preserve_thinking`.
Top-level API effort overrides the generic thinking-switch mapping for this
protocol. Explicit whole-response guidance is incompatible with native Harmony.
Rejected selected calls use the shared one-retry policy with a whole-message
checkpoint and a legal, literal developer correction.

### Local coding qualification

Qualification requires both protocol fidelity and the **real Copilot CLI** coding
workflow, not just an offline harness self-test. With the server already running:

Expand Down
3 changes: 3 additions & 0 deletions sdk_v2/cpp/src/contracts/chat_completions_converter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,9 @@ void MapRequestParameters(const ChatCompletionRequest& req, Request& session_req
session_request.options["chat_template_kwargs"] =
ResolveReasoningTemplateKwargs(req.chat_template_kwargs, req.reasoning_effort).dump();
}
if (req.reasoning_effort.has_value()) {
session_request.options["reasoning_effort"] = *req.reasoning_effort;
}

// Extract metadata extensions (matching C# GetTopK/GetRandomSeed)
if (req.metadata.has_value()) {
Expand Down
2 changes: 1 addition & 1 deletion sdk_v2/cpp/src/hosting/item_codec.cc
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ std::unique_ptr<Item> Decode(const Json& value, const ItemDecodingContext& conte
? std::nullopt
: std::optional(ReadEnum<ToolKind>(declared_kind, 0, static_cast<int>(ToolKind::kCustom)));
call->generated_encoding = ReadEnum<GeneratedCallEncoding>(value.at("encoding"), 0,
static_cast<int>(GeneratedCallEncoding::kRawEnvelope));
static_cast<int>(GeneratedCallEncoding::kNativeJson));
call->replayed_from_store = value.at("replayed").get<bool>();
call->replayed_arguments = value.at("replayed_arguments").get<std::string>();
const auto& replayed_kind = value.at("replayed_kind");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ struct ChatTurnUsage {
std::optional<flFinishReason> finish_reason;
std::optional<BackendTerminationCause> termination_cause;
int cached_prompt_tokens = 0;
std::optional<int32_t> terminal_token_id = std::nullopt;
};

class RetainedPromptMismatchError : public std::runtime_error {
Expand Down
Loading
Loading