Skip to content

Add reasoning controls and preserve reasoning across chat turns - #1009

Open
jennyf19 wants to merge 13 commits into
microsoft:mainfrom
jennyf19:feature/chat-template-kwargs
Open

jennyf19 wants to merge 13 commits into
microsoft:mainfrom
jennyf19:feature/chat-template-kwargs

Conversation

@jennyf19

@jennyf19 jennyf19 commented Aug 18, 2026

Copy link
Copy Markdown

Add reasoning controls and preserve reasoning across chat turns

Summary

Adds typed chat_template_kwargs to Chat Completions and preserves prior assistant reasoning for qualified model templates, including Qwen 3.8.

This combines two prompt-identity requirements:

  1. Template controls such as enable_thinking, preserve_thinking, and reasoning_effort must be applied consistently to every render and retained-state decision.
  2. Templates that explicitly support historical reasoning_content must receive the reasoning already stored in Foundry Local's typed conversation transcript.

Changes

Chat template kwargs

  • Parses typed chat_template_kwargs values without flattening booleans, numbers, arrays, or nested objects.
  • Supports session defaults, request overrides, JSON payload precedence, and explicit {} clearing.
  • Applies or clears kwargs under the shared tokenizer render lock.
  • Threads effective kwargs through Generator, Engine, media, and continued-turn prompt rendering.
  • Treats effective kwargs as retained-generator identity and rebuilds when they change.
  • Supports stable GenAI packages and explicit capability selection for local GenAI builds.

Preserved reasoning

  • Retains incoming Chat Completions reasoning_content as reasoning-typed assistant transcript content.
  • Preserves reasoning across stored and stateless Responses replay.
  • Adds a behavioral template capability probe instead of branching on a model name alone.
  • Qualifies preservation only when the loaded template:
    • renders distinct historical reasoning by default;
    • keeps visible assistant content distinct; and
    • omits historical reasoning when preserve_thinking=false.
  • Emits assistant reasoning_content into canonical message JSON only for qualified templates.
  • Lets the model template own <think> markers and preserve_thinking behavior.
  • Uses the same capability, transcript, and effective kwargs for Generator and Engine prompt paths.
  • Keeps the existing conservative omission behavior for unqualified templates.

Why

Qwen 3.8 templates read prior assistant reasoning from message.reasoning_content. Foundry Local already classifies and stores generated reasoning separately, but previously omitted it when rebuilding canonical message JSON. Stateless Chat Completions also parsed incoming reasoning_content without retaining the bytes.

As a result, reconstructed prompts could contain empty historical thinking blocks and diverge from the conversation the model actually generated. This change makes the canonical prompt represent the typed transcript for templates that explicitly support preserved reasoning, while exact token-prefix comparison remains the final authority for retained-state reuse.

Compatibility

  • No reasoning is inserted into visible content.
  • Unqualified templates do not receive reasoning_content.
  • Existing tool-call, raw-envelope, positional-result, rollback, cancellation, and exact-prefix behavior remains in place.
  • Qwen 3.8 support uses the existing GenAI qwen3_5_text model type and is qualified by rendered template behavior.

Validation

  • Added focused tests for Chat Completions reasoning ingestion and ordering.
  • Added policy-driven message-projection tests.
  • Added positive and fail-closed preserved-reasoning capability-probe tests.
  • Updated warm/cold Responses replay tests for typed reasoning retention.
  • Updated stateless Responses reasoning replay tests.
  • Standalone 18-case chat-template kwargs CMake capability matrix passes.
  • git diff --check passes.
  • Editor diagnostics report no errors in changed source and test files.

@vercel

vercel Bot commented Aug 18, 2026

Copy link
Copy Markdown

Someone is attempting to deploy a commit to the MSFT-AIP Team on Vercel.

A member of the Team first needs to authorize it.

@jennyf19
jennyf19 force-pushed the feature/chat-template-kwargs branch from 883bebe to 1a9ac05 Compare August 18, 2026 16:06
@jennyf19
jennyf19 force-pushed the feature/chat-template-kwargs branch from b4e472b to b0b2932 Compare September 8, 2026 19:26
@jennyf19
jennyf19 marked this pull request as ready for review September 8, 2026 20:29
Copilot AI balanced review requested due to automatic review settings September 8, 2026 20:29
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Stable and release builds remain pinned to a GenAI version that lacks the required tokenizer-option support, and rendering/cache behavior lacks coverage.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds typed chat-template kwargs to OpenAI-compatible chat completions and propagates them through prompt rendering.

Changes:

  • Preserves typed and nested JSON kwargs during conversion.
  • Applies kwargs across text, media, and continued-turn rendering.
  • Invalidates cached generators when kwargs change.
File summaries
File Description
.pipelines/foundry-local-packaging.yml Updates non-release GenAI dependency.
sdk_v2/cpp/src/contracts/chat_completions.h Adds the request field.
sdk_v2/cpp/src/contracts/chat_completions.cc Deserializes and validates kwargs.
sdk_v2/cpp/src/contracts/chat_completions_converter.cc Serializes kwargs into request options.
sdk_v2/cpp/src/inferencing/generative/preprocessor.h Declares option-aware template rendering.
sdk_v2/cpp/src/inferencing/generative/preprocessor.cc Configures tokenizer template kwargs.
sdk_v2/cpp/src/inferencing/generative/toolcalling/tool_call_context.h Carries serialized kwargs.
sdk_v2/cpp/src/inferencing/generative/chat/chat_template.h Extends prompt-building interface.
sdk_v2/cpp/src/inferencing/generative/chat/chat_template.cc Forwards kwargs during rendering.
sdk_v2/cpp/src/inferencing/generative/chat/onnx_chat_generator.h Extends continued-turn API.
sdk_v2/cpp/src/inferencing/generative/chat/onnx_chat_generator.cc Routes kwargs through text and media prompts.
sdk_v2/cpp/src/inferencing/generative/chat/chat_session.cc Tracks changes and invalidates cached generators.
sdk_v2/cpp/test/internal_api/chat_completions_test.cc Tests parsing and validation.
sdk_v2/cpp/test/internal_api/chat_completions_converter_test.cc Tests typed-value conversion.
Review details
  • Files reviewed: 14/14 changed files
  • Comments generated: 3
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread .pipelines/foundry-local-packaging.yml Outdated
Comment thread sdk_v2/cpp/src/inferencing/generative/chat/chat_session.cc Outdated
Comment thread sdk_v2/cpp/src/inferencing/generative/preprocessor.cc
@jennyf19

jennyf19 commented Sep 8, 2026

Copy link
Copy Markdown
Author

Baiju Meswani (@baijumeswani) The published GenAI package is now wired into the canonical/release pins, and all three Copilot review findings are addressed and resolved on 9613638c. Fork validation built the full C++ test target against 0.16.0-dev1001407373 and passed the 68 self-contained affected tests: https://github.com/jennyf19/foundry-local/actions/runs/34281760470

Could you please review and authorize the official pipeline with /azp run so the new Qwen2.5/Qwen3.5 model-backed tests execute against the repository test-model cache?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found one cache/rollback correctness issue that should be addressed before approval; details are inline.

Comment thread sdk_v2/cpp/src/inferencing/generative/chat/chat_session.cc Outdated
Comment thread sdk_v2/deps_versions.json Outdated
Jenny Ferries and others added 8 commits September 10, 2026 10:13
Preserve typed JSON values from the OpenAI-compatible request through text, media, and continued-turn prompt rendering.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: a9f5bae1-1b88-4311-8fac-6224769e0400
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e95773ce-13b6-4f15-b288-ed69843a385c
Pin release and SDK dependency manifests to the first GenAI package containing tokenizer option support. Add rendering coverage for typed kwargs and state clearing, plus multi-turn coverage for cache reuse and rebuilds when kwargs change or are removed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e95773ce-13b6-4f15-b288-ed69843a385c
Compare continuous decoding and kwargs-triggered rebuilds against fresh full-history session baselines instead of relying on absolute prompt-token deltas.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e95773ce-13b6-4f15-b288-ed69843a385c
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e95773ce-13b6-4f15-b288-ed69843a385c
Keep stable dependency pins, capability-gate the nightly feature, preserve kwargs through Engine prompts, and cover undo/cancellation replay.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Require the nightly pipeline to enable the feature and route Generator and Engine prompts through one tested ToolCallContext overload.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Treat an empty JSON object as omitted while preserving explicit rejection for non-empty kwargs on unsupported packages.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jennyf19
jennyf19 force-pushed the feature/chat-template-kwargs branch from 9613638 to 1fe396c Compare September 10, 2026 19:14
@baijumeswani

Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
1 pipeline(s) were filtered out due to trigger conditions.

bmehta001 and others added 5 commits September 14, 2026 14:51
Keep main's stable GenAI dependency instead of requiring a nightly feed. Adapt kwargs forwarding and cache invalidation to the authoritative transcript, preserve valid undo/cancellation boundaries, and exercise typed rendering with the tiny repository fixture.

Files changed:
- .github/instructions/cpp-ci-test-policy.instructions.md
- .pipelines/foundry-local-packaging.yml
- .pipelines/v2/sdk_v2-pipeline-plan.md
- .pipelines/v2/templates/stages-sdk-v2.yml
- .pipelines/v2/templates/stages-test-engine.yml
- .pipelines/v2/templates/steps-prefetch-nuget.yml
- .pipelines/v2/templates/steps-test-cs.yml
- memories/repo/cpp-custom-tool-payloads.md
- samples/cs/tool-calling-foundry-local-sdk/Program.cs
- sdk_v2/cpp/CMakeLists.txt
- sdk_v2/cpp/README.md
- sdk_v2/cpp/docs/CodingConventionsAndStandards.md
- sdk_v2/cpp/include/foundry_local/foundry_local_c.h
- sdk_v2/cpp/include/foundry_local/foundry_local_cpp.h
- sdk_v2/cpp/include/foundry_local/foundry_local_cpp.inline.h
- sdk_v2/cpp/src/c_api.cc
- sdk_v2/cpp/src/contracts/chat_completions.cc
- sdk_v2/cpp/src/contracts/chat_completions.h
- sdk_v2/cpp/src/contracts/chat_completions_converter.cc
- sdk_v2/cpp/src/contracts/responses.h
- sdk_v2/cpp/src/contracts/responses_json.cc
- sdk_v2/cpp/src/ep_detection/cuda_ep_manifest.cc
- sdk_v2/cpp/src/inferencing/generative/chat/chat_generator.h
- sdk_v2/cpp/src/inferencing/generative/chat/chat_session.cc
- sdk_v2/cpp/src/inferencing/generative/chat/chat_session.h
- sdk_v2/cpp/src/inferencing/generative/chat/chat_template.cc
- sdk_v2/cpp/src/inferencing/generative/chat/chat_template.h
- sdk_v2/cpp/src/inferencing/generative/chat/chat_transcript.cc
- sdk_v2/cpp/src/inferencing/generative/chat/chat_transcript.h
- sdk_v2/cpp/src/inferencing/generative/chat/media_input.cc
- sdk_v2/cpp/src/inferencing/generative/chat/media_input.h
- sdk_v2/cpp/src/inferencing/generative/chat/onnx_chat_engine.cc
- sdk_v2/cpp/src/inferencing/generative/chat/onnx_chat_engine.h
- sdk_v2/cpp/src/inferencing/generative/chat/onnx_chat_generator.cc
- sdk_v2/cpp/src/inferencing/generative/chat/onnx_chat_generator.h
- sdk_v2/cpp/src/inferencing/generative/chat/onnx_engine_chat_stream.cc
- sdk_v2/cpp/src/inferencing/generative/chat/onnx_engine_chat_stream.h
- sdk_v2/cpp/src/inferencing/generative/chat/reasoning_stream_splitter.h
- sdk_v2/cpp/src/inferencing/generative/chat/search_options.cc
- sdk_v2/cpp/src/inferencing/generative/chat/search_options.h
- sdk_v2/cpp/src/inferencing/generative/chat/stop_strings.cc
- sdk_v2/cpp/src/inferencing/generative/chat/stop_strings.h
- sdk_v2/cpp/src/inferencing/generative/genai_model_instance.cc
- sdk_v2/cpp/src/inferencing/generative/genai_model_instance.h
- sdk_v2/cpp/src/inferencing/generative/openresponses/response_chain.h
- sdk_v2/cpp/src/inferencing/generative/openresponses/response_converter.cc
- sdk_v2/cpp/src/inferencing/generative/openresponses/response_converter.h
- sdk_v2/cpp/src/inferencing/generative/openresponses/response_store.cc
- sdk_v2/cpp/src/inferencing/generative/openresponses/response_store.h
- sdk_v2/cpp/src/inferencing/generative/toolcalling/grammar.cc
- sdk_v2/cpp/src/inferencing/generative/toolcalling/grammar.h
- sdk_v2/cpp/src/inferencing/generative/toolcalling/tool_call_context.h
- sdk_v2/cpp/src/inferencing/generative/toolcalling/tool_call_stream_accumulator.h
- sdk_v2/cpp/src/inferencing/generative/toolcalling/tool_call_utils.cc
- sdk_v2/cpp/src/inferencing/generative/toolcalling/tool_call_utils.h
- sdk_v2/cpp/src/inferencing/session/callback_handler.h
- sdk_v2/cpp/src/inferencing/session/request.h
- sdk_v2/cpp/src/inferencing/session/session.cc
- sdk_v2/cpp/src/inferencing/session/session.h
- sdk_v2/cpp/src/inferencing/session/session_manager.cc
- sdk_v2/cpp/src/inferencing/session/session_manager.h
- sdk_v2/cpp/src/inferencing/session/tool_registry.cc
- sdk_v2/cpp/src/inferencing/session/tool_registry.h
- sdk_v2/cpp/src/inferencing/session/types.h
- sdk_v2/cpp/src/items/tool_call_item.h
- sdk_v2/cpp/src/service/chat_completions_handler.cc
- sdk_v2/cpp/src/service/handler_utils.h
- sdk_v2/cpp/src/service/responses_handler.cc
- sdk_v2/cpp/src/service/responses_handler.h
- sdk_v2/cpp/src/service/web_service.cc
- sdk_v2/cpp/src/util/scope_guard.h
- sdk_v2/cpp/src/util/string_utils.h
- sdk_v2/cpp/test/CMakeLists.txt
- sdk_v2/cpp/test/internal_api/c_api_test.cc
- sdk_v2/cpp/test/internal_api/callback_handler_test.cc
- sdk_v2/cpp/test/internal_api/chat/chat_generator_test.cc
- sdk_v2/cpp/test/internal_api/chat/chat_session_test.cc
- sdk_v2/cpp/test/internal_api/chat/chat_template_test.cc
- sdk_v2/cpp/test/internal_api/chat/chat_transcript_test.cc
- sdk_v2/cpp/test/internal_api/chat/dynamic_engine_chat_test.cc
- sdk_v2/cpp/test/internal_api/chat/media_input_test.cc
- sdk_v2/cpp/test/internal_api/chat/reasoning_stream_splitter_test.cc
- sdk_v2/cpp/test/internal_api/chat/search_options_test.cc
- sdk_v2/cpp/test/internal_api/chat/stop_strings_test.cc
- sdk_v2/cpp/test/internal_api/chat_completions_converter_test.cc
- sdk_v2/cpp/test/internal_api/chat_completions_test.cc
- sdk_v2/cpp/test/internal_api/cuda_ep_bootstrapper_test.cc
- sdk_v2/cpp/test/internal_api/handler_error_mapping_test.cc
- sdk_v2/cpp/test/internal_api/replay_equivalence_test.cc
- sdk_v2/cpp/test/internal_api/response_converter_test.cc
- sdk_v2/cpp/test/internal_api/response_lifecycle_test.cc
- sdk_v2/cpp/test/internal_api/response_store_test.cc
- sdk_v2/cpp/test/internal_api/responses_json_test.cc
- sdk_v2/cpp/test/internal_api/scope_guard_test.cc
- sdk_v2/cpp/test/internal_api/session_manager_test.cc
- sdk_v2/cpp/test/internal_api/toolcalling/grammar_test.cc
- sdk_v2/cpp/test/internal_api/toolcalling/tool_call_stream_accumulator_test.cc
- sdk_v2/cpp/test/internal_api/toolcalling/tool_call_utils_test.cc
- sdk_v2/cpp/test/internal_api/toolcalling/tool_definition_abi_test.cc
- sdk_v2/cpp/test/internal_api/toolcalling/tool_registry_test.cc
- sdk_v2/cpp/test/sdk_api/chat_session_test.cc
- sdk_v2/cpp/test/sdk_api/cpp_api_test.cc
- sdk_v2/cpp/test/sdk_api/responses_test.cc
- sdk_v2/cpp/test/sdk_api/responses_vision_test.cc
- sdk_v2/cpp/test/testdata/tiny-paged-attention/README.md
- sdk_v2/cpp/test/testdata/tiny-paged-attention/decoder.onnx
- sdk_v2/cpp/test/testdata/tiny-paged-attention/genai_config.json
- sdk_v2/cpp/test/testdata/tiny-paged-attention/generate-model.py
- sdk_v2/cpp/test/testdata/tiny-paged-attention/requirements.txt
- sdk_v2/cpp/test/testdata/tiny-paged-attention/tokenizer.json
- sdk_v2/cpp/test/testdata/tiny-paged-attention/tokenizer_config.json
- sdk_v2/cs/src/ChatSession.cs
- sdk_v2/cs/src/Detail/FoundryLocalApi.cs
- sdk_v2/cs/src/Detail/NativeMethods.cs
- sdk_v2/cs/src/Items/ToolCallItem.cs
- sdk_v2/cs/src/Items/ToolResultItem.cs
- sdk_v2/cs/test/FoundryLocal.Tests/ChatCompletionsRequestSerializationTests.cs
- sdk_v2/cs/test/FoundryLocal.Tests/ChatCompletionsTests.cs
- sdk_v2/cs/test/FoundryLocal.Tests/ChatSessionTests.cs
- sdk_v2/cs/test/FoundryLocal.Tests/NativeRuntimeCompatibilityTests.cs
- sdk_v2/cs/test/FoundryLocal.Tests/ToolDefinitionAbiTests.cs
- sdk_v2/cs/test/FoundryLocal.Tests/ToolDefinitionValidationTests.cs
- sdk_v2/cs/test/FoundryLocal.Tests/ToolItemValidationTests.cs
- sdk_v2/deps_versions.json
- sdk_v2/js/native/src/items.cc
- sdk_v2/js/native/src/session.cc
- sdk_v2/js/package.json
- sdk_v2/js/src/detail/native.ts
- sdk_v2/js/src/index.ts
- sdk_v2/js/src/items.ts
- sdk_v2/js/src/request.ts
- sdk_v2/js/src/session.ts
- sdk_v2/js/test/chat-session.test.ts
- sdk_v2/js/test/install-native.test.ts
- sdk_v2/js/test/items.test.ts
- sdk_v2/js/test/tool-definition.types.ts
- sdk_v2/js/test/tool-definition.validation.test.ts
- sdk_v2/js/tsconfig.types.json
- sdk_v2/python/.gitignore
- sdk_v2/python/README.md
- sdk_v2/python/src/foundry_local_sdk/_native/api.py
- sdk_v2/python/src/foundry_local_sdk/_native/build_cffi.py
- sdk_v2/python/src/foundry_local_sdk/items.py
- sdk_v2/python/src/foundry_local_sdk/session.py
- sdk_v2/python/test/unit/test_items.py
- sdk_v2/python/test/unit/test_lib_loader.py
- sdk_v2/python/test/unit/test_tool_definition_abi.py
- sdk_v2/rust/deps_versions.json
- sdk_v2/rust/docs/api.md
- sdk_v2/rust/src/detail/api.rs
- sdk_v2/rust/src/detail/ffi.rs
- sdk_v2/rust/src/detail/session.rs
- sdk_v2/rust/src/item.rs
- sdk_v2/rust/src/lib.rs
- sdk_v2/rust/src/session.rs
- sdk_v2/rust/tests/integration/chat_client_test.rs
- sdk_v2/rust/tests/integration/session_test.rs
- sdk_v2/rust/tests/public_api.rs
- www/src/routes/models/+page.svelte
- www/src/routes/models/components/ModelFilters.svelte
- www/src/routes/models/service.ts
- www/src/routes/models/types.ts
- www/test-validation.js
- www/test/accessibility.test.mjs

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolve template controls from the same effective options as generation so session defaults, request overrides, and JSON payload precedence stay consistent. Let capable local GenAI builds opt in explicitly instead of inferring support from missing or stale package metadata. Keep stable dependency pins unchanged and cover defaults, cache invalidation, and configuration modes.

Files changed:
- sdk_v2/cpp/CMakeLists.txt
- sdk_v2/cpp/README.md
- sdk_v2/cpp/cmake/chat-template-kwargs.cmake
- sdk_v2/cpp/src/inferencing/generative/chat/chat_session.cc
- sdk_v2/cpp/src/inferencing/generative/chat/chat_session.h
- sdk_v2/cpp/src/inferencing/generative/preprocessor.cc
- sdk_v2/cpp/test/CMakeLists.txt
- sdk_v2/cpp/test/cmake/chat-template-kwargs-test.cmake
- sdk_v2/cpp/test/internal_api/chat/chat_session_test.cc
- sdk_v2/cpp/test/internal_api/chat/chat_template_test.cc

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c33e536f-0fdd-469b-b79c-7e0f8eaf24ad
@sayanshaw24 Sayan Shaw (sayanshaw24) changed the title Add chat template kwargs to chat completions Add reasoning controls and preserve reasoning across chat turns Sep 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants