|
14 | 14 | #include <algorithm> |
15 | 15 | #include <cctype> |
16 | 16 | #include <cstdint> |
| 17 | +#include <cstring> |
17 | 18 | #include <map> |
18 | 19 | #include <memory> |
19 | 20 | #include <random> |
@@ -52,6 +53,7 @@ ProviderRequestOptions request_options_from_entry(const ModelProfile& entry) { |
52 | 53 | options.api_protocol = model_profile_api_protocol(entry); |
53 | 54 | options.max_output_tokens = entry.max_output_tokens; |
54 | 55 | options.reasoning = entry.reasoning; |
| 56 | + options.sampling = entry.sampling; |
55 | 57 | if (entry.provider == "anthropic" && entry.reasoning.has_value()) { |
56 | 58 | options.reasoning_protocol = ReasoningWireProtocol::Anthropic; |
57 | 59 | } else if (entry.provider == "openai" && entry.reasoning.has_value() && |
@@ -88,6 +90,24 @@ void append_optional_int(std::string& out, const std::optional<T>& value) { |
88 | 90 | } |
89 | 91 | } |
90 | 92 |
|
| 93 | +void append_optional_double(std::string& out, |
| 94 | + const std::optional<double>& value) { |
| 95 | + append_bool(out, value.has_value()); |
| 96 | + if (!value.has_value()) return; |
| 97 | + // 按位编码:0.95 与 0.9500000001 必须得到不同指纹,不能先格式化成文本。 |
| 98 | + std::uint64_t bits = 0; |
| 99 | + static_assert(sizeof(bits) == sizeof(double), "double must be 64-bit"); |
| 100 | + std::memcpy(&bits, &*value, sizeof(bits)); |
| 101 | + append_u64(out, bits); |
| 102 | +} |
| 103 | + |
| 104 | +void append_sampling(std::string& out, const ModelSamplingOptions& sampling) { |
| 105 | + append_optional_double(out, sampling.temperature); |
| 106 | + append_optional_double(out, sampling.top_p); |
| 107 | + append_optional_int(out, sampling.top_k); |
| 108 | + append_optional_double(out, sampling.min_p); |
| 109 | +} |
| 110 | + |
91 | 111 | void append_optional_bool(std::string& out, |
92 | 112 | const std::optional<bool>& value) { |
93 | 113 | append_bool(out, value.has_value()); |
@@ -142,6 +162,7 @@ std::string canonical_plan_bytes(const EffectiveProviderBuildPlan& plan) { |
142 | 162 | append_u64(out, static_cast<std::uint64_t>( |
143 | 163 | plan.request_options.reasoning_protocol)); |
144 | 164 | append_reasoning(out, plan.request_options.reasoning); |
| 165 | + append_sampling(out, plan.request_options.sampling); |
145 | 166 | } |
146 | 167 | append_bool(out, plan.applies_vision_routing); |
147 | 168 | if (plan.applies_vision_routing) { |
@@ -209,18 +230,20 @@ std::optional<EffectiveProviderBuildPlan> effective_plan_from_entry( |
209 | 230 | } else if (entry.provider == "copilot") { |
210 | 231 | if (!entry.base_url.empty() || !entry.api_key.empty() || |
211 | 232 | !entry.request_headers.empty() || entry.endpoint_mode.has_value() || |
212 | | - entry.max_output_tokens.has_value() || entry.reasoning.has_value()) { |
| 233 | + entry.max_output_tokens.has_value() || entry.reasoning.has_value() || |
| 234 | + !entry.sampling.empty()) { |
213 | 235 | LOG_WARN("[provider_factory] refusing unsupported Copilot endpoint, " |
214 | | - "credential, output, or reasoning customization"); |
| 236 | + "credential, output, reasoning, or sampling customization"); |
215 | 237 | return std::nullopt; |
216 | 238 | } |
217 | 239 | } else if (entry.provider == "grok") { |
218 | 240 | if (!entry.base_url.empty() || !entry.api_key.empty() || |
219 | 241 | !entry.request_headers.empty() || entry.endpoint_mode.has_value() || |
220 | 242 | entry.max_output_tokens.has_value() || entry.reasoning.has_value() || |
221 | | - entry.stream_timeout_ms.has_value()) { |
| 243 | + entry.stream_timeout_ms.has_value() || !entry.sampling.empty()) { |
222 | 244 | LOG_WARN("[provider_factory] refusing unsupported Grok endpoint, " |
223 | | - "credential, timeout, output, or reasoning customization"); |
| 245 | + "credential, timeout, output, reasoning, or sampling " |
| 246 | + "customization"); |
224 | 247 | return std::nullopt; |
225 | 248 | } |
226 | 249 | } else if (entry.provider == "codex") { |
|
0 commit comments