Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion cmake/deps.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pybind11;https://github.com/pybind/pybind11/archive/refs/tags/v2.13.6.zip;f78029
googletest;https://github.com/google/googletest/archive/530d5c8c84abd2a46f38583ee817743c9b3a42b4.zip;5e3a61db2aa975cfd0f97ba92c818744e7fa7034
microsoft_wil;https://github.com/microsoft/wil/archive/refs/tags/v1.0.230629.1.zip;e4a542a323c070376f7c2d1973d0f7ddbc1d2fa5
directx_headers;https://github.com/microsoft/DirectX-Headers/archive/refs/tags/v1.613.1.zip;47653509a3371eabb156360f42faf582f314bf2e
onnxruntime_extensions;https://github.com/microsoft/onnxruntime-extensions.git;60687b12b9de56df9a702a0e548eacc26836fab9
onnxruntime_extensions;https://github.com/microsoft/onnxruntime-extensions.git;bd0e21c11187e0b8a2385d1c61122a4d259a53a0

# These two dependencies are for the optional constrained decoding feature (USE_GUIDANCE)
llguidance;https://github.com/microsoft/llguidance.git;94fa39128ef184ffeda33845f6d333f332a34b4d
Expand Down
16 changes: 16 additions & 0 deletions src/models/model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,22 @@ std::string Tokenizer::ApplyChatTemplate(const char* template_str, const char* m
return text_ptr;
}

std::string Tokenizer::ApplyChatTemplateWithOptions(const char* template_str, const char* messages, const char* tools,
const char* template_kwargs, bool add_generation_prompt) const {
ort_extensions::OrtxObjectPtr<OrtxTensorResult> templated_text;
CheckResult(OrtxApplyChatTemplateWithOptions(tokenizer_, template_str, messages, tools, template_kwargs,
templated_text.ToBeAssigned(), add_generation_prompt,
false /*tokenize*/));

ort_extensions::OrtxObjectPtr<OrtxTensor> tensor;
CheckResult(OrtxTensorResultGetAt(templated_text.get(), 0, tensor.ToBeAssigned()));

const char* text_ptr{};
CheckResult(OrtxGetTensorData(tensor.get(), reinterpret_cast<const void**>(&text_ptr), nullptr, nullptr));

return text_ptr;
}

std::vector<int32_t> Tokenizer::EncodeBatch(std::span<const std::string> strings) const {
std::vector<std::vector<int32_t>> sequences;
std::vector<std::span<const int32_t>> span_sequences;
Expand Down
2 changes: 2 additions & 0 deletions src/models/model.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ struct Tokenizer : std::enable_shared_from_this<Tokenizer>, LeakChecked<Tokenize
std::vector<int32_t> Encode(const char* text) const;
std::string Decode(std::span<const int32_t> tokens) const;
std::string ApplyChatTemplate(const char* template_str, const char* messages, const char* tools, bool add_generation_prompt) const;
std::string ApplyChatTemplateWithOptions(const char* template_str, const char* messages, const char* tools,
const char* template_kwargs, bool add_generation_prompt) const;

std::vector<int32_t> EncodeBatch(std::span<const std::string> strings) const;
std::shared_ptr<Tensor> EncodeBatch(std::span<const char*> strings) const;
Expand Down
8 changes: 8 additions & 0 deletions src/ort_genai.h
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,14 @@ struct OgaTokenizer : OgaAbstract {
return p;
}

OgaString ApplyChatTemplateWithOptions(const char* template_str, const char* messages, const char* tools,
const char* template_kwargs, bool add_generation_prompt) const {
const char* p{};
OgaCheckResult(OgaTokenizerApplyChatTemplateWithOptions(this, template_str, messages, tools, template_kwargs,
add_generation_prompt, &p));
return p;
}

#if OGA_USE_SPAN
OgaString Decode(std::span<const int32_t> tokens) const {
const char* p;
Expand Down
11 changes: 11 additions & 0 deletions src/ort_genai_c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1041,6 +1041,17 @@ OgaResult* OGA_API_CALL OgaTokenizerApplyChatTemplate(const OgaTokenizer* tokeni
OGA_CATCH
}

OgaResult* OGA_API_CALL OgaTokenizerApplyChatTemplateWithOptions(const OgaTokenizer* tokenizer, const char* template_str,
const char* messages, const char* tools,
const char* template_kwargs, bool add_generation_prompt,
const char** out_string) {
OGA_TRY
*out_string = AllocOgaString(tokenizer->ApplyChatTemplateWithOptions(template_str, messages, tools,
template_kwargs, add_generation_prompt));
return nullptr;
OGA_CATCH
}

OgaResult* OGA_API_CALL OgaTokenizerDecodeBatch(const OgaTokenizer* tokenizer, const OgaTensor* tensor, OgaStringArray** out) {
OGA_TRY
auto shape = tensor->GetShape();
Expand Down
14 changes: 14 additions & 0 deletions src/ort_genai_c.h
Original file line number Diff line number Diff line change
Expand Up @@ -989,6 +989,20 @@ OGA_EXPORT OgaResult* OGA_API_CALL OgaProcessorDecode(const OgaMultiModalProcess
*/
OGA_EXPORT OgaResult* OGA_API_CALL OgaTokenizerApplyChatTemplate(const OgaTokenizer*, const char* template_str, const char* messages, const char* tools, bool add_generation_prompt, const char** out_string);

/**
* @brief Applies a chat template to input messages with additional template context values.
*
* \param[in] tokenizer OgaTokenizer used for template processing.
* \param[in] template_str Null-terminated string representing the chat template. Use nullptr to fall back to the default chat template from the tokenizer config.
* \param[in] messages Null-terminated string containing the input messages to be processed.
* \param[in] tools Null-terminated string containing the chat function calls if any. Use nullptr if none.
* \param[in] template_kwargs Null-terminated JSON object containing additional template context values. Use nullptr if none.
* \param[in] add_generation_prompt Indicates whether to add a generation prompt to the output.
* \param[out] out_string Pointer to where the output will be stored. The returned pointer must be freed with OgaDestroyString.
* \return OgaResult* containing the error message if the function fails.
*/
OGA_EXPORT OgaResult* OGA_API_CALL OgaTokenizerApplyChatTemplateWithOptions(const OgaTokenizer*, const char* template_str, const char* messages, const char* tools, const char* template_kwargs, bool add_generation_prompt, const char** out_string);

/** OgaTokenizerStream is to decoded token strings incrementally, one token at a time.
*/
OGA_EXPORT OgaResult* OGA_API_CALL OgaCreateTokenizerStream(const OgaTokenizer*, OgaTokenizerStream** out);
Expand Down
15 changes: 15 additions & 0 deletions test/c_api_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,21 @@ TEST(CAPITests, ChatTemplate) {
auto out_string = tokenizer->ApplyChatTemplate(chat_template, messages_json, nullptr, true);
ASSERT_STREQ(expected_output, out_string);

const char* kwargs_template =
"{% if enable_thinking is defined and not enable_thinking %}NO_THINK{% else %}THINK{% endif %}"
"|{{ reasoning_effort }}|{{ level }}";
const char* template_kwargs =
R"({"enable_thinking":false,"reasoning_effort":"low","level":2})";
auto kwargs_output = tokenizer->ApplyChatTemplateWithOptions(
kwargs_template, messages_json, nullptr, template_kwargs, true);
ASSERT_STREQ("NO_THINK|low|2", kwargs_output);

auto legacy_output = tokenizer->ApplyChatTemplate(
"{{ messages[0].content }}", messages_json, nullptr, true);
auto null_options_output = tokenizer->ApplyChatTemplateWithOptions(
"{{ messages[0].content }}", messages_json, nullptr, nullptr, true);
ASSERT_STREQ(legacy_output, null_options_output);

#endif
}

Expand Down
Loading