mirror of
https://github.com/LostRuins/koboldcpp.git
synced 2026-09-19 09:15:18 +02:00
Merge branch 'upstream' into concedo_experimental
# Conflicts: # AGENTS.md # CODEOWNERS # CONTRIBUTING.md # docs/backend/OPENCL.md # docs/development/HOWTO-add-model.md # examples/training/finetune.cpp # ggml/src/ggml-hexagon/ggml-hexagon.cpp # ggml/src/ggml-hexagon/htp-drv.cpp # ggml/src/ggml-hexagon/htp/act-ops.c # ggml/src/ggml-hexagon/htp/dma-queue.c # ggml/src/ggml-hexagon/htp/dma-queue.h # ggml/src/ggml-hexagon/htp/flash-attn-ops.c # ggml/src/ggml-hexagon/htp/flash-attn-ops.h # ggml/src/ggml-hexagon/htp/hmx-mm-kernels-tiled.h # ggml/src/ggml-hexagon/htp/htp-ctx.h # ggml/src/ggml-hexagon/htp/htp-ops.h # ggml/src/ggml-hexagon/htp/htp-tensor.c # ggml/src/ggml-hexagon/htp/htp-tensor.h # ggml/src/ggml-hexagon/htp/hvx-fa-kernels.h # ggml/src/ggml-hexagon/htp/hvx-reduce.h # ggml/src/ggml-hexagon/htp/main.c # ggml/src/ggml-hexagon/htp/matmul-ops.c # ggml/src/ggml-hexagon/htp/matmul-ops.h # ggml/src/ggml-hexagon/htp/unary-ops.c # ggml/src/ggml-hexagon/htp/unary-ops.h # ggml/src/ggml-opencl/CMakeLists.txt # ggml/src/ggml-opencl/ggml-opencl.cpp # scripts/compare-llama-bench.py # scripts/snapdragon/ggml-hexagon-profile.py # scripts/snapdragon/ggml-hexagon-trace.py # scripts/sync_vendor.py # tests/test-arg-parser.cpp # tests/test-chat.cpp # tests/test-model-load-cancel.cpp # tests/test-quantize-stats.cpp # tools/cli/README.md # tools/completion/README.md # tools/llama-bench/llama-bench.cpp # tools/server/README.md # tools/ui/src/lib/constants/settings-registry.ts
This commit is contained in:
+62
-7
@@ -5,6 +5,7 @@
|
||||
#include "common.h"
|
||||
#include "download.h"
|
||||
#include "json-schema-to-grammar.h"
|
||||
#include "llama.h"
|
||||
#include "log.h"
|
||||
#include "sampling.h"
|
||||
#include "speculative.h"
|
||||
@@ -352,6 +353,10 @@ static std::string get_default_local_path(const std::string & url) {
|
||||
return fs_get_cache_file(string_split<std::string>(f, '/').back());
|
||||
}
|
||||
|
||||
static bool spec_types_is_default(const common_params & params) {
|
||||
return params.speculative.types == std::vector<enum common_speculative_type>{COMMON_SPECULATIVE_TYPE_NONE};
|
||||
}
|
||||
|
||||
common_models_handler common_models_handler_init(const common_params & params, llama_example curr_ex) {
|
||||
common_download_hf_plan plan;
|
||||
common_download_hf_plan plan_spec;
|
||||
@@ -392,7 +397,14 @@ common_models_handler common_models_handler_init(const common_params & params, l
|
||||
}
|
||||
|
||||
if (!params.speculative.draft.mparams.hf_repo.empty()) {
|
||||
plan_spec = common_download_get_hf_plan(params.speculative.draft.mparams, opts);
|
||||
// without a requested type, discover every sidecar the draft repo ships to infer the type later
|
||||
auto opts_spec = opts;
|
||||
if (spec_types_is_default(params)) {
|
||||
opts_spec.download_mtp = true;
|
||||
opts_spec.download_dflash = true;
|
||||
opts_spec.download_eagle3 = true;
|
||||
}
|
||||
plan_spec = common_download_get_hf_plan(params.speculative.draft.mparams, opts_spec);
|
||||
}
|
||||
|
||||
if (!params.vocoder.model.hf_repo.empty()) {
|
||||
@@ -528,6 +540,20 @@ void common_models_handler_apply(common_models_handler & handler, common_params
|
||||
}
|
||||
};
|
||||
|
||||
// infer the speculative type from the sidecar shipped by the draft repo when none is requested
|
||||
if (spec_types_is_default(params)) {
|
||||
if (!plan_spec.mtp.local_path.empty()) {
|
||||
params.speculative.types = { COMMON_SPECULATIVE_TYPE_DRAFT_MTP };
|
||||
plan_spec.dflash = {};
|
||||
plan_spec.eagle3 = {};
|
||||
} else if (!plan_spec.dflash.local_path.empty()) {
|
||||
params.speculative.types = { COMMON_SPECULATIVE_TYPE_DRAFT_DFLASH };
|
||||
plan_spec.eagle3 = {};
|
||||
} else if (!plan_spec.eagle3.local_path.empty()) {
|
||||
params.speculative.types = { COMMON_SPECULATIVE_TYPE_DRAFT_EAGLE3 };
|
||||
}
|
||||
}
|
||||
|
||||
// when a sidecar type is requested, the draft repo resolves to its sidecar instead of a full model
|
||||
const bool spec_sidecar_found = !plan_spec.mtp.local_path.empty() ||
|
||||
!plan_spec.dflash.local_path.empty() ||
|
||||
@@ -761,6 +787,17 @@ static bool common_params_parse_ex(int argc, char ** argv, common_params_context
|
||||
arg.c_str(), e.what(), opt.to_string().c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove this check after deprecating --mmap|mlock|dio
|
||||
auto has_arg = [&](std::initializer_list<const char *> names) {
|
||||
return std::any_of(names.begin(), names.end(), [&](const char * name) {
|
||||
return seen_args.count(name);
|
||||
});
|
||||
};
|
||||
if (has_arg({"-lm", "--load-mode"}) &&
|
||||
has_arg({"--mlock", "--mmap", "--no-mmap", "-dio", "--direct-io", "-ndio", "--no-direct-io"})) {
|
||||
LOG_WRN("DEPRECATED: `--load-mode` and `--mlock`/`--mmap`/`--direct-io` should not be combined; only the last flag on the command line will take effect\n");
|
||||
}
|
||||
};
|
||||
|
||||
// parse all CLI args now, so that -hf is available below for remote preset resolution
|
||||
@@ -2471,27 +2508,45 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
|
||||
}
|
||||
add_opt(common_arg(
|
||||
{"--mlock"},
|
||||
"force system to keep model in RAM rather than swapping or compressing",
|
||||
"DEPRECATED in favor of `--load-mode`: mmap + force system to keep model in RAM rather than swapping or compressing",
|
||||
[](common_params & params) {
|
||||
params.use_mlock = true;
|
||||
LOG_WRN("DEPRECATED: --mlock is deprecated. use --load-mode mlock instead\n");
|
||||
params.load_mode = LLAMA_LOAD_MODE_MLOCK;
|
||||
}
|
||||
).set_env("LLAMA_ARG_MLOCK"));
|
||||
add_opt(common_arg(
|
||||
{"--mmap"},
|
||||
{"--no-mmap"},
|
||||
string_format("whether to memory-map model. (if mmap disabled, slower load but may reduce pageouts if not using mlock) (default: %s)", params.use_mmap ? "enabled" : "disabled"),
|
||||
"DEPRECATED in favor of `--load-mode`: whether to memory-map model. (if mmap disabled, slower load but may reduce pageouts if not using mlock)",
|
||||
[](common_params & params, bool value) {
|
||||
params.use_mmap = value;
|
||||
LOG_WRN("DEPRECATED: --mmap and --no-mmap are deprecated. use --load-mode mmap instead\n");
|
||||
params.load_mode = value ? LLAMA_LOAD_MODE_MMAP : LLAMA_LOAD_MODE_NONE;
|
||||
}
|
||||
).set_env("LLAMA_ARG_MMAP"));
|
||||
add_opt(common_arg(
|
||||
{"-dio", "--direct-io"},
|
||||
{"-ndio", "--no-direct-io"},
|
||||
string_format("use DirectIO if available. (default: %s)", params.use_direct_io ? "enabled" : "disabled"),
|
||||
"DEPRECATED in favor of `--load-mode`: use DirectIO if available",
|
||||
[](common_params & params, bool value) {
|
||||
params.use_direct_io = value;
|
||||
LOG_WRN("DEPRECATED: --direct-io and --no-direct-io are deprecated. use --load-mode dio instead\n");
|
||||
params.load_mode = value ? LLAMA_LOAD_MODE_DIRECT_IO : LLAMA_LOAD_MODE_NONE;
|
||||
}
|
||||
).set_env("LLAMA_ARG_DIO"));
|
||||
add_opt(common_arg(
|
||||
{"-lm", "--load-mode"}, "MODE",
|
||||
"model loading mode (default: mmap)\n"
|
||||
"- none: no special loading mode\n"
|
||||
"- mmap: memory-map model (if mmap disabled, slower load but may reduce pageouts if not using mlock)\n"
|
||||
"- mlock: mmap + force system to keep model in RAM rather than swapping or compressing\n"
|
||||
"- dio: use DirectIO if available\n",
|
||||
[](common_params & params, const std::string & value) {
|
||||
/**/ if (value == "none") { params.load_mode = LLAMA_LOAD_MODE_NONE; }
|
||||
else if (value == "mmap") { params.load_mode = LLAMA_LOAD_MODE_MMAP; }
|
||||
else if (value == "mlock") { params.load_mode = LLAMA_LOAD_MODE_MLOCK; }
|
||||
else if (value == "dio") { params.load_mode = LLAMA_LOAD_MODE_DIRECT_IO; }
|
||||
else { throw std::invalid_argument("invalid value"); }
|
||||
}
|
||||
).set_env("LLAMA_ARG_LOAD_MODE"));
|
||||
add_opt(common_arg(
|
||||
{"--numa"}, "TYPE",
|
||||
"attempt optimizations that help on some NUMA systems\n"
|
||||
|
||||
+14
-5
@@ -2192,9 +2192,10 @@ static common_chat_params common_chat_params_init_cohere2moe(const common_chat_t
|
||||
{ COMMON_CHAT_ROLE_SYSTEM, TURN_START + SYSTEM },
|
||||
};
|
||||
|
||||
auto has_tools = inputs.tools.is_array() && !inputs.tools.empty();
|
||||
auto extract_reasoning = inputs.reasoning_format != COMMON_REASONING_FORMAT_NONE;
|
||||
auto include_grammar = has_tools && inputs.tool_choice != COMMON_CHAT_TOOL_CHOICE_NONE;
|
||||
auto has_tools = inputs.tools.is_array() && !inputs.tools.empty();
|
||||
auto has_response_format = inputs.json_schema.is_object() && !inputs.json_schema.empty();
|
||||
auto extract_reasoning = inputs.reasoning_format != COMMON_REASONING_FORMAT_NONE;
|
||||
auto include_grammar = has_response_format || (has_tools && inputs.tool_choice != COMMON_CHAT_TOOL_CHOICE_NONE);
|
||||
|
||||
if (inputs.has_continuation()) {
|
||||
const auto & msg = inputs.continue_msg;
|
||||
@@ -2225,7 +2226,11 @@ static common_chat_params common_chat_params_init_cohere2moe(const common_chat_t
|
||||
p.optional(p.literal(THINK_END))));
|
||||
}
|
||||
|
||||
auto text_content = p.literal(TEXT_START) + p.content(p.until(TEXT_END)) + p.optional(p.literal(TEXT_END));
|
||||
auto text_content = has_response_format
|
||||
? p.literal(TEXT_START) +
|
||||
p.content(p.schema(p.json(), "response-format-schema", inputs.json_schema)) +
|
||||
p.optional(p.literal(TEXT_END))
|
||||
: p.literal(TEXT_START) + p.content(p.until(TEXT_END)) + p.optional(p.literal(TEXT_END));
|
||||
|
||||
if (!has_tools || inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_NONE) {
|
||||
return generation_prompt + reasoning + text_content + p.optional(p.literal(TURN_END)) + end;
|
||||
@@ -2253,13 +2258,17 @@ static common_chat_params common_chat_params_init_cohere2moe(const common_chat_t
|
||||
data.parser = parser.save();
|
||||
|
||||
if (include_grammar) {
|
||||
data.grammar_lazy = inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_AUTO;
|
||||
data.grammar_lazy = !has_response_format && inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_AUTO;
|
||||
data.grammar = build_grammar([&](const common_grammar_builder & builder) {
|
||||
foreach_function(inputs.tools, [&](const json & tool) {
|
||||
const auto & function = tool.at("function");
|
||||
auto schema = function.at("parameters");
|
||||
builder.resolve_refs(schema);
|
||||
});
|
||||
if (has_response_format) {
|
||||
auto schema = inputs.json_schema;
|
||||
builder.resolve_refs(schema);
|
||||
}
|
||||
parser.build_grammar(builder, data.grammar_lazy);
|
||||
});
|
||||
|
||||
|
||||
+1
-3
@@ -1564,10 +1564,8 @@ struct llama_model_params common_model_params_to_llama(common_params & params) {
|
||||
mparams.n_gpu_layers = params.n_gpu_layers;
|
||||
mparams.main_gpu = params.main_gpu;
|
||||
mparams.split_mode = params.split_mode;
|
||||
mparams.load_mode = params.load_mode;
|
||||
mparams.tensor_split = params.tensor_split;
|
||||
mparams.use_mmap = params.use_mmap;
|
||||
mparams.use_direct_io = params.use_direct_io;
|
||||
mparams.use_mlock = params.use_mlock;
|
||||
mparams.check_tensors = params.check_tensors;
|
||||
mparams.use_extra_bufts = !params.no_extra_bufts;
|
||||
mparams.no_host = params.no_host;
|
||||
|
||||
+2
-3
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "ggml-opt.h"
|
||||
#include "ggml.h"
|
||||
#include "llama.h"
|
||||
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
@@ -483,6 +484,7 @@ struct common_params {
|
||||
std::vector<size_t> fit_params_target = std::vector<size_t>(llama_max_devices(), 1024 * 1024*1024);
|
||||
|
||||
enum llama_split_mode split_mode = LLAMA_SPLIT_MODE_LAYER; // how to split the model across GPUs
|
||||
enum llama_load_mode load_mode = LLAMA_LOAD_MODE_MMAP; // how to load the model
|
||||
|
||||
common_cpu_params cpuparams;
|
||||
common_cpu_params cpuparams_batch;
|
||||
@@ -573,9 +575,6 @@ struct common_params {
|
||||
bool kv_unified = false; // enable unified KV cache
|
||||
|
||||
bool input_prefix_bos = false; // prefix BOS to user inputs, preceding input_prefix
|
||||
bool use_mmap = true; // enable mmap to use filesystem cache
|
||||
bool use_direct_io = false; // read from disk without buffering
|
||||
bool use_mlock = false; // use mlock to keep model in memory
|
||||
bool verbose_prompt = false; // print prompt tokens before generation
|
||||
bool display_prompt = true; // print prompt before generation
|
||||
bool no_kv_offload = false; // disable KV offloading
|
||||
|
||||
+1
-2
@@ -54,8 +54,7 @@ static std::vector<llama_device_memory_data> common_get_device_memory_data_impl(
|
||||
|
||||
llama_model_params mparams_copy = *mparams;
|
||||
mparams_copy.no_alloc = true;
|
||||
mparams_copy.use_mmap = false;
|
||||
mparams_copy.use_mlock = false;
|
||||
mparams_copy.load_mode = LLAMA_LOAD_MODE_NONE;
|
||||
|
||||
llama_model * model = llama_model_load_from_file(path_model, mparams_copy);
|
||||
if (model == nullptr) {
|
||||
|
||||
@@ -26,7 +26,7 @@ void caps_apply_preserve_reasoning(jinja::context & ctx, bool enabled) {
|
||||
ctx.set_val("preserve_thinking", mk_val<value_bool>(enabled));
|
||||
ctx.set_val("clear_thinking", mk_val<value_bool>(!enabled));
|
||||
ctx.set_val("truncate_history_thinking", mk_val<value_bool>(!enabled));
|
||||
ctx.set_val("drop_reasoning", mk_val<value_bool>(!enabled));
|
||||
ctx.set_val("drop_thinking", mk_val<value_bool>(!enabled));
|
||||
}
|
||||
|
||||
static void caps_try_execute(jinja::program & prog,
|
||||
|
||||
+2
-1
@@ -369,12 +369,13 @@ class NomicBertModel(BertModel):
|
||||
return super().filter_tensors(item)
|
||||
|
||||
def modify_tensors(self, data_torch: torch.Tensor, name: str, bid: int | None) -> Iterable[tuple[str, torch.Tensor]]:
|
||||
n_experts = self.find_hparam(["num_local_experts", "num_experts"])
|
||||
if "mlp.experts.mlp.w1" in name:
|
||||
n_experts = self.find_hparam(["num_local_experts", "num_experts"])
|
||||
data_torch = data_torch.view(n_experts, self.hparams["n_inner"], self.hparams["n_embd"])
|
||||
name += ".weight"
|
||||
|
||||
if "mlp.experts.mlp.w2" in name:
|
||||
n_experts = self.find_hparam(["num_local_experts", "num_experts"])
|
||||
data_torch = data_torch.view(n_experts, self.hparams["n_inner"], self.hparams["n_embd"])
|
||||
data_torch = data_torch.transpose(1, 2)
|
||||
name += ".weight"
|
||||
|
||||
@@ -117,9 +117,7 @@ int main(int argc, char ** argv) {
|
||||
llama_model_params model_params = llama_model_default_params();
|
||||
model_params.n_gpu_layers = params.n_gpu_layers;
|
||||
model_params.devices = params.devices.data();
|
||||
model_params.use_mmap = params.use_mmap;
|
||||
model_params.use_direct_io = params.use_direct_io;
|
||||
model_params.use_mlock = params.use_mlock;
|
||||
model_params.load_mode = params.load_mode;
|
||||
model_params.check_tensors = params.check_tensors;
|
||||
|
||||
llama_model * model = llama_model_load_from_file(params.model.path.c_str(), model_params);
|
||||
|
||||
@@ -369,6 +369,15 @@ static bool blackwell_mma_available(const int cc) {
|
||||
ggml_cuda_highest_compiled_arch(cc) < GGML_CUDA_CC_RUBIN;
|
||||
}
|
||||
|
||||
// Checks whether the tensor's base data pointer and higher-dimensional strides are byte-aligned to `alignment` bytes.
|
||||
static bool ggml_cuda_is_aligned(const ggml_tensor * tensor, const size_t alignment) {
|
||||
GGML_ASSERT(tensor != nullptr);
|
||||
return (reinterpret_cast<uintptr_t>(tensor->data) % alignment) == 0 &&
|
||||
tensor->nb[1] % alignment == 0 &&
|
||||
tensor->nb[2] % alignment == 0 &&
|
||||
tensor->nb[3] % alignment == 0;
|
||||
}
|
||||
|
||||
static constexpr __device__ int ggml_cuda_get_physical_warp_size() {
|
||||
#if defined(GGML_USE_HIP) && (defined(__GFX9__) || defined(__GFX8__))
|
||||
return 64;
|
||||
|
||||
+41
-22
@@ -25,12 +25,7 @@ static void ggml_cuda_mul_mat_q_switch_type(ggml_backend_cuda_context & ctx, con
|
||||
case GGML_TYPE_Q8_0:
|
||||
mul_mat_q_case<GGML_TYPE_Q8_0>(ctx, args, stream);
|
||||
break;
|
||||
case GGML_TYPE_MXFP4:
|
||||
mul_mat_q_case<GGML_TYPE_MXFP4>(ctx, args, stream);
|
||||
break;
|
||||
case GGML_TYPE_NVFP4:
|
||||
mul_mat_q_case<GGML_TYPE_NVFP4>(ctx, args, stream);
|
||||
break;
|
||||
// -----------------------------------------------------------------------
|
||||
case GGML_TYPE_Q2_K:
|
||||
mul_mat_q_case<GGML_TYPE_Q2_K>(ctx, args, stream);
|
||||
break;
|
||||
@@ -46,6 +41,10 @@ static void ggml_cuda_mul_mat_q_switch_type(ggml_backend_cuda_context & ctx, con
|
||||
case GGML_TYPE_Q6_K:
|
||||
mul_mat_q_case<GGML_TYPE_Q6_K>(ctx, args, stream);
|
||||
break;
|
||||
// -----------------------------------------------------------------------
|
||||
case GGML_TYPE_IQ1_S:
|
||||
mul_mat_q_case<GGML_TYPE_IQ1_S>(ctx, args, stream);
|
||||
break;
|
||||
case GGML_TYPE_IQ2_XXS:
|
||||
mul_mat_q_case<GGML_TYPE_IQ2_XXS>(ctx, args, stream);
|
||||
break;
|
||||
@@ -61,15 +60,19 @@ static void ggml_cuda_mul_mat_q_switch_type(ggml_backend_cuda_context & ctx, con
|
||||
case GGML_TYPE_IQ3_S:
|
||||
mul_mat_q_case<GGML_TYPE_IQ3_S>(ctx, args, stream);
|
||||
break;
|
||||
case GGML_TYPE_IQ1_S:
|
||||
mul_mat_q_case<GGML_TYPE_IQ1_S>(ctx, args, stream);
|
||||
break;
|
||||
case GGML_TYPE_IQ4_XS:
|
||||
mul_mat_q_case<GGML_TYPE_IQ4_XS>(ctx, args, stream);
|
||||
break;
|
||||
case GGML_TYPE_IQ4_NL:
|
||||
mul_mat_q_case<GGML_TYPE_IQ4_NL>(ctx, args, stream);
|
||||
break;
|
||||
// -----------------------------------------------------------------------
|
||||
case GGML_TYPE_MXFP4:
|
||||
mul_mat_q_case<GGML_TYPE_MXFP4>(ctx, args, stream);
|
||||
break;
|
||||
case GGML_TYPE_NVFP4:
|
||||
mul_mat_q_case<GGML_TYPE_NVFP4>(ctx, args, stream);
|
||||
break;
|
||||
default:
|
||||
GGML_ABORT("fatal error");
|
||||
break;
|
||||
@@ -130,14 +133,20 @@ void ggml_cuda_mul_mat_q(
|
||||
const size_t nbytes_src1_q8_1 = ne13*ne12 * ne11*ne10_padded * y_block_size/y_values_per_block +
|
||||
ggml_cuda_mmq_get_J_max(src0->type, fallback, cc, ne11) * sizeof(block_q8_1_mmq);
|
||||
ggml_cuda_pool_alloc<char> src1_q8_1(ctx.pool(), nbytes_src1_q8_1);
|
||||
ggml_cuda_pool_alloc<float> src1_scale(ctx.pool());
|
||||
if (src0->type == GGML_TYPE_NVFP4 && use_native_fp4) {
|
||||
src1_scale.alloc(ne13*ne12*ne11);
|
||||
}
|
||||
|
||||
{
|
||||
const int64_t s11 = src1->nb[1] / ts_src1;
|
||||
const int64_t s12 = src1->nb[2] / ts_src1;
|
||||
const int64_t s13 = src1->nb[3] / ts_src1;
|
||||
if (use_native_fp4) {
|
||||
static constexpr size_t align_float8 = 32;
|
||||
const bool use_aligned_float8 = ggml_cuda_is_aligned(src1, align_float8);
|
||||
static_assert(sizeof(block_fp4_mmq) == 4 * sizeof(block_q8_1));
|
||||
quantize_mmq_fp4_cuda(src1_d, nullptr, src1_q8_1.get(), src0->type, ne10, s11, s12, s13, ne10_padded,
|
||||
quantize_mmq_fp4_cuda(src1_d, nullptr, src1_q8_1.get(), src1_scale.ptr, src0->type, use_aligned_float8, ne10, s11, s12, s13, ne10_padded,
|
||||
ne11, ne12, ne13, stream);
|
||||
|
||||
} else {
|
||||
@@ -155,6 +164,7 @@ void ggml_cuda_mul_mat_q(
|
||||
|
||||
const mmq_args args = {
|
||||
src0_d, src0->type, (const int *) src1_q8_1.ptr, nullptr, nullptr, dst_d,
|
||||
src0->type == GGML_TYPE_NVFP4 && use_native_fp4 ? src1_scale.ptr : nullptr,
|
||||
ne00, ne01, ne1, s01, ne11, s1,
|
||||
ne02, ne12, s02, s12, s2,
|
||||
ne03, ne13, s03, s13, s3,
|
||||
@@ -192,6 +202,10 @@ void ggml_cuda_mul_mat_q(
|
||||
const size_t nbytes_src1_q8_1 = ne12*n_expert_used*ne10_padded * y_block_size/y_values_per_block +
|
||||
ggml_cuda_mmq_get_J_max(src0->type, fallback, cc, ne11) * sizeof(block_q8_1_mmq);
|
||||
ggml_cuda_pool_alloc<char> src1_q8_1(ctx.pool(), nbytes_src1_q8_1);
|
||||
ggml_cuda_pool_alloc<float> src1_scale(ctx.pool());
|
||||
if (src0->type == GGML_TYPE_NVFP4 && use_native_fp4) {
|
||||
src1_scale.alloc(ne12*n_expert_used);
|
||||
}
|
||||
|
||||
const int64_t ne11_flat = ne12*n_expert_used;
|
||||
const int64_t ne12_flat = 1;
|
||||
@@ -202,18 +216,19 @@ void ggml_cuda_mul_mat_q(
|
||||
const int64_t s12 = src1->nb[2] / ts_src1;
|
||||
const int64_t s13 = src1->nb[3] / ts_src1;
|
||||
|
||||
if (dedup_bcast) {
|
||||
// quantize each token once, scatter its block to all n_expert_used slots
|
||||
if (use_native_fp4) {
|
||||
quantize_scatter_mmq_fp4_cuda(src1_d, ids_src1.get(), src1_q8_1.get(), src0->type, ne10,
|
||||
if (use_native_fp4) {
|
||||
static constexpr size_t align_float8 = 32;
|
||||
const bool use_aligned_float8 = ggml_cuda_is_aligned(src1, align_float8);
|
||||
if (dedup_bcast) {
|
||||
quantize_scatter_mmq_fp4_cuda(src1_d, ids_src1.get(), src1_q8_1.get(), src1_scale.ptr, src0->type, use_aligned_float8, ne10,
|
||||
/*stride_token=*/s12, ne10_padded, ne12, ne11_flat, n_expert_used, stream);
|
||||
} else {
|
||||
quantize_scatter_mmq_q8_1_cuda(src1_d, ids_src1.get(), src1_q8_1.get(), src0->type, ne10,
|
||||
/*stride_token=*/s12, ne10_padded, ne12, ne11_flat, n_expert_used, stream);
|
||||
quantize_mmq_fp4_cuda(src1_d, ids_src1.get(), src1_q8_1.get(), src1_scale.ptr, src0->type, use_aligned_float8, ne10, s11, s12, s13,
|
||||
ne10_padded, ne11_flat, ne12_flat, ne13_flat, stream);
|
||||
}
|
||||
} else if (use_native_fp4) {
|
||||
quantize_mmq_fp4_cuda(src1_d, ids_src1.get(), src1_q8_1.get(), src0->type, ne10, s11, s12, s13,
|
||||
ne10_padded, ne11_flat, ne12_flat, ne13_flat, stream);
|
||||
} else if (dedup_bcast) {
|
||||
quantize_scatter_mmq_q8_1_cuda(src1_d, ids_src1.get(), src1_q8_1.get(), src0->type, ne10,
|
||||
/*stride_token=*/s12, ne10_padded, ne12, ne11_flat, n_expert_used, stream);
|
||||
} else {
|
||||
quantize_mmq_q8_1_cuda(src1_d, ids_src1.get(), src1_q8_1.get(), src0->type, ne10, s11, s12, s13,
|
||||
ne10_padded, ne11_flat, ne12_flat, ne13_flat, stream);
|
||||
@@ -229,6 +244,7 @@ void ggml_cuda_mul_mat_q(
|
||||
// Note that ne02 is used instead of ne12 because the number of y channels determines the z dimension of the CUDA grid.
|
||||
const mmq_args args = {
|
||||
src0_d, src0->type, (const int *) src1_q8_1.get(), ids_dst.get(), expert_bounds.get(), dst_d,
|
||||
src1_scale.ptr,
|
||||
ne00, ne01, ne_get_rows, s01, ne_get_rows, s1,
|
||||
ne02, ne02, s02, s12, s2,
|
||||
ne03, ne13, s03, s13, s3,
|
||||
@@ -253,21 +269,24 @@ bool ggml_cuda_should_use_mmq(enum ggml_type type, int cc, int64_t ne11, int64_t
|
||||
case GGML_TYPE_Q5_0:
|
||||
case GGML_TYPE_Q5_1:
|
||||
case GGML_TYPE_Q8_0:
|
||||
case GGML_TYPE_MXFP4:
|
||||
case GGML_TYPE_NVFP4:
|
||||
// -------------------------------------------------
|
||||
case GGML_TYPE_Q2_K:
|
||||
case GGML_TYPE_Q3_K:
|
||||
case GGML_TYPE_Q4_K:
|
||||
case GGML_TYPE_Q5_K:
|
||||
case GGML_TYPE_Q6_K:
|
||||
// -------------------------------------------------
|
||||
case GGML_TYPE_IQ1_S:
|
||||
case GGML_TYPE_IQ2_XXS:
|
||||
case GGML_TYPE_IQ2_XS:
|
||||
case GGML_TYPE_IQ2_S:
|
||||
case GGML_TYPE_IQ3_XXS:
|
||||
case GGML_TYPE_IQ3_S:
|
||||
case GGML_TYPE_IQ1_S:
|
||||
case GGML_TYPE_IQ4_XS:
|
||||
case GGML_TYPE_IQ4_NL:
|
||||
// -------------------------------------------------
|
||||
case GGML_TYPE_MXFP4:
|
||||
case GGML_TYPE_NVFP4:
|
||||
mmq_supported = true;
|
||||
break;
|
||||
default:
|
||||
|
||||
+96
-21
@@ -14,7 +14,7 @@
|
||||
typedef void (*ggml_cuda_mmq_load_tiles_t)(const char * __restrict__ x, int * x_tile, const int kbx0, const int i_max, const int stride);
|
||||
typedef void (*ggml_cuda_mmq_vec_dot_t)(const int * __restrict__ x, const int * __restrict__ y, float * __restrict__ sum, const int k00);
|
||||
typedef void (*ggml_cuda_mmq_write_back_t)(const float * __restrict__ sum, const int32_t * __restrict__ get_rows_to_sorted,
|
||||
float * __restrict__ dst, const int stride, const int i_max, const int j_max);
|
||||
float * __restrict__ dst, const float * __restrict__ y_scale, const int stride, const int i_max, const int j_max);
|
||||
|
||||
enum mmq_q8_1_ds_layout {
|
||||
MMQ_Q8_1_DS_LAYOUT_D4,
|
||||
@@ -414,11 +414,13 @@ static __host__ int ggml_cuda_mmq_get_nbytes_shared_x(const ggml_cuda_mmq_config
|
||||
|
||||
template <ggml_type type, int J, bool fallback> static __device__ __forceinline__ void ggml_cuda_mmq_write_back_dp4a(
|
||||
const float * __restrict__ sum, const int32_t * __restrict__ ids_dst, float * __restrict__ dst,
|
||||
const int stride, const int i_max, const int j_max) {
|
||||
const float * __restrict__ y_scale, const int stride, const int i_max, const int j_max) {
|
||||
constexpr int warp_size = ggml_cuda_get_physical_warp_size();
|
||||
constexpr int nwarps = ggml_cuda_mmq_get_nthreads(type, J, fallback) / warp_size;
|
||||
constexpr int I = ggml_cuda_mmq_get_I(type, J, fallback);
|
||||
|
||||
const bool y_scale_used = y_scale != nullptr;
|
||||
|
||||
#pragma unroll
|
||||
for (int j0 = 0; j0 < J; j0 += nwarps) {
|
||||
const int j = j0 + threadIdx.y;
|
||||
@@ -435,7 +437,16 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
continue;
|
||||
}
|
||||
|
||||
dst[ids_dst[j]*stride + i] = sum[(j0/nwarps) * (I/warp_size) + i0/warp_size];
|
||||
if constexpr (type == GGML_TYPE_NVFP4) {
|
||||
if (y_scale_used) {
|
||||
dst[ids_dst[j]*stride + i] = y_scale[j] * sum[(j0/nwarps) * (I/warp_size) + i0/warp_size];
|
||||
} else {
|
||||
dst[ids_dst[j]*stride + i] = sum[(j0/nwarps) * (I/warp_size) + i0/warp_size];
|
||||
}
|
||||
} else {
|
||||
dst[ids_dst[j]*stride + i] = sum[(j0/nwarps) * (I/warp_size) + i0/warp_size];
|
||||
GGML_UNUSED(y_scale_used);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -443,7 +454,8 @@ template <ggml_type type, int J, bool fallback> static __device__ __forceinline_
|
||||
template<ggml_type type, int J, bool fallback>
|
||||
static __device__ __forceinline__ void ggml_cuda_mmq_write_back_mma(
|
||||
const float * __restrict__ sum, const int * __restrict__ ids_dst, float * __restrict__ dst,
|
||||
const int stride, const int i_max, const int j_max) {
|
||||
const float * __restrict__ y_scale, const int stride, const int i_max, const int j_max) {
|
||||
|
||||
#if defined(AMD_MFMA_AVAILABLE) || defined(AMD_WMMA_AVAILABLE)
|
||||
typedef tile<16, 16, int, DATA_LAYOUT_J_MAJOR> tile_C;
|
||||
#else
|
||||
@@ -458,6 +470,8 @@ static __device__ __forceinline__ void ggml_cuda_mmq_write_back_mma(
|
||||
|
||||
const int i0 = (threadIdx.y / ntx) * (ntx*tile_C::I);
|
||||
|
||||
const bool y_scale_used = y_scale != nullptr;
|
||||
|
||||
#pragma unroll
|
||||
for (int j0 = 0; j0 < J; j0 += ntx*tile_C::J) {
|
||||
#pragma unroll
|
||||
@@ -476,7 +490,16 @@ static __device__ __forceinline__ void ggml_cuda_mmq_write_back_mma(
|
||||
continue;
|
||||
}
|
||||
|
||||
dst[ids_dst[j]*stride + i] = sum[(j0/tile_C::J + n)*tile_C::ne + l];
|
||||
if constexpr (type == GGML_TYPE_NVFP4) {
|
||||
if (y_scale_used) {
|
||||
dst[ids_dst[j]*stride + i] = y_scale[j] * sum[(j0/tile_C::J + n)*tile_C::ne + l];
|
||||
} else {
|
||||
dst[ids_dst[j]*stride + i] = sum[(j0/tile_C::J + n)*tile_C::ne + l];
|
||||
}
|
||||
} else {
|
||||
dst[ids_dst[j]*stride + i] = sum[(j0/tile_C::J + n)*tile_C::ne + l];
|
||||
GGML_UNUSED(y_scale_used);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -820,6 +843,7 @@ template <ggml_type type, int J, bool fallback, bool fixup>
|
||||
static __device__ __forceinline__ void mul_mat_q_process_tile(
|
||||
const char * __restrict__ x, const int offset_x, const int * __restrict__ y,
|
||||
const int * __restrict__ ids_dst, float * __restrict__ dst, float * __restrict__ tmp_fixup,
|
||||
const float * __restrict__ y_scale,
|
||||
const int stride_row_x, const int ncols_y, const int stride_col_dst,
|
||||
const int tile_x_max_i, const int tile_y_max_j, const int kb0_start, const int kb0_stop) {
|
||||
|
||||
@@ -885,9 +909,9 @@ static __device__ __forceinline__ void mul_mat_q_process_tile(
|
||||
}
|
||||
|
||||
if (fixup) {
|
||||
write_back(sum, ids_dst, tmp_fixup + blockIdx.x*(J*I), I, I, J);
|
||||
write_back(sum, ids_dst, tmp_fixup + blockIdx.x*(J*I), y_scale, I, I, J);
|
||||
} else {
|
||||
write_back(sum, ids_dst, dst, stride_col_dst, tile_x_max_i, tile_y_max_j);
|
||||
write_back(sum, ids_dst, dst, y_scale, stride_col_dst, tile_x_max_i, tile_y_max_j);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -899,6 +923,7 @@ __launch_bounds__(ggml_cuda_mmq_get_nthreads(type, J, fallback), ggml_cuda_mmq_g
|
||||
static __global__ void mul_mat_q(
|
||||
const char * __restrict__ x, const int * __restrict__ y, const int32_t * __restrict__ ids_dst,
|
||||
const int32_t * __restrict__ expert_bounds, float * __restrict__ dst, float * __restrict__ tmp_fixup,
|
||||
const float * __restrict__ y_scale,
|
||||
const uint3 blocks_per_ne00, const int nrows_x, const int ncols_dst, const int stride_row_x, const int ncols_y, const int stride_col_dst,
|
||||
const uint3 channel_ratio, const uint3 nchannels_y, const int stride_channel_x, const int stride_channel_y, const int stride_channel_dst,
|
||||
const uint3 sample_ratio, const uint3 nsamples_y, const int stride_sample_x, const int stride_sample_y, const int stride_sample_dst,
|
||||
@@ -944,8 +969,14 @@ static __global__ void mul_mat_q(
|
||||
int col_low = 0;
|
||||
int col_high = ncols_dst;
|
||||
int col_diff = ncols_dst;
|
||||
int offset_y = wt*stride_sample_y + zt*stride_channel_y;
|
||||
int offset_dst = wt*stride_sample_dst + zt*stride_channel_dst + jt*J*stride_col_dst;
|
||||
int offset_y = wt*stride_sample_y + zt*stride_channel_y;
|
||||
int offset_dst = wt*stride_sample_dst + zt*stride_channel_dst + jt*J*stride_col_dst;
|
||||
int offset_y_scale;
|
||||
if constexpr (type == GGML_TYPE_NVFP4) {
|
||||
offset_y_scale = wt*nchannels_y.z*ncols_y + zt*ncols_y;
|
||||
} else {
|
||||
GGML_UNUSED(offset_y_scale);
|
||||
}
|
||||
|
||||
if (ids_dst) {
|
||||
col_low = expert_bounds[zt + 0];
|
||||
@@ -954,6 +985,9 @@ static __global__ void mul_mat_q(
|
||||
|
||||
offset_y = 0;
|
||||
offset_dst = 0;
|
||||
if constexpr (type == GGML_TYPE_NVFP4) {
|
||||
offset_y_scale = 0;
|
||||
}
|
||||
|
||||
if (jt*J >= col_diff) {
|
||||
return;
|
||||
@@ -975,6 +1009,11 @@ static __global__ void mul_mat_q(
|
||||
|
||||
offset_y += (col_low + jt*J)*(sizeof(block_q8_1_mmq)/sizeof(int));
|
||||
offset_dst += it*I;
|
||||
const float * y_scale_tile = nullptr;
|
||||
if constexpr (type == GGML_TYPE_NVFP4) {
|
||||
offset_y_scale += col_low + jt*J;
|
||||
y_scale_tile = y_scale ? y_scale + offset_y_scale : nullptr;
|
||||
}
|
||||
|
||||
const int tile_x_max_i = nrows_x - it*I - 1;
|
||||
const int tile_y_max_j = col_diff - jt*J - 1;
|
||||
@@ -983,7 +1022,8 @@ static __global__ void mul_mat_q(
|
||||
|
||||
constexpr bool fixup = false;
|
||||
mul_mat_q_process_tile<type, J, fallback, fixup>
|
||||
(x, offset_x, y + offset_y, ids_dst_shared, dst + offset_dst, tmp_fixup, stride_row_x, ncols_y, stride_col_dst,
|
||||
(x, offset_x, y + offset_y, ids_dst_shared, dst + offset_dst, tmp_fixup, y_scale_tile,
|
||||
stride_row_x, ncols_y, stride_col_dst,
|
||||
tile_x_max_i, tile_y_max_j, 0, blocks_per_ne00.z);
|
||||
return;
|
||||
}
|
||||
@@ -1017,8 +1057,14 @@ static __global__ void mul_mat_q(
|
||||
int col_low = 0;
|
||||
int col_high = ncols_dst;
|
||||
int col_diff = ncols_dst;
|
||||
int offset_y = wt*stride_sample_y + zt*stride_channel_y;
|
||||
int offset_dst = wt*stride_sample_dst + zt*stride_channel_dst + jt*J*stride_col_dst;
|
||||
int offset_y = wt*stride_sample_y + zt*stride_channel_y;
|
||||
int offset_dst = wt*stride_sample_dst + zt*stride_channel_dst + jt*J*stride_col_dst;
|
||||
int offset_y_scale;
|
||||
if constexpr (type == GGML_TYPE_NVFP4) {
|
||||
offset_y_scale = wt*nchannels_y.z*ncols_y + zt*ncols_y;
|
||||
} else {
|
||||
GGML_UNUSED(offset_y_scale);
|
||||
}
|
||||
|
||||
if (ids_dst) {
|
||||
col_low = expert_bounds[zt + 0];
|
||||
@@ -1027,6 +1073,9 @@ static __global__ void mul_mat_q(
|
||||
|
||||
offset_y = 0;
|
||||
offset_dst = 0;
|
||||
if constexpr (type == GGML_TYPE_NVFP4) {
|
||||
offset_y_scale = 0;
|
||||
}
|
||||
|
||||
if (jt*J >= col_diff) {
|
||||
kbc += blocks_per_ne00.z;
|
||||
@@ -1054,6 +1103,11 @@ static __global__ void mul_mat_q(
|
||||
|
||||
offset_y += (col_low + jt * J) * (sizeof(block_q8_1_mmq) / sizeof(int));
|
||||
offset_dst += it*I;
|
||||
const float * y_scale_tile = nullptr;
|
||||
if constexpr (type == GGML_TYPE_NVFP4) {
|
||||
offset_y_scale += col_low + jt * J;
|
||||
y_scale_tile = y_scale ? y_scale + offset_y_scale : nullptr;
|
||||
}
|
||||
|
||||
const int tile_x_max_i = nrows_x - it*I - 1;
|
||||
const int tile_y_max_j = col_diff - jt*J - 1;
|
||||
@@ -1062,7 +1116,8 @@ static __global__ void mul_mat_q(
|
||||
|
||||
constexpr bool fixup = false; // All but (potentially) the last iterations write their data to dst rather than the fixup buffer.
|
||||
mul_mat_q_process_tile<type, J, fallback, fixup>
|
||||
(x, offset_x, y + offset_y, ids_dst_shared, dst + offset_dst, tmp_fixup, stride_row_x, ncols_y, stride_col_dst,
|
||||
(x, offset_x, y + offset_y, ids_dst_shared, dst + offset_dst, tmp_fixup, y_scale_tile,
|
||||
stride_row_x, ncols_y, stride_col_dst,
|
||||
tile_x_max_i, tile_y_max_j, kb0_start, kb0_stop);
|
||||
|
||||
kbc += blocks_per_ne00.z;
|
||||
@@ -1091,8 +1146,14 @@ static __global__ void mul_mat_q(
|
||||
int col_low = 0;
|
||||
int col_high = ncols_dst;
|
||||
int col_diff = ncols_dst;
|
||||
int offset_y = wt*stride_sample_y + zt*stride_channel_y;
|
||||
int offset_dst = wt*stride_sample_dst + zt*stride_channel_dst + jt*J*stride_col_dst;
|
||||
int offset_y = wt*stride_sample_y + zt*stride_channel_y;
|
||||
int offset_dst = wt*stride_sample_dst + zt*stride_channel_dst + jt*J*stride_col_dst;
|
||||
int offset_y_scale;
|
||||
if constexpr (type == GGML_TYPE_NVFP4) {
|
||||
offset_y_scale = wt*nchannels_y.z*ncols_y + zt*ncols_y;
|
||||
} else {
|
||||
GGML_UNUSED(offset_y_scale);
|
||||
}
|
||||
|
||||
if (ids_dst) {
|
||||
col_low = expert_bounds[zt + 0];
|
||||
@@ -1101,6 +1162,9 @@ static __global__ void mul_mat_q(
|
||||
|
||||
offset_y = 0;
|
||||
offset_dst = 0;
|
||||
if constexpr (type == GGML_TYPE_NVFP4) {
|
||||
offset_y_scale = 0;
|
||||
}
|
||||
|
||||
if (jt*J >= col_diff) {
|
||||
return;
|
||||
@@ -1123,6 +1187,11 @@ static __global__ void mul_mat_q(
|
||||
|
||||
offset_y += (col_low + jt * J) * (sizeof(block_q8_1_mmq) / sizeof(int));
|
||||
offset_dst += it*I;
|
||||
const float * y_scale_tile = nullptr;
|
||||
if constexpr (type == GGML_TYPE_NVFP4) {
|
||||
offset_y_scale += col_low + jt * J;
|
||||
y_scale_tile = y_scale ? y_scale + offset_y_scale : nullptr;
|
||||
}
|
||||
|
||||
const int tile_x_max_i = nrows_x - it*I - 1;
|
||||
const int tile_y_max_j = col_diff - jt*J - 1;
|
||||
@@ -1131,7 +1200,8 @@ static __global__ void mul_mat_q(
|
||||
|
||||
constexpr bool fixup = true; // Last index writes its data to fixup buffer to avoid data races with other blocks.
|
||||
mul_mat_q_process_tile<type, J, fallback, fixup>
|
||||
(x, offset_x, y + offset_y, ids_dst_shared, dst + offset_dst, tmp_fixup, stride_row_x, ncols_y, stride_col_dst,
|
||||
(x, offset_x, y + offset_y, ids_dst_shared, dst + offset_dst, tmp_fixup, y_scale_tile,
|
||||
stride_row_x, ncols_y, stride_col_dst,
|
||||
tile_x_max_i, tile_y_max_j, kb0_start, kb0_stop);
|
||||
}
|
||||
|
||||
@@ -1275,6 +1345,7 @@ static __global__ void mul_mat_q_stream_k_fixup(
|
||||
|
||||
struct mmq_args {
|
||||
const char * x; ggml_type type_x; const int * y; const int32_t * ids_dst; const int32_t * expert_bounds; float * dst;
|
||||
const float * y_scale;
|
||||
int64_t ncols_x; int64_t nrows_x; int64_t ncols_dst; int64_t stride_row_x; int64_t ncols_y; int64_t nrows_dst;
|
||||
int64_t nchannels_x; int64_t nchannels_y; int64_t stride_channel_x; int64_t stride_channel_y; int64_t stride_channel_dst;
|
||||
int64_t nsamples_x; int64_t nsamples_y; int64_t stride_sample_x; int64_t stride_sample_y; int64_t stride_sample_dst;
|
||||
@@ -1324,7 +1395,7 @@ static void launch_mul_mat_q(ggml_backend_cuda_context & ctx, const mmq_args & a
|
||||
|
||||
if (!ggml_cuda_mmq_get_stream_k(type, J, fallback, cc)) {
|
||||
mul_mat_q<type, J, fallback><<<block_nums_xy_tiling, block_dims, nbytes_shared, stream>>>
|
||||
(args.x, args.y, args.ids_dst, args.expert_bounds, args.dst, nullptr,
|
||||
(args.x, args.y, args.ids_dst, args.expert_bounds, args.dst, nullptr, args.y_scale,
|
||||
blocks_per_ne00_fd, args.nrows_x, args.ncols_dst, args.stride_row_x, args.ncols_y, args.nrows_dst,
|
||||
channel_ratio_fd, nchannels_y_fd, args.stride_channel_x, args.stride_channel_y, args.stride_channel_dst,
|
||||
sample_ratio_fd, nsamples_y_fd, args.stride_sample_x, args.stride_sample_y, args.stride_sample_dst,
|
||||
@@ -1353,7 +1424,7 @@ static void launch_mul_mat_q(ggml_backend_cuda_context & ctx, const mmq_args & a
|
||||
const dim3 block_dims_fixup(block_dims.x, block_dims.y/2, block_dims.z);
|
||||
|
||||
mul_mat_q<type, J, fallback><<<block_nums_stream_k, block_dims, nbytes_shared, stream>>>
|
||||
(args.x, args.y, args.ids_dst, args.expert_bounds, args.dst, tmp_fixup.ptr,
|
||||
(args.x, args.y, args.ids_dst, args.expert_bounds, args.dst, tmp_fixup.ptr, args.y_scale,
|
||||
blocks_per_ne00_fd, args.nrows_x, args.ncols_dst, args.stride_row_x, args.ncols_y, args.nrows_dst,
|
||||
channel_ratio_fd, nchannels_y_fd, args.stride_channel_x, args.stride_channel_y, args.stride_channel_dst,
|
||||
sample_ratio_fd, nsamples_y_fd, args.stride_sample_x, args.stride_sample_y, args.stride_sample_dst,
|
||||
@@ -1467,26 +1538,30 @@ void mul_mat_q_case(ggml_backend_cuda_context & ctx, const mmq_args & args, cuda
|
||||
#define DECL_MMQ_CASE(type) \
|
||||
template void mul_mat_q_case<type>(ggml_backend_cuda_context & ctx, const mmq_args & args, cudaStream_t stream) \
|
||||
|
||||
extern DECL_MMQ_CASE(GGML_TYPE_Q1_0);
|
||||
extern DECL_MMQ_CASE(GGML_TYPE_Q4_0);
|
||||
extern DECL_MMQ_CASE(GGML_TYPE_Q4_1);
|
||||
extern DECL_MMQ_CASE(GGML_TYPE_Q5_0);
|
||||
extern DECL_MMQ_CASE(GGML_TYPE_Q5_1);
|
||||
extern DECL_MMQ_CASE(GGML_TYPE_Q8_0);
|
||||
extern DECL_MMQ_CASE(GGML_TYPE_MXFP4);
|
||||
extern DECL_MMQ_CASE(GGML_TYPE_NVFP4);
|
||||
// -----------------------------------------
|
||||
extern DECL_MMQ_CASE(GGML_TYPE_Q2_K);
|
||||
extern DECL_MMQ_CASE(GGML_TYPE_Q3_K);
|
||||
extern DECL_MMQ_CASE(GGML_TYPE_Q4_K);
|
||||
extern DECL_MMQ_CASE(GGML_TYPE_Q5_K);
|
||||
extern DECL_MMQ_CASE(GGML_TYPE_Q6_K);
|
||||
// -----------------------------------------
|
||||
extern DECL_MMQ_CASE(GGML_TYPE_IQ1_S);
|
||||
extern DECL_MMQ_CASE(GGML_TYPE_IQ2_XXS);
|
||||
extern DECL_MMQ_CASE(GGML_TYPE_IQ2_XS);
|
||||
extern DECL_MMQ_CASE(GGML_TYPE_IQ2_S);
|
||||
extern DECL_MMQ_CASE(GGML_TYPE_IQ3_XXS);
|
||||
extern DECL_MMQ_CASE(GGML_TYPE_IQ3_S);
|
||||
extern DECL_MMQ_CASE(GGML_TYPE_IQ1_S);
|
||||
extern DECL_MMQ_CASE(GGML_TYPE_IQ4_NL);
|
||||
extern DECL_MMQ_CASE(GGML_TYPE_IQ4_XS);
|
||||
// -----------------------------------------
|
||||
extern DECL_MMQ_CASE(GGML_TYPE_MXFP4);
|
||||
extern DECL_MMQ_CASE(GGML_TYPE_NVFP4);
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
+245
-94
@@ -1,6 +1,55 @@
|
||||
#include "quantize.cuh"
|
||||
#include <cstdint>
|
||||
|
||||
#if defined(BLACKWELL_MMA_AVAILABLE)
|
||||
// this maps to 256-bit loads in PTX on supported devices,
|
||||
// and otherwise falls back to 2 128-bit loads
|
||||
struct __builtin_align__(32) float8 {
|
||||
float x; float y; float z; float w;
|
||||
float p; float q; float r; float s;
|
||||
};
|
||||
#endif
|
||||
|
||||
#if CUDART_VERSION >= 12080
|
||||
static __device__ __forceinline__ float nvfp4_native_scale_error(
|
||||
const float vals[QK_NVFP4_SUB], const float inv_col_scale, const float inv_scale, const float scale) {
|
||||
const float scale_dequant = 2.0f * scale;
|
||||
float err = 0.0f;
|
||||
|
||||
#pragma unroll
|
||||
for (int k = 0; k < QK_NVFP4_SUB; k += 4) {
|
||||
const float v0 = vals[k + 0] * inv_col_scale;
|
||||
const float v1 = vals[k + 1] * inv_col_scale;
|
||||
const float v2 = vals[k + 2] * inv_col_scale;
|
||||
const float v3 = vals[k + 3] * inv_col_scale;
|
||||
|
||||
const __nv_fp4x4_e2m1 q(make_float4(v0 * inv_scale, v1 * inv_scale, v2 * inv_scale, v3 * inv_scale));
|
||||
const __nv_fp4x4_storage_t q_storage = q.__x;
|
||||
const __nv_fp4x2_storage_t q_lo = static_cast<__nv_fp4x2_storage_t>(q_storage);
|
||||
const __nv_fp4x2_storage_t q_hi = static_cast<__nv_fp4x2_storage_t>(q_storage >> 8U);
|
||||
|
||||
const __half2_raw hraw2_lo = __nv_cvt_fp4x2_to_halfraw2(q_lo, __NV_E2M1);
|
||||
const __half2_raw hraw2_hi = __nv_cvt_fp4x2_to_halfraw2(q_hi, __NV_E2M1);
|
||||
const __half2 h2_lo = static_cast<__half2>(hraw2_lo);
|
||||
const __half2 h2_hi = static_cast<__half2>(hraw2_hi);
|
||||
const float2 dq_lo = __half22float2(h2_lo);
|
||||
const float2 dq_hi = __half22float2(h2_hi);
|
||||
|
||||
const float err0 = fabsf(v0) - fabsf(dq_lo.x) * scale_dequant;
|
||||
const float err1 = fabsf(v1) - fabsf(dq_lo.y) * scale_dequant;
|
||||
const float err2 = fabsf(v2) - fabsf(dq_hi.x) * scale_dequant;
|
||||
const float err3 = fabsf(v3) - fabsf(dq_hi.y) * scale_dequant;
|
||||
|
||||
err = fmaf(err0, err0, err);
|
||||
err = fmaf(err1, err1, err);
|
||||
err = fmaf(err2, err2, err);
|
||||
err = fmaf(err3, err3, err);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
#endif // CUDART_VERSION >= 12080
|
||||
|
||||
__launch_bounds__(CUDA_QUANTIZE_BLOCK_SIZE, 1)
|
||||
static __global__ void quantize_q8_1(
|
||||
const float * x_ptr, void * vy_ptr,
|
||||
@@ -74,115 +123,209 @@ __device__ __forceinline__ uint8_t compute_e8m0_scale(float amax) {
|
||||
return static_cast<uint8_t>(biased);
|
||||
}
|
||||
|
||||
|
||||
// scatter: grid over tokens, quantize once, write to all the token's compact rows
|
||||
template <bool scatter>
|
||||
template <bool scatter, bool use_aligned_float8>
|
||||
static __global__ void quantize_mmq_nvfp4(
|
||||
const float * __restrict__ x, const int32_t * __restrict__ ids, void * __restrict__ vy,
|
||||
const float * __restrict__ x, const int32_t * __restrict__ ids, void * __restrict__ vy, float * __restrict__ scale,
|
||||
const int64_t ne00, const int64_t s01, const int64_t s02, const int64_t s03,
|
||||
const int64_t ne0, const int64_t ne1, const int64_t ne2, const int n_expert_used) {
|
||||
#if defined(BLACKWELL_MMA_AVAILABLE)
|
||||
|
||||
const int64_t i0_base = ((int64_t) blockDim.x * blockIdx.y + threadIdx.x) * QK_NVFP4_SUB;
|
||||
if (i0_base >= ne0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int64_t k_block = i0_base / QK_FP4_MMQ;
|
||||
const int64_t blocks_per_col = (ne0 + QK_FP4_MMQ - 1) / QK_FP4_MMQ;
|
||||
if (k_block >= blocks_per_col) {
|
||||
return;
|
||||
}
|
||||
const int sub = (i0_base % QK_FP4_MMQ) / QK_NVFP4_SUB;
|
||||
|
||||
int64_t base_idx;
|
||||
if constexpr (scatter) {
|
||||
base_idx = (int64_t) blockIdx.x * s02; // one physical row per token
|
||||
} else {
|
||||
const int64_t i2 = blockIdx.z % ne2;
|
||||
const int64_t i3 = blockIdx.z / ne2;
|
||||
const int64_t i2 = blockIdx.y % ne2;
|
||||
const int64_t i3 = blockIdx.y / ne2;
|
||||
const int64_t i01 = ids ? ids[blockIdx.x] : blockIdx.x;
|
||||
base_idx = i3 * s03 + i2 * s02 + i01 * s01;
|
||||
}
|
||||
const float * __restrict__ x_row = x + base_idx;
|
||||
|
||||
float vals_raw[QK_NVFP4_SUB];
|
||||
float amax_raw = 0.0f;
|
||||
float amax = 0.0f;
|
||||
if constexpr (use_aligned_float8) {
|
||||
for (int64_t i0 = 8 * threadIdx.x; i0 < ne00; i0 += 8 * blockDim.x) {
|
||||
const float * x_base = x_row + i0;
|
||||
const float8 v = reinterpret_cast<const float8 *>(x_base)[0];
|
||||
amax = fmaxf(amax, fabsf(v.x));
|
||||
amax = fmaxf(amax, fabsf(v.y));
|
||||
amax = fmaxf(amax, fabsf(v.z));
|
||||
amax = fmaxf(amax, fabsf(v.w));
|
||||
amax = fmaxf(amax, fabsf(v.p));
|
||||
amax = fmaxf(amax, fabsf(v.q));
|
||||
amax = fmaxf(amax, fabsf(v.r));
|
||||
amax = fmaxf(amax, fabsf(v.s));
|
||||
}
|
||||
} else {
|
||||
for (int64_t i0 = threadIdx.x; i0 < ne00; i0 += blockDim.x) {
|
||||
amax = fmaxf(amax, fabsf(x_row[i0]));
|
||||
}
|
||||
}
|
||||
|
||||
amax = warp_reduce_max<WARP_SIZE>(amax);
|
||||
|
||||
__shared__ float warp_amax[CUDA_QUANTIZE_BLOCK_SIZE_MMQ / WARP_SIZE];
|
||||
const int lane = threadIdx.x % WARP_SIZE;
|
||||
const int warp = threadIdx.x / WARP_SIZE;
|
||||
|
||||
if (lane == 0) {
|
||||
warp_amax[warp] = amax;
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
if (warp == 0) {
|
||||
amax = threadIdx.x < int(CUDA_QUANTIZE_BLOCK_SIZE_MMQ / WARP_SIZE) ? warp_amax[lane] : 0.0f;
|
||||
amax = warp_reduce_max<WARP_SIZE>(amax);
|
||||
if (lane == 0) {
|
||||
warp_amax[0] = amax / (6.0f * 448.0f);
|
||||
if constexpr (scatter) {
|
||||
#pragma unroll
|
||||
for (int k = 0; k < QK_NVFP4_SUB; k++) {
|
||||
const int64_t i00 = i0_base + k;
|
||||
if (i00 < ne00) {
|
||||
const float v = x[base_idx + i00];
|
||||
vals_raw[k] = v;
|
||||
amax_raw = fmaxf(amax_raw, fabsf(v));
|
||||
} else {
|
||||
vals_raw[k] = 0.0f;
|
||||
for (int slot = 0; slot < n_expert_used; ++slot) {
|
||||
const int64_t i = ids[(int64_t) blockIdx.x * n_expert_used + slot];
|
||||
scale[i] = warp_amax[0];
|
||||
}
|
||||
} else {
|
||||
scale[blockIdx.y * ne1 + blockIdx.x] = warp_amax[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr int test_offsets[5] = { 0, -1, 1, -2, 2};
|
||||
const int first_fp8_code = (int) ggml_cuda_fp32_to_ue4m3(amax_raw / 6.0f);
|
||||
|
||||
float best_err = FLT_MAX;
|
||||
uint8_t fp8_code = 0;
|
||||
float subblock_scale = 0.0f;
|
||||
|
||||
#pragma unroll // Check +/- 2 to find best code to reduce NVFP4 activation loss. Negligible overhead on Blackwell.
|
||||
for (int i = 0; i < 5; i++) {
|
||||
const int test_code = first_fp8_code + test_offsets[i];
|
||||
if (test_code < 0 || test_code > 0x7e) {
|
||||
continue;
|
||||
}
|
||||
const uint8_t code = (uint8_t) test_code;
|
||||
const float test_scale = ggml_cuda_ue4m3_to_fp32(code);
|
||||
const float test_inv_scale = test_scale > 0.0f ? 0.5f / test_scale : 0.0f;
|
||||
float cur_err = 0.0f;
|
||||
#pragma unroll
|
||||
for (int k = 0; k < QK_NVFP4_SUB; ++k) {
|
||||
const float v = vals_raw[k];
|
||||
const uint8_t q = ggml_cuda_float_to_fp4_e2m1(v, test_inv_scale);
|
||||
const float err_diff = fabsf(v) - fabsf(kvalues_mxfp4[q & 0x7]) * test_scale;
|
||||
cur_err = fmaf(err_diff, err_diff, cur_err);
|
||||
}
|
||||
|
||||
if (cur_err < best_err) {
|
||||
best_err = cur_err;
|
||||
fp8_code = test_code;
|
||||
subblock_scale = test_scale;
|
||||
}
|
||||
}
|
||||
|
||||
const float inv_scale = subblock_scale > 0.0f ? 0.5f / subblock_scale : 0.0f;
|
||||
uint32_t q0 = 0;
|
||||
uint32_t q1 = 0;
|
||||
#pragma unroll // this is faster than the previous __nv_fp4x4_e2m1
|
||||
for (int k = 0; k < QK_NVFP4_SUB / 4; ++k) {
|
||||
q0 |= (uint32_t) ggml_cuda_float_to_fp4_e2m1(vals_raw[k + 0], inv_scale) << (8 * k);
|
||||
q0 |= (uint32_t) ggml_cuda_float_to_fp4_e2m1(vals_raw[k + 8], inv_scale) << (8 * k + 4);
|
||||
q1 |= (uint32_t) ggml_cuda_float_to_fp4_e2m1(vals_raw[k + 4], inv_scale) << (8 * k);
|
||||
q1 |= (uint32_t) ggml_cuda_float_to_fp4_e2m1(vals_raw[k + 12], inv_scale) << (8 * k + 4);
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
block_fp4_mmq * y = (block_fp4_mmq *) vy;
|
||||
if constexpr (scatter) {
|
||||
const int64_t n_subblocks = (ne0 + QK_NVFP4_SUB - 1) / QK_NVFP4_SUB;
|
||||
|
||||
for (int64_t isb = threadIdx.x; isb < n_subblocks; isb += blockDim.x) {
|
||||
const int64_t i0_base = isb * QK_NVFP4_SUB;
|
||||
const int64_t k_block = i0_base / QK_FP4_MMQ;
|
||||
const int sub = (i0_base % QK_FP4_MMQ) / QK_NVFP4_SUB;
|
||||
|
||||
const float row_scale = warp_amax[0];
|
||||
const float inv_col_scale = row_scale > 0.0f ? 1.0f / row_scale : 0.0f;
|
||||
|
||||
float vals[QK_NVFP4_SUB];
|
||||
if constexpr (use_aligned_float8) {
|
||||
const float * x_base = x_row + i0_base;
|
||||
const float8 v0 = i0_base + 7 < ne00 ? reinterpret_cast<const float8 *>(x_base)[0] : float8{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
|
||||
const float8 v1 = i0_base + 15 < ne00 ? reinterpret_cast<const float8 *>(x_base + 8)[0] : float8{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
|
||||
vals[0] = v0.x; vals[1] = v0.y; vals[2] = v0.z; vals[3] = v0.w;
|
||||
vals[4] = v0.p; vals[5] = v0.q; vals[6] = v0.r; vals[7] = v0.s;
|
||||
vals[8] = v1.x; vals[9] = v1.y; vals[10] = v1.z; vals[11] = v1.w;
|
||||
vals[12] = v1.p; vals[13] = v1.q; vals[14] = v1.r; vals[15] = v1.s;
|
||||
} else {
|
||||
#pragma unroll
|
||||
for (int slot = 0; slot < n_expert_used; ++slot) {
|
||||
const int64_t i = ids[(int64_t) blockIdx.x * n_expert_used + slot];
|
||||
block_fp4_mmq * yb = y + (k_block * ne1 + i);
|
||||
for (int k = 0; k < QK_NVFP4_SUB; ++k) {
|
||||
const int64_t i00 = i0_base + k;
|
||||
vals[k] = i00 < ne00 ? x_row[i00] : 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t q0 = 0;
|
||||
uint32_t q1 = 0;
|
||||
|
||||
float amax_sub = 0.0f;
|
||||
#pragma unroll
|
||||
for (int k = 0; k < QK_NVFP4_SUB; ++k) {
|
||||
amax_sub = fmaxf(amax_sub, fabsf(vals[k] * inv_col_scale));
|
||||
}
|
||||
|
||||
static constexpr int test_offsets[5] = { 0, -1, 1, -2, 2 };
|
||||
const int first_fp8_code = (int) ggml_cuda_fp32_to_ue4m3(amax_sub / 6.0f);
|
||||
|
||||
uint8_t fp8_code = (uint8_t) first_fp8_code;
|
||||
float subblock_scale = ggml_cuda_ue4m3_to_fp32(fp8_code);
|
||||
float inv_scale_err = subblock_scale > 0.0f ? 0.5f / subblock_scale : 0.0f;
|
||||
#if CUDART_VERSION >= 12080
|
||||
float best_err = nvfp4_native_scale_error(vals, inv_col_scale, inv_scale_err, subblock_scale);
|
||||
#else
|
||||
float best_err = 0.0f;
|
||||
#pragma unroll
|
||||
for (int k = 0; k < QK_NVFP4_SUB; ++k) {
|
||||
const float v = vals[k] * inv_col_scale;
|
||||
const uint8_t q = ggml_cuda_float_to_fp4_e2m1(v, inv_scale_err);
|
||||
const float err_diff = fabsf(v) - fabsf(kvalues_fp4[q & 0x7]) * subblock_scale;
|
||||
best_err = fmaf(err_diff, err_diff, best_err);
|
||||
}
|
||||
#endif // CUDART_VERSION >= 12080
|
||||
|
||||
#pragma unroll
|
||||
for (int i = 1; i < 5; ++i) {
|
||||
const int test_code = first_fp8_code + test_offsets[i];
|
||||
if (test_code < 0 || test_code > 0x7e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const float test_scale = ggml_cuda_ue4m3_to_fp32((uint8_t) test_code);
|
||||
const float test_inv_scale = test_scale > 0.0f ? 0.5f / test_scale : 0.0f;
|
||||
#if CUDART_VERSION >= 12080
|
||||
const float cur_err = nvfp4_native_scale_error(vals, inv_col_scale, test_inv_scale, test_scale);
|
||||
#else
|
||||
float cur_err = 0.0f;
|
||||
#pragma unroll
|
||||
for (int k = 0; k < QK_NVFP4_SUB; ++k) {
|
||||
const float v = vals[k] * inv_col_scale;
|
||||
const uint8_t q = ggml_cuda_float_to_fp4_e2m1(v, test_inv_scale);
|
||||
const float err_diff = fabsf(v) - fabsf(kvalues_fp4[q & 0x7]) * test_scale;
|
||||
cur_err = fmaf(err_diff, err_diff, cur_err);
|
||||
}
|
||||
#endif // CUDART_VERSION >= 12080
|
||||
|
||||
if (cur_err < best_err) {
|
||||
best_err = cur_err;
|
||||
fp8_code = (uint8_t) test_code;
|
||||
subblock_scale = test_scale;
|
||||
}
|
||||
}
|
||||
#if CUDART_VERSION >= 12080
|
||||
const float inv_scale = subblock_scale > 0.0f ? 0.5f / subblock_scale : 0.0f;
|
||||
const float s = inv_col_scale * inv_scale;
|
||||
|
||||
__nv_fp4x4_e2m1 q0_lo(make_float4(vals[0] * s, vals[8] * s, vals[1] * s, vals[9] * s));
|
||||
__nv_fp4x4_e2m1 q0_hi(make_float4(vals[2] * s, vals[10] * s, vals[3] * s, vals[11] * s));
|
||||
__nv_fp4x4_e2m1 q1_lo(make_float4(vals[4] * s, vals[12] * s, vals[5] * s, vals[13] * s));
|
||||
__nv_fp4x4_e2m1 q1_hi(make_float4(vals[6] * s, vals[14] * s, vals[7] * s, vals[15] * s));
|
||||
|
||||
const char2 q0_lo_c = *reinterpret_cast<char2 *>(&q0_lo);
|
||||
const char2 q0_hi_c = *reinterpret_cast<char2 *>(&q0_hi);
|
||||
const char2 q1_lo_c = *reinterpret_cast<char2 *>(&q1_lo);
|
||||
const char2 q1_hi_c = *reinterpret_cast<char2 *>(&q1_hi);
|
||||
|
||||
q0 = uint32_t(uint8_t(q0_lo_c.x)) | (uint32_t(uint8_t(q0_lo_c.y)) << 8) |
|
||||
(uint32_t(uint8_t(q0_hi_c.x)) << 16) | (uint32_t(uint8_t(q0_hi_c.y)) << 24);
|
||||
q1 = uint32_t(uint8_t(q1_lo_c.x)) | (uint32_t(uint8_t(q1_lo_c.y)) << 8) |
|
||||
(uint32_t(uint8_t(q1_hi_c.x)) << 16) | (uint32_t(uint8_t(q1_hi_c.y)) << 24);
|
||||
#else
|
||||
const float inv_scale = subblock_scale > 0.0f ? 0.5f / subblock_scale : 0.0f;
|
||||
#pragma unroll
|
||||
for (int k = 0; k < QK_NVFP4_SUB / 4; ++k) {
|
||||
q0 |= uint32_t(ggml_cuda_float_to_fp4_e2m1(vals[k + 0] * inv_col_scale, inv_scale)) << (8 * k);
|
||||
q0 |= uint32_t(ggml_cuda_float_to_fp4_e2m1(vals[k + 8] * inv_col_scale, inv_scale)) << (8 * k + 4);
|
||||
q1 |= uint32_t(ggml_cuda_float_to_fp4_e2m1(vals[k + 4] * inv_col_scale, inv_scale)) << (8 * k);
|
||||
q1 |= uint32_t(ggml_cuda_float_to_fp4_e2m1(vals[k + 12] * inv_col_scale, inv_scale)) << (8 * k + 4);
|
||||
}
|
||||
#endif // CUDART_VERSION >= 12080
|
||||
|
||||
if constexpr (scatter) {
|
||||
#pragma unroll
|
||||
for (int slot = 0; slot < n_expert_used; ++slot) {
|
||||
const int64_t i = ids[(int64_t) blockIdx.x * n_expert_used + slot];
|
||||
block_fp4_mmq * yb = y + (k_block * ne1 + i);
|
||||
uint32_t * yqs = reinterpret_cast<uint32_t *>(yb->qs);
|
||||
yqs[2 * sub + 0] = q0;
|
||||
yqs[2 * sub + 1] = q1;
|
||||
reinterpret_cast<uint8_t *>(yb->d4)[sub] = fp8_code;
|
||||
}
|
||||
} else {
|
||||
block_fp4_mmq * yb = y + (blockIdx.y * ((int64_t) blocks_per_col * ne1) + k_block * ne1 + blockIdx.x);
|
||||
uint32_t * yqs = reinterpret_cast<uint32_t *>(yb->qs);
|
||||
yqs[2 * sub + 0] = q0;
|
||||
yqs[2 * sub + 1] = q1;
|
||||
reinterpret_cast<uint8_t *>(yb->d4)[sub] = fp8_code;
|
||||
}
|
||||
} else {
|
||||
block_fp4_mmq * yb = y + (blockIdx.z * ((int64_t) blocks_per_col * ne1) + k_block * ne1 + blockIdx.x);
|
||||
uint32_t * yqs = reinterpret_cast<uint32_t *>(yb->qs);
|
||||
yqs[2 * sub + 0] = q0;
|
||||
yqs[2 * sub + 1] = q1;
|
||||
reinterpret_cast<uint8_t *>(yb->d4)[sub] = fp8_code;
|
||||
}
|
||||
GGML_UNUSED(n_expert_used);
|
||||
#else
|
||||
GGML_UNUSED(n_expert_used);
|
||||
GGML_UNUSED_VARS(x, ids, vy, scale, ne00, s01, s02, s03, ne0, ne1, ne2, n_expert_used);
|
||||
NO_DEVICE_CODE; // This is for Blackwell NVFP4 activations only.
|
||||
#endif // defined(BLACKWELL_MMA_AVAILABLE)
|
||||
|
||||
@@ -491,18 +634,22 @@ void quantize_scatter_mmq_q8_1_cuda(
|
||||
|
||||
// scatter=true reuses the quant kernels: grid over tokens, ids = inverse map (token slot -> compact row)
|
||||
void quantize_scatter_mmq_fp4_cuda(
|
||||
const float * x, const int32_t * ids_src1_inv, void * vy, const ggml_type type_src0,
|
||||
const float * x, const int32_t * ids_src1_inv, void * vy, float * scale, const ggml_type type_src0, const bool use_aligned_float8,
|
||||
const int64_t ne00, const int64_t stride_token, const int64_t ne0,
|
||||
const int64_t n_tokens, const int64_t nrows_dst, const int n_expert_used, cudaStream_t stream) {
|
||||
GGML_ASSERT(ne0 > 0);
|
||||
if (type_src0 == GGML_TYPE_NVFP4) {
|
||||
GGML_ASSERT(scale);
|
||||
GGML_ASSERT(ne00 % QK_NVFP4 == 0);
|
||||
constexpr int nvfp4_block_size = 128;
|
||||
const int64_t block_num_y = (ne0 + QK_NVFP4_SUB * nvfp4_block_size - 1) / (QK_NVFP4_SUB * nvfp4_block_size);
|
||||
const dim3 block_size(nvfp4_block_size, 1, 1);
|
||||
const dim3 num_blocks(n_tokens, block_num_y, 1);
|
||||
quantize_mmq_nvfp4<true><<<num_blocks, block_size, 0, stream>>>(
|
||||
x, ids_src1_inv, vy, ne00, /*s01=*/0, /*s02=*/stride_token, /*s03=*/0, ne0, /*ne1=*/nrows_dst, /*ne2=*/1, n_expert_used);
|
||||
const dim3 block_size(CUDA_QUANTIZE_BLOCK_SIZE_MMQ, 1, 1);
|
||||
const dim3 num_blocks(n_tokens, 1, 1);
|
||||
if (use_aligned_float8) {
|
||||
quantize_mmq_nvfp4<true, true><<<num_blocks, block_size, 0, stream>>>(
|
||||
x, ids_src1_inv, vy, scale, ne00, /*s01=*/0, /*s02=*/stride_token, /*s03=*/0, ne0, /*ne1=*/nrows_dst, /*ne2=*/1, n_expert_used);
|
||||
} else {
|
||||
quantize_mmq_nvfp4<true, false><<<num_blocks, block_size, 0, stream>>>(
|
||||
x, ids_src1_inv, vy, scale, ne00, /*s01=*/0, /*s02=*/stride_token, /*s03=*/0, ne0, /*ne1=*/nrows_dst, /*ne2=*/1, n_expert_used);
|
||||
}
|
||||
} else {
|
||||
GGML_ASSERT(type_src0 == GGML_TYPE_MXFP4);
|
||||
constexpr int nwarps = 8;
|
||||
@@ -516,20 +663,24 @@ void quantize_scatter_mmq_fp4_cuda(
|
||||
}
|
||||
|
||||
void quantize_mmq_fp4_cuda(
|
||||
const float * x, const int32_t * ids, void * vy, const ggml_type type_src0,
|
||||
const float * x, const int32_t * ids, void * vy, float * scale, const ggml_type type_src0, const bool use_aligned_float8,
|
||||
const int64_t ne00, const int64_t s01, const int64_t s02, const int64_t s03,
|
||||
const int64_t ne0, const int64_t ne1, const int64_t ne2, const int64_t ne3, cudaStream_t stream) {
|
||||
GGML_ASSERT(type_src0 == GGML_TYPE_MXFP4 || type_src0 == GGML_TYPE_NVFP4);
|
||||
GGML_ASSERT(ne0 > 0);
|
||||
|
||||
if (type_src0 == GGML_TYPE_NVFP4) {
|
||||
GGML_ASSERT(scale);
|
||||
GGML_ASSERT(ne00 % QK_NVFP4 == 0);
|
||||
constexpr int nvfp4_block_size = 128;
|
||||
const int64_t block_num_y = (ne0 + QK_NVFP4_SUB * nvfp4_block_size - 1) / (QK_NVFP4_SUB * nvfp4_block_size);
|
||||
const dim3 block_size(nvfp4_block_size, 1, 1);
|
||||
const dim3 num_blocks(ne1, block_num_y, ne2 * ne3);
|
||||
quantize_mmq_nvfp4<false><<<num_blocks, block_size, 0, stream>>>(
|
||||
x, ids, vy, ne00, s01, s02, s03, ne0, ne1, ne2, /*n_expert_used=*/0);
|
||||
const dim3 block_size(CUDA_QUANTIZE_BLOCK_SIZE_MMQ, 1, 1);
|
||||
const dim3 num_blocks(ne1, ne2 * ne3, 1);
|
||||
if (use_aligned_float8) {
|
||||
quantize_mmq_nvfp4<false, true><<<num_blocks, block_size, 0, stream>>>(
|
||||
x, ids, vy, scale, ne00, s01, s02, s03, ne0, ne1, ne2, /*n_expert_used=*/0);
|
||||
} else {
|
||||
quantize_mmq_nvfp4<false, false><<<num_blocks, block_size, 0, stream>>>(
|
||||
x, ids, vy, scale, ne00, s01, s02, s03, ne0, ne1, ne2, /*n_expert_used=*/0);
|
||||
}
|
||||
} else {
|
||||
GGML_ASSERT(ne0 % (2 * QK_MXFP4) == 0);
|
||||
|
||||
|
||||
@@ -29,7 +29,9 @@ void quantize_mmq_q8_1_cuda(
|
||||
void quantize_mmq_fp4_cuda(const float * x,
|
||||
const int32_t * ids,
|
||||
void * vy,
|
||||
float * scale,
|
||||
ggml_type type_src0,
|
||||
bool use_aligned_float8,
|
||||
int64_t ne00,
|
||||
int64_t s01,
|
||||
int64_t s02,
|
||||
@@ -44,7 +46,9 @@ void quantize_mmq_fp4_cuda(const float * x,
|
||||
void quantize_scatter_mmq_fp4_cuda(const float * x,
|
||||
const int32_t * ids_src1_inv,
|
||||
void * vy,
|
||||
float * scale,
|
||||
ggml_type type_src0,
|
||||
bool use_aligned_float8,
|
||||
int64_t ne00,
|
||||
int64_t stride_token,
|
||||
int64_t ne0,
|
||||
|
||||
@@ -1224,8 +1224,9 @@ bool ggml_metal_device_supports_op(ggml_metal_device_t dev, const struct ggml_te
|
||||
(ggml_get_op_params_i32(op, 4) == 0) && (ggml_get_op_params_i32(op, 6) == 0);
|
||||
case GGML_OP_PAD_REFLECT_1D:
|
||||
case GGML_OP_TIMESTEP_EMBEDDING:
|
||||
case GGML_OP_LEAKY_RELU:
|
||||
return op->src[0]->type == GGML_TYPE_F32;
|
||||
case GGML_OP_LEAKY_RELU:
|
||||
return op->src[0]->type == GGML_TYPE_F32 || op->src[0]->type == GGML_TYPE_F16;
|
||||
case GGML_OP_ARGSORT:
|
||||
case GGML_OP_TOP_K:
|
||||
case GGML_OP_ARANGE:
|
||||
|
||||
@@ -0,0 +1,453 @@
|
||||
// Match the version setup ggml-opencl.cpp uses, so any cl.h declarations we
|
||||
// touch are consistent across this backend's translation units.
|
||||
#define CL_TARGET_OPENCL_VERSION GGML_OPENCL_TARGET_VERSION
|
||||
#define CL_USE_DEPRECATED_OPENCL_1_2_APIS
|
||||
|
||||
#include "cl-program-cache.h"
|
||||
|
||||
#include "ggml-impl.h" // GGML_LOG_INFO / WARN
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <system_error>
|
||||
#include <vector>
|
||||
|
||||
#if defined(_WIN32)
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
# ifndef NOMINMAX
|
||||
# define NOMINMAX
|
||||
# endif
|
||||
# include <windows.h>
|
||||
# include <process.h>
|
||||
# define ggml_getpid() ((int) GetCurrentProcessId())
|
||||
#else
|
||||
# include <unistd.h>
|
||||
# define ggml_getpid() ((int) getpid())
|
||||
#endif
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// SHA-256 (FIPS 180-4). Self-contained, ~80 lines, public-domain reference.
|
||||
// Hot path is a few KB of source per kernel ⇒ <1 ms total per process init.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
namespace {
|
||||
|
||||
struct sha256_ctx {
|
||||
uint32_t state[8];
|
||||
uint64_t bitlen;
|
||||
uint8_t buf[64];
|
||||
size_t buf_len;
|
||||
};
|
||||
|
||||
const uint32_t K256[64] = {
|
||||
0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
|
||||
0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
|
||||
0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
|
||||
0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
|
||||
0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
|
||||
0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
|
||||
0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
|
||||
0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2,
|
||||
};
|
||||
|
||||
inline uint32_t rotr32(uint32_t x, unsigned n) { return (x >> n) | (x << (32 - n)); }
|
||||
|
||||
void sha256_compress(uint32_t state[8], const uint8_t block[64]) {
|
||||
uint32_t w[64];
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
w[i] = ((uint32_t)block[i*4 ] << 24) |
|
||||
((uint32_t)block[i*4 + 1] << 16) |
|
||||
((uint32_t)block[i*4 + 2] << 8) |
|
||||
((uint32_t)block[i*4 + 3] );
|
||||
}
|
||||
for (int i = 16; i < 64; ++i) {
|
||||
uint32_t s0 = rotr32(w[i-15], 7) ^ rotr32(w[i-15], 18) ^ (w[i-15] >> 3);
|
||||
uint32_t s1 = rotr32(w[i-2], 17) ^ rotr32(w[i-2], 19) ^ (w[i-2] >> 10);
|
||||
w[i] = w[i-16] + s0 + w[i-7] + s1;
|
||||
}
|
||||
|
||||
uint32_t a = state[0],b = state[1],c = state[2],d = state[3],e = state[4],f = state[5],g = state[6],h = state[7];
|
||||
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
uint32_t S1 = rotr32(e, 6) ^ rotr32(e, 11) ^ rotr32(e, 25);
|
||||
uint32_t ch = (e & f) ^ ((~e) & g);
|
||||
uint32_t t1 = h + S1 + ch + K256[i] + w[i];
|
||||
uint32_t S0 = rotr32(a, 2) ^ rotr32(a, 13) ^ rotr32(a, 22);
|
||||
uint32_t maj = (a & b) ^ (a & c) ^ (b & c);
|
||||
uint32_t t2 = S0 + maj;
|
||||
h = g; g = f; f = e; e = d + t1;
|
||||
d = c; c = b; b = a; a = t1 + t2;
|
||||
}
|
||||
state[0]+=a; state[1]+=b; state[2]+=c; state[3]+=d;
|
||||
state[4]+=e; state[5]+=f; state[6]+=g; state[7]+=h;
|
||||
}
|
||||
|
||||
void sha256_init(sha256_ctx & c) {
|
||||
c.state[0]=0x6a09e667; c.state[1]=0xbb67ae85; c.state[2]=0x3c6ef372; c.state[3]=0xa54ff53a;
|
||||
c.state[4]=0x510e527f; c.state[5]=0x9b05688c; c.state[6]=0x1f83d9ab; c.state[7]=0x5be0cd19;
|
||||
c.bitlen = 0;
|
||||
c.buf_len = 0;
|
||||
}
|
||||
|
||||
void sha256_update(sha256_ctx & c, const void * data, size_t len) {
|
||||
const uint8_t * p = (const uint8_t *) data;
|
||||
c.bitlen += (uint64_t) len * 8;
|
||||
if (c.buf_len > 0) {
|
||||
size_t n = 64 - c.buf_len;
|
||||
if (n > len) { n = len; }
|
||||
memcpy(c.buf + c.buf_len, p, n);
|
||||
c.buf_len += n;
|
||||
p += n;
|
||||
len -= n;
|
||||
if (c.buf_len == 64) {
|
||||
sha256_compress(c.state, c.buf);
|
||||
c.buf_len = 0;
|
||||
}
|
||||
}
|
||||
while (len >= 64) {
|
||||
sha256_compress(c.state, p);
|
||||
p += 64;
|
||||
len -= 64;
|
||||
}
|
||||
if (len > 0) {
|
||||
memcpy(c.buf, p, len);
|
||||
c.buf_len = len;
|
||||
}
|
||||
}
|
||||
|
||||
void sha256_final(sha256_ctx & c, uint8_t out[32]) {
|
||||
uint64_t bitlen = c.bitlen;
|
||||
c.buf[c.buf_len++] = 0x80;
|
||||
if (c.buf_len > 56) {
|
||||
while (c.buf_len < 64) { c.buf[c.buf_len++] = 0; }
|
||||
sha256_compress(c.state, c.buf);
|
||||
c.buf_len = 0;
|
||||
}
|
||||
while (c.buf_len < 56) { c.buf[c.buf_len++] = 0; }
|
||||
for (int i = 7; i >= 0; --i) { c.buf[c.buf_len++] = (uint8_t) (bitlen >> (i * 8)); }
|
||||
sha256_compress(c.state, c.buf);
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
out[i*4 ] = (uint8_t) (c.state[i] >> 24);
|
||||
out[i*4 + 1] = (uint8_t) (c.state[i] >> 16);
|
||||
out[i*4 + 2] = (uint8_t) (c.state[i] >> 8);
|
||||
out[i*4 + 3] = (uint8_t) (c.state[i] );
|
||||
}
|
||||
}
|
||||
|
||||
std::string sha256_hex(const uint8_t digest[32]) {
|
||||
static const char hex[] = "0123456789abcdef";
|
||||
std::string s(64, '0');
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
s[i*2 ] = hex[digest[i] >> 4];
|
||||
s[i*2 + 1] = hex[digest[i] & 0xf];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string compute_key(const std::string & key_suffix,
|
||||
const char * source,
|
||||
const std::string & compile_opts) {
|
||||
sha256_ctx c;
|
||||
sha256_init(c);
|
||||
|
||||
static const uint8_t sep = 0;
|
||||
sha256_update(c, source, strlen(source));
|
||||
sha256_update(c, &sep, 1);
|
||||
sha256_update(c, compile_opts.data(), compile_opts.size());
|
||||
sha256_update(c, &sep, 1);
|
||||
sha256_update(c, key_suffix.data(), key_suffix.size());
|
||||
|
||||
uint8_t digest[32];
|
||||
sha256_final(c, digest);
|
||||
return sha256_hex(digest);
|
||||
}
|
||||
|
||||
bool make_dir_recursive(const std::string & path) {
|
||||
if (path.empty()) { return false; }
|
||||
// create_directories() already creates missing parents. It returns false
|
||||
// (with ec clear) when the directory is already there, so re-check.
|
||||
const fs::path p = fs::u8path(path);
|
||||
std::error_code ec;
|
||||
if (fs::create_directories(p, ec)) { return true; }
|
||||
std::error_code ec_stat;
|
||||
return fs::is_directory(p, ec_stat);
|
||||
}
|
||||
|
||||
std::string default_cache_dir() {
|
||||
#if defined(_WIN32)
|
||||
const char * base = std::getenv("LOCALAPPDATA");
|
||||
if (!base || !*base) { base = std::getenv("APPDATA"); }
|
||||
if (!base || !*base) { base = std::getenv("TEMP"); }
|
||||
if (!base || !*base) { base = "."; }
|
||||
return std::string(base) + "\\llama.cpp\\cl-cache";
|
||||
#elif defined(__APPLE__)
|
||||
const char * home = std::getenv("HOME");
|
||||
if (!home || !*home) { home = "."; }
|
||||
return std::string(home) + "/Library/Caches/llama.cpp/cl-cache";
|
||||
#else
|
||||
// The throwing overload aborts the process when no usable temp directory
|
||||
// exists (e.g. Android app contexts with TMPDIR unset); an empty return
|
||||
// here just disables the cache instead.
|
||||
std::error_code ec;
|
||||
const fs::path tmp_path = fs::temp_directory_path(ec);
|
||||
if (ec || tmp_path.empty()) { return {}; }
|
||||
return tmp_path.string() + "/llama.cpp/cl-cache";
|
||||
#endif
|
||||
}
|
||||
|
||||
// Query a NUL-terminated string from clGetDeviceInfo / clGetPlatformInfo.
|
||||
template <typename GetInfoFn, typename Object>
|
||||
std::string query_string(GetInfoFn fn, Object obj, cl_uint name) {
|
||||
size_t sz = 0;
|
||||
if (fn(obj, name, 0, nullptr, &sz) != CL_SUCCESS || sz == 0) {
|
||||
return {};
|
||||
}
|
||||
std::string s(sz, '\0');
|
||||
if (fn(obj, name, sz, &s[0], nullptr) != CL_SUCCESS) {
|
||||
return {};
|
||||
}
|
||||
if (!s.empty() && s.back() == '\0') {
|
||||
s.pop_back();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string compute_key_suffix(cl_device_id device) {
|
||||
cl_platform_id platform = nullptr;
|
||||
clGetDeviceInfo(device, CL_DEVICE_PLATFORM, sizeof(platform), &platform, nullptr);
|
||||
|
||||
std::string s;
|
||||
s.reserve(512);
|
||||
s += query_string(clGetDeviceInfo, device, CL_DEVICE_NAME); s.push_back('\0');
|
||||
s += query_string(clGetDeviceInfo, device, CL_DRIVER_VERSION); s.push_back('\0');
|
||||
s += query_string(clGetDeviceInfo, device, CL_DEVICE_VERSION); s.push_back('\0');
|
||||
if (platform) {
|
||||
s += query_string(clGetPlatformInfo, platform, CL_PLATFORM_VERSION); s.push_back('\0');
|
||||
}
|
||||
s += "fmt=" + std::to_string(CL_PROGRAM_CACHE_FORMAT_VERSION);
|
||||
return s;
|
||||
}
|
||||
|
||||
const uint8_t MAGIC[8] = { 'G','G','M','L','C','L','B','C' };
|
||||
|
||||
bool read_all(const std::string & path, std::vector<uint8_t> & out) {
|
||||
std::ifstream f(fs::u8path(path), std::ios::binary);
|
||||
if (!f) { return false; }
|
||||
f.seekg(0, std::ios::end);
|
||||
std::streamsize sz = f.tellg();
|
||||
if (sz < 0) { return false; }
|
||||
f.seekg(0, std::ios::beg);
|
||||
out.resize((size_t) sz);
|
||||
if (sz > 0) { f.read((char *) out.data(), sz); }
|
||||
return f.good() || f.eof();
|
||||
}
|
||||
|
||||
bool write_atomic(const std::string & path, const uint8_t * data, size_t len) {
|
||||
const fs::path dst = fs::u8path(path);
|
||||
const fs::path tmp = fs::u8path(path + ".tmp." + std::to_string(ggml_getpid()));
|
||||
{
|
||||
std::ofstream f(tmp, std::ios::binary | std::ios::trunc);
|
||||
if (!f) { return false; }
|
||||
f.write((const char *) data, (std::streamsize) len);
|
||||
if (!f.good()) {
|
||||
std::error_code ec_rm;
|
||||
fs::remove(tmp, ec_rm);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
fs::rename(tmp, dst, ec);
|
||||
if (ec) {
|
||||
std::error_code ec_rm;
|
||||
fs::remove(tmp, ec_rm);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
static bool cache_debug_enabled() {
|
||||
static int cached = -1;
|
||||
if (cached < 0) {
|
||||
const char * e = std::getenv("GGML_OPENCL_KERNEL_CACHE_DEBUG");
|
||||
cached = (e && *e) ? 1 : 0;
|
||||
}
|
||||
return cached != 0;
|
||||
}
|
||||
|
||||
static std::string opts_preview(const std::string & opts, size_t n = 120) {
|
||||
if (opts.size() <= n) { return opts; }
|
||||
return opts.substr(0, n) + "...";
|
||||
}
|
||||
|
||||
// Running cache tally (diagnostic; plain ints — a benign race in the rare
|
||||
// multi-threaded lazy-compile case at worst miscounts by one).
|
||||
static int g_cache_hits = 0, g_cache_misses = 0, g_cache_saves = 0;
|
||||
|
||||
// Debug trace directly to stderr
|
||||
static void cache_debug_line(const char * kind, const std::string & key,
|
||||
const char * source, const std::string & opts) {
|
||||
if (!cache_debug_enabled()) { return; }
|
||||
fprintf(stderr, "ggml_opencl: cache %-4s [h=%d m=%d s=%d] key=%s src=%zuB opts='%s'\n",
|
||||
kind, g_cache_hits, g_cache_misses, g_cache_saves,
|
||||
key.substr(0, 16).c_str(), strlen(source), opts_preview(opts).c_str());
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
cl_program_cache_state cl_program_cache_init(cl_device_id device) {
|
||||
cl_program_cache_state st;
|
||||
|
||||
const char * env = std::getenv("GGML_OPENCL_KERNEL_CACHE_DIR");
|
||||
if (env && (!std::strcmp(env, "0") || !std::strcmp(env, "off") ||
|
||||
!std::strcmp(env, "none") || !std::strcmp(env, "disable") ||
|
||||
!std::strcmp(env, "disabled"))) {
|
||||
if (cache_debug_enabled()) {
|
||||
fprintf(stderr, "ggml_opencl: kernel cache disabled by GGML_OPENCL_KERNEL_CACHE_DIR=%s\n", env);
|
||||
fflush(stderr);
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
std::string dir;
|
||||
if (!env || !*env || !std::strcmp(env, "1") || !std::strcmp(env, "default")) {
|
||||
dir = default_cache_dir();
|
||||
if (dir.empty()) {
|
||||
GGML_LOG_INFO("ggml_opencl: kernel cache disabled (no usable default cache directory)\n");
|
||||
return st;
|
||||
}
|
||||
} else {
|
||||
dir = env;
|
||||
}
|
||||
|
||||
if (!make_dir_recursive(dir)) {
|
||||
GGML_LOG_INFO("ggml_opencl: kernel cache disabled (cannot create directory '%s')\n", dir.c_str());
|
||||
return st;
|
||||
}
|
||||
|
||||
st.dir = dir;
|
||||
st.key_suffix = compute_key_suffix(device);
|
||||
GGML_LOG_INFO("ggml_opencl: kernel cache enabled at '%s'\n", st.dir.c_str());
|
||||
if (cache_debug_enabled()) {
|
||||
fprintf(stderr, "ggml_opencl: kernel cache enabled at '%s' "
|
||||
"(GGML_OPENCL_KERNEL_CACHE_DIR=off to disable)\n", st.dir.c_str());
|
||||
fflush(stderr);
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
cl_program cl_program_cache_try_load(
|
||||
const cl_program_cache_state & state,
|
||||
cl_context context,
|
||||
cl_device_id device,
|
||||
const char * source,
|
||||
const std::string & compile_opts) {
|
||||
|
||||
if (state.dir.empty() || !source) { return nullptr; }
|
||||
|
||||
const std::string key = compute_key(state.key_suffix, source, compile_opts);
|
||||
const std::string path = state.dir + "/" + key + ".clbin";
|
||||
|
||||
std::vector<uint8_t> file;
|
||||
if (!read_all(path, file)) {
|
||||
++g_cache_misses;
|
||||
cache_debug_line("MISS", key, source, compile_opts);
|
||||
return nullptr;
|
||||
}
|
||||
if (file.size() < 16 || std::memcmp(file.data(), MAGIC, 8) != 0) { return nullptr; }
|
||||
|
||||
uint32_t fmt =
|
||||
((uint32_t) file[ 8]) | ((uint32_t) file[ 9] << 8) |
|
||||
((uint32_t) file[10] << 16) | ((uint32_t) file[11] << 24);
|
||||
if (fmt != CL_PROGRAM_CACHE_FORMAT_VERSION) { return nullptr; }
|
||||
|
||||
const size_t hdr_len = 16;
|
||||
const unsigned char * bin = file.data() + hdr_len;
|
||||
const size_t bin_len = file.size() - hdr_len;
|
||||
|
||||
cl_int err = CL_SUCCESS;
|
||||
cl_int bin_err = CL_SUCCESS;
|
||||
cl_program p = clCreateProgramWithBinary(context, 1, &device, &bin_len, &bin, &bin_err, &err);
|
||||
if (err != CL_SUCCESS || bin_err != CL_SUCCESS || p == nullptr) {
|
||||
if (p) { clReleaseProgram(p); }
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
err = clBuildProgram(p, 0, nullptr, compile_opts.c_str(), nullptr, nullptr);
|
||||
if (err != CL_SUCCESS) {
|
||||
clReleaseProgram(p);
|
||||
return nullptr;
|
||||
}
|
||||
++g_cache_hits;
|
||||
cache_debug_line("HIT", key, source, compile_opts);
|
||||
return p;
|
||||
}
|
||||
|
||||
void cl_program_cache_try_save(
|
||||
const cl_program_cache_state & state,
|
||||
cl_program program,
|
||||
cl_device_id /*device*/,
|
||||
const char * source,
|
||||
const std::string & compile_opts) {
|
||||
|
||||
if (state.dir.empty() || !program || !source) {
|
||||
return;
|
||||
}
|
||||
|
||||
cl_uint n_dev = 0;
|
||||
if (clGetProgramInfo(program, CL_PROGRAM_NUM_DEVICES, sizeof(n_dev), &n_dev, nullptr) != CL_SUCCESS || n_dev == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<size_t> sizes(n_dev);
|
||||
if (clGetProgramInfo(program, CL_PROGRAM_BINARY_SIZES, sizeof(size_t) * n_dev, sizes.data(), nullptr) != CL_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
if (sizes.empty() || sizes[0] == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::vector<uint8_t>> binaries(n_dev);
|
||||
std::vector<unsigned char *> bin_ptrs(n_dev);
|
||||
for (cl_uint i = 0; i < n_dev; ++i) {
|
||||
binaries[i].resize(sizes[i]);
|
||||
bin_ptrs[i] = binaries[i].data();
|
||||
}
|
||||
if (clGetProgramInfo(program, CL_PROGRAM_BINARIES, sizeof(unsigned char *) * n_dev, bin_ptrs.data(), nullptr) != CL_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We only care about the first device's binary — that's the one we'd
|
||||
// re-load with on a future cache hit. Multi-device contexts aren't a
|
||||
// pattern this backend uses today.
|
||||
const std::vector<uint8_t> & bin = binaries[0];
|
||||
|
||||
std::vector<uint8_t> file;
|
||||
file.reserve(16 + bin.size());
|
||||
file.insert(file.end(), MAGIC, MAGIC + 8);
|
||||
uint32_t fmt = CL_PROGRAM_CACHE_FORMAT_VERSION;
|
||||
file.push_back((uint8_t) (fmt & 0xff));
|
||||
file.push_back((uint8_t) ((fmt >> 8) & 0xff));
|
||||
file.push_back((uint8_t) ((fmt >> 16) & 0xff));
|
||||
file.push_back((uint8_t) ((fmt >> 24) & 0xff));
|
||||
file.push_back(0); file.push_back(0); file.push_back(0); file.push_back(0); // reserved
|
||||
file.insert(file.end(), bin.begin(), bin.end());
|
||||
|
||||
const std::string key = compute_key(state.key_suffix, source, compile_opts);
|
||||
const std::string path = state.dir + "/" + key + ".clbin";
|
||||
if (!write_atomic(path, file.data(), file.size())) {
|
||||
GGML_LOG_INFO("ggml_opencl: kernel cache: failed to write '%s'\n", path.c_str());
|
||||
} else {
|
||||
++g_cache_saves;
|
||||
cache_debug_line("SAVE", key, source, compile_opts);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// On-disk cache for OpenCL cl_program binaries. Lets a fresh process skip the
|
||||
// expensive clBuildProgram-from-source step when a binary for the exact same
|
||||
// (source, compile options, device, driver, platform) was previously saved.
|
||||
//
|
||||
// Activation: default on via GGML_OPENCL_KERNEL_CACHE_DIR:
|
||||
// unset / empty / "1" / "default" : platform default cache dir
|
||||
// (%LOCALAPPDATA%\llama.cpp\cl-cache,
|
||||
// ~/Library/Caches/llama.cpp/cl-cache,
|
||||
// <temp dir>/llama.cpp/cl-cache elsewhere)
|
||||
// "0" / "off" / "none" / "disable(d)" : disabled (all functions no-op)
|
||||
// any other value : used verbatim as the cache path
|
||||
// If the chosen directory cannot be created/used, the cache silently disables
|
||||
// itself for the process and falls back to source compile.
|
||||
// GGML_OPENCL_KERNEL_CACHE_DEBUG=1 prints a HIT/MISS/SAVE trace (with a running
|
||||
// tally) straight to stderr — visible even in tools that filter INFO/WARN logs;
|
||||
// redirect stderr to record it.
|
||||
//
|
||||
// Cache key (SHA-256 hex):
|
||||
// sha256(source_bytes || '\x00' ||
|
||||
// compile_opts || '\x00' ||
|
||||
// CL_DEVICE_NAME || '\x00' ||
|
||||
// CL_DRIVER_VERSION || '\x00' ||
|
||||
// CL_PLATFORM_VERSION || '\x00' ||
|
||||
// CL_PROGRAM_CACHE_FORMAT_VERSION)
|
||||
//
|
||||
// The key fully captures everything that can affect the produced binary,
|
||||
// without needing the host source revision (a kernel source change shows up
|
||||
// in source_bytes; a compile-option change shows up in compile_opts).
|
||||
//
|
||||
// File layout per cache entry: <cache_dir>/<sha256-hex>.clbin
|
||||
// bytes [0..7] : magic "GGMLCLBC"
|
||||
// bytes [8..11] : uint32_t format version (CL_PROGRAM_CACHE_FORMAT_VERSION)
|
||||
// bytes [12..15] : uint32_t reserved (0)
|
||||
// bytes [16..] : raw cl_program binary as returned by
|
||||
// clGetProgramInfo(CL_PROGRAM_BINARIES)
|
||||
//
|
||||
// Concurrency: writes go to <name>.tmp.<pid> then atomic rename. On race,
|
||||
// last-writer-wins. No locks.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <CL/cl.h>
|
||||
#include <string>
|
||||
|
||||
// Bumped manually if host-side OpenCL API usage changes in a way that
|
||||
// affects compile semantics but does not show up in source_bytes /
|
||||
// compile_opts (e.g. switching from clCreateProgramWithSource to
|
||||
// clCompileProgram + clLinkProgram, or changing how multiple sources
|
||||
// are concatenated). Most commits — including kernel changes — do NOT
|
||||
// require bumping this; the source bytes already capture those.
|
||||
#define CL_PROGRAM_CACHE_FORMAT_VERSION 1u
|
||||
|
||||
struct cl_program_cache_state {
|
||||
// Empty string means cache is disabled.
|
||||
std::string dir;
|
||||
// Concatenated device/driver/platform identity + cache format version,
|
||||
// computed once at init and folded into every key.
|
||||
std::string key_suffix;
|
||||
};
|
||||
|
||||
cl_program_cache_state cl_program_cache_init(cl_device_id device);
|
||||
|
||||
cl_program cl_program_cache_try_load(
|
||||
const cl_program_cache_state & state,
|
||||
cl_context context,
|
||||
cl_device_id device,
|
||||
const char * source,
|
||||
const std::string & compile_opts);
|
||||
|
||||
void cl_program_cache_try_save(
|
||||
const cl_program_cache_state & state,
|
||||
cl_program program,
|
||||
cl_device_id device,
|
||||
const char * source,
|
||||
const std::string & compile_opts);
|
||||
+6
-14
@@ -502,9 +502,7 @@ static size_t estimate_draft_autofit_tax_mb(
|
||||
llama_model_params draft_model_params = llama_model_default_params();
|
||||
llama_context_params draft_ctx_params = llama_context_default_params();
|
||||
|
||||
draft_model_params.use_mmap = base_model_params.use_mmap;
|
||||
draft_model_params.use_mlock = base_model_params.use_mlock;
|
||||
draft_model_params.use_direct_io = base_model_params.use_direct_io;
|
||||
draft_model_params.load_mode = base_model_params.load_mode;
|
||||
draft_model_params.n_gpu_layers = has_draft_model ? draft_gpulayers : 0;
|
||||
draft_model_params.devices = base_model_params.devices;
|
||||
draft_model_params.main_gpu = base_model_params.main_gpu;
|
||||
@@ -554,8 +552,7 @@ static size_t estimate_draft_autofit_tax_mb(
|
||||
{
|
||||
llama_model_params draft_probe_params = draft_model_params;
|
||||
draft_probe_params.no_alloc = true;
|
||||
draft_probe_params.use_mmap = false;
|
||||
draft_probe_params.use_mlock = false;
|
||||
draft_probe_params.load_mode = LLAMA_LOAD_MODE_NONE;
|
||||
|
||||
llama_model * draft_probe = llama_model_load_from_file(spec_model_filename.c_str(), draft_probe_params);
|
||||
if(draft_probe != nullptr)
|
||||
@@ -591,8 +588,7 @@ static size_t estimate_draft_autofit_tax_mb(
|
||||
{
|
||||
llama_model_params ctx_other_model_params = base_model_params;
|
||||
ctx_other_model_params.no_alloc = true;
|
||||
ctx_other_model_params.use_mmap = false;
|
||||
ctx_other_model_params.use_mlock = false;
|
||||
ctx_other_model_params.load_mode = LLAMA_LOAD_MODE_NONE;
|
||||
|
||||
ctx_other_model = llama_model_load_from_file(main_model_filename.c_str(), ctx_other_model_params);
|
||||
if(ctx_other_model != nullptr)
|
||||
@@ -923,9 +919,7 @@ static void speculative_decoding_setup(std::string spec_model_filename, llama_co
|
||||
llama_model_params draft_model_params = llama_model_default_params();
|
||||
llama_context_params draft_ctx_params = llama_context_default_params();
|
||||
|
||||
draft_model_params.use_mmap = base_model_params.use_mmap;
|
||||
draft_model_params.use_mlock = base_model_params.use_mlock;
|
||||
draft_model_params.use_direct_io = base_model_params.use_direct_io;
|
||||
draft_model_params.load_mode = base_model_params.load_mode;
|
||||
draft_model_params.n_gpu_layers = draft_gpulayers; //layers offload the speculative model.
|
||||
draft_model_params.devices = base_model_params.devices;
|
||||
draft_ctx_params.n_ctx = base_ctx_params.n_ctx;
|
||||
@@ -3235,11 +3229,9 @@ ModelLoadResult gpttype_load_model(const load_model_inputs inputs, FileFormat in
|
||||
// Match llama-server's target rollback slots for speculative verification.
|
||||
llama_ctx_params.n_rs_seq = inputs.draft_amount;
|
||||
}
|
||||
model_params.use_mmap = inputs.use_mmap;
|
||||
model_params.use_mlock = inputs.use_mlock;
|
||||
model_params.use_direct_io = false; //no direct io for now until stable
|
||||
model_params.load_mode = inputs.use_mlock ? LLAMA_LOAD_MODE_MLOCK : (inputs.use_mmap ? LLAMA_LOAD_MODE_MMAP : LLAMA_LOAD_MODE_NONE);
|
||||
model_params.n_gpu_layers = inputs.gpulayers;
|
||||
kcpp_permit_any_repack = (model_params.use_mmap?false:true);
|
||||
kcpp_permit_any_repack = (inputs.use_mmap?false:true);
|
||||
|
||||
//set device overrides if needed
|
||||
std::vector<ggml_backend_dev_t> devices_override;
|
||||
|
||||
+11
-3
@@ -205,6 +205,16 @@ extern "C" {
|
||||
LLAMA_SPLIT_MODE_TENSOR = 3,
|
||||
};
|
||||
|
||||
enum llama_load_mode {
|
||||
LLAMA_LOAD_MODE_NONE = 0, // no special loading mode
|
||||
LLAMA_LOAD_MODE_MMAP = 1, // memory map the model
|
||||
LLAMA_LOAD_MODE_MLOCK = 2, // mmap + force system to keep model in RAM rather than swapping or compressing
|
||||
LLAMA_LOAD_MODE_DIRECT_IO = 3, // use direct I/O if available
|
||||
};
|
||||
|
||||
LLAMA_API const char * llama_load_mode_name(enum llama_load_mode load_mode);
|
||||
LLAMA_API enum llama_load_mode llama_load_mode_from_str(const char * str);
|
||||
|
||||
enum llama_context_type {
|
||||
LLAMA_CONTEXT_TYPE_DEFAULT = 0,
|
||||
LLAMA_CONTEXT_TYPE_MTP = 1,
|
||||
@@ -304,6 +314,7 @@ extern "C" {
|
||||
|
||||
int32_t n_gpu_layers; // number of layers to store in VRAM, a negative value means all layers
|
||||
enum llama_split_mode split_mode; // how to split the model across multiple GPUs
|
||||
enum llama_load_mode load_mode; // how to load the model
|
||||
|
||||
// the GPU that is used for the entire model when split_mode is LLAMA_SPLIT_MODE_NONE
|
||||
int32_t main_gpu;
|
||||
@@ -324,9 +335,6 @@ extern "C" {
|
||||
|
||||
// Keep the booleans together to avoid misalignment during copy-by-value.
|
||||
bool vocab_only; // only load the vocabulary, no weights
|
||||
bool use_mmap; // use mmap if possible
|
||||
bool use_direct_io; // use direct io, takes precedence over use_mmap when supported
|
||||
bool use_mlock; // force system to keep model in RAM
|
||||
bool check_tensors; // validate model tensor data
|
||||
bool use_extra_bufts; // use extra buffer types (used for weight repacking)
|
||||
bool no_host; // bypass host buffer allowing extra buffers to be used
|
||||
|
||||
@@ -98,8 +98,7 @@ bool embeddingstype_load_model(const embeddings_load_model_inputs inputs)
|
||||
llama_model_params model_params = llama_model_default_params();
|
||||
llama_context_params ctx_params = llama_context_default_params();
|
||||
const int nthreads = inputs.threads;
|
||||
model_params.use_mmap = inputs.use_mmap;
|
||||
model_params.use_mlock = false;
|
||||
model_params.load_mode = inputs.use_mmap ? LLAMA_LOAD_MODE_MMAP : LLAMA_LOAD_MODE_NONE;
|
||||
model_params.n_gpu_layers = inputs.gpulayers; //offload if possible
|
||||
int kcpp_parseinfo_maindevice = inputs.kcpp_main_gpu<=0?0:inputs.kcpp_main_gpu;
|
||||
model_params.main_gpu = kcpp_parseinfo_maindevice;
|
||||
|
||||
@@ -584,8 +584,7 @@ bool ttstype_load_model(const tts_load_model_inputs inputs)
|
||||
|
||||
nthreads = inputs.threads;
|
||||
|
||||
tts_model_params.use_mmap = false;
|
||||
tts_model_params.use_mlock = false;
|
||||
tts_model_params.load_mode = LLAMA_LOAD_MODE_NONE;
|
||||
tts_model_params.n_gpu_layers = inputs.gpulayers; //offload if possible
|
||||
tts_model_params.split_mode = llama_split_mode::LLAMA_SPLIT_MODE_LAYER;
|
||||
int kcpp_parseinfo_maindevice = inputs.kcpp_main_gpu<=0?0:inputs.kcpp_main_gpu;
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
---
|
||||
name: add-new-model
|
||||
description: Guided workflow for adding a new model architecture to llama.cpp. Use when the user wants to add/port a new model architecture.
|
||||
---
|
||||
|
||||
# Add a new model architecture to llama.cpp
|
||||
|
||||
This skill walks a contributor through adding a new model architecture. AI-generated code is permitted in this project, so you may write full implementations for the steps below rather than only pointing at patterns - but follow `AGENTS.md`'s AI usage policy throughout:
|
||||
|
||||
- The contributor is 100% responsible for every line, however it was produced. They must be able to explain and defend any part of it to a reviewer. Check in with them as you go (don't silently generate everything and hand over a finished diff) so they actually absorb what was written.
|
||||
- Before writing code, make sure the contributor owns the design choices for this architecture (which reference model to follow, how non-standard bits like RoPE variants or MoE routing should be handled) - AI accelerates a design the contributor has already made, it doesn't make the design for them.
|
||||
- Disclosure is mandatory: any AI-meaningful contribution must be disclosed per the PR template. Remind the contributor of this before they open the PR.
|
||||
- Never write the PR description, commit message, GitHub issue/discussion post, or reviewer replies - those must come from the contributor. If asked to commit on their behalf, use `Assisted-by:` (never `Co-authored-by:`) and only after explicit confirmation.
|
||||
- If the requested change looks large or introduces a new pattern not covered here, pause and tell the user this kind of change is likely to need prior discussion with maintainers before a PR.
|
||||
- Keep the PR self-contained. If the work would require a lot of unconventional changes outside the new model file(s) (e.g. touching shared graph-building code, the sampler, or core APIs in ways other models don't), STOP and tell the contributor to open a discussion/issue first - invasive or excessive changes get closed without full review.
|
||||
- Do not bundle unrelated work into this PR - see Step 4 and Step 5 below for the specifics on multimodal and chat-template/parsing work.
|
||||
- Never hack around RoPE with a custom sin/cos implementation. Several past PRs tried this and were closed. If the existing `ggml_rope_ext` (see Step 2's RoPE tips) genuinely cannot express what this model needs, the contributor should open an issue to discuss it with maintainers first - not send a PR with a custom RoPE implementation.
|
||||
|
||||
Before starting, read `CONTRIBUTING.md`, `AGENTS.md` and `docs/development/HOWTO-add-model.md` if they are not already in context. Also run `git log --oneline -- src/models` and look at at least 3 recent PRs that added a model (their merge commits/diffs) - this shows current convention more reliably than the docs, which can lag behind.
|
||||
|
||||
## Step 0 - Scope and dedup check
|
||||
|
||||
Ask the contributor:
|
||||
1. Which model (HF repo id or name)? Is it text-only or does it have a multimodal (vision/audio) encoder?
|
||||
2. Do they already have the HF `config.json`/weights available locally?
|
||||
3. Have they checked for an existing PR/issue on this model? Suggest `gh search issues "<model name>"` and `gh search prs "<model name>"` in the `ggml-org/llama.cpp` repo. If an existing PR covers it, the contributor should comment there and collaborate rather than open a duplicate (per CONTRIBUTING.md's AI Usage Policy).
|
||||
4. What existing supported architecture is this model closest to (e.g. "Llama-like with sliding window", "MoE like DBRX", "BERT-style encoder")?
|
||||
|
||||
If the contributor doesn't know the closest reference architecture, you may grep `conversion/*.py` and `src/models/*.cpp` for architectures with a similar config shape (layer count, head count, MoE expert count, norm placement) and suggest 1-2 candidates - but let the contributor confirm the choice rather than picking one yourself; this choice is a design decision they need to own.
|
||||
|
||||
Do not proceed to Step 1 until the contributor has answered these and named a reference architecture.
|
||||
|
||||
## Step 1 - Convert the model to GGUF
|
||||
|
||||
Follow HOWTO-add-model.md section 1 for the actual touch points (conversion class registration, `constants.py`, `tensor_mapping.py`, etc.) - don't re-derive them here, read them from the doc.
|
||||
|
||||
Skill-specific addition: for each touch point, show the contributor the equivalent code in the reference architecture they named in Step 0 before writing the new version, and check that they understand what's different about their model (e.g. non-standard tensor shapes, extra hparams) rather than just copying the pattern silently.
|
||||
|
||||
## Step 2 - Define the architecture in llama.cpp
|
||||
|
||||
Follow HOWTO-add-model.md section 2 for the actual touch points (`llm_arch` enum, `LLM_ARCH_NAMES`, hparam loading, RoPE type case, etc.), including its "Tips and tricks" section for `ggml_rope_ext` gotchas.
|
||||
|
||||
Skill-specific addition: never hack around RoPE with a custom sin/cos implementation - see the RoPE rule above.
|
||||
|
||||
## Step 3 - Build the GGML graph
|
||||
|
||||
Follow HOWTO-add-model.md section 3 for the actual touch points (`src/models/<name>.cpp` struct, `llama_model_mapping` registration, etc.).
|
||||
|
||||
Skill-specific addition: before writing `src/models/<name>.cpp`, read at least 10 other files under `src/models/` (pick a mix, not just the one reference architecture) to confirm the struct layout, naming, and style you're about to write actually matches current convention - the pattern drifts over time and the HOWTO doc can lag behind it.
|
||||
|
||||
## Step 4 - Optional: multimodal encoder
|
||||
|
||||
Only do this if the contributor flagged a vision/audio encoder in Step 0. Follow HOWTO-add-model.md section 4 and `docs/multimodal.md` for the actual touch points (`MmprojModel` subclass, `clip.cpp`, `mtmd.cpp`, encoder graph in `tools/mtmd/models`, etc.).
|
||||
|
||||
Skill-specific addition, and read this carefully: **whether the multimodal encoder can be bundled into the same PR as the base text-model support depends on how conventional the change is.** It's OK to bundle it if the encoder support is conventional - i.e. no new infra or logic is needed, it's just a new cgraph reusing existing preprocessing/projector machinery (e.g. siglip/pixtral/qwen with just a new projector). If it requires anything beyond that - a new preprocessor, non-standard projector logic, or changes to shared `libmtmd` infra/logic - STOP, tell the contributor this is non-conventional, and have them land the text model first with the encoder as a dedicated follow-up PR. Do not let this decision pass silently - call it out explicitly to the contributor before writing any `clip.cpp`/`mtmd.cpp` code.
|
||||
|
||||
## Step 5 - Optional: chat template / parsing support
|
||||
|
||||
Only do this if the model needs a new built-in chat template (`src/llama-chat.cpp`) or a new output parser (see `docs/development/parsing.md` and `docs/autoparser.md`). If either is needed beyond what a user-supplied Jinja template already covers, treat it as its own dedicated follow-up PR, not part of the base model-support PR - call this out explicitly to the contributor rather than silently bundling it in.
|
||||
|
||||
## Common pitfalls (from past PR reviews)
|
||||
|
||||
These recur often enough in review comments on past add-model PRs that they're worth checking proactively, not just waiting for a reviewer to catch them:
|
||||
|
||||
- Don't validate the same hparam/config assumption in both the Python conversion script and the C++ load path - pick one layer to own the check, duplicating it just adds maintenance surface.
|
||||
- Optional hparams that are genuinely absent from some configs (e.g. a shared-expert count) should be read with an explicit optional/fallback accessor, not assumed present.
|
||||
- Hparams that are actually load-bearing (the model produces wrong output or crashes without them, e.g. `sliding_window_pattern`, norm-eps) must hard-error if missing, not silently fall back to a default.
|
||||
- Don't bake a default chat template into the C++ binary - inject it into the GGUF at conversion time instead, since one `llm_arch` can be reused by multiple fine-tunes with different templates, and a baked-in C++ default fails silently for those.
|
||||
- Before writing a dedicated tool-call/output parser, check whether the existing autoparser already handles the template (`llama-debug-template-parser <jinja>` shows what it detects).
|
||||
- Marking a custom EOS/closing-tag token as `eot` at conversion time isn't always sufficient - in long/agentic generations a model can emit the closing sequence as literal text instead of the token, so generation never stops on EOG and raw text leaks past the parser. Verify this case, not just the token path.
|
||||
- If reusing or aliasing an existing pre-tokenizer for convenience, justify and test that choice explicitly - silent reuse is an easy source of subtle tokenizer bugs.
|
||||
- Watch for excessive graph splits caused by building per-layer view/index tensors inside the layer loop - hoist tensors that don't vary per layer out of the loop (relevant if you hit `GGML_SCHED_MAX_SPLIT_INPUTS`).
|
||||
- A custom KQ mask fed into flash attention must match FA's expected dtype - cast it to F16 before passing it to `build_attn_mha` when FA is enabled.
|
||||
- When padding a custom KV-cache size to an alignment (e.g. `GGML_PAD(..., 256)`), apply the padding after all other size adjustments, not before - otherwise later logic can un-align it again.
|
||||
- For non-standard cache/SWA (sliding-window-attention) semantics, override the dedicated hook (e.g. `llama_model_n_swa()`) rather than mutating hparams to fake the behavior - hparams may be read elsewhere for unrelated purposes.
|
||||
- Don't ship unfinished or unverified speculative-decoding (e.g. MTP) scaffolding in the base model PR - if it hasn't actually been confirmed to work, pull it out and land it as its own follow-up.
|
||||
- Conversion code should call into the base class's existing hparam logic (e.g. `super().set_gguf_parameters()`) rather than re-deriving it - large blocks of code that duplicate what `TextModel`/`MmprojModel` already provide will get flagged as redundant.
|
||||
- Do constant tensor modifications (e.g. `norm(1 + weight)`) and permutations/chunking at conversion time, not in the graph - see HOWTO-add-model.md's "Prefer conversion-time tensor modifications" tip (Gemma 3 folds its `1 +` into the weights, Qwen3-Next permutes in `modify_tensors`). Doing these at runtime in the graph is very likely to be rejected as over-complicated; if you genuinely can't do it at conversion time, open a discussion first explaining why rather than implementing it in the graph.
|
||||
|
||||
## Validation checklist
|
||||
|
||||
Reference: `examples/model-conversion/README.md`.
|
||||
|
||||
1. Convert to GGUF, then inspect/run both the original and converted tensors.
|
||||
2. Run logits verification (original vs converted). If this model is a new version of an already-supported family, verify the *previous* version still passes logits verification first - numerical differences may be pre-existing, not caused by the new work. The tools to perform full logits validation are available in `examples/model-conversion`.
|
||||
3. Quantize (including QAT variants if relevant) and re-verify.
|
||||
4. Run perplexity evaluation (simple and full).
|
||||
5. Sanity-check across `tools/cli`, `tools/completion`, `tools/imatrix`, `tools/quantize`, and `tools/server`.
|
||||
6. CPU backend first; other backends (CUDA, Metal, ...) can be separate follow-up PRs per `CONTRIBUTING.md`.
|
||||
7. Re-review every changed file against the coding/naming guidelines in `AGENTS.md` (and `CONTRIBUTING.md`'s "Coding guidelines"/"Naming guidelines" sections) - this is a separate pass from functional testing and is just as important: no forced line-wrapping, no unicode punctuation, minimal/non-redundant comments, `snake_case` naming (`kebab-case` for file names), matching indentation/brace style, etc.
|
||||
|
||||
## Before opening a PR
|
||||
|
||||
- Run the `code-review` skill on the diff first - it catches the convention and scope issues reviewers flag most often, and it's recommended to do this locally before pushing the PR.
|
||||
- Confirm the contributor can explain every changed line to a reviewer and is prepared to be asked about any of it - this is required regardless of how much of the code was AI-generated.
|
||||
- Confirm they did a comprehensive manual review of the full diff, not just a skim.
|
||||
- Fill in the AI-disclosure section of `.github/pull_request_template.md` describing how AI was used (do not omit or understate this).
|
||||
- Do not write the PR description, commit message, GitHub issue/discussion text, or any reviewer replies yourself - the contributor writes these.
|
||||
@@ -0,0 +1,134 @@
|
||||
---
|
||||
name: code-review
|
||||
description: Review llama.cpp changes against project conventions and common reviewer pitfalls before a PR. Use when the user wants to review a diff, branch, or PR.
|
||||
---
|
||||
|
||||
# Review llama.cpp changes
|
||||
|
||||
This skill reviews changes against llama.cpp's conventions and the pitfalls that reviewers flag most often, so the contributor can fix them before a maintainer has to. It has two modes:
|
||||
|
||||
- **Self-review (default):** review the contributor's own local changes (uncommitted work, or a branch vs `master`) as a pre-PR pass. Ask which if it's ambiguous; default to `git diff master...HEAD` plus any uncommitted changes.
|
||||
- **Read-only review of a PR/file:** if the user points at a PR number or specific files (including code they didn't write), review those and report findings.
|
||||
|
||||
In both modes the output is **private review notes for the user to read and act on** - it is never something to post. This is a hard rule from `AGENTS.md`: an agent must NEVER write, or help write, a PR comment, a review comment, or a reply to a reviewer, by any means including `gh`. Do not offer to. If the user asks you to post the notes, refuse and point them at that rule. Present findings in the conversation only.
|
||||
|
||||
Before starting, read `AGENTS.md` and `CONTRIBUTING.md` if not already in context - the "Coding guidelines", "Naming guidelines", and AI usage sections are the baseline this review enforces. For a diff that adds a new model architecture, also read `docs/development/HOWTO-add-model.md` and consider the dedicated `add-new-model` skill.
|
||||
|
||||
## Step 0 - Scope the diff and pick the checklists
|
||||
|
||||
Identify what actually changed and which area checklists below apply. Run `git diff --stat` (or `gh pr view <n> --json files` for PR mode) and bucket the touched paths:
|
||||
|
||||
- `conversion/`, `gguf-py/`, `src/models/`, `src/llama-arch.*` -> **New model / architecture**
|
||||
- `ggml/` (any backend, op, or `ggml.h`) -> **ggml / backend**
|
||||
- `include/llama.h` and other public headers -> **Public API**
|
||||
- `tools/server/` -> **Server**
|
||||
- anything else, plus all of the above -> **General** (always runs)
|
||||
|
||||
Always run the **Scope and quick-reject gate**, the **Security review**, and the **General** checklist. Run each area checklist whose paths were touched. Additionally, if the diff introduces a new component, subsystem, or piece of infrastructure (a new file/class/module, a new abstraction, or hand-rolled machinery), run the **Approach and design** review. Tell the user which checklists you're running and why.
|
||||
|
||||
## Scope and quick-reject gate (always)
|
||||
|
||||
These are the patterns that get PRs closed without a full review. Check them first - a finding here is more important than any code nit, because it can mean the change shouldn't be a PR in its current form at all.
|
||||
|
||||
- Is there a prior issue/discussion for this? Features are supposed to start as an issue, not a PR (`CONTRIBUTING.md`). If this is a nontrivial feature with no linked issue, flag it and suggest opening one first.
|
||||
- Is it a duplicate of existing/in-flight work? Suggest `gh search prs` / `gh search issues` for the feature. Many closed PRs were duplicates of something already queued.
|
||||
- Is it self-contained and single-purpose? Multiple unrelated changes/optimizations bundled together get sent back to be split. Flag unrelated changes and suggest separate PRs.
|
||||
- Does it touch multiple ggml backends at once? Initial support should be CPU-only, other backends as follow-ups (`CONTRIBUTING.md`). Flag CUDA/Metal/Vulkan/etc. changes bundled into a feature's first PR.
|
||||
- Does it add a new `ggml_type` / quantization type? That carries a disproportionate maintenance burden and needs the full justification package (GGUF sample upload, perplexity vs FP16/BF16 and similar sizes, KL-divergence data, CPU perf numbers). Absent that, it will be rejected regardless of code quality.
|
||||
- Is it invasive - new subsystem, core-API reshaping, changes to shared graph/sampler code that other models don't need? Flag it and suggest a discussion with maintainers before investing further.
|
||||
- Is it niche/vendor-specific in a way that adds a maintenance burden nobody will own long-term? Flag the maintenance-ownership question.
|
||||
- Is the change semantically correct, or a plausible-looking "fix" that misunderstands the code? Sanity-check the actual behavior, not just that it compiles.
|
||||
- AI-disclosure: if AI meaningfully contributed, is the PR template's disclosure section filled in? Remind the user. Never suggest writing the PR description or commit message for them.
|
||||
|
||||
## Security review (mandatory)
|
||||
|
||||
Mandatory on every review; any finding here is **blocking**. Rule of thumb: GGUF metadata, tensor shapes, tokenizer/grammar input, and all server/RPC fields are attacker-controlled - bound them before use.
|
||||
|
||||
- **Sizes/counts from tensor dims:** validate before allocating. Products like `ne[i]*nb[i]`/nbytes can overflow on crafted dims into an undersized alloc then heap overflow. Overflow checks must run BEFORE the arithmetic they guard - padding/alignment macros wrap to 0 near `SIZE_MAX`, so a guard after the pad passes.
|
||||
- **GGUF strings/arrays:** cap declared lengths and element counts before using them to size a loop or buffer; validate element type and length before casting an array to a pointer or reading fixed indices (`[i+1]`, `[0..2]`).
|
||||
- **File-supplied counts indexing fixed arrays:** bound any count (e.g. layer/block count into a `LLAMA_MAX_*` array) before indexing; watch checks that only fire when an optional key is present.
|
||||
- **Bounds comparisons:** flag narrowing casts (`size_t`->`int32_t`) and signed/unsigned mixing that can bypass a length check and copy past a buffer.
|
||||
- **Parsed/derived indices:** range-check `stoi`/`atoi` results and catch parse throws; never use a default or derived token id (EOS/BOS/...) as an index without a bounds check.
|
||||
- **Reused/reserved buffers:** recheck bounds after a buffer is shrunk or reused; watch `reserve()` then index-by-assumed-size, and header fields read before their length is checked.
|
||||
- **Server JSON ints:** clamp client-supplied integers (token/discard counts, offsets) to non-negative and an upper bound before they reach index/pointer arithmetic.
|
||||
- **RPC-deserialized fields:** treat every field (type/buffer/data/ne/nb/op_params) as hostile - validate before use. Null/zero buffers skipping validation, attacker data pointers, out-of-range type indices, and negative strides sign-extending past a corner-only assert all give arbitrary read/write.
|
||||
- **Lifetime/UAF:** flag stored raw pointers to caller/temporary storage, cached pointers to buffers a later free releases, async ops whose source may drop before completion, and structures not invalidated on free/realloc. Null-check conditionally-built or "not required" tensors before dereferencing.
|
||||
|
||||
## Approach and design (when a new component/infra is introduced)
|
||||
|
||||
Run this whenever the diff adds a new component, subsystem, or piece of infrastructure. Reviews too often stop at "does it work" - a diff can be correct and still be the wrong approach, and a messy design costs more long-term than a bug. Evaluate the *approach*, not just the behavior; raising a cleaner one is a high-value finding, not a nit. If you see a better design, describe it concretely rather than just calling the current one bad.
|
||||
|
||||
- **Simpler approach upstream:** the biggest win is often a different data model or design that removes whole subsystems, not tweaks to the code as written. Complexity must be justified by the problem, not by the first thing that worked.
|
||||
- **Reuse over reinvention:** grep for an existing helper, library, object, or mechanism before adding a new one. Reimplementing what the codebase already has reintroduces solved bugs and adds maintenance surface.
|
||||
- **Clear ownership/lifetime:** prefer RAII and obvious ownership over manual liveness flags, hand-tracked pointers, and "is it still alive?" checks - manual lifetime tracking is a recurring source of subtle bugs.
|
||||
- **Right-sized machinery:** flag redundant, overkill, or heavier-than-needed primitives and abstractions; use the minimum the design actually needs.
|
||||
- **Right structure and fit:** a new type should earn its place (split it if it serves two roles); follow existing patterns, idioms, and naming, and avoid constructs the project shuns.
|
||||
- **Root cause vs symptom:** fixes layered on fixes signal a design to correct, not guard around.
|
||||
|
||||
## New model / architecture
|
||||
|
||||
See the `add-new-model` skill and `docs/development/HOWTO-add-model.md` for the full workflow; this is the review-time subset that reviewers most often catch:
|
||||
|
||||
- Don't branch on `model.arch` when the real dependency is a config/capability value - gate on the hparam/capability, not the architecture enum.
|
||||
- If the model is a close variant of an existing arch, is the delta justified? Prefer reusing or subclassing the existing arch/model class over duplicating it. A near-duplicate class or `src/models/<name>.cpp` will be asked to merge with its sibling.
|
||||
- New tensor names go through `tensor_mapping.py`, not ad-hoc name matching.
|
||||
- For QKV, split the *activation* with `ggml_view`, not the *weight* tensor; rely on ggml broadcasting instead of manually duplicating tensors.
|
||||
- New graph inputs are declared at the top of the graph-build function, not inline where first used.
|
||||
- Hparams that the model can't run correctly without must be mandatory (hard-error if missing), not read with a silent default fallback. Only genuinely-optional-across-configs values get a fallback accessor.
|
||||
- New/optional weight tensors (scales, etc.) must route through `build_lora_mm` and the existing helpers, matching convention - don't leave raw matmuls copied from another arch.
|
||||
- Don't hack RoPE with a custom sin/cos implementation. If `ggml_rope_ext` genuinely can't express it, that's an issue for discussion, not a PR.
|
||||
- Test the quantized-KV path (`-ctk`/`-ctv q8_0`), not just default f16 - new speculative/attention features silently break there.
|
||||
- Preserve existing explanatory comments about model-specific quirks when copying code; note the provenance ("copied from X, with Y added").
|
||||
- Remove dead code/branches left over from adapting a reference implementation.
|
||||
|
||||
## ggml / backend
|
||||
|
||||
- `supports_op` (and any dispatch/gating condition) must be scoped exactly to the cases being changed - a condition meant for a few quant types must not silently disable or enable everything else.
|
||||
- No hardcoded warp/lane size - use `ggml_cuda_get_physical_warp_size()` (32 on CUDA, 64 on HIP/ROCm) and the portable helpers.
|
||||
- Strip leftover debug/profiling/logging code before review.
|
||||
- New or changed op? Update `docs/ops.md` and the relevant `docs/ops/*.csv` for the touched backend.
|
||||
- New op or operator change needs corresponding `test-backend-ops` cases, and (per `CONTRIBUTING.md`) consistency across at least two backends.
|
||||
- New kernels are expected to come with concrete perf data (throughput across realistic tensor shapes), not just correctness.
|
||||
- Don't have a backend mutate the cgraph as a shortcut - that's an unresolved architectural question, not something to slip in.
|
||||
- Expect this to need two maintainer approvals; that's normal for `ggml/` changes, not a sign something is wrong.
|
||||
- For CUDA: Avoid excessively templating kernels, only add this where it shows visible performance gain.
|
||||
|
||||
## Public API (`include/llama.h`)
|
||||
|
||||
Public API changes carry a higher bar than internal ones (`CONTRIBUTING.md`). Review for:
|
||||
|
||||
- Justification: why doesn't an existing mechanism (e.g. `cb_eval`, existing batch/sampler knobs) suffice? If it does, the change likely shouldn't add public surface. This is the single most common reason these PRs are rejected.
|
||||
- Experimental or stop-gap surface belongs in a side header (`llama-ext.h`), not in `llama.h`.
|
||||
- Keep it minimal and general: prefer one general call over several narrow convenience wrappers; make new calls forward-compatible (e.g. mixed-modality batches) rather than assuming today's shape.
|
||||
- The C API is the first-class, stable, ABI-defining surface - don't propose a parallel C++ API as a replacement. `llama-cpp.h` stays a thin convenience layer.
|
||||
- Types and naming: sized integer types (`int32_t`, `size_t` for sizes/offsets); `snake_case`; `<class>_<method>` = `<class>_<action>_<noun>`; enum values upper-case and prefixed with the enum name; `_t` suffix for opaque types. Avoid gratuitous signature/ABI changes to existing exported functions.
|
||||
- Every new API needs a working example/tool exercising it in the same PR - reviewers find real bugs by requiring it to be wired into `server`, `embedding`, `perplexity`, etc.
|
||||
|
||||
## Server (`tools/server/`)
|
||||
|
||||
- Is the feature within server's defined scope? Check `tools/server/README-dev.md` - out-of-scope features get declined.
|
||||
- Security: don't trust client-supplied headers (e.g. `X-Forwarded-For`) or add footguns; things like IP allowlisting belong at a reverse proxy unless there's a trusted-proxy design.
|
||||
- Wire new behavior into the existing request/response and checkpoint paths correctly; watch for resource leaks across requests.
|
||||
|
||||
## General (always)
|
||||
|
||||
Enforce the `AGENTS.md` / `CONTRIBUTING.md` coding and naming guidelines on every changed line - this is a distinct pass from checking that the code works, and matters just as much for review speed:
|
||||
|
||||
- ASCII only in code and comments - no emdash, unicode arrows, `x`, `...` used as unicode; use `-`, `->`, `x`, `...` ASCII equivalents.
|
||||
- Comments are concise and explain non-obvious *why*, not *what*. Flag verbose comments, comments that restate the code, comments that reference the current task/PR, and comments hard-wrapped to a fixed column width.
|
||||
- Do not force-wrap prose/comments to a fixed character count or split a sentence across lines.
|
||||
- `snake_case` names; `kebab-case` (lowercase-with-dashes) file names for C/C++, `.h` headers; Python files lowercase-with-underscores. Naming optimizes for longest common prefix (`number_small`, not `small_number`).
|
||||
- 4-space indentation, brackets on the same line, `void * ptr`, `int & a`, no trailing whitespace; match the surrounding style.
|
||||
- Reuse existing infrastructure over introducing new components; no new third-party dependencies, extra headers, or files unless clearly justified.
|
||||
- Keep it simple: a simpler change doing 90% is often preferable to a complex one doing 100%. Flag unnecessary templates/fancy STL; basic `for` loops are fine here.
|
||||
- Every added line should be something the contributor can explain and defend to a reviewer without AI help - flag anything that looks copied-in without understanding.
|
||||
|
||||
## Reporting
|
||||
|
||||
Group findings by severity so the user knows what actually blocks a merge:
|
||||
|
||||
1. **Blocking** - quick-reject/scope issues and correctness bugs; these can sink the PR regardless of everything else.
|
||||
2. **Will slow the review** - convention/naming/comment violations, missing tests/docs/perf data, missing API justification or example.
|
||||
3. **Nits** - minor style, optional cleanups.
|
||||
|
||||
For each finding, point to the file and line and say concretely what to change and why. Do not rewrite the whole diff unprompted; let the contributor make the fixes so they own and understand them. And do not draft any PR text, commit message, or reviewer reply - that is the contributor's to write.
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "ggml.h"
|
||||
#include "gguf.h"
|
||||
#include "llama-hparams.h"
|
||||
#include "llama.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
@@ -522,8 +523,7 @@ llama_model_loader::llama_model_loader(
|
||||
const std::string & fname,
|
||||
std::vector<std::string> & splits,
|
||||
FILE * file,
|
||||
bool use_mmap,
|
||||
bool use_direct_io,
|
||||
llama_load_mode load_mode,
|
||||
bool check_tensors,
|
||||
bool no_alloc,
|
||||
const llama_model_kv_override * param_overrides_p,
|
||||
@@ -542,6 +542,9 @@ llama_model_loader::llama_model_loader(
|
||||
|
||||
tensor_buft_overrides = param_tensor_buft_overrides_p;
|
||||
|
||||
this->use_mmap = load_mode == LLAMA_LOAD_MODE_MMAP || load_mode == LLAMA_LOAD_MODE_MLOCK;
|
||||
this->use_direct_io = load_mode == LLAMA_LOAD_MODE_DIRECT_IO;
|
||||
|
||||
if (!fname.empty()) {
|
||||
// Load the main GGUF
|
||||
struct ggml_context * ctx = NULL;
|
||||
@@ -562,20 +565,6 @@ llama_model_loader::llama_model_loader(
|
||||
files.emplace_back(new llama_file(fname.c_str(), "rb", use_direct_io));
|
||||
contexts.emplace_back(ctx);
|
||||
|
||||
if (use_mmap && use_direct_io) {
|
||||
if (files.back()->has_direct_io()) {
|
||||
LLAMA_LOG_WARN("%s: direct I/O is enabled, disabling mmap\n", __func__);
|
||||
use_mmap = false;
|
||||
} else {
|
||||
LLAMA_LOG_WARN("%s: direct I/O is not available, using mmap\n", __func__);
|
||||
use_direct_io = false;
|
||||
|
||||
// reopen file using std::fopen for mmap
|
||||
files.pop_back();
|
||||
files.emplace_back(new llama_file(fname.c_str(), "rb", false));
|
||||
}
|
||||
}
|
||||
|
||||
// Save tensors data offset of the main file.
|
||||
// For subsidiary files, `meta` tensor data offset must not be used,
|
||||
// so we build a unified tensors index for weights.
|
||||
@@ -817,13 +806,11 @@ llama_model_loader::llama_model_loader(
|
||||
}
|
||||
}
|
||||
|
||||
if (!llama_mmap::SUPPORTED) {
|
||||
if (this->use_mmap && !llama_mmap::SUPPORTED) {
|
||||
LLAMA_LOG_WARN("%s: mmap is not supported on this platform\n", __func__);
|
||||
use_mmap = false;
|
||||
this->use_mmap = false;
|
||||
}
|
||||
|
||||
this->use_mmap = use_mmap;
|
||||
this->use_direct_io = use_direct_io;
|
||||
this->check_tensors = check_tensors;
|
||||
this->no_alloc = no_alloc;
|
||||
}
|
||||
|
||||
@@ -126,8 +126,7 @@ struct llama_model_loader {
|
||||
const std::string & fname,
|
||||
std::vector<std::string> & splits, // optional, only need if the split does not follow naming scheme
|
||||
FILE * file,
|
||||
bool use_mmap,
|
||||
bool use_direct_io,
|
||||
llama_load_mode load_mode,
|
||||
bool check_tensors,
|
||||
bool no_alloc,
|
||||
const llama_model_kv_override * param_overrides_p,
|
||||
|
||||
+5
-6
@@ -16,6 +16,7 @@
|
||||
#include "llama-memory-hybrid-iswa.h"
|
||||
#include "llama-memory-recurrent.h"
|
||||
|
||||
#include "llama.h"
|
||||
#include "models/models.h"
|
||||
|
||||
#include "ggml.h"
|
||||
@@ -1383,7 +1384,7 @@ void llama_model_base::load_vocab(llama_model_loader & ml) {
|
||||
|
||||
bool llama_model_base::load_tensors(llama_model_loader & ml) {
|
||||
const auto & split_mode = params.split_mode;
|
||||
const auto & use_mlock = params.use_mlock;
|
||||
const bool use_mlock = params.load_mode == LLAMA_LOAD_MODE_MLOCK;
|
||||
const auto & tensor_split = params.tensor_split;
|
||||
|
||||
const int n_layer_all = hparams.n_layer_all;
|
||||
@@ -1393,8 +1394,8 @@ bool llama_model_base::load_tensors(llama_model_loader & ml) {
|
||||
|
||||
this->ml = &ml; // to be used by create_tensor() and load_arch_tensors()
|
||||
|
||||
LLAMA_LOG_INFO("%s: loading model tensors, this can take a while... (mmap = %s, direct_io = %s)\n",
|
||||
__func__, ml.use_mmap ? "true" : "false", ml.use_direct_io ? "true" : "false");
|
||||
LLAMA_LOG_INFO("%s: loading model tensors, this can take a while... (load_mode = %s)\n",
|
||||
__func__, llama_load_mode_name(params.load_mode));
|
||||
|
||||
// build a list of buffer types for the CPU and GPU devices
|
||||
pimpl->cpu_buft_list = make_cpu_buft_list(devices, params.use_extra_bufts, params.no_host);
|
||||
@@ -2458,15 +2459,13 @@ llama_model_params llama_model_default_params() {
|
||||
/*.tensor_buft_overrides =*/ nullptr,
|
||||
/*.n_gpu_layers =*/ -1,
|
||||
/*.split_mode =*/ LLAMA_SPLIT_MODE_LAYER,
|
||||
/*.load_mode =*/ LLAMA_LOAD_MODE_MMAP,
|
||||
/*.main_gpu =*/ 0,
|
||||
/*.tensor_split =*/ nullptr,
|
||||
/*.progress_callback =*/ nullptr,
|
||||
/*.progress_callback_user_data =*/ nullptr,
|
||||
/*.kv_overrides =*/ nullptr,
|
||||
/*.vocab_only =*/ false,
|
||||
/*.use_mmap =*/ true,
|
||||
/*.use_direct_io =*/ false,
|
||||
/*.use_mlock =*/ false,
|
||||
/*.check_tensors =*/ false,
|
||||
/*.use_extra_bufts =*/ true,
|
||||
/*.no_host =*/ false,
|
||||
|
||||
+4
-3
@@ -2,6 +2,7 @@
|
||||
#include "llama-model.h"
|
||||
#include "llama-model-loader.h"
|
||||
#include "llama-ext.h"
|
||||
#include "llama.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
@@ -878,15 +879,15 @@ static void llama_model_quantize_impl(const std::string & fname_inp, const std::
|
||||
// mmap consistently increases speed on Linux, and also increases speed on Windows with
|
||||
// hot cache. It may cause a slowdown on macOS, possibly related to free memory.
|
||||
#if defined(__linux__) || defined(_WIN32)
|
||||
constexpr bool use_mmap = true;
|
||||
constexpr llama_load_mode load_mode = LLAMA_LOAD_MODE_MMAP;
|
||||
#else
|
||||
constexpr bool use_mmap = false;
|
||||
constexpr llama_load_mode load_mode = LLAMA_LOAD_MODE_NONE;
|
||||
#endif
|
||||
|
||||
const llama_model_kv_override * kv_overrides = params->kv_overrides;
|
||||
std::vector<std::string> splits = {};
|
||||
llama_model_loader ml(/*metadata*/ nullptr, /*set_tensor_data*/ nullptr, /*set_tensor_data_ud*/ nullptr,
|
||||
fname_inp, splits, /*file*/ nullptr, use_mmap, /*use_direct_io*/ false, /*check_tensors*/ true, /*no_alloc*/ false, kv_overrides, nullptr);
|
||||
fname_inp, splits, /*file*/ nullptr, /*load_mode*/ load_mode, /*check_tensors*/ true, /*no_alloc*/ false, kv_overrides, nullptr);
|
||||
ml.init_mappings(false); // no prefetching
|
||||
|
||||
auto mparams = llama_model_default_params();
|
||||
|
||||
+24
-2
@@ -66,6 +66,28 @@ const char * llama_flash_attn_type_name(enum llama_flash_attn_type flash_attn_ty
|
||||
GGML_ABORT("fatal error");
|
||||
}
|
||||
|
||||
const char * llama_load_mode_name(enum llama_load_mode load_mode) {
|
||||
switch (load_mode) {
|
||||
case LLAMA_LOAD_MODE_NONE:
|
||||
return "none";
|
||||
case LLAMA_LOAD_MODE_MMAP:
|
||||
return "mmap";
|
||||
case LLAMA_LOAD_MODE_MLOCK:
|
||||
return "mlock";
|
||||
case LLAMA_LOAD_MODE_DIRECT_IO:
|
||||
return "dio";
|
||||
}
|
||||
GGML_ABORT("fatal error");
|
||||
}
|
||||
|
||||
enum llama_load_mode llama_load_mode_from_str(const char * str) {
|
||||
if (std::strcmp(str, "none") == 0) { return LLAMA_LOAD_MODE_NONE; }
|
||||
if (std::strcmp(str, "mmap") == 0) { return LLAMA_LOAD_MODE_MMAP; }
|
||||
if (std::strcmp(str, "mlock") == 0) { return LLAMA_LOAD_MODE_MLOCK; }
|
||||
if (std::strcmp(str, "dio") == 0) { return LLAMA_LOAD_MODE_DIRECT_IO; }
|
||||
throw std::invalid_argument(std::string("unknown load mode: ") + str);
|
||||
}
|
||||
|
||||
struct llama_sampler_chain_params llama_sampler_chain_default_params() {
|
||||
struct llama_sampler_chain_params result = {
|
||||
/*.no_perf =*/ true,
|
||||
@@ -299,7 +321,7 @@ static bool llama_prepare_model_devices(const llama_model_params & params, llama
|
||||
static std::pair<int, llama_model *> llama_model_load(struct gguf_context * metadata, llama_model_set_tensor_data_t set_tensor_data, void * set_tensor_data_ud,
|
||||
const std::string & fname, std::vector<std::string> & splits, FILE * file, llama_model_params & params) {
|
||||
try {
|
||||
llama_model_loader ml(metadata, set_tensor_data, set_tensor_data_ud, fname, splits, file, params.use_mmap, params.use_direct_io,
|
||||
llama_model_loader ml(metadata, set_tensor_data, set_tensor_data_ud, fname, splits, file, params.load_mode,
|
||||
params.check_tensors, params.no_alloc, params.kv_overrides, params.tensor_buft_overrides);
|
||||
|
||||
ml.print_info();
|
||||
@@ -432,7 +454,7 @@ struct llama_model * llama_model_init_from_user(
|
||||
GGML_ASSERT(metadata != nullptr);
|
||||
std::string path_model;
|
||||
std::vector<std::string> splits = {};
|
||||
params.use_mmap = false;
|
||||
params.load_mode = LLAMA_LOAD_MODE_NONE;
|
||||
params.use_extra_bufts = false;
|
||||
return llama_model_load_from_file_impl(metadata, set_tensor_data, set_tensor_data_ud, path_model, splits, /*file*/ nullptr, params);
|
||||
}
|
||||
|
||||
+24
-13
@@ -239,6 +239,29 @@ struct decode_embd_batch {
|
||||
}
|
||||
};
|
||||
|
||||
// Helper class to set non-causal attention via RAII
|
||||
class scope_non_causal {
|
||||
public:
|
||||
scope_non_causal(llama_context * context, bool enabled) : context_(context), enabled_(enabled) {
|
||||
if (enabled_) {
|
||||
// TODO @ngxson : need to make sure only one image is processed at a time, and n_ubatch must be enough to hold the image
|
||||
llama_set_causal_attn(context_, false);
|
||||
}
|
||||
}
|
||||
~scope_non_causal() {
|
||||
if (enabled_) {
|
||||
llama_set_causal_attn(context_, true);
|
||||
}
|
||||
}
|
||||
|
||||
scope_non_causal(const scope_non_causal &) = delete;
|
||||
scope_non_causal & operator=(const scope_non_causal &) = delete;
|
||||
|
||||
private:
|
||||
llama_context * context_;
|
||||
bool enabled_;
|
||||
};
|
||||
|
||||
// Helper function for decoding an image whose embeddings have already been calculated
|
||||
int32_t mtmd_helper_decode_image_chunk(
|
||||
mtmd_context * ctx,
|
||||
@@ -289,10 +312,7 @@ int32_t mtmd_helper_decode_image_chunk(
|
||||
}
|
||||
|
||||
const bool use_non_causal = mtmd_decode_use_non_causal(ctx, chunk);
|
||||
if (use_non_causal) {
|
||||
llama_set_causal_attn(lctx, false);
|
||||
// TODO @ngxson : need to make sure only one image is processed at a time, and n_ubatch must be enough to hold the image
|
||||
}
|
||||
const scope_non_causal non_causal(lctx, use_non_causal);
|
||||
|
||||
while (i_batch < n_img_batches) { // split into batches
|
||||
int pos_offset = i_batch*n_batch;
|
||||
@@ -305,9 +325,6 @@ int32_t mtmd_helper_decode_image_chunk(
|
||||
int32_t ret = llama_decode(lctx, batch_embd_view);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("failed to decode %s\n", name);
|
||||
if (use_non_causal) {
|
||||
llama_set_causal_attn(lctx, true);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -315,9 +332,6 @@ int32_t mtmd_helper_decode_image_chunk(
|
||||
ret = callback(batch_embd_view, user_data);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("post-decode callback failed\n");
|
||||
if (use_non_causal) {
|
||||
llama_set_causal_attn(lctx, true);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -330,9 +344,6 @@ int32_t mtmd_helper_decode_image_chunk(
|
||||
n_past += mtmd_input_chunk_get_n_pos(chunk);
|
||||
*new_n_past = n_past;
|
||||
|
||||
if (use_non_causal) {
|
||||
llama_set_causal_attn(lctx, true);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -852,7 +852,7 @@ void server_models::load(const std::string & name, const load_options & opts) {
|
||||
// so that we can use stdout for commands and stderr for logging
|
||||
int options = subprocess_option_no_window | subprocess_option_combined_stdout_stderr;
|
||||
inst.subproc->sproc.emplace();
|
||||
int result = subprocess_create_ex(argv.data(), options, envp.data(), &inst.subproc->get());
|
||||
int result = subprocess_create_ex(argv.data(), options, envp.data(), nullptr, &inst.subproc->get());
|
||||
if (result != 0) {
|
||||
throw std::runtime_error("failed to spawn server instance");
|
||||
}
|
||||
|
||||
@@ -36,3 +36,7 @@ static/favicon*
|
||||
*storybook.log
|
||||
storybook-static
|
||||
*.code-workspace
|
||||
|
||||
# Vitest browser mode failure artifacts
|
||||
.vitest-attachments/
|
||||
tests/**/__screenshots__/
|
||||
|
||||
@@ -16,3 +16,6 @@ build/
|
||||
/build/
|
||||
/.svelte-kit/
|
||||
test-results
|
||||
|
||||
# Vendored third party sources, kept byte identical to upstream
|
||||
src/lib/vendors/
|
||||
|
||||
@@ -59,7 +59,8 @@ export default ts.config(
|
||||
'.svelte-kit/**',
|
||||
'test-results/**',
|
||||
'.storybook/**/*',
|
||||
'src/lib/services/sandbox-worker.js'
|
||||
'src/lib/services/sandbox-worker.js',
|
||||
'src/lib/vendors/**'
|
||||
]
|
||||
},
|
||||
storybook.configs['flat/recommended']
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import { build } from 'esbuild';
|
||||
import { dirname, resolve } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import type { Plugin } from 'vite';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const VENDORS_DIR = resolve(__dirname, '../src/lib/vendors');
|
||||
const VIRTUAL_ID = 'virtual:nerdamer';
|
||||
const RESOLVED_ID = '\0' + VIRTUAL_ID;
|
||||
|
||||
/**
|
||||
* Bundle the vendored nerdamer-prime source into a minified IIFE string,
|
||||
* exposed as the `virtual:nerdamer` module. Flags mirror the upstream
|
||||
* build (esbuild --bundle --minify --format=iife --global-name=nerdamer),
|
||||
* so only human readable source lives in the repo and minification is a
|
||||
* build artifact. Vendored under src/lib/vendors/, upstream snapshot:
|
||||
* https://github.com/together-science/nerdamer-prime/commit/1936145f8af306ec0d883b9bfd7730aedd175c24
|
||||
*/
|
||||
export function nerdamerPlugin(): Plugin {
|
||||
let bundled: string | null = null;
|
||||
|
||||
return {
|
||||
name: 'llamacpp:nerdamer',
|
||||
resolveId(id) {
|
||||
return id === VIRTUAL_ID ? RESOLVED_ID : undefined;
|
||||
},
|
||||
async load(id) {
|
||||
if (id !== RESOLVED_ID) return undefined;
|
||||
if (bundled === null) {
|
||||
const result = await build({
|
||||
entryPoints: [resolve(VENDORS_DIR, 'nerdamer-prime/all.js')],
|
||||
bundle: true,
|
||||
minify: true,
|
||||
format: 'iife',
|
||||
globalName: 'nerdamer',
|
||||
alias: {
|
||||
'big-integer': resolve(VENDORS_DIR, 'big-integer/BigInteger.js'),
|
||||
'decimal.js': resolve(VENDORS_DIR, 'decimal.js/decimal.js')
|
||||
},
|
||||
write: false,
|
||||
logLevel: 'silent'
|
||||
});
|
||||
bundled = result.outputFiles[0].text;
|
||||
}
|
||||
return `export default ${JSON.stringify(bundled)};`;
|
||||
}
|
||||
};
|
||||
}
|
||||
+3
-1
@@ -71,7 +71,9 @@
|
||||
|
||||
<div class="flex items-center gap-1 {className}">
|
||||
<DropdownMenu.Root bind:open={dropdownOpen}>
|
||||
<Tooltip.Root>
|
||||
<!-- ignoreNonKeyboardFocus prevents the tooltip from flashing when the
|
||||
menu closes and focus returns to the trigger -->
|
||||
<Tooltip.Root ignoreNonKeyboardFocus>
|
||||
<Tooltip.Trigger>
|
||||
{#snippet child({ props })}
|
||||
<DropdownMenu.Trigger
|
||||
|
||||
+13
-14
@@ -5,18 +5,18 @@
|
||||
import * as Tooltip from '$lib/components/ui/tooltip';
|
||||
import { useReasoningMenu } from '$lib/hooks/use-reasoning-menu.svelte';
|
||||
|
||||
let subOpen = $state(false);
|
||||
|
||||
const reasoning = useReasoningMenu();
|
||||
</script>
|
||||
|
||||
{#if reasoning.modelSupportsThinking}
|
||||
<DropdownMenu.Sub bind:open={subOpen}>
|
||||
<DropdownMenu.Sub>
|
||||
<DropdownMenu.SubTrigger class="flex cursor-pointer items-center gap-2">
|
||||
{#if reasoning.thinkingEnabled}
|
||||
<Lightbulb class="{ICON_CLASS_DEFAULT} shrink-0 text-amber-400" />
|
||||
{:else}
|
||||
{:else if reasoning.isOff}
|
||||
<LightbulbOff class="{ICON_CLASS_DEFAULT} shrink-0 text-muted-foreground" />
|
||||
{:else}
|
||||
<Lightbulb class="{ICON_CLASS_DEFAULT} shrink-0 text-muted-foreground" />
|
||||
{/if}
|
||||
|
||||
<span
|
||||
@@ -27,7 +27,7 @@
|
||||
Reasoning
|
||||
|
||||
<span class="capitalize text-muted-foreground">
|
||||
{reasoning.thinkingEnabled ? reasoning.currentEffort : 'off'}
|
||||
{reasoning.currentEffort}
|
||||
</span>
|
||||
</span>
|
||||
</DropdownMenu.SubTrigger>
|
||||
@@ -37,14 +37,13 @@
|
||||
>
|
||||
{#each reasoning.levels as level (level.value)}
|
||||
{@const tokenLabel = reasoning.tokenLabel(level)}
|
||||
<button
|
||||
type="button"
|
||||
class="flex w-full cursor-pointer items-center gap-3 rounded-md px-2 py-1.75 text-left text-sm transition-colors hover:bg-accent"
|
||||
class:bg-accent={reasoning.isSelected(level)}
|
||||
onclick={() => {
|
||||
reasoning.select(level);
|
||||
subOpen = false;
|
||||
}}
|
||||
<DropdownMenu.Item
|
||||
class="flex w-full cursor-pointer items-center gap-3 rounded-md px-2 py-1.75 text-left text-sm transition-colors hover:bg-accent {reasoning.isSelected(
|
||||
level
|
||||
)
|
||||
? 'bg-accent'
|
||||
: ''}"
|
||||
onclick={() => reasoning.select(level)}
|
||||
>
|
||||
{#if reasoning.isSelected(level)}
|
||||
<Check class="{ICON_CLASS_DEFAULT} shrink-0 text-foreground" />
|
||||
@@ -70,7 +69,7 @@
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
{/if}
|
||||
</button>
|
||||
</DropdownMenu.Item>
|
||||
{/each}
|
||||
</DropdownMenu.SubContent>
|
||||
</DropdownMenu.Sub>
|
||||
|
||||
+4
-2
@@ -116,14 +116,16 @@
|
||||
|
||||
{#if reasoning.thinkingEnabled}
|
||||
<Lightbulb class="{ICON_CLASS_DEFAULT} shrink-0 text-amber-400" />
|
||||
{:else}
|
||||
{:else if reasoning.isOff}
|
||||
<LightbulbOff class="{ICON_CLASS_DEFAULT} shrink-0 text-muted-foreground" />
|
||||
{:else}
|
||||
<Lightbulb class="{ICON_CLASS_DEFAULT} shrink-0 text-muted-foreground" />
|
||||
{/if}
|
||||
|
||||
<span class="flex-1">Reasoning</span>
|
||||
|
||||
<span class="text-xs capitalize text-muted-foreground">
|
||||
{reasoning.thinkingEnabled ? reasoning.currentEffort : 'off'}
|
||||
{reasoning.currentEffort}
|
||||
</span>
|
||||
</Collapsible.Trigger>
|
||||
|
||||
|
||||
-127
@@ -1,127 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { Check, Info, Lightbulb, LightbulbOff } from '@lucide/svelte';
|
||||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
|
||||
import * as Tooltip from '$lib/components/ui/tooltip';
|
||||
import { ReasoningEffort, MessageRole } from '$lib/enums';
|
||||
import { REASONING_EFFORT_TOKENS } from '$lib/constants/reasoning-effort-tokens';
|
||||
import { REASONING_EFFORT_LEVELS } from '$lib/constants/reasoning-effort';
|
||||
import type { ReasoningEffortLevel } from '$lib/types';
|
||||
import { DIALOG_SUBMENU_CONTENT, ICON_CLASS_DEFAULT } from '$lib/constants/css-classes';
|
||||
import {
|
||||
modelsStore,
|
||||
checkModelSupportsThinking,
|
||||
supportsThinking,
|
||||
propsCacheVersion,
|
||||
loadedModelIds
|
||||
} from '$lib/stores/models.svelte';
|
||||
import { chatStore } from '$lib/stores/chat.svelte';
|
||||
import { conversationsStore, activeMessages } from '$lib/stores/conversations.svelte';
|
||||
import { isRouterMode } from '$lib/stores/server.svelte';
|
||||
import type { DatabaseMessage } from '$lib/types/database';
|
||||
|
||||
let thinkingEnabled = $derived(conversationsStore.getThinkingEnabled());
|
||||
let currentEffort = $derived(conversationsStore.getReasoningEffort());
|
||||
let isOff = $derived(!thinkingEnabled);
|
||||
let subOpen = $state(false);
|
||||
|
||||
// Get conversation model from message history
|
||||
let conversationModel = $derived(
|
||||
chatStore.getConversationModel(activeMessages() as DatabaseMessage[])
|
||||
);
|
||||
|
||||
let modelSupportsThinkingFromMessages = $derived.by(() => {
|
||||
const modelId = isRouterMode() ? modelsStore.selectedModelName || conversationModel : null;
|
||||
if (!modelId) return false;
|
||||
|
||||
const messages = conversationsStore.activeMessages;
|
||||
|
||||
return messages.some(
|
||||
(m: DatabaseMessage) =>
|
||||
m.role === MessageRole.ASSISTANT && m.model === modelId && !!m.reasoningContent
|
||||
);
|
||||
});
|
||||
|
||||
let modelSupportsThinking = $derived.by(() => {
|
||||
loadedModelIds();
|
||||
propsCacheVersion();
|
||||
|
||||
if (isRouterMode()) {
|
||||
const modelId = modelsStore.selectedModelName || conversationModel;
|
||||
return checkModelSupportsThinking(modelId ?? '') || modelSupportsThinkingFromMessages;
|
||||
}
|
||||
|
||||
return supportsThinking() || modelSupportsThinkingFromMessages;
|
||||
});
|
||||
|
||||
function isSelected(item: ReasoningEffortLevel): boolean {
|
||||
if (item.isOff) return isOff;
|
||||
|
||||
return thinkingEnabled && currentEffort === item.value;
|
||||
}
|
||||
|
||||
function handleSelection(item: ReasoningEffortLevel) {
|
||||
if (item.isOff) {
|
||||
conversationsStore.setThinkingEnabled(false);
|
||||
} else {
|
||||
conversationsStore.setThinkingEnabled(true);
|
||||
conversationsStore.setReasoningEffort(item.value as ReasoningEffort);
|
||||
}
|
||||
subOpen = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if modelSupportsThinking}
|
||||
<DropdownMenu.Sub bind:open={subOpen}>
|
||||
<DropdownMenu.SubTrigger class="flex cursor-pointer items-center gap-2">
|
||||
{#if thinkingEnabled}
|
||||
<Lightbulb class="{ICON_CLASS_DEFAULT} shrink-0 text-amber-400" />
|
||||
{:else}
|
||||
<LightbulbOff class="{ICON_CLASS_DEFAULT} shrink-0 text-muted-foreground" />
|
||||
{/if}
|
||||
|
||||
<span class="flex-1">Thinking</span>
|
||||
|
||||
{#if thinkingEnabled}
|
||||
<span class="text-xs text-muted-foreground">{currentEffort}</span>
|
||||
{:else}
|
||||
<span class="text-xs text-muted-foreground">off</span>
|
||||
{/if}
|
||||
</DropdownMenu.SubTrigger>
|
||||
|
||||
<DropdownMenu.SubContent class={DIALOG_SUBMENU_CONTENT}>
|
||||
{#each REASONING_EFFORT_LEVELS as level (level.value)}
|
||||
<button
|
||||
type="button"
|
||||
class="flex w-full cursor-pointer items-center gap-2"
|
||||
class:bg-accent={isSelected(level)}
|
||||
onclick={() => handleSelection(level)}
|
||||
>
|
||||
<span class="flex-1 text-left">{level.label}</span>
|
||||
|
||||
{#if !level.isOff}
|
||||
<span class="text-[11px] text-muted-foreground opacity-60">
|
||||
{REASONING_EFFORT_TOKENS[level.value] === -1
|
||||
? 'Unlimited'
|
||||
: `Max ${REASONING_EFFORT_TOKENS[level.value].toLocaleString()} tokens`}
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
{#if level.hasInfo}
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger>
|
||||
<Info class="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Content side="left">
|
||||
<p>Maximum thinking effort with extended context usage</p>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
{/if}
|
||||
|
||||
{#if isSelected(level)}
|
||||
<Check class="{ICON_CLASS_DEFAULT} shrink-0 text-foreground" />
|
||||
{/if}
|
||||
</button>
|
||||
{/each}
|
||||
</DropdownMenu.SubContent>
|
||||
</DropdownMenu.Sub>
|
||||
{/if}
|
||||
@@ -68,6 +68,9 @@ export const BEARER_PREFIX = 'Bearer ';
|
||||
/** Canonical casing for the Authorization header (RFC 7235) */
|
||||
export const AUTHORIZATION_HEADER = 'Authorization';
|
||||
|
||||
/** Content-Type HTTP header name */
|
||||
export const CONTENT_TYPE_HEADER = 'Content-Type';
|
||||
|
||||
/** Header names whose values should be redacted in diagnostic logs */
|
||||
export const REDACTED_HEADERS = new Set([
|
||||
'authorization',
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { ReasoningEffortLevel } from '$lib/types';
|
||||
* Keys match the ReasoningEffort enum values for type-safe lookups.
|
||||
*/
|
||||
export const REASONING_EFFORT_LABELS: Record<string, string> = {
|
||||
[ReasoningEffort.DEFAULT]: 'Default',
|
||||
[ReasoningEffort.OFF]: 'Off',
|
||||
[ReasoningEffort.LOW]: 'Low',
|
||||
[ReasoningEffort.MEDIUM]: 'Medium',
|
||||
@@ -14,7 +15,8 @@ export const REASONING_EFFORT_LABELS: Record<string, string> = {
|
||||
};
|
||||
|
||||
export const REASONING_EFFORT_LEVELS: ReasoningEffortLevel[] = [
|
||||
{ value: ReasoningEffort.OFF, label: 'Off', isOff: true },
|
||||
{ value: ReasoningEffort.DEFAULT, label: 'Default' },
|
||||
{ value: ReasoningEffort.OFF, label: 'Off' },
|
||||
{ value: ReasoningEffort.LOW, label: 'Low' },
|
||||
{ value: ReasoningEffort.MEDIUM, label: 'Medium' },
|
||||
{ value: ReasoningEffort.HIGH, label: 'High' },
|
||||
|
||||
@@ -13,27 +13,44 @@ export const SANDBOX_EMPTY_OUTPUT = '(no output)';
|
||||
|
||||
export const SANDBOX_TRUNCATION_NOTICE = '[output truncated]';
|
||||
|
||||
export const SANDBOX_TOOL_DEFINITION: OpenAIToolDefinition = {
|
||||
type: ToolCallType.FUNCTION,
|
||||
function: {
|
||||
name: SANDBOX_TOOL_NAME,
|
||||
description:
|
||||
'Execute JavaScript in a sandboxed browser worker (no DOM, no page access). ' +
|
||||
'Top level await is supported. Use console.log to print intermediate values; ' +
|
||||
'a top level return statement is captured as the result.',
|
||||
parameters: {
|
||||
type: JsonSchemaType.OBJECT,
|
||||
properties: {
|
||||
code: {
|
||||
type: JsonSchemaType.STRING,
|
||||
description: 'JavaScript source to execute'
|
||||
const NERDAMER_DESCRIPTION = `
|
||||
Symbolic/numeric math via \`nerdamer\` (pre-loaded, do not require, use it directly).
|
||||
nerdamer('diff(sin(x)/x,x)') or nerdamer.diff('sin(x)/x','x') → Expression; convert with .toString()/.text()/.toTeX(), or .evaluate() (→ still Expression, then .toString()).
|
||||
nerdamer(expr,{x:2}) substitutes only; chain .evaluate() or pass 'numer' for numeric result.
|
||||
solve(expr,var)→Symbol[]; solveEquations([eq1,..])→[[var,val],..] pairs.
|
||||
Functions: simplify/expand/factor(expr), diff(expr,var[,n]), integrate(expr,var), defint(expr,from,to,var), limit(expr,var,to), laplace(expr,t,s), ilt(expr,s,t), gcd/lcm(a,b), roots/coeffs/partfrac(expr,var), pfactor(n), numer/decimals/erf(expr), product/sum(expr,var,from,to), mean/median/stdev/variance(...vals).
|
||||
Object.keys(nerdamer).filter(k=>typeof nerdamer[k]==='function') lists all available functions. If you need a function not documented above, list them first — do not guess function names.`;
|
||||
|
||||
/**
|
||||
* Build the sandbox tool definition. When `includeSymbolicMath` is true,
|
||||
* the description includes nerdamer API documentation; otherwise it
|
||||
* describes a plain JavaScript sandbox.
|
||||
*/
|
||||
export function buildSandboxToolDefinition(includeSymbolicMath: boolean): OpenAIToolDefinition {
|
||||
return {
|
||||
type: ToolCallType.FUNCTION,
|
||||
function: {
|
||||
name: SANDBOX_TOOL_NAME,
|
||||
description: includeSymbolicMath
|
||||
? `Execute JS in a sandboxed browser worker (no DOM/page access). Top-level await ok; console.log for intermediates; top-level return is captured as result.${NERDAMER_DESCRIPTION}`
|
||||
: 'Execute JS in a sandboxed browser worker (no DOM/page access). Top-level await ok; console.log for intermediates; top-level return is captured as result.',
|
||||
parameters: {
|
||||
type: JsonSchemaType.OBJECT,
|
||||
properties: {
|
||||
code: {
|
||||
type: JsonSchemaType.STRING,
|
||||
description: 'JavaScript source to execute'
|
||||
},
|
||||
timeout_ms: {
|
||||
type: JsonSchemaType.NUMBER,
|
||||
description: `Execution timeout in milliseconds, default ${SANDBOX_TIMEOUT_MS_DEFAULT}, max ${SANDBOX_TIMEOUT_MS_MAX}`
|
||||
}
|
||||
},
|
||||
timeout_ms: {
|
||||
type: JsonSchemaType.NUMBER,
|
||||
description: `Execution timeout in milliseconds, default ${SANDBOX_TIMEOUT_MS_DEFAULT}, max ${SANDBOX_TIMEOUT_MS_MAX}`
|
||||
}
|
||||
},
|
||||
required: ['code']
|
||||
required: ['code']
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/** @deprecated Use {@link buildSandboxToolDefinition} instead. Kept for backward compatibility. */
|
||||
export const SANDBOX_TOOL_DEFINITION = buildSandboxToolDefinition(true);
|
||||
|
||||
@@ -67,6 +67,7 @@ export const SETTINGS_KEYS = {
|
||||
EXCLUDE_REASONING_FROM_CONTEXT: 'excludeReasoningFromContext',
|
||||
SHOW_RAW_OUTPUT_SWITCH: 'showRawOutputSwitch',
|
||||
JS_SANDBOX_ENABLED: 'jsSandboxEnabled',
|
||||
SYMBOLIC_MATH_ENABLED: 'symbolicMathEnabled',
|
||||
// PY_INTERPRETER_ENABLED: 'pyInterpreterEnabled',
|
||||
CUSTOM_JSON: 'customJson',
|
||||
CUSTOM_CSS: 'customCss'
|
||||
|
||||
@@ -95,8 +95,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
defaultValue: ColorMode.SYSTEM,
|
||||
type: SettingsFieldType.SELECT,
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL,
|
||||
options: COLOR_MODE_OPTIONS,
|
||||
sync: { serverKey: SETTINGS_KEYS.THEME, paramType: SyncableParameterType.STRING }
|
||||
options: COLOR_MODE_OPTIONS
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.API_KEY,
|
||||
@@ -112,11 +111,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
help: 'The starting message that defines how model should behave.',
|
||||
defaultValue: '',
|
||||
type: SettingsFieldType.TEXTAREA,
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.SYSTEM_MESSAGE,
|
||||
paramType: SyncableParameterType.STRING
|
||||
}
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.PASTE_LONG_TEXT_TO_FILE_LEN,
|
||||
@@ -124,11 +119,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
help: 'On pasting long text, it will be converted to a file. You can control the file length by setting the value of this parameter. Value 0 means disable.',
|
||||
defaultValue: 2500,
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.PASTE_LONG_TEXT_TO_FILE_LEN,
|
||||
paramType: SyncableParameterType.NUMBER
|
||||
}
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.SEND_ON_ENTER,
|
||||
@@ -136,11 +127,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
help: 'Use Enter to send messages and Shift + Enter for new lines. When disabled, use Ctrl/Cmd + Enter.',
|
||||
defaultValue: true,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.SEND_ON_ENTER,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.AUTO_MIC_ON_EMPTY,
|
||||
@@ -149,11 +136,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL,
|
||||
isExperimental: true,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.AUTO_MIC_ON_EMPTY,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
isExperimental: true
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.ENABLE_CONTINUE_GENERATION,
|
||||
@@ -162,22 +145,14 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
defaultValue: true,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL,
|
||||
isExperimental: false,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.ENABLE_CONTINUE_GENERATION,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
isExperimental: false
|
||||
},
|
||||
{
|
||||
...TITLE_GENERATION_BASE,
|
||||
key: SETTINGS_KEYS.TITLE_GENERATION_USE_FIRST_LINE,
|
||||
label: 'Conversation title',
|
||||
help: 'Choose how conversation titles are generated. The first non-empty line uses a fast deterministic rule; the LLM option uses a model-generated title from the first message exchange.',
|
||||
defaultValue: true,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.TITLE_GENERATION_USE_FIRST_LINE,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
defaultValue: true
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.TITLE_GENERATION_PROMPT,
|
||||
@@ -186,11 +161,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
defaultValue: TITLE_GENERATION.DEFAULT_PROMPT,
|
||||
type: SettingsFieldType.TEXTAREA,
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL,
|
||||
dependsOn: SETTINGS_KEYS.TITLE_GENERATION_USE_LLM,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.TITLE_GENERATION_PROMPT,
|
||||
paramType: SyncableParameterType.STRING
|
||||
}
|
||||
dependsOn: SETTINGS_KEYS.TITLE_GENERATION_USE_LLM
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.COPY_TEXT_ATTACHMENTS_AS_PLAIN_TEXT,
|
||||
@@ -198,11 +169,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
help: 'When copying a message with text attachments, combine them into a single plain text string instead of a special format that can be pasted back as attachments.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.COPY_TEXT_ATTACHMENTS_AS_PLAIN_TEXT,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.PDF_AS_IMAGE,
|
||||
@@ -210,11 +177,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
help: 'Parse PDF as image instead of text. Automatically falls back to text processing for non-vision models.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.PDF_AS_IMAGE,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.MAX_IMAGE_RESOLUTION,
|
||||
@@ -222,11 +185,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
help: 'Images larger than this will be resized before sending to server. Set to 0 to disable.',
|
||||
defaultValue: 0,
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.MAX_IMAGE_RESOLUTION,
|
||||
paramType: SyncableParameterType.NUMBER
|
||||
}
|
||||
section: SETTINGS_SECTION_SLUGS.GENERAL
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -241,11 +200,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
help: 'Display generation statistics (tokens/second, token count, duration) below each assistant message.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.SHOW_MESSAGE_STATS,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.SHOW_AGENTIC_TURN_STATS,
|
||||
@@ -262,11 +217,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
help: 'Expand thought process by default when generating messages.',
|
||||
defaultValue: true,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.SHOW_THOUGHT_IN_PROGRESS,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.ALWAYS_SHOW_TOOL_CALL_CONTENT,
|
||||
@@ -274,11 +225,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
help: 'Automatically expand tool call details while executing and keep them expanded after completion.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.ALWAYS_SHOW_TOOL_CALL_CONTENT,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.RENDER_USER_CONTENT_AS_MARKDOWN,
|
||||
@@ -286,11 +233,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
help: 'Render user messages using markdown formatting in the chat.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.RENDER_USER_CONTENT_AS_MARKDOWN,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.RENDER_THINKING_AS_MARKDOWN,
|
||||
@@ -298,11 +241,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
help: 'Render the reasoning/thinking block content as formatted Markdown instead of plain text.',
|
||||
defaultValue: true,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.RENDER_THINKING_AS_MARKDOWN,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.FULL_HEIGHT_CODE_BLOCKS,
|
||||
@@ -310,11 +249,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
help: 'Always display code blocks at their full natural height, overriding any height limits.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.FULL_HEIGHT_CODE_BLOCKS,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.DISABLE_AUTO_SCROLL,
|
||||
@@ -322,11 +257,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
help: 'Disable automatic scrolling while messages stream so you can control the viewport position manually.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.DISABLE_AUTO_SCROLL,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.ALWAYS_SHOW_SIDEBAR_ON_DESKTOP,
|
||||
@@ -334,11 +265,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
help: 'Always keep the sidebar visible on desktop instead of auto-hiding it.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.ALWAYS_SHOW_SIDEBAR_ON_DESKTOP,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.SHOW_RAW_MODEL_NAMES,
|
||||
@@ -346,11 +273,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
help: 'Display full raw model identifiers (e.g. "ggml-org/GLM-4.7-Flash-GGUF:Q8_0") instead of parsed names with badges.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.SHOW_RAW_MODEL_NAMES,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.SHOW_MODEL_QUANTIZATION,
|
||||
@@ -358,11 +281,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
help: 'Display quantization badges (e.g. Q8_0, Q4_K_M) next to model names throughout the interface.',
|
||||
defaultValue: true,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.SHOW_MODEL_QUANTIZATION,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.SHOW_MODEL_TAGS,
|
||||
@@ -370,11 +289,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
help: 'Display model tags (e.g. "vision", "reasoning") next to model names throughout the interface.',
|
||||
defaultValue: true,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.SHOW_MODEL_TAGS,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.SHOW_BUILD_VERSION,
|
||||
@@ -382,11 +297,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
help: 'Display the current build version in the bottom-right corner of the interface.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.SHOW_BUILD_VERSION,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
section: SETTINGS_SECTION_SLUGS.DISPLAY
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -518,11 +429,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
help: 'Enable backend-based samplers. When enabled, supported samplers run on the accelerator backend for faster sampling.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.SAMPLING,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.BACKEND_SAMPLING,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
section: SETTINGS_SECTION_SLUGS.SAMPLING
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -638,11 +545,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
defaultValue: 10,
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.AGENTIC,
|
||||
isPositiveInteger: true,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.AGENTIC_MAX_TURNS,
|
||||
paramType: SyncableParameterType.NUMBER
|
||||
}
|
||||
isPositiveInteger: true
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.MCP_REQUEST_TIMEOUT_SECONDS,
|
||||
@@ -651,11 +554,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
defaultValue: DEFAULT_MCP_CONFIG.requestTimeoutSeconds,
|
||||
type: SettingsFieldType.INPUT,
|
||||
section: SETTINGS_SECTION_SLUGS.AGENTIC,
|
||||
isPositiveInteger: true,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.MCP_REQUEST_TIMEOUT_SECONDS,
|
||||
paramType: SyncableParameterType.NUMBER
|
||||
}
|
||||
isPositiveInteger: true
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -670,11 +569,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
help: 'After each response, re-submit the conversation to pre-fill the server KV cache. Makes the next turn faster since the prompt is already encoded while you read the response.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DEVELOPER,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.PRE_ENCODE_CONVERSATION,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
section: SETTINGS_SECTION_SLUGS.DEVELOPER
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.DISABLE_REASONING_PARSING,
|
||||
@@ -682,11 +577,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
help: 'Send reasoning_format=none so the server returns thinking tokens inline instead of extracting them into a separate field.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DEVELOPER,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.DISABLE_REASONING_PARSING,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
section: SETTINGS_SECTION_SLUGS.DEVELOPER
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.EXCLUDE_REASONING_FROM_CONTEXT,
|
||||
@@ -694,11 +585,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
help: 'Strip thinking from previous messages before sending. When off, thinking is sent back via the reasoning_content field so the model sees its own chain-of-thought across turns.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DEVELOPER,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.EXCLUDE_REASONING_FROM_CONTEXT,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
section: SETTINGS_SECTION_SLUGS.DEVELOPER
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.SHOW_RAW_OUTPUT_SWITCH,
|
||||
@@ -706,11 +593,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
help: 'Show toggle button to display messages as plain text instead of Markdown-formatted content',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DEVELOPER,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.SHOW_RAW_OUTPUT_SWITCH,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
section: SETTINGS_SECTION_SLUGS.DEVELOPER
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.JS_SANDBOX_ENABLED,
|
||||
@@ -718,11 +601,16 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
help: 'Expose a run_javascript tool to the model. Code runs in a Web Worker inside a sandboxed iframe with an opaque origin, isolated from the WebUI and its API, with a hard timeout.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DEVELOPER
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.SYMBOLIC_MATH_ENABLED,
|
||||
label: 'Symbolic math (nerdamer)',
|
||||
help: 'Pre-load nerdamer in the sandbox for symbolic computation: simplify, diff, integrate, solve, and more. Requires "JavaScript sandbox tool" to be enabled.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
section: SETTINGS_SECTION_SLUGS.DEVELOPER,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.JS_SANDBOX_ENABLED,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
dependsOn: SETTINGS_KEYS.JS_SANDBOX_ENABLED
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.CUSTOM_JSON,
|
||||
@@ -738,11 +626,7 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
|
||||
help: 'CSS injected into the page at runtime. Set it here, or ship it server side via the --ui-config customCss field.',
|
||||
defaultValue: '',
|
||||
type: SettingsFieldType.TEXTAREA,
|
||||
section: SETTINGS_SECTION_SLUGS.DEVELOPER,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.CUSTOM_CSS,
|
||||
paramType: SyncableParameterType.STRING
|
||||
}
|
||||
section: SETTINGS_SECTION_SLUGS.DEVELOPER
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -754,30 +638,21 @@ const NON_UI_SETTINGS: SettingsEntry[] = [
|
||||
label: 'Show system message',
|
||||
help: 'Display the system message at the top of each conversation.',
|
||||
defaultValue: true,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.SHOW_SYSTEM_MESSAGE,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
type: SettingsFieldType.CHECKBOX
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.MCP_SERVERS,
|
||||
label: 'MCP servers',
|
||||
help: 'Configure MCP servers as a JSON list. Use the form in the MCP Client settings section to edit.',
|
||||
defaultValue: '[]',
|
||||
type: SettingsFieldType.INPUT,
|
||||
sync: { serverKey: SETTINGS_KEYS.MCP_SERVERS, paramType: SyncableParameterType.STRING }
|
||||
type: SettingsFieldType.INPUT
|
||||
},
|
||||
{
|
||||
key: SETTINGS_KEYS.TITLE_GENERATION_USE_LLM,
|
||||
label: 'Generate title with LLM',
|
||||
help: 'Counterpart of the conversation title radio; stored and synced without a dedicated UI field.',
|
||||
defaultValue: false,
|
||||
type: SettingsFieldType.CHECKBOX,
|
||||
sync: {
|
||||
serverKey: SETTINGS_KEYS.TITLE_GENERATION_USE_LLM,
|
||||
paramType: SyncableParameterType.BOOLEAN
|
||||
}
|
||||
type: SettingsFieldType.CHECKBOX
|
||||
}
|
||||
// {
|
||||
// key: SETTINGS_KEYS.PY_INTERPRETER_ENABLED,
|
||||
@@ -786,7 +661,7 @@ const NON_UI_SETTINGS: SettingsEntry[] = [
|
||||
// defaultValue: false,
|
||||
// type: SettingsFieldType.CHECKBOX,
|
||||
// isExperimental: true,
|
||||
// sync: { serverKey: SETTINGS_KEYS.PY_INTERPRETER_ENABLED, paramType: SyncableParameterType.BOOLEAN }
|
||||
//
|
||||
// }
|
||||
];
|
||||
|
||||
|
||||
@@ -180,6 +180,7 @@ export enum UriPattern {
|
||||
|
||||
// MIME type enums
|
||||
export enum MimeTypeApplication {
|
||||
JSON = 'application/json',
|
||||
PDF = 'application/pdf',
|
||||
OCTET_STREAM = 'application/octet-stream',
|
||||
ZIP = 'application/zip'
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* These values are sent to the server and mapped to token budgets.
|
||||
*/
|
||||
export enum ReasoningEffort {
|
||||
DEFAULT = 'default',
|
||||
OFF = 'off',
|
||||
LOW = 'low',
|
||||
MEDIUM = 'medium',
|
||||
|
||||
@@ -17,6 +17,7 @@ import { isRouterMode } from '$lib/stores/server.svelte';
|
||||
export interface UseReasoningMenuReturn {
|
||||
readonly modelSupportsThinking: boolean;
|
||||
readonly thinkingEnabled: boolean;
|
||||
readonly isOff: boolean;
|
||||
readonly currentEffort: ReasoningEffort;
|
||||
readonly levels: ReasoningEffortLevel[];
|
||||
isSelected(level: ReasoningEffortLevel): boolean;
|
||||
@@ -59,8 +60,10 @@ export function useReasoningMenu(): UseReasoningMenuReturn {
|
||||
return supportsThinking() || modelSupportsThinkingFromMessages;
|
||||
});
|
||||
|
||||
const thinkingEnabled = $derived(conversationsStore.getThinkingEnabled());
|
||||
const currentEffort = $derived(conversationsStore.getReasoningEffort());
|
||||
const thinkingEnabled = $derived(
|
||||
currentEffort !== ReasoningEffort.OFF && currentEffort !== ReasoningEffort.DEFAULT
|
||||
);
|
||||
|
||||
return {
|
||||
get modelSupportsThinking() {
|
||||
@@ -69,6 +72,9 @@ export function useReasoningMenu(): UseReasoningMenuReturn {
|
||||
get thinkingEnabled() {
|
||||
return thinkingEnabled;
|
||||
},
|
||||
get isOff() {
|
||||
return currentEffort === ReasoningEffort.OFF;
|
||||
},
|
||||
get currentEffort() {
|
||||
return currentEffort;
|
||||
},
|
||||
@@ -76,20 +82,15 @@ export function useReasoningMenu(): UseReasoningMenuReturn {
|
||||
return REASONING_EFFORT_LEVELS;
|
||||
},
|
||||
isSelected(level: ReasoningEffortLevel): boolean {
|
||||
if (level.isOff) return !thinkingEnabled;
|
||||
return thinkingEnabled && currentEffort === level.value;
|
||||
return currentEffort === level.value;
|
||||
},
|
||||
tokenLabel(level: ReasoningEffortLevel): string | null {
|
||||
if (level.isOff) return null;
|
||||
if (level.value === ReasoningEffort.DEFAULT) return 'Model default';
|
||||
const tokens = REASONING_EFFORT_TOKENS[level.value];
|
||||
if (tokens === undefined) return null;
|
||||
return tokens === -1 ? 'Unlimited' : `Max ${tokens.toLocaleString()} tokens`;
|
||||
},
|
||||
select(level: ReasoningEffortLevel): void {
|
||||
if (level.isOff) {
|
||||
conversationsStore.setThinkingEnabled(false);
|
||||
return;
|
||||
}
|
||||
conversationsStore.setThinkingEnabled(true);
|
||||
conversationsStore.setReasoningEffort(level.value as ReasoningEffort);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -271,10 +271,14 @@ export class ChatService {
|
||||
const reasoningBudgetTokens =
|
||||
enableThinking && reasoningEffort ? (REASONING_EFFORT_TOKENS[reasoningEffort] ?? -1) : -1;
|
||||
|
||||
requestBody.chat_template_kwargs = {
|
||||
...(requestBody.chat_template_kwargs ?? {}),
|
||||
enable_thinking: enableThinking
|
||||
};
|
||||
// an explicit user choice injects the kwarg, otherwise it is omitted so
|
||||
// the server default applies (--reasoning flag or chat template)
|
||||
if (enableThinking !== undefined) {
|
||||
requestBody.chat_template_kwargs = {
|
||||
...(requestBody.chat_template_kwargs ?? {}),
|
||||
enable_thinking: enableThinking
|
||||
};
|
||||
}
|
||||
|
||||
if (reasoningBudgetTokens >= 0) {
|
||||
requestBody.thinking_budget_tokens = reasoningBudgetTokens;
|
||||
|
||||
@@ -276,7 +276,7 @@ export { MCPService } from './mcp.service';
|
||||
* - **toolsStore**: Exposes the tool definition when the sandbox is enabled
|
||||
* - **agenticStore**: Dispatches ToolSource.FRONTEND calls here
|
||||
*
|
||||
* @see SANDBOX_TOOL_DEFINITION in constants/sandbox.ts - tool schema sent to the LLM
|
||||
* @see buildSandboxToolDefinition in constants/sandbox.ts - tool schema sent to the LLM
|
||||
* @see agenticStore in stores/agentic.svelte.ts - tool dispatch
|
||||
*/
|
||||
export { SandboxService } from './sandbox.service';
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { ParameterSyncService } from './parameter-sync.service';
|
||||
import { ColorMode } from '$lib/enums';
|
||||
|
||||
describe('ParameterSyncService', () => {
|
||||
describe('roundFloatingPoint', () => {
|
||||
@@ -132,18 +131,13 @@ describe('ParameterSyncService', () => {
|
||||
expect(result.temperature).toBe(0.7);
|
||||
});
|
||||
|
||||
it('should merge ui settings from props when provided', () => {
|
||||
const result = ParameterSyncService.extractServerDefaults(null, {
|
||||
pasteLongTextToFileLen: 0,
|
||||
pdfAsImage: true,
|
||||
renderUserContentAsMarkdown: false,
|
||||
theme: ColorMode.DARK
|
||||
});
|
||||
it('extracts sampling twins only, never ui settings', () => {
|
||||
const result = ParameterSyncService.extractServerDefaults(null);
|
||||
|
||||
expect(result.pasteLongTextToFileLen).toBe(0);
|
||||
expect(result.pdfAsImage).toBe(true);
|
||||
expect(result.renderUserContentAsMarkdown).toBe(false);
|
||||
expect(result.theme).toBeUndefined();
|
||||
expect(result).toEqual({});
|
||||
expect(ParameterSyncService.canSyncParameter('theme')).toBe(false);
|
||||
expect(ParameterSyncService.canSyncParameter('pdfAsImage')).toBe(false);
|
||||
expect(ParameterSyncService.canSyncParameter('temperature')).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -29,12 +29,10 @@ export class ParameterSyncService {
|
||||
* Converts samplers array to semicolon-delimited string for UI display.
|
||||
*
|
||||
* @param serverParams - Raw generation settings from server `/props` endpoint
|
||||
* @param uiSettings - Optional UI-specific settings from server
|
||||
* @returns Record of extracted parameter key-value pairs with normalized precision
|
||||
*/
|
||||
static extractServerDefaults(
|
||||
serverParams: ApiLlamaCppServerProps['default_generation_settings']['params'] | null,
|
||||
uiSettings?: Record<string, string | number | boolean>
|
||||
serverParams: ApiLlamaCppServerProps['default_generation_settings']['params'] | null
|
||||
): ParameterRecord {
|
||||
const extracted: ParameterRecord = {};
|
||||
|
||||
@@ -57,18 +55,6 @@ export class ParameterSyncService {
|
||||
}
|
||||
}
|
||||
|
||||
if (uiSettings) {
|
||||
for (const param of SYNCABLE_PARAMETERS) {
|
||||
if (param.canSync && param.serverKey in uiSettings) {
|
||||
const value = uiSettings[param.serverKey];
|
||||
|
||||
if (value !== undefined) {
|
||||
extracted[param.key] = this.roundFloatingPoint(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return extracted;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,25 @@
|
||||
import { NEWLINE } from '$lib/constants';
|
||||
import WORKER_SHIM from './sandbox-worker.js?raw';
|
||||
|
||||
/**
|
||||
* CSP for the harness document, inherited by the blob worker. connect-src
|
||||
* falls back to default-src, removing network egress for model and vendored
|
||||
* code. 'unsafe-eval' is required by the worker's AsyncFunction constructor,
|
||||
* 'unsafe-inline' by the inline script below, worker-src by the blob worker.
|
||||
*/
|
||||
const HARNESS_CSP = `default-src 'none'; script-src 'unsafe-inline' 'unsafe-eval'; worker-src blob:`;
|
||||
|
||||
/**
|
||||
* Harness loaded as srcdoc into a sandboxed iframe (allow-scripts only).
|
||||
* The opaque origin is the security boundary: no access to the app origin,
|
||||
* its storage or its API. The harness spawns a worker so model code never
|
||||
* runs on a main thread, which makes the parent timeout enforceable by
|
||||
* removing the iframe.
|
||||
* removing the iframe. The prelude runs in the worker before the shim,
|
||||
* exposing globals such as `nerdamer` to model code.
|
||||
*/
|
||||
export const SANDBOX_HARNESS_HTML = `<!doctype html><script>
|
||||
const SHIM = ${JSON.stringify(WORKER_SHIM)};
|
||||
export function buildSandboxHarness(preludeJs: string): string {
|
||||
return `<!doctype html><meta http-equiv="Content-Security-Policy" content="${HARNESS_CSP}"><script>
|
||||
const SHIM = ${JSON.stringify(preludeJs + NEWLINE + WORKER_SHIM)};
|
||||
addEventListener('message', (event) => {
|
||||
const respond = (payload) => parent.postMessage(payload, '*');
|
||||
let worker;
|
||||
@@ -23,3 +34,4 @@ addEventListener('message', (event) => {
|
||||
worker.postMessage({ code: event.data.code });
|
||||
});
|
||||
</script>`;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,9 @@ self.onmessage = async (event) => {
|
||||
const reply = { logs, result: null, error: null };
|
||||
try {
|
||||
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
|
||||
const value = await new AsyncFunction(event.data.code)();
|
||||
// The prelude bundled ahead of this shim defines self.nerdamer,
|
||||
// passed into the execution scope as the `nerdamer` parameter.
|
||||
const value = await new AsyncFunction('nerdamer', event.data.code)(self.nerdamer);
|
||||
if (value !== undefined) reply.result = fmt(value);
|
||||
} catch (err) {
|
||||
reply.error = err instanceof Error ? err.stack || err.message : String(err);
|
||||
|
||||
@@ -7,9 +7,32 @@ import {
|
||||
SANDBOX_TOOL_NAME,
|
||||
SANDBOX_TRUNCATION_NOTICE
|
||||
} from '$lib/constants';
|
||||
import { SANDBOX_HARNESS_HTML } from './sandbox-harness';
|
||||
import { buildSandboxHarness } from './sandbox-harness';
|
||||
import { config } from '$lib/stores/settings.svelte';
|
||||
import type { ToolExecutionResult } from '$lib/types';
|
||||
|
||||
/** Cached harnesses keyed by whether nerdamer is included. */
|
||||
const harnessCache: Record<string, string> = {};
|
||||
|
||||
/**
|
||||
* Build the sandbox harness. When symbolic math is enabled, loads the
|
||||
* nerdamer prelude lazily; otherwise builds a plain harness with an empty
|
||||
* prelude. Cached per variant so toggling the setting is instant.
|
||||
*/
|
||||
async function getHarness(): Promise<string> {
|
||||
const enabled = !!config().symbolicMathEnabled;
|
||||
const key = enabled ? 'nerdamer' : 'plain';
|
||||
if (!harnessCache[key]) {
|
||||
if (enabled) {
|
||||
const { default: nerdamerJs } = await import('virtual:nerdamer');
|
||||
harnessCache[key] = buildSandboxHarness(nerdamerJs);
|
||||
} else {
|
||||
harnessCache[key] = buildSandboxHarness('');
|
||||
}
|
||||
}
|
||||
return harnessCache[key];
|
||||
}
|
||||
|
||||
interface SandboxReply {
|
||||
logs?: unknown;
|
||||
result?: unknown;
|
||||
@@ -45,20 +68,22 @@ export class SandboxService {
|
||||
* timeout or abort. Removing the iframe terminates the worker
|
||||
* at the browser level, so runaway code cannot outlive it.
|
||||
*/
|
||||
static executeTool(
|
||||
static async executeTool(
|
||||
toolName: string,
|
||||
params: Record<string, unknown>,
|
||||
signal?: AbortSignal
|
||||
): Promise<ToolExecutionResult> {
|
||||
if (toolName !== SANDBOX_TOOL_NAME) {
|
||||
return Promise.resolve({ content: `Unknown frontend tool: ${toolName}`, isError: true });
|
||||
return { content: `Unknown frontend tool: ${toolName}`, isError: true };
|
||||
}
|
||||
|
||||
const code = typeof params.code === 'string' ? params.code : '';
|
||||
if (!code) {
|
||||
return Promise.resolve({ content: 'Missing required parameter: code', isError: true });
|
||||
return { content: 'Missing required parameter: code', isError: true };
|
||||
}
|
||||
|
||||
const harness = await getHarness();
|
||||
|
||||
const requested = Number(params.timeout_ms);
|
||||
const timeoutMs =
|
||||
Number.isFinite(requested) && requested > 0
|
||||
@@ -69,7 +94,7 @@ export class SandboxService {
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('sandbox', 'allow-scripts');
|
||||
iframe.style.display = 'none';
|
||||
iframe.srcdoc = SANDBOX_HARNESS_HTML;
|
||||
iframe.srcdoc = harness;
|
||||
|
||||
let settled = false;
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ import { DatabaseService } from '$lib/services/database.service';
|
||||
import { ChatService } from '$lib/services/chat.service';
|
||||
import { streamIdentity } from '$lib/utils/stream-identity';
|
||||
import { getAuthHeaders } from '$lib/utils/api-headers';
|
||||
import { CONTENT_TYPE_HEADER } from '$lib/constants';
|
||||
import { MimeTypeApplication } from '$lib/enums';
|
||||
import { conversationsStore } from '$lib/stores/conversations.svelte';
|
||||
import { config } from '$lib/stores/settings.svelte';
|
||||
import { agenticStore } from '$lib/stores/agentic.svelte';
|
||||
@@ -208,7 +210,7 @@ class ChatStore {
|
||||
// POST the one conv id we are probing
|
||||
listResp = await fetch(`./v1/streams/lookup`, {
|
||||
method: 'POST',
|
||||
headers: { ...getAuthHeaders(), 'Content-Type': 'application/json' },
|
||||
headers: { ...getAuthHeaders(), [CONTENT_TYPE_HEADER]: MimeTypeApplication.JSON },
|
||||
body: JSON.stringify({ conversation_ids: [convId] })
|
||||
});
|
||||
} catch (e) {
|
||||
@@ -672,7 +674,7 @@ class ChatStore {
|
||||
try {
|
||||
const resp = await fetch('./v1/streams/lookup', {
|
||||
method: 'POST',
|
||||
headers: { ...getAuthHeaders(), 'Content-Type': 'application/json' },
|
||||
headers: { ...getAuthHeaders(), [CONTENT_TYPE_HEADER]: MimeTypeApplication.JSON },
|
||||
body: JSON.stringify({ conversation_ids: lookupIds })
|
||||
});
|
||||
if (!resp.ok) return;
|
||||
@@ -2373,9 +2375,12 @@ class ChatStore {
|
||||
|
||||
if (currentConfig.excludeReasoningFromContext) apiOptions.excludeReasoningFromContext = true;
|
||||
|
||||
apiOptions.enableThinking = conversationsStore.getThinkingEnabled();
|
||||
// an explicit reasoning choice overrides the server default, DEFAULT sends nothing
|
||||
const effort = conversationsStore.getReasoningEffort();
|
||||
if (effort !== ReasoningEffort.OFF) apiOptions.reasoningEffort = effort;
|
||||
if (effort !== ReasoningEffort.DEFAULT) {
|
||||
apiOptions.enableThinking = effort !== ReasoningEffort.OFF;
|
||||
if (effort !== ReasoningEffort.OFF) apiOptions.reasoningEffort = effort;
|
||||
}
|
||||
|
||||
if (hasValue(currentConfig.temperature))
|
||||
apiOptions.temperature = Number(currentConfig.temperature);
|
||||
|
||||
@@ -80,25 +80,17 @@ class ConversationsStore {
|
||||
/** Whether the store has been initialized */
|
||||
isInitialized = $state(false);
|
||||
|
||||
/** Global (non-conversation-specific) thinking toggle default, derived from reasoning effort */
|
||||
pendingThinkingEnabled = $state(false);
|
||||
|
||||
/** Global (non-conversation-specific) reasoning effort default */
|
||||
pendingReasoningEffort = $state<ReasoningEffort | ReasoningEffort.OFF>(
|
||||
ConversationsStore.loadReasoningEffortDefault()
|
||||
);
|
||||
pendingReasoningEffort = $state<ReasoningEffort>(ConversationsStore.loadReasoningEffortDefault());
|
||||
|
||||
/** Last non-off reasoning effort, restored when re-enabling thinking globally */
|
||||
private lastNonOffEffort: ReasoningEffort | null = null;
|
||||
|
||||
/** Load reasoning effort default from localStorage */
|
||||
private static loadReasoningEffortDefault(): ReasoningEffort | ReasoningEffort.OFF {
|
||||
if (typeof globalThis.localStorage === 'undefined') return ReasoningEffort.OFF;
|
||||
/** Load reasoning effort default from localStorage, DEFAULT defers to the server */
|
||||
private static loadReasoningEffortDefault(): ReasoningEffort {
|
||||
if (typeof globalThis.localStorage === 'undefined') return ReasoningEffort.DEFAULT;
|
||||
try {
|
||||
const raw = localStorage.getItem(REASONING_EFFORT_DEFAULT_LOCALSTORAGE_KEY);
|
||||
return (raw as ReasoningEffort | ReasoningEffort.OFF) || ReasoningEffort.OFF;
|
||||
return (raw as ReasoningEffort) || ReasoningEffort.DEFAULT;
|
||||
} catch {
|
||||
return ReasoningEffort.OFF;
|
||||
return ReasoningEffort.DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,17 +227,10 @@ class ConversationsStore {
|
||||
// servers without a per-conversation override to `mcpServers[i].enabled`,
|
||||
// and only explicit toggles are stored on the conversation.
|
||||
|
||||
// Inherit global thinking/reasoning defaults into the new conversation
|
||||
const thinkingEnabled = this.getThinkingEnabled();
|
||||
conversation.thinkingEnabled = thinkingEnabled;
|
||||
conversation.reasoningEffort =
|
||||
this.pendingReasoningEffort === ReasoningEffort.OFF ? undefined : this.pendingReasoningEffort;
|
||||
// Inherit the global reasoning default into the new conversation
|
||||
conversation.reasoningEffort = this.pendingReasoningEffort;
|
||||
await DatabaseService.updateConversation(conversation.id, {
|
||||
thinkingEnabled,
|
||||
reasoningEffort:
|
||||
this.pendingReasoningEffort === ReasoningEffort.OFF
|
||||
? undefined
|
||||
: this.pendingReasoningEffort
|
||||
reasoningEffort: this.pendingReasoningEffort
|
||||
});
|
||||
|
||||
this.conversations = [conversation, ...this.conversations];
|
||||
@@ -793,63 +778,21 @@ class ConversationsStore {
|
||||
await this.setMcpServerOverride(serverId, undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the effective thinking-enabled state for the active conversation.
|
||||
* Returns the conversation override if set, otherwise the global default.
|
||||
*/
|
||||
getThinkingEnabled(): boolean {
|
||||
if (this.activeConversation) {
|
||||
if (this.activeConversation.thinkingEnabled !== undefined) {
|
||||
return this.activeConversation.thinkingEnabled;
|
||||
}
|
||||
}
|
||||
return this.getReasoningEffort() !== ReasoningEffort.OFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the thinking-enabled state for the active conversation.
|
||||
* If no conversation exists, stores the global default.
|
||||
* @param enabled - The enabled state
|
||||
*/
|
||||
async setThinkingEnabled(enabled: boolean): Promise<void> {
|
||||
if (!this.activeConversation) {
|
||||
if (enabled) {
|
||||
const effort = this.lastNonOffEffort ?? ReasoningEffort.LOW;
|
||||
this.pendingReasoningEffort = effort;
|
||||
this.saveReasoningEffortDefaults();
|
||||
} else {
|
||||
if (this.pendingReasoningEffort !== ReasoningEffort.OFF) {
|
||||
this.lastNonOffEffort = this.pendingReasoningEffort;
|
||||
}
|
||||
this.pendingReasoningEffort = ReasoningEffort.OFF;
|
||||
this.saveReasoningEffortDefaults();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.activeConversation = {
|
||||
...this.activeConversation,
|
||||
thinkingEnabled: enabled
|
||||
};
|
||||
|
||||
await DatabaseService.updateConversation(this.activeConversation.id, {
|
||||
thinkingEnabled: enabled
|
||||
});
|
||||
|
||||
const convIndex = this.conversations.findIndex((c) => c.id === this.activeConversation!.id);
|
||||
if (convIndex !== -1) {
|
||||
this.conversations[convIndex].thinkingEnabled = enabled;
|
||||
this.conversations = [...this.conversations];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the effective reasoning effort for the active conversation.
|
||||
* Returns the conversation override if set, otherwise the global default.
|
||||
* DEFAULT means no override is sent and the server decides.
|
||||
*/
|
||||
getReasoningEffort(): ReasoningEffort | ReasoningEffort.OFF {
|
||||
getReasoningEffort(): ReasoningEffort {
|
||||
if (this.activeConversation) {
|
||||
return this.activeConversation.reasoningEffort ?? this.pendingReasoningEffort;
|
||||
if (this.activeConversation.reasoningEffort !== undefined) {
|
||||
return this.activeConversation.reasoningEffort;
|
||||
}
|
||||
// conversations created before the tri-state store an explicit
|
||||
// opt-out only as thinkingEnabled = false
|
||||
if (this.activeConversation.thinkingEnabled === false) {
|
||||
return ReasoningEffort.OFF;
|
||||
}
|
||||
}
|
||||
return this.pendingReasoningEffort;
|
||||
}
|
||||
@@ -857,7 +800,7 @@ class ConversationsStore {
|
||||
/**
|
||||
* Sets the reasoning effort for the active conversation.
|
||||
* If no conversation exists, stores the global default.
|
||||
* @param effort - The effort level ('low' | 'medium' | 'high' | 'max')
|
||||
* @param effort - The effort level ('default' | 'off' | 'low' | 'medium' | 'high' | 'max')
|
||||
*/
|
||||
async setReasoningEffort(effort: ReasoningEffort): Promise<void> {
|
||||
if (!this.activeConversation) {
|
||||
|
||||
@@ -64,6 +64,10 @@ class SettingsStore {
|
||||
isInitialized = $state(false);
|
||||
userOverrides = $state<Set<string>>(new Set());
|
||||
|
||||
// True until a config exists in localStorage; gates the one-time
|
||||
// application of server ui_settings defaults for new users.
|
||||
private isFirstVisit = false;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
@@ -77,10 +81,7 @@ class SettingsStore {
|
||||
* Centralizes the pattern of getting and extracting server defaults
|
||||
*/
|
||||
private getServerDefaults(): Record<string, string | number | boolean> {
|
||||
const serverParams = serverStore.defaultParams;
|
||||
const uiSettings = serverStore.uiSettings;
|
||||
|
||||
return ParameterSyncService.extractServerDefaults(serverParams, uiSettings);
|
||||
return ParameterSyncService.extractServerDefaults(serverStore.defaultParams);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
@@ -121,6 +122,11 @@ class SettingsStore {
|
||||
|
||||
try {
|
||||
const storedConfigRaw = localStorage.getItem(CONFIG_LOCALSTORAGE_KEY);
|
||||
|
||||
// First visit: no stored config yet. Server ui_settings apply once in
|
||||
// this state, then the user's config diverges freely.
|
||||
this.isFirstVisit = storedConfigRaw === null;
|
||||
|
||||
const savedVal = JSON.parse(storedConfigRaw || '{}');
|
||||
|
||||
// Merge with defaults to prevent breaking changes
|
||||
@@ -349,9 +355,12 @@ class SettingsStore {
|
||||
}
|
||||
}
|
||||
|
||||
// UI settings need actual values in config (no placeholder mechanism),
|
||||
// so write them for non-overridden keys
|
||||
if (uiSettings) {
|
||||
// UI settings are the admin's defaults for new users: applied once on
|
||||
// the first visit, never on later loads, so the user's config can
|
||||
// diverge. "Reset to Default" is the explicit way back to the baseline.
|
||||
if (uiSettings && this.isFirstVisit) {
|
||||
this.isFirstVisit = false;
|
||||
|
||||
for (const [key, value] of Object.entries(uiSettings)) {
|
||||
if (!this.userOverrides.has(key) && value !== undefined) {
|
||||
setConfigValue(this.config, key, value);
|
||||
@@ -391,6 +400,27 @@ class SettingsStore {
|
||||
this.userOverrides.delete(key);
|
||||
}
|
||||
|
||||
// Non-syncable keys: reset is a full return to the instance state, the
|
||||
// admin baseline value when defined, the factory default otherwise.
|
||||
for (const key of Object.keys(SETTING_CONFIG_DEFAULT)) {
|
||||
if (ParameterSyncService.canSyncParameter(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const value =
|
||||
uiSettings && key in uiSettings && uiSettings[key] !== undefined
|
||||
? uiSettings[key]
|
||||
: getConfigValue(SETTING_CONFIG_DEFAULT, key);
|
||||
|
||||
setConfigValue(this.config, key, value);
|
||||
|
||||
if (key === SETTINGS_KEYS.THEME) {
|
||||
setMode(value as ColorMode);
|
||||
}
|
||||
|
||||
this.userOverrides.delete(key);
|
||||
}
|
||||
|
||||
this.saveConfig();
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { HealthCheckStatus, JsonSchemaType, ToolCallType, ToolSource } from '$li
|
||||
import { config } from '$lib/stores/settings.svelte';
|
||||
import {
|
||||
DISABLED_TOOL_KEYS_LOCALSTORAGE_KEY,
|
||||
SANDBOX_TOOL_DEFINITION,
|
||||
buildSandboxToolDefinition,
|
||||
TOOL_GROUP_LABELS,
|
||||
TOOL_SERVER_LABELS
|
||||
} from '$lib/constants';
|
||||
@@ -143,7 +143,9 @@ class ToolsStore {
|
||||
}
|
||||
|
||||
get frontendTools(): OpenAIToolDefinition[] {
|
||||
return config().jsSandboxEnabled ? [SANDBOX_TOOL_DEFINITION] : [];
|
||||
return config().jsSandboxEnabled
|
||||
? [buildSandboxToolDefinition(!!config().symbolicMathEnabled)]
|
||||
: [];
|
||||
}
|
||||
|
||||
get customTools(): OpenAIToolDefinition[] {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
export interface ReasoningEffortLevel {
|
||||
value: string;
|
||||
label: string;
|
||||
isOff?: boolean;
|
||||
hasInfo?: boolean;
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@ import { config } from '$lib/stores/settings.svelte';
|
||||
import {
|
||||
AUTHORIZATION_HEADER,
|
||||
BEARER_PREFIX,
|
||||
CONTENT_TYPE_HEADER,
|
||||
CORS_PROXY_HEADER_PREFIX,
|
||||
REDACTED_HEADERS
|
||||
} from '$lib/constants';
|
||||
import { MimeTypeApplication } from '$lib/enums';
|
||||
import { redactValue } from './redact';
|
||||
|
||||
/**
|
||||
@@ -23,7 +25,7 @@ export function getAuthHeaders(): Record<string, string> {
|
||||
*/
|
||||
export function getJsonHeaders(): Record<string, string> {
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
[CONTENT_TYPE_HEADER]: MimeTypeApplication.JSON,
|
||||
...getAuthHeaders()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { base } from '$app/paths';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { browser } from '$app/environment';
|
||||
import { AUTHORIZATION_HEADER, BEARER_PREFIX } from '$lib/constants';
|
||||
import { AUTHORIZATION_HEADER, BEARER_PREFIX, CONTENT_TYPE_HEADER } from '$lib/constants';
|
||||
import { MimeTypeApplication } from '$lib/enums';
|
||||
import { config } from '$lib/stores/settings.svelte';
|
||||
|
||||
/**
|
||||
@@ -15,19 +16,18 @@ export async function validateApiKey(fetch: typeof globalThis.fetch): Promise<vo
|
||||
|
||||
const apiKey = config().apiKey;
|
||||
|
||||
// No API key configured — server doesn't require auth, skip the request entirely.
|
||||
// The /props endpoint is only protected when the server has API keys configured,
|
||||
// and in that case the client always has one set (from settings).
|
||||
if (!apiKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
[AUTHORIZATION_HEADER]: `${BEARER_PREFIX}${apiKey}`
|
||||
[CONTENT_TYPE_HEADER]: MimeTypeApplication.JSON
|
||||
};
|
||||
|
||||
// Probe /props even without a stored key: on a server started with
|
||||
// --api-key the unauthenticated request returns 401 and surfaces the
|
||||
// API key splash, which is the onboarding path for entering the key.
|
||||
if (apiKey) {
|
||||
headers[AUTHORIZATION_HEADER] = `${BEARER_PREFIX}${apiKey}`;
|
||||
}
|
||||
|
||||
const response = await fetch(`${base}/props`, { headers });
|
||||
|
||||
if (!response.ok) {
|
||||
|
||||
+1453
File diff suppressed because it is too large
Load Diff
+24
@@ -0,0 +1,24 @@
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org>
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
The MIT Licence.
|
||||
|
||||
Copyright (c) 2025 Michael Mclaughlin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
+4951
File diff suppressed because it is too large
Load Diff
+6213
File diff suppressed because it is too large
Load Diff
+3487
File diff suppressed because it is too large
Load Diff
+926
@@ -0,0 +1,926 @@
|
||||
/*
|
||||
* Author : Martin Donk
|
||||
* Website : http://www.nerdamer.com
|
||||
* Email : martin.r.donk@gmail.com
|
||||
* License : MIT
|
||||
* Source : https://github.com/jiggzson/nerdamer
|
||||
*/
|
||||
|
||||
// Type imports for JSDoc ======================================================
|
||||
// These typedefs provide type aliases for the interfaces defined in index.d.ts.
|
||||
// They enable proper type checking when working with the classes defined in this file.
|
||||
//
|
||||
// Usage patterns:
|
||||
// - For return types: @returns {NerdamerSymbolType}
|
||||
// - For parameters: @param {NerdamerSymbolType} symbol
|
||||
// - For variable declarations: /** @type {NerdamerSymbolType} */
|
||||
//
|
||||
// Note: When casting local class instances to interface types, use the pattern:
|
||||
// /** @type {InterfaceType} */ (/** @type {unknown} */ (localInstance))
|
||||
// This is needed because TypeScript sees local classes and interfaces as separate types.
|
||||
|
||||
/**
|
||||
* Core type aliases from index.d.ts
|
||||
*
|
||||
* @typedef {import('./index').NerdamerCore.NerdamerSymbol} NerdamerSymbolType
|
||||
*
|
||||
* @typedef {import('./index').NerdamerCore.Frac} FracType
|
||||
*
|
||||
* @typedef {import('./index').NerdamerCore.Vector} VectorType
|
||||
*
|
||||
* @typedef {import('./index').NerdamerCore.Matrix} MatrixType
|
||||
*
|
||||
* @typedef {import('./index').NerdamerCore.Parser} ParserType
|
||||
*
|
||||
* @typedef {import('./index').NerdamerCore.Settings} SettingsType
|
||||
*
|
||||
* @typedef {import('./index').NerdamerExpression} ExpressionType
|
||||
*
|
||||
* @typedef {typeof import('./index')} NerdamerType
|
||||
*
|
||||
* @typedef {import('./index').NerdamerCore.Utils} UtilsInterface
|
||||
*
|
||||
* @typedef {import('./index').NerdamerCore.Math2} Math2Interface
|
||||
*
|
||||
* @typedef {import('./index').NerdamerCore.Core} CoreType
|
||||
*
|
||||
* @typedef {import('./index').ExpressionParam} ExpressionParam
|
||||
*
|
||||
* @typedef {import('./index').ArithmeticOperand} ArithmeticOperand
|
||||
*
|
||||
* @typedef {import('./index').NerdamerCore.AlgebraModule} AlgebraModuleType
|
||||
*
|
||||
* @typedef {import('./index').NerdamerCore.PartFracSubModule} PartFracSubModuleType
|
||||
*
|
||||
* @typedef {import('./index').NerdamerCore.CalculusModule} CalculusModuleType
|
||||
*
|
||||
* @typedef {import('./index').NerdamerCore.ExtraModule} ExtraModuleType
|
||||
*
|
||||
* @typedef {import('./index').NerdamerCore.LaPlaceSubModule} LaPlaceSubModuleType
|
||||
*
|
||||
* @typedef {import('./index').NerdamerCore.StatisticsSubModule} StatisticsSubModuleType
|
||||
*
|
||||
* @typedef {import('./index').NerdamerCore.UnitsSubModule} UnitsSubModuleType
|
||||
*
|
||||
* @typedef {import('./index').NerdamerCore.DecomposeResultObject} DecomposeResultType
|
||||
*/
|
||||
|
||||
// Check if nerdamer exists globally (browser) or needs to be required (Node.js)
|
||||
let nerdamer = typeof globalThis !== 'undefined' && globalThis.nerdamer ? globalThis.nerdamer : undefined;
|
||||
if (typeof module !== 'undefined' && nerdamer === undefined) {
|
||||
nerdamer = require('./nerdamer.core.js');
|
||||
require('./Calculus');
|
||||
require('./Algebra');
|
||||
}
|
||||
|
||||
/** @returns {ExtraModuleType} */
|
||||
(function initExtraModule() {
|
||||
/** @type {CoreType} */
|
||||
const core = nerdamer.getCore();
|
||||
/** @type {ParserType} */
|
||||
const _ = core.PARSER;
|
||||
const {
|
||||
NerdamerSymbol,
|
||||
Vector: _Vector,
|
||||
/** @type {AlgebraModuleType} */
|
||||
Algebra,
|
||||
/** @type {CalculusModuleType} */
|
||||
Calculus,
|
||||
} = core;
|
||||
const { format, isVector, isArray, isSymbol } = core.Utils;
|
||||
const { S, EX: _EX, CP, PL, CB, FN } = core.groups;
|
||||
core.Settings.Laplace_integration_depth = 40;
|
||||
|
||||
/**
|
||||
* Check if a symbol's power is itself a symbol with group S or CB
|
||||
*
|
||||
* @param {NerdamerSymbolType} sym
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function hasPowerGroupSOrCB(sym) {
|
||||
return isSymbol(sym.power) && (sym.power.group === S || sym.power.group === CB);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a function by name within this symbol's tree.
|
||||
*
|
||||
* @this {NerdamerSymbolType}
|
||||
* @param {string} fname The function name to search for
|
||||
* @returns {NerdamerSymbolType | undefined} The found function symbol clone, or undefined if not found
|
||||
*/
|
||||
NerdamerSymbol.prototype.findFunction = function findFunction(fname) {
|
||||
// This is what we're looking for
|
||||
if (this.group === FN && this.fname === fname) {
|
||||
return this.clone();
|
||||
}
|
||||
let found;
|
||||
if (this.symbols) {
|
||||
for (const x in this.symbols) {
|
||||
if (!Object.hasOwn(this.symbols, x)) {
|
||||
continue;
|
||||
}
|
||||
found = this.symbols[x].findFunction(fname);
|
||||
if (found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
};
|
||||
|
||||
/** @type {ExtraModuleType} */
|
||||
const __ = (core.Extra = {
|
||||
version: '1.4.2',
|
||||
// http://integral-table.com/downloads/LaplaceTable.pdf
|
||||
// Laplace assumes all coefficients to be positive
|
||||
LaPlace: {
|
||||
// Using: integral_0^oo f(t)*e^(-s*t) dt
|
||||
/**
|
||||
* @param {NerdamerSymbolType} symbol
|
||||
* @param {NerdamerSymbolType | string} t
|
||||
* @param {NerdamerSymbolType | string} s
|
||||
* @returns {NerdamerSymbolType}
|
||||
*/
|
||||
transform(symbol, t, s) {
|
||||
/** @type {NerdamerSymbolType} */
|
||||
symbol = symbol.clone();
|
||||
|
||||
t = t.toString();
|
||||
// First try a lookup for a speed boost
|
||||
symbol = NerdamerSymbol.unwrapSQRT(symbol, true);
|
||||
/** @type {NerdamerSymbolType} */
|
||||
let retval;
|
||||
const coeff = symbol.stripVar(t);
|
||||
const g = symbol.group;
|
||||
|
||||
symbol = /** @type {NerdamerSymbolType} */ (_.divide(symbol, coeff.clone()));
|
||||
|
||||
if (symbol.isConstant() || !symbol.contains(t, true)) {
|
||||
retval = _.parse(format('({0})/({1})', symbol, s));
|
||||
} else if (g === S && core.Utils.isInt(symbol.power)) {
|
||||
const n = String(symbol.power);
|
||||
retval = _.parse(format('factorial({0})/({1})^({0}+1)', n, s));
|
||||
} else if (symbol.group === S && symbol.power.equals(1 / 2)) {
|
||||
retval = _.parse(format('sqrt(pi)/(2*({0})^(3/2))', s));
|
||||
} else if (symbol.isComposite()) {
|
||||
retval = new NerdamerSymbol(0);
|
||||
symbol.each(x => {
|
||||
retval = /** @type {NerdamerSymbolType} */ (_.add(retval, __.LaPlace.transform(x, t, s)));
|
||||
}, true);
|
||||
} else if (symbol.isE() && hasPowerGroupSOrCB(symbol)) {
|
||||
const a = /** @type {NerdamerSymbolType} */ (symbol.power).stripVar(t);
|
||||
retval = _.parse(format('1/(({1})-({0}))', a, s));
|
||||
} else {
|
||||
const fns = ['sin', 'cos', 'sinh', 'cosh'];
|
||||
// Support for symbols in fns with arguments in the form a*t or n*t where a = symbolic and n = Number
|
||||
if (
|
||||
symbol.group === FN &&
|
||||
fns.indexOf(symbol.fname) !== -1 &&
|
||||
(symbol.args[0].group === S || symbol.args[0].group === CB)
|
||||
) {
|
||||
const a = symbol.args[0].stripVar(t);
|
||||
|
||||
switch (symbol.fname) {
|
||||
case 'sin':
|
||||
retval = _.parse(format('({0})/(({1})^2+({0})^2)', a, s));
|
||||
break;
|
||||
case 'cos':
|
||||
retval = _.parse(format('({1})/(({1})^2+({0})^2)', a, s));
|
||||
break;
|
||||
case 'sinh':
|
||||
retval = _.parse(format('({0})/(({1})^2-({0})^2)', a, s));
|
||||
break;
|
||||
case 'cosh':
|
||||
retval = _.parse(format('({1})/(({1})^2-({0})^2)', a, s));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Try to integrate for a solution
|
||||
// we need at least the Laplace integration depth
|
||||
const depthIsLower = core.Settings.integration_depth < core.Settings.Laplace_integration_depth;
|
||||
|
||||
let savedIntegrationDepth;
|
||||
if (depthIsLower) {
|
||||
savedIntegrationDepth = core.Settings.integration_depth; // Save the depth
|
||||
core.Settings.integration_depth = core.Settings.Laplace_integration_depth; // Transforms need a little more room
|
||||
}
|
||||
|
||||
core.Utils.block(
|
||||
'PARSE2NUMBER',
|
||||
() => {
|
||||
const u = t;
|
||||
const sym = symbol.sub(t, u);
|
||||
const integrationExpr = _.parse(`e^(-${s}*${u})*${sym}`);
|
||||
retval = Calculus.integrate(integrationExpr, u);
|
||||
if (retval.hasIntegral?.()) {
|
||||
retval = _.symfunction('laplace', [symbol, _.parse(String(t)), _.parse(String(s))]);
|
||||
return;
|
||||
}
|
||||
// _.error('Unable to compute transform');
|
||||
retval = retval.sub(t, 0);
|
||||
retval = /** @type {NerdamerSymbolType} */ (
|
||||
_.expand(_.multiply(retval, new NerdamerSymbol(-1)))
|
||||
);
|
||||
retval = retval.sub(u, t);
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
retval = /** @type {NerdamerSymbolType} */ (
|
||||
core.Utils.block('PARSE2NUMBER', () => _.parse(retval), true)
|
||||
);
|
||||
|
||||
if (depthIsLower) // Put the integration depth as it was
|
||||
{
|
||||
core.Settings.integration_depth = savedIntegrationDepth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return /** @type {NerdamerSymbolType} */ (_.multiply(retval, coeff));
|
||||
},
|
||||
/**
|
||||
* @param {NerdamerSymbolType} symbol
|
||||
* @param {NerdamerSymbolType | string} s_
|
||||
* @param {NerdamerSymbolType | string} t
|
||||
* @returns {NerdamerSymbolType}
|
||||
*/
|
||||
inverse(symbol, s_, t) {
|
||||
const inputSymbol = symbol.clone();
|
||||
return core.Utils.block(
|
||||
'POSITIVE_MULTIPLIERS',
|
||||
() => {
|
||||
/** @type {NerdamerSymbolType | undefined} */
|
||||
let retval;
|
||||
// Expand and get partial fractions
|
||||
if (symbol.group === CB) {
|
||||
symbol = /** @type {NerdamerSymbolType} */ (
|
||||
/** @type {PartFracSubModuleType} */ (Algebra.PartFrac).partfrac(
|
||||
/** @type {NerdamerSymbolType} */ (_.expand(symbol)),
|
||||
s_
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (symbol.group === S || symbol.group === CB || symbol.isComposite()) {
|
||||
/** @type {number | FracType} */
|
||||
let p;
|
||||
/** @type {FracType} */
|
||||
let denP;
|
||||
/** @type {NerdamerSymbolType} */
|
||||
let a;
|
||||
/** @type {NerdamerSymbolType | string} */
|
||||
let b;
|
||||
/** @type {NerdamerSymbolType} */
|
||||
let d;
|
||||
/** @type {string} */
|
||||
let exp;
|
||||
/** @type {DecomposeResultType} */
|
||||
let f2;
|
||||
/** @type {string | number} */
|
||||
let fact;
|
||||
// Remove the multiplier
|
||||
const m = symbol.multiplier.clone();
|
||||
symbol.toUnitMultiplier();
|
||||
// Get the numerator and denominator
|
||||
let num = symbol.getNum();
|
||||
const den = symbol.getDenom().toUnitMultiplier();
|
||||
|
||||
// TODO: Make it so factor doesn't destroy pi
|
||||
// num = core.Algebra.Factor.factor(symbol.getNum());
|
||||
// den = core.Algebra.Factor.factor(symbol.getDenom().invert(null, true));
|
||||
|
||||
if (den.group === CP || den.group === PL) {
|
||||
denP = /** @type {FracType} */ (den.power.clone());
|
||||
den.toLinear();
|
||||
} else {
|
||||
denP = new core.Frac(1);
|
||||
}
|
||||
|
||||
// Convert s to a string
|
||||
const s = s_.toString();
|
||||
// Split up the denominator if in the form ax+b
|
||||
/** @type {DecomposeResultType} */
|
||||
const f = core.Utils.decompose_fn(den, s, true);
|
||||
// Move the multiplier to the numerator
|
||||
/** @type {DecomposeResultType} */
|
||||
const _fe = core.Utils.decompose_fn(
|
||||
/** @type {NerdamerSymbolType} */ (_.expand(num.clone())),
|
||||
s,
|
||||
true
|
||||
);
|
||||
num.multiplier = num.multiplier.multiply(m);
|
||||
|
||||
const finalize = function () {
|
||||
// Put back the numerator
|
||||
retval = /** @type {NerdamerSymbolType} */ (_.multiply(retval, num));
|
||||
retval.multiplier = retval.multiplier.multiply(symbol.multiplier);
|
||||
// Put back a
|
||||
retval = /** @type {NerdamerSymbolType} */ (_.divide(retval, f.a));
|
||||
};
|
||||
|
||||
// Store the parts in variables for easy recognition
|
||||
// check if in the form t^n where n = integer
|
||||
if (
|
||||
(den.group === S || den.group === CB) &&
|
||||
f.x.value === s &&
|
||||
f.b.equals(0) &&
|
||||
core.Utils.isInt(f.x.power)
|
||||
) {
|
||||
p = /** @type {number} */ (/** @type {unknown} */ (f.x.power)) - 1;
|
||||
fact = core.Math2.factorial(p);
|
||||
// N!/s^(n-1)
|
||||
retval = /** @type {NerdamerSymbolType} */ (
|
||||
_.divide(_.pow(_.parse(String(t)), new NerdamerSymbol(p)), new NerdamerSymbol(fact))
|
||||
);
|
||||
// Wrap it up
|
||||
finalize();
|
||||
} else if (den.group === CP && denP.equals(1)) {
|
||||
if (f.x.group === core.groups.PL && Algebra.degree(den).equals(2)) {
|
||||
// Possibly in the form 1/(s^2+2*s+1)
|
||||
// Try factoring to get it in a more familiar form{
|
||||
// Apply inverse of F(s-a)
|
||||
/**
|
||||
* @type {{
|
||||
* f: NerdamerSymbolType;
|
||||
* a: NerdamerSymbolType;
|
||||
* h: NerdamerSymbolType;
|
||||
* c?: NerdamerSymbolType;
|
||||
* }}
|
||||
*/
|
||||
const completed = Algebra.sqComplete(den, s);
|
||||
const u = core.Utils.getU(den);
|
||||
// Get a for the function above
|
||||
a = core.Utils.decompose_fn(completed.a, s, true).b;
|
||||
const tf = __.LaPlace.inverse(
|
||||
_.parse(`1/((${u})^2+(${completed.c}))`),
|
||||
u,
|
||||
String(t)
|
||||
);
|
||||
retval = /** @type {NerdamerSymbolType} */ (
|
||||
_.multiply(tf, _.parse(`(${m})*e^(-(${a})*(${t}))`))
|
||||
);
|
||||
// A/(b*s-c) -> ae^(-bt)
|
||||
} else if (f.x.isLinear() && !num.contains(s)) {
|
||||
t = /** @type {NerdamerSymbolType | string} */ (
|
||||
_.divide(_.parse(String(t)), f.a.clone())
|
||||
);
|
||||
|
||||
// Don't add factorial of one or zero
|
||||
p = /** @type {number} */ (/** @type {unknown} */ (denP)) - 1;
|
||||
fact = p === 0 || p === 1 ? '1' : `(${denP}-1)!`;
|
||||
retval = _.parse(
|
||||
format(
|
||||
'(({0})^({3}-1)*e^(-(({2})*({0}))/({1})))/(({4})*({1})^({3}))',
|
||||
t,
|
||||
f.a,
|
||||
f.b,
|
||||
denP,
|
||||
fact
|
||||
)
|
||||
);
|
||||
// Wrap it up
|
||||
finalize();
|
||||
} else if (f.x.group === S && f.x.power.equals(2)) {
|
||||
if (num.contains(s)) {
|
||||
// A*s/(b*s^2+c^2)
|
||||
a = new NerdamerSymbol(1);
|
||||
if (num.group === CB) {
|
||||
/** @type {NerdamerSymbolType} */
|
||||
let newNum = new NerdamerSymbol(1);
|
||||
num.each(x => {
|
||||
if (x.contains(s)) {
|
||||
newNum = /** @type {NerdamerSymbolType} */ (_.multiply(newNum, x));
|
||||
} else {
|
||||
a = /** @type {NerdamerSymbolType} */ (_.multiply(a, x));
|
||||
}
|
||||
});
|
||||
num = newNum;
|
||||
}
|
||||
|
||||
// We need more information about the denominator to decide
|
||||
f2 = core.Utils.decompose_fn(num, s, true);
|
||||
const fn1 = f2.a;
|
||||
const fn2 = f2.b;
|
||||
const aHasSin = fn1.containsFunction('sin');
|
||||
const aHasCos = fn1.containsFunction('cos');
|
||||
const bHasCos = fn2.containsFunction('cos');
|
||||
const bHasSin = fn2.containsFunction('sin');
|
||||
if (
|
||||
f2.x.value === s &&
|
||||
f2.x.isLinear() &&
|
||||
!((aHasSin && bHasCos) || aHasCos || bHasSin)
|
||||
) {
|
||||
retval = _.parse(
|
||||
format(
|
||||
'(({1})*cos((sqrt(({2})*({3}))*({0}))/({2})))/({2})',
|
||||
t,
|
||||
f2.a,
|
||||
f.a,
|
||||
f.b
|
||||
)
|
||||
);
|
||||
} else if (aHasSin && bHasCos) {
|
||||
const sin = /** @type {NerdamerSymbolType} */ (fn1.findFunction?.('sin'));
|
||||
const cos = /** @type {NerdamerSymbolType} */ (fn2.findFunction?.('cos'));
|
||||
// Who has the s?
|
||||
if (sin?.args?.[0].equals(cos?.args?.[0]) && !sin?.args?.[0].contains(s)) {
|
||||
b = /** @type {NerdamerSymbolType} */ (
|
||||
_.divide(fn2, cos.toUnitMultiplier())
|
||||
).toString();
|
||||
const c = sin.args[0].toString();
|
||||
d = f.b;
|
||||
const e = _.divide(fn1, sin.toUnitMultiplier());
|
||||
exp =
|
||||
'(({1})*({2})*cos({3})*sin(sqrt({4})*({0})))/sqrt({4})+({1})*sin({3})*({5})*cos(sqrt({4})*({0}))';
|
||||
retval = _.parse(format(exp, t, a, b, c, d, e));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
retval = _.parse(
|
||||
format(
|
||||
'(({1})*sin((sqrt(({2})*({3}))*({0}))/({2})))/sqrt(({2})*({3}))',
|
||||
t,
|
||||
num,
|
||||
f.a,
|
||||
f.b
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
/** @type {FracType} */ (f.x.power).num &&
|
||||
/** @type {FracType} */ (f.x.power).num.equals(3) &&
|
||||
/** @type {FracType} */ (f.x.power).den.equals(2) &&
|
||||
num.contains('sqrt(pi)') &&
|
||||
!num.contains(s) &&
|
||||
num.isLinear()
|
||||
) {
|
||||
b = /** @type {NerdamerSymbolType} */ (_.divide(num.clone(), _.parse('sqrt(pi)')));
|
||||
retval = _.parse(format('(2*({2})*sqrt({0}))/({1})', t, f.a, b, num));
|
||||
} else if (denP.equals(2) && f.x.power.equals(2)) {
|
||||
if (num.contains(s)) {
|
||||
// Decompose the numerator to check value of s
|
||||
f2 = core.Utils.decompose_fn(
|
||||
/** @type {NerdamerSymbolType} */ (_.expand(num.clone())),
|
||||
s,
|
||||
true
|
||||
);
|
||||
if (f2.x.isComposite()) {
|
||||
/** @type {DecomposeResultType[]} */
|
||||
const sTerms = [];
|
||||
// First collect the factors e.g. (a)(bx)(cx^2+d)
|
||||
/** @type {DecomposeResultType[]} */
|
||||
const symbols = /** @type {DecomposeResultType[]} */ (
|
||||
num
|
||||
.collectSymbols(x => {
|
||||
x = NerdamerSymbol.unwrapPARENS(x);
|
||||
/** @type {DecomposeResultType} */
|
||||
const decomp = core.Utils.decompose_fn(x, s, true);
|
||||
decomp.symbol = x;
|
||||
return decomp;
|
||||
})
|
||||
// Then sort them by power hightest to lowest
|
||||
.sort((x1, x2) => {
|
||||
const p1 =
|
||||
/** @type {DecomposeResultType} */ (x1).x.value === s
|
||||
? /** @type {number} */ (
|
||||
/** @type {unknown} */ (
|
||||
/** @type {DecomposeResultType} */ (x1).x.power
|
||||
)
|
||||
)
|
||||
: 0;
|
||||
const p2 =
|
||||
/** @type {DecomposeResultType} */ (x2).x.value === s
|
||||
? /** @type {number} */ (
|
||||
/** @type {unknown} */ (
|
||||
/** @type {DecomposeResultType} */ (x2).x.power
|
||||
)
|
||||
)
|
||||
: 0;
|
||||
return p2 - p1;
|
||||
})
|
||||
);
|
||||
a = new NerdamerSymbol(-1);
|
||||
// Grab only the ones which have s
|
||||
for (let i = 0; i < symbols.length; i++) {
|
||||
const fc = symbols[i];
|
||||
if (fc.x.value === s) {
|
||||
sTerms.push(fc);
|
||||
} else {
|
||||
a = /** @type {NerdamerSymbolType} */ (_.multiply(a, fc.symbol));
|
||||
}
|
||||
}
|
||||
// The following 2 assumptions are made
|
||||
// 1. since the numerator was factored above then each s_term has a unique power
|
||||
// 2. because the terms are sorted by descending powers then the first item
|
||||
// has the highest power
|
||||
// We can now check for the next type s(s^2-a^2)/(s^2+a^2)^2
|
||||
if (
|
||||
sTerms[0].x.power.equals(2) &&
|
||||
sTerms[1].x.power.equals(1) &&
|
||||
sTerms[1].b.equals(0) &&
|
||||
!sTerms[0].b.equals(0)
|
||||
) {
|
||||
b = sTerms[0].a.negate();
|
||||
exp =
|
||||
'-(({1})*({2})*({5})*({0})*sin((sqrt(({4})*({5}))*({0}))/({4})))/' +
|
||||
'(2*({4})^2*sqrt(({4})*({5})))-(({1})*({3})*({0})*sin((sqrt(({4})*({5}))*({0}))/({4})))' +
|
||||
'/(2*({4})*sqrt(({4})*({5})))+(({1})*({2})*cos((sqrt(({4})*({5}))*({0}))/({4})))/({4})^2';
|
||||
retval = _.parse(format(exp, t, a, b, sTerms[0].b, f.a, f.b));
|
||||
}
|
||||
} else if (f2.x.isLinear()) {
|
||||
a = /** @type {NerdamerSymbolType} */ (_.divide(f2.a, new NerdamerSymbol(2)));
|
||||
exp =
|
||||
'(({1})*({0})*sin((sqrt(({2})*({3}))*({0}))/({2})))/(({2})*sqrt(({2})*({3})))';
|
||||
retval = _.parse(format(exp, t, a, f.a, f.b));
|
||||
} else if (f2.x.power.equals(2)) {
|
||||
if (f2.b.equals(0)) {
|
||||
a = /** @type {NerdamerSymbolType} */ (
|
||||
_.divide(f2.a, new NerdamerSymbol(2))
|
||||
);
|
||||
exp =
|
||||
'(({1})*sin((sqrt(({2})*({3}))*({0}))/({2})))/(({2})*sqrt(({2})*({3})))+(({1})*({0})*cos((sqrt(({2})*({3}))*({0}))/({2})))/({2})^2';
|
||||
retval = _.parse(format(exp, t, a, f.a, f.b));
|
||||
} else {
|
||||
a = /** @type {NerdamerSymbolType} */ (
|
||||
_.divide(f2.a, new NerdamerSymbol(2))
|
||||
);
|
||||
d = f2.b.negate();
|
||||
exp =
|
||||
'-((({2})*({4})-2*({1})*({3}))*sin((sqrt(({2})*({3}))*({0}))/({2})))/(2*({2})*({3})*sqrt(({2})*({3})))+' +
|
||||
'(({4})*({0})*cos((sqrt(({2})*({3}))*({0}))/({2})))/(2*({2})*({3}))+(({1})*({0})*cos((sqrt(({2})*({3}))*({0}))/({2})))/({2})^2';
|
||||
retval = _.parse(format(exp, t, a, f.a, f.b, d));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
a = /** @type {NerdamerSymbolType} */ (_.divide(num, new NerdamerSymbol(2)));
|
||||
exp =
|
||||
'(({1})*sin((sqrt(({2})*({3}))*({0}))/({2})))/(({3})*sqrt(({2})*({3})))-(({1})*({0})*cos((sqrt(({2})*({3}))*({0}))/({2})))/(({2})*({3}))';
|
||||
retval = _.parse(format(exp, t, a, f.a, f.b));
|
||||
}
|
||||
} else if (symbol.isComposite()) {
|
||||
// 1/(s+1)^2
|
||||
if (denP.equals(2) && f.x.group === S) {
|
||||
retval = _.parse(`(${m})*(${t})*e^(-(${f.b})*(${t}))`);
|
||||
} else {
|
||||
retval = new NerdamerSymbol(0);
|
||||
|
||||
symbol = /** @type {NerdamerSymbolType} */ (
|
||||
/** @type {PartFracSubModuleType} */ (Algebra.PartFrac).partfrac(
|
||||
/** @type {NerdamerSymbolType} */ (_.expand(symbol)),
|
||||
s_
|
||||
)
|
||||
);
|
||||
|
||||
symbol.each(x => {
|
||||
retval = /** @type {NerdamerSymbolType} */ (
|
||||
_.add(retval, __.LaPlace.inverse(x, s_, t))
|
||||
);
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
retval ||= _.symfunction('ilt', [inputSymbol, _.parse(String(s_)), _.parse(String(t))]);
|
||||
|
||||
return /** @type {NerdamerSymbolType} */ (retval);
|
||||
},
|
||||
true
|
||||
);
|
||||
},
|
||||
},
|
||||
Statistics: {
|
||||
/**
|
||||
* @param {NerdamerSymbolType[]} arr
|
||||
* @returns {Record<string, number>}
|
||||
*/
|
||||
frequencyMap(arr) {
|
||||
/** @type {Record<string, number>} */
|
||||
const map = {};
|
||||
// Get the frequency map
|
||||
for (let i = 0, l = arr.length; i < l; i++) {
|
||||
const e = arr[i];
|
||||
const key = e.toString();
|
||||
map[key] ||= 0; // Default it to zero
|
||||
map[key]++; // Increment
|
||||
}
|
||||
return map;
|
||||
},
|
||||
/**
|
||||
* @param {NerdamerSymbolType[]} arr
|
||||
* @returns {NerdamerSymbolType[]}
|
||||
*/
|
||||
sort(arr) {
|
||||
return arr.sort((a, b) => {
|
||||
if (!a.isConstant() || !b.isConstant()) {
|
||||
_.error('Unable to sort! All values must be numeric');
|
||||
}
|
||||
return /** @type {number} */ (/** @type {unknown} */ (a.multiplier.subtract(b.multiplier)));
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @param {NerdamerSymbolType[]} arr
|
||||
* @returns {NerdamerSymbolType}
|
||||
*/
|
||||
count(arr) {
|
||||
return new NerdamerSymbol(arr.length);
|
||||
},
|
||||
/**
|
||||
* @param {NerdamerSymbolType[]} arr
|
||||
* @param {NerdamerSymbolType} [x_]
|
||||
* @returns {NerdamerSymbolType}
|
||||
*/
|
||||
sum(arr, x_) {
|
||||
/** @type {NerdamerSymbolType} */
|
||||
let sum = new NerdamerSymbol(0);
|
||||
for (let i = 0, l = arr.length; i < l; i++) {
|
||||
const xi = arr[i].clone();
|
||||
if (x_) {
|
||||
sum = /** @type {NerdamerSymbolType} */ (
|
||||
_.add(_.pow(_.subtract(xi, x_.clone()), new NerdamerSymbol(2)), sum)
|
||||
);
|
||||
} else {
|
||||
sum = /** @type {NerdamerSymbolType} */ (_.add(xi, sum));
|
||||
}
|
||||
}
|
||||
|
||||
return sum;
|
||||
},
|
||||
/**
|
||||
* @param {...NerdamerSymbolType} args
|
||||
* @returns {NerdamerSymbolType}
|
||||
*/
|
||||
mean(...args) {
|
||||
// Handle arrays
|
||||
if (isVector(args[0])) {
|
||||
return __.Statistics.mean(.../** @type {NerdamerSymbolType[]} */ (args[0].elements));
|
||||
}
|
||||
return /** @type {NerdamerSymbolType} */ (_.divide(__.Statistics.sum(args), __.Statistics.count(args)));
|
||||
},
|
||||
/**
|
||||
* @param {...NerdamerSymbolType} args
|
||||
* @returns {NerdamerSymbolType}
|
||||
*/
|
||||
median(...args) {
|
||||
/** @type {NerdamerSymbolType} */
|
||||
let retval;
|
||||
// Handle arrays
|
||||
if (isVector(args[0])) {
|
||||
return __.Statistics.median(.../** @type {NerdamerSymbolType[]} */ (args[0].elements));
|
||||
}
|
||||
try {
|
||||
const sorted = __.Statistics.sort(args);
|
||||
const l = args.length;
|
||||
if (core.Utils.even(l)) {
|
||||
const mid = l / 2;
|
||||
retval = __.Statistics.mean(sorted[mid - 1], sorted[mid]);
|
||||
} else {
|
||||
retval = sorted[Math.floor(l / 2)];
|
||||
}
|
||||
} catch (e) {
|
||||
if (/** @type {Error} */ (e).message === 'timeout') {
|
||||
throw e;
|
||||
}
|
||||
retval = _.symfunction('median', args);
|
||||
}
|
||||
return retval;
|
||||
},
|
||||
/**
|
||||
* @param {...NerdamerSymbolType} args
|
||||
* @returns {NerdamerSymbolType}
|
||||
*/
|
||||
mode(...args) {
|
||||
/** @type {NerdamerSymbolType} */
|
||||
let retval;
|
||||
// Handle arrays
|
||||
if (isVector(args[0])) {
|
||||
return __.Statistics.mode(.../** @type {NerdamerSymbolType[]} */ (args[0].elements));
|
||||
}
|
||||
|
||||
const map = __.Statistics.frequencyMap(args);
|
||||
|
||||
// The mode of 1 item is that item as per issue #310 (verified by Happypig375).
|
||||
if (core.Utils.keys(map).length === 1) {
|
||||
retval = args[0];
|
||||
} else {
|
||||
// Invert by arraning them according to their frequency
|
||||
/** @type {Record<number, string | string[]>} */
|
||||
const inverse = {};
|
||||
for (const x in map) {
|
||||
if (!Object.hasOwn(map, x)) {
|
||||
continue;
|
||||
}
|
||||
const freq = map[x];
|
||||
// Check if it's in the inverse already
|
||||
if (freq in inverse) {
|
||||
const e = inverse[freq];
|
||||
// If it's already an array then just add it
|
||||
if (isArray(e)) {
|
||||
e.push(x);
|
||||
}
|
||||
// Convert it to and array
|
||||
else {
|
||||
inverse[freq] = [x, /** @type {string} */ (inverse[freq])];
|
||||
}
|
||||
} else {
|
||||
inverse[freq] = x;
|
||||
}
|
||||
}
|
||||
// The keys now represent the maxes. We want the max of those keys
|
||||
const keyNums = core.Utils.keys(inverse).map(k => Number(k));
|
||||
const maxKey = Math.max.apply(null, keyNums);
|
||||
const max = inverse[maxKey];
|
||||
// Check it's an array. If it is then map over the results and convert
|
||||
// them to NerdamerSymbol
|
||||
if (isArray(max)) {
|
||||
retval = _.symfunction(
|
||||
'mode',
|
||||
max.sort().map(v => _.parse(v))
|
||||
);
|
||||
} else {
|
||||
retval = _.parse(/** @type {string} */ (max));
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
},
|
||||
/**
|
||||
* @param {NerdamerSymbolType} k
|
||||
* @param {NerdamerSymbolType[]} args
|
||||
* @returns {NerdamerSymbolType}
|
||||
*/
|
||||
gVariance(k, args) {
|
||||
const x_ = __.Statistics.mean(...args);
|
||||
const sum = __.Statistics.sum(args, x_);
|
||||
return /** @type {NerdamerSymbolType} */ (_.multiply(k, sum));
|
||||
},
|
||||
/**
|
||||
* @param {...NerdamerSymbolType} args
|
||||
* @returns {NerdamerSymbolType}
|
||||
*/
|
||||
variance(...args) {
|
||||
// Handle arrays
|
||||
if (isVector(args[0])) {
|
||||
return __.Statistics.variance(.../** @type {NerdamerSymbolType[]} */ (args[0].elements));
|
||||
}
|
||||
const k = /** @type {NerdamerSymbolType} */ (
|
||||
_.divide(new NerdamerSymbol(1), __.Statistics.count(args))
|
||||
);
|
||||
return __.Statistics.gVariance(k, args);
|
||||
},
|
||||
/**
|
||||
* @param {...NerdamerSymbolType} args
|
||||
* @returns {NerdamerSymbolType}
|
||||
*/
|
||||
sampleVariance(...args) {
|
||||
// Handle arrays
|
||||
if (isVector(args[0])) {
|
||||
return __.Statistics.sampleVariance(.../** @type {NerdamerSymbolType[]} */ (args[0].elements));
|
||||
}
|
||||
|
||||
const k = /** @type {NerdamerSymbolType} */ (
|
||||
_.divide(new NerdamerSymbol(1), _.subtract(__.Statistics.count(args), new NerdamerSymbol(1)))
|
||||
);
|
||||
return __.Statistics.gVariance(k, args);
|
||||
},
|
||||
/**
|
||||
* @param {...NerdamerSymbolType} args
|
||||
* @returns {NerdamerSymbolType}
|
||||
*/
|
||||
standardDeviation(...args) {
|
||||
// Handle arrays
|
||||
if (isVector(args[0])) {
|
||||
return __.Statistics.standardDeviation(.../** @type {NerdamerSymbolType[]} */ (args[0].elements));
|
||||
}
|
||||
return /** @type {NerdamerSymbolType} */ (
|
||||
_.pow(__.Statistics.variance(...args), new NerdamerSymbol(1 / 2))
|
||||
);
|
||||
},
|
||||
/**
|
||||
* @param {...NerdamerSymbolType} args
|
||||
* @returns {NerdamerSymbolType}
|
||||
*/
|
||||
sampleStandardDeviation(...args) {
|
||||
// Handle arrays
|
||||
if (isVector(args[0])) {
|
||||
return __.Statistics.sampleStandardDeviation(
|
||||
.../** @type {NerdamerSymbolType[]} */ (args[0].elements)
|
||||
);
|
||||
}
|
||||
return /** @type {NerdamerSymbolType} */ (
|
||||
_.pow(__.Statistics.sampleVariance(...args), new NerdamerSymbol(1 / 2))
|
||||
);
|
||||
},
|
||||
/**
|
||||
* @param {NerdamerSymbolType} x
|
||||
* @param {NerdamerSymbolType} mean
|
||||
* @param {NerdamerSymbolType} stdev
|
||||
* @returns {NerdamerSymbolType}
|
||||
*/
|
||||
zScore(x, mean, stdev) {
|
||||
return /** @type {NerdamerSymbolType} */ (_.divide(_.subtract(x, mean), stdev));
|
||||
},
|
||||
},
|
||||
Units: {
|
||||
table: {
|
||||
foot: '12 inch',
|
||||
meter: '100 cm',
|
||||
decimeter: '10 cm',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
nerdamer.register([
|
||||
{
|
||||
name: 'laplace',
|
||||
visible: true,
|
||||
numargs: 3,
|
||||
build() {
|
||||
return __.LaPlace.transform;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'ilt',
|
||||
visible: true,
|
||||
numargs: 3,
|
||||
build() {
|
||||
return __.LaPlace.inverse;
|
||||
},
|
||||
},
|
||||
// Statistical
|
||||
{
|
||||
name: 'mean',
|
||||
visible: true,
|
||||
numargs: -1,
|
||||
build() {
|
||||
return __.Statistics.mean;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'median',
|
||||
visible: true,
|
||||
numargs: -1,
|
||||
build() {
|
||||
return __.Statistics.median;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'mode',
|
||||
visible: true,
|
||||
numargs: -1,
|
||||
build() {
|
||||
return __.Statistics.mode;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'smpvar',
|
||||
visible: true,
|
||||
numargs: -1,
|
||||
build() {
|
||||
return __.Statistics.sampleVariance;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'variance',
|
||||
visible: true,
|
||||
numargs: -1,
|
||||
build() {
|
||||
return __.Statistics.variance;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'smpstdev',
|
||||
visible: true,
|
||||
numargs: -1,
|
||||
build() {
|
||||
return __.Statistics.sampleStandardDeviation;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'stdev',
|
||||
visible: true,
|
||||
numargs: -1,
|
||||
build() {
|
||||
return __.Statistics.standardDeviation;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'zscore',
|
||||
visible: true,
|
||||
numargs: 3,
|
||||
build() {
|
||||
return __.Statistics.zScore;
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
// Link registered functions externally
|
||||
nerdamer.updateAPI();
|
||||
})();
|
||||
|
||||
// Added for all.min.js
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = nerdamer;
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 together-science
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+2370
File diff suppressed because it is too large
Load Diff
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Author : Martin Donk
|
||||
* Website : http://www.nerdamer.com
|
||||
* Email : martin.r.donk@gmail.com
|
||||
* Source : https://github.com/jiggzson/nerdamer
|
||||
* Can be used to load all add-ons with one require
|
||||
*/
|
||||
|
||||
const nerdamer = require('./nerdamer.core.js');
|
||||
require('./Algebra.js');
|
||||
require('./Calculus.js');
|
||||
require('./Solve.js');
|
||||
require('./Extra.js');
|
||||
|
||||
// Export nerdamer
|
||||
module.exports = nerdamer;
|
||||
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Mathematical constants for nerdamer
|
||||
* This file contains precomputed values and mathematical constants
|
||||
* used throughout the library.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Container of pregenerated prime numbers up to 2083 This array is used as a cache and can be extended at runtime by
|
||||
* functions like generatePrimes()
|
||||
*/
|
||||
const PRIMES = [
|
||||
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109,
|
||||
113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239,
|
||||
241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379,
|
||||
383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521,
|
||||
523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661,
|
||||
673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827,
|
||||
829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991,
|
||||
997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109,
|
||||
1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259,
|
||||
1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423,
|
||||
1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543,
|
||||
1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667,
|
||||
1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823,
|
||||
1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987,
|
||||
1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083,
|
||||
];
|
||||
|
||||
/** Set representation of PRIMES for O(1) lookup This object is used as a cache and can be extended at runtime */
|
||||
/** @type {Record<number, boolean>} */
|
||||
const PRIMES_SET = {};
|
||||
for (const p of PRIMES) {
|
||||
PRIMES_SET[p] = true;
|
||||
}
|
||||
|
||||
/** High precision value of Pi (200 decimal places) Used for high-precision calculations */
|
||||
const LONG_PI =
|
||||
'3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214' +
|
||||
'808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196';
|
||||
|
||||
/** High precision value of Euler's number e (200 decimal places) Used for high-precision calculations */
|
||||
const LONG_E =
|
||||
'2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427427466' +
|
||||
'39193200305992181741359662904357290033429526059563073813232862794349076323382988075319525101901';
|
||||
|
||||
/**
|
||||
* Precomputed high-precision fraction values for the bigLog function. These are used for arbitrary-precision logarithm
|
||||
* calculations. Each entry is a string representation of a high-precision rational number.
|
||||
*/
|
||||
const BIG_LOG_CACHE = [
|
||||
'-253631954333118718762629409109262279926288908775918712466601196032/39970093576053625963957478139049824030906352922262642968060706375',
|
||||
'0',
|
||||
'24553090145869607172412918483124184864289170814122579923404694986469653261608528681589949629750677407356463601998534945057511664951799678336/35422621391945757431676178435630229283255250779216421054188228659061954317501699707236864189383591478024245495110561124597124995986978302375',
|
||||
'369017335340917140706044240090243368728616279239227943871048759140274862131699550043150713059889196223917527172547/335894053932612728969975338549993764554481173661218585876475837409922537622385232776657791604345125227005476864000',
|
||||
'24606853025626737903121303930100462245506322607985779603220820323211395607931699126390918477501325805513849611930008427268176602460462988972957593458726734897129954728102144/17750092415977639787139561330326170936321452137635322313122938207611787444311735251389066106937796085669460151963285086542745859461943369606018450213014148175716400146484375',
|
||||
'399073568781976806715759409052286641738926636328983929439450824555613704676637191564699164303012247386095942144825603522401740680808466858044/247958349743620302021733249049411604982786755454514947379317600613433680222511897950658049325685140346169718465773927872179874971908848116625',
|
||||
'1468102989495846944084741146947295378041808701256909016224309866143294556551407470861354311593351276612463858816796714569499021375899793849136855085849133702029337910502448189055357182595424959360/819363879309286303497217527375463120404739098260200279520788950777458900438307356738082930586032462601215802636320993648007907724899611296693997216938989854861043298494990214825163523387600982777',
|
||||
'5896704855274661767824574093605344871722790278354431422729640950821239030785642943033153793245906863203822369276271050164634206965056233097479117980782641839669/3030306850569309344013726745100070601277982132543905537366562638553198167007159067544789592089960911065181606283478843359856123992707598685058297067179343872000',
|
||||
'76631772943534985713873427262830314617912556928476573358548256872141516989538374761909611879922349479420014771499018155447198112155515453671128814488139633810493264352294560043912066253026059140653027326566801398784/36852092933388988649396042883218509607503204211148493545892849595498822817623842579026942621098851631842754395231561679671400197056377380063233740202370686144673585955581403046886083948450136247134308381940165804875',
|
||||
'3159076083816399509754948610929467278257473888282947311280653574634802580912280940686954763313882823327077171624015737719617373932318151594325834524000275847475866299387913048/1437757485694188822758304467756419845842037623148461107362957994816554782989250555362514354661961482939226272309026092009962414616417412938087494467254146002233028411865234375',
|
||||
'22266067259907364984531611601870291368272674573653403965630628996687370994139884833897773468149149664829922302484782423514167405397665098388400450149078982462318781750661005833037235183394221496186539779712428265837926417581952/9670030144664428565128962309657100138096047028794689249320859276197340398920725569428532293373676415359965773460364494998334259893079003125373872108770534788283842907318071170285038777091588292539102269617376180390982915567375',
|
||||
'14604654564989239958569331443385369522850975185358647132770022716433280072271007767111036877803328768910274400515590151934676819262085211828028638417329558229123989556376108454497813055/6090614019162516693013973409650613208227889078878781039105047015752493519149314227721984436973374032279421344818329285207124280297611253861173835238379831004010748379874393292231671808',
|
||||
'1901241885407696031217292877862925220917660047127261026827869027159993239567933534052663335498281439239753018507182016153657409777749792228538380379703411298411623469292891476969894084838876001545818141543890273256985768690847587711270930688/765116019778838839812655402103512685695769161212360553099732689795578904762091216998790589926057819838537805856579109910198553330075924857419395160755642371550113347465300208422126945265887065434116781678702741657275181694851670325469434625',
|
||||
'139459806786604751793737926146840623607010208216289543036026206208962059593900745886202214788747453279179283344350478734275973878932538430194363355795823581315329311220701640235653288975569812161436/54371368534412517053056101353618694718215711767266376573138772968257303578467926450212293233332401067673270853953399269852376592855992724934941173346260129257754416412476202526978443681584633116375',
|
||||
'1045669091124493070709683241190022970908640501171378776604126771144008324358233819560649021940145166254659028524319517244711645162132513416238958170819347361185944945680269442845829390112062101255500836072082817820950448463314034677353723256969344/396228259004446234921310936915931611736815598535963504660076315228798989932959459406702091180060429080345146735173591749448509810270759531977278642135591672189002006272326131885315743181289970885337574780897529347356567086535505950450897216796875',
|
||||
'9912919238915437302006264477931031611447467070103973106567538528951878797932559935860738745374437522819124347510590800370471910492338584284092534264608801221235029062881964101996762011296996851893455828946521/3660537472668264151218961634689665210933936249986285290553357254224360417386515311493310199319523687171757653216994741150377508234317025158302057758196429623723072084157928224798322861732880034847243894784000',
|
||||
'9263710175433181746575186369318246002919895649622127410824041370079225200282403368319370743363303164313395723904510539050157032684710468364067204876434546848634842333436957245275217583248805993142227630297924119330553308466662488683624783307023014909360640/3341177182697517248552428837661919299725031035849865632511882688786226888137634168024976033652753689210700218163621739078534353578510364301481093730054725078138658805025014615651043313990684347632166030359086885561104034510990826655289288319840595753002771',
|
||||
'5116082230713622171832327542439052727465114322479570603905499496221224653983960598946033081212909066917137546065542953865612718836914393275681318667667521726785633638189373998191090501201427906618075889744489190209584/1805752553736060443820406101277706970767657006346276183748749630179442318063568286372320188433843729960294965366346522303898609655762491623098453269916163621089005711823488749297418113474056676109581110715068124438875',
|
||||
'246569125619713282434448566970352231845414317018379160824176638351574938993535464763890962336882760882398479702237564384291290459961036068916857265499633061660562532011248501476114401629839742058389195725393702000011860799793778295606988057303225493814005789533570432/85307063020836305797178273029353623060860009152114361453434032434699636078115114412588719432277441055049132559782203988387794711585368296817222565434951256788867244687081233632650953850383220864394261763844194948389861147622944651546912394593164406926489862036343375',
|
||||
'133672026303452911046163998480860917119290576658330909785707604886881155606725822685088929236266583416708668502760907677019598002175122453170574729028452721476464728566191464897928696630979863154661704374206171469014225143/45398130975270785045482567762871405072140548998125471025451666500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
||||
'6041693953360002800224091673336562508913199995987479264605216252220579740134601435770085920869376641180763419907442721705887169884230643795126568815123647603047739799302562095542459344811429882053086550900803768964612193941424128649976704727183797495759082741166938351872/2016766992122395667828553277997478570503475626107286343497917705446132017125079612756035254750822860815515899557855166824523851779156336235294914777307802256439645525835223691751931866188957324792276149549076500784191791380803500156776088683900346065830066370370083309875',
|
||||
'705868391597244582764749229356331441978820024796066870551110486625729826111158236686696326058778874201639006234449557592353247542995871491078308187261304930042019640830629526023972693107193897009168955674240659026247094657679060/231848642748474339277532000336338632910990823562381469441716922006107433404523316252618490265927265734670539384485699132080062215196462178933963957679882342083893417545858074378754089719547920901917516016346211301054206383643383',
|
||||
'101832160604157943093944673541651013907278188571533075311673249923948856034633446617630054761681006062910980371900782781226979391765818325065031889334563981235894369036439929651260587335544056975715076598739977065390678221999918899003881778449092038750712969437519295878491018112/32944277910571666002449086492515464541550138004002141571670657643770713783329063548790202120805341989608877739811787937782240802963962520261844114327432160788193314874913687387269408387417806176202979244637915812905426565263196954203487934225589622864145960079736633434831996625',
|
||||
'10655703119271468913597640479490594180964700448340778168715956712130636958373270202484276402718566314881119559090842449610957974112230306343486091910217340665146602598568991520563987490686996746558858366002301982443029430290679385551/3398412687366638541233365137084722368200311117891192348532156645374786104142009695796409107380345795998400850838706661851176885183144928701608654514812261697598380070746520197171576610572921007069104300695592751543563472456384512000',
|
||||
'1903039332876763837419920240543738799531131775028971323439870868730321221615515008394327723508670975623498588291298064320786970626232668956372004004897872810230178526101184611242511193415796638694370503100219710864543168952682617801833318493436174387568067811938490953495819438108686336/598806534367503338307287246320963280558134937382149405305466709787179429317914803617527827862441615350396864359976273212272586892074799651088317544101755361439294687323233086696182687664637422796995789967075271448560870681210580691574924544896656175563265378514188341796398162841796875',
|
||||
'525573915563826130963525826191411949262846916750432019596028344808298471293378917508549164993368392834023782480702893643486699787870059946429810070222126260200026332874480239090370088123833491499400991181659445914352500247596757005142623368/163278727324937389095822405034435687776345799835442022795533783889356344755225815267819508608559076191292900367982490827396869405536484846115955581043091229202052407483776587687967125885665493681707461345895999542381476164157058393971431375',
|
||||
'9263815657177858787273494705338516861045771674838057329170239610953039987023429736752079544014780707408666628475997291124805562998227296677616204140605356257712022384368492575381355563976330347792504605666631512343447560301417325154003481040250148561839861837778597346623630046623751094400/2843321709948499955095590862256744532227698001408929142548057792217790532624003190447363578048562448168721539177458065482170148482375585867230123873178100117094533143052886527452665480614620123764036974180917207421482431983407742154634391264619615289225747664532332469783301704643254076601',
|
||||
'407959339726114455622180187758753007349209016396248763075759257357925636039752474207685682218422721827857994768023399625060206708378433960993946156803948655098667156937949174400873748557248801874735834957795040139401560494087476967548060208243867/123780218751812156744401121690996305978134694678934447237402511116731459214498784497436358160964198336874043702652746834763131444030185151143987331404604087778514863973633941401826334750268416015224906056576641018962863645043976537664227639296000',
|
||||
'2547676391598917379516698439971914695230548782904479778605691338364453606537643088857116141939170899135026552016969320061900926954008522781162186995856580955090548471448276736878300717869625651893741316530109438876067419826217901657017506157997588944233677467357220316084583383623602865379325184/764562034757392298786420374672266498815021229519853724850874576419885380830752931701831256959159800764672605004880389358601658343203513177084389490286723240185146570925957286083025676875197029662038213216541352875570101363668917766225709569356861275434470568767077844675593176178611021135573625',
|
||||
'186545352286463730559933346565311535598243666022232037054735807289501173444103692309735768703898330430135399033529355360391658728987379385732098960609744313878477967971557204207043802935782878745271859468248704012618254203101767841517569443555143252/55399179641621656233589820996143825959365789093262978988289445625153099592463372579496245442338653053662134699646413817866770218574795378644415019944304868289119443774932782235638737888469746745621382139263856603239588594078668393194675445556640625',
|
||||
'664884440164786473344854955309049113269357314957985265728106924238588705533437169796551912202931185746193155801905841712503407258166135075966280435780812714252670362202091663287095423712596462690753468682634261029392794173636943978404002804413009590005984736612421172979101972556772005594499779860608/195485517776407145286424460448995460754674039560651791192647586550615878988380153730602665795647187884543361218962125172808792176382956599256188706636727418572541254480798303566840010217729386905041217793614214518363859058348249961790104618910877813067510758225302884815410347238200133693756493703875',
|
||||
'2614957283934314904315471338485451166053664494383241929385424599389309215073267052860464009981063483440201193771607520572077231889699858482582363845275452280606276949653970992719332472370351170732899676316967244504534154616036371979031399425846100527685/761493664432749089312665480773496290658029971027686543404885407644062485746072719559288231362060149626237939029641098328278650939665665969011529293869562636656650999759724704272743235210867676873525147820749560155294022488994426729939894753293900972032',
|
||||
'124843380518493746761140367283007507854364503961156704095198010255465940085534099747297600085903814014415830785663764373057896014399822131175202342399536439284123918855893825207202244831315575594886675813256448846863723093240955901916229136393454605455444105444987028391748121054399538064686074523506176/36022228212051654395480210378626648518430280334458144892889271272122662467638331091863215146548048144675657239846337165813938424387499358852301016926312083940212100001220180762189978024821166744964908871443681332664798940660421469519997746775275873085770018269706847741064037876137315001228315806659875',
|
||||
'827992369063043155578730871896750570951766628472810506926098505028264552046829097082095665194000002802661600196840639204300804225352337632259980703832713031790922485730615305441309917696044954289187837653933158950774246017223571461858939407386087081525130831392/236805932823686534991153393869288530368011574665859226704279685567723830696754821658770176385138917722808377962346690757191122309876922069867472518117628639913077442806147910884267694879089753138429767401700283014143248445966474839193628309668702223994071394625',
|
||||
'17347276886878323736540051321582548724378497839789943634071026331001588645519865992773157565595886250230140452154269197770615097377486013097979087647774513500701793885978192218455687078883766086309728287172567466406449372659680040183273634701092561727514713494914793425407149186041796935055187281744386432/4919325621804683623339606849970832094714371903709195539440424738973575902329797546592497378000858196173718145883783709223158260700365224756081275272021856393735663399552166737690038832550853145831185979094979556715294990257315369124065787473707136464772247917156232366320267601622617803514003753662109375',
|
||||
'137984231830526866236186357461458917020538108058615632801298091031540729111527734872044790487396302545910108285921421417358113055522725197998483383380192391312304647004240060970929072498293210057120617332323445379424867965764749534125081131327565507524502163460761/38810445792642817561168950890315210470940006613819790543653745327778579787694809782601777514116858514049585074667085399925278459138508514838268321349069481334967221455722811414399738756151414906092225265355449011152267068726417045644222323488445626292574879744000',
|
||||
'746567120547823334914136339633766098626636643449144032626270358619125402826113269699709721071135471625588981126637674402048519990010499180844665151971356149292818375448504122545400227696621572263621729512461528550588108384619064912224884465737417596190735966915167530332762203074440688676123756162572829692160/208334337057923929636884170505570363171441147899816815785150954417598643614152856767186132467069365605496210036171429712485182162940460120834349006784956522600679357307849981862006710239311750261522832996877712350330290831638640913932265004107623954913155144975252743257846945609734368518424172846119306643431',
|
||||
'64649371728330695076928013661001819989330953381731372450140483779536126948957993261299287753791770622512248630224724990234903928056275080682537641377393210728546364176267034339221558641084730052304770498929958838997239635790469536857863963589118888238069738647239076/17903951498200212327802847425913723358452100686246224008745414214690047078122925247086521362329833307849817944645647750649290248110509395628305970523384831671737569872597295947593410067364379687588919135621621162007748635920864926867870502568935739725312687094047375',
|
||||
'2454918942158003099688922026016393688092399295166304634317616773083386087532869193458590448918958337530406410803840837646465522656670050113548208618655070231274778592766244282964463702354872753657766121825196898916725498553882689210280080206627916046484942827487726300822318764058084323314109595329304407466188383616/674880185931325925966586583820010578979699141814417326552629206140252348822939845006845669570885271576698771404162512001549922909048916000017837898649100825976232784446638776021483802989797501705685620612986771521390439936066527738682396560462899753657942715306792783283782238662155922082005591512296007820682995125',
|
||||
'74018558041066162916454010680594042518462756234254788158141115244349044958441521749277686851928706433556285971088455226217644009628399441967508838553345152310730562224910795446341601049647392069373970101491741830623078126344928804029524181578945586663110848142571149861/20204153620006780689923328634586091101021423979622170579036140596085566172775051595588438592742563923428900864000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
||||
'2127032036758045513335690185608563023954009095206088224487365541995326714285119384743928987635752931664240752323937321097955456543854943206092931247498833001499955456190701695430459583885125382086777607021670447795321669948733328973350279846928613949120929250312666393359442423066212311060931469017737106028339882830848/576612418511902928757340062840968526862381326698309578771238715462180282212422302261044980131594522407066369222998903808960617461164985318633518680304995784614308979881735537678182134128319596636920719106506829571072447362052319438091347699720147003209417806230149598345068078717948025207635448205253184540936478445125',
|
||||
'35289653975561083576641954928762116897061274899517309102784750384002335187117263273488751066569234386120759866204372398611196356888479036949053282301027789530999737306501029700128744408015642211359442183943916106790666114870974212159410284751571905275610921784716184508440/9503006066880728386808143045924119024212377150217533250562188228062174064693375135306438120385877320162710918716613546077156389583384656340709638430674364232343609717735574035535102953482366914421205216675248471695111720986346092738728929878538430662191272737183832556131',
|
||||
'102633551023964794485575491065909467125458972250222581133681080524371507544152979467328048718122409841060527545925136196267751819689935599599321090571687632103850847605493223603751038996548520557330016046032671961857623066292962260173840972332108111505971231021442896036760967107060309991355545554631003681544611731245475968/27459658121882266328752886605529964804078316737648012166874496015808620265471203512606463219297059547428855195782384236337998738233668399173746663289852416697917397644234441300570212555870401420579737973722145663287124151049692290432756231390864184491891697469874600345958989433125942336757049639797225309327019275689074625',
|
||||
'10034393558388390065766795008210457368713365491566387292163814915435906649268119060550511145023450790393353937124495488860451123302412204483570913557762460385297770427946219119911920640306914453207097103853766023934602534502476962159682750262143380527529536498215384467975023/2667919902603322771586358077760621955455470781865624844984169443739075976572061827709528710108877015489050369589117491611045518221354793418884447632063538994046714401229510497599783726376490260140723032102883617341970952663947646017489439179953454964374887388652792446976000',
|
||||
'248528145263843375390386172800048509380966183384567983242213959113927668429802237067505890436957693495616107089384741585283620097982859345081736730899912519273262934785992235852866637878831878448348444611412764161078458068549719800733237024285525816723480868704742804077255242682077291713092790250511567621735004237450946304/65676865669148624809340872151906045781446981664561196686217551358486802274698228825404698950974939545099727242259547145392352658637333562345477931951890984276718673618736565926663528625796412420753961231404680876558659735251469326707567479071881966875336951133475135427640218972722939427821842173216282390058040618896484375',
|
||||
'7805448718805635696495809414501206964843262114470109146341305656318015059743127114324245035489577134938579856003956861881125856595981500593426840968087618241785931128978516340812066502964561231235073012672356530509663384739132686548934288703179479011016719045530855033205271548/2050525178024039744126592505352202216905491833360272553169520915020715464206141942151086176509423406413311520838568324134077402841030113427309725873344806030836314500267104070131451720947531994814710189000076651895520222646974590481497382830325485174899169093049299764813276375',
|
||||
'3355325071293197839434119105039673324264765809771192815982246040415580387729382404624613875653005261578877047405365032178619450963731719777167015959920645055600439987161800547901539269321100559393048973255388860193948274255340335876890491746900991668165565729269698196233805991206691196045182214641935483083662356666996922240/876402579119117579582569839757462461050855174353108858954282915644790659429341853404829661899850841645529640454766173209897510988090318303454542547519850473808789222552969933222203420847859171250332350076509996295844203965564448154484566493395403967626596213792922784509892086361572955175655987334882030766001799867659814117',
|
||||
'218871061991045868372866381545267589365410350294028138778572466235486397478028823720846191998825628156716190463263492304639890659254282445466806224943413446008645087186307985343574807361972238230520975439736199291019544576443791916302825193643774360055545186783819367378492631806297/56849560726416896431557940314760680962653658127458002233782028041537121216487790008085876994020812492987733987414743604239935223783349870516284048368761617736127892160849065895223288023531930411718807065209903593668117085505482007061969339237404945180379460053180570404846043136000',
|
||||
'5008685108365226931582937964451700746853986170633433728409171904803795018146152804690759530990140552460596075588463394200510044617816085275660078502126507209302951286606953039953843685800941558212440519542602092919776366067720586295390886070120828199562643208637974347390938772070049344991272621102622931576339988103674070876518912/1293888539680354282541277646947380627241979967611883341823378331667976045287311988103163380651334828012840330710760757271860219584371109472132211215957402251594055009937397184768184517621978947384029376766290498101728971145633139541827544539988344772578184316843734267915665730981857376872622787627370859411909330227080697966353375',
|
||||
'15388340113525711660227566446101909585796746979396093776960989868457211684028149502578116456785221720682202816140911944661051001675127262774824593420825587319436537346311831003212424497488485098543512314062112948777572038731823948224734505930748371522309451168088057190162878224801232/3954220582960831691377435160890656173654063611768428458807273708040518769541211737927975894584024448193835165167801976423275767590502552964407494549049777006346189436817215329891530811451811864579644894987864267389290848598289794977382504890216219362031324635609053075313568115234375',
|
||||
'5099039333987561374222193551155323470675617979816941646196895589439391685938046865391119484510329634015275893520725135141878751153360264368353595348921951280561029028912953500944814771064409611917475818956659775131751121312316084465321917769679881052144364834485866477379437705913911371481828140817759401117780199246301705600020671104/1303503600297679371136943454060319958680553228879031326679449263682048703103464872914972900105569835004878963701599765030590097739639045890060548760692125546754294514068052902543220382104483822438283040090444827980927544440984823535260277595466339403795403200720622852069244768910603820007632395190204569927612348189089161551951106625',
|
||||
'4902837141334073026145827027361937996261324349722726869116185158777439337041263482852376194988371853413467559557923410949898048139830183335197992754748294810838187068126867611615800383834975563313220497573778480109264178673389149671194149749735833378557143135481387904961537942569904075/1247045310545991266291285730016853118981099516935251861146038369985109288084420528171217942065832292739130145780833406014673689119563698528225048800794718789218267628507713621235056538202070171596177775095071513194885568843375526804796016261173388452184505503341132236719484809714335744',
|
||||
'38114743522716832107917466438257616720476488812538316101658139632867788464381862291240727309611460187159930652186486096300862388591521625093237019662273764387591494074792574929490381910446287947994150655077877204446864004067956087975012773988833339521775463977233068498404144221045837190392670308437391686081418318624745039402145439223552/9647001083383999453668111809775451078976046488746916070976218645431946648087171586252172936600115032316383427265217993193444199863138429602138841976586190525451324093772097241349417938578878934577091671046050326087898259692917931230974174799815198493279413438192301437068820185757869608523761456160341754512329264442115351926967120404125',
|
||||
'573695055225225727008803730767518906490704995929177617646275646884555707960986625481944101622708415415988844740028718027554452662358957933526173824325955904005404113684003841990198157072540659184995738719040024647370869010473254071681533880576462368600901824622431045529064651675640055917092/144509482511118816399089096021290587489594541280398871255876563615464628718527634679330291741479135415168539765887291789615790513527330600394937614433502341116068305347468133950204152174094704092402978083370792135432486240914953928188835819767755172666693219213868545854371103120604946200875',
|
||||
'23876960329653589647925126180903391687666378233201794403339630995420215267415575142266707357255726330536094448314199602616026935251126469221925945960901748679919435908556550271504767784553484434363646489174587463466333864577705745452492395785557425904735048180164697040313528831173448025400634629163795223739061661461986923675833880378496/5986312408594306954013526197465608559068621248896320652512228238115589875514604632230098997609482248000888567135685167138762172475788060284232459813998201719590208742091697294562538265829954186149162974972471533202880368317237508987477069872431064075005305838801862900501819963793062041081601844759452202282545840716920793056488037109375',
|
||||
'58168289917567723171226992383559866214094157894992327555495441698028867727845766488121900626912848698952863438654895252811583144479300382761129433911280049009362667380001406579175563745824368613319103673817094498117944856004415812877213722455299491145649879676787079744410765053845551958756701/14517067289347903655500020160671113450349743650636953726251191692074385521975132268313263723831804150872238173602847065423463131917373356798750100313145228608894881457107689499956903046984443545789053438946050974567665049237414588435796381674590098629779384355275820782532479708807512981504000',
|
||||
'728621890568281859295409481422447012528302594365693410763821707074444799793690738137592101239862736313347273167450056625929591960610208335290882047413011571781161008296084630072829079783328937418641417642857196346026366370059522990813537731394823630207433267854616768658990289454635793326766697884798538576055949457122067828153655416688640/181030730759516991863708593747964787874073354051675597050399087612142539517308720603687322924426591889179726492403913356461908748733972707460063017057809060190437917851790767968877215795679844983288935075688219234885360839984681619084834228226744165610073685719017596630302462070188937998558312507638434329299017584329479516410907786681093',
|
||||
'86855946923438322218622470067224691860808273886184997065663554841573982963995340977083049132518812923329423480393306918856650577072525633920456721265953575424233701929892019410099166322511413146891121248381648145391642571638857576890568882512129960291171866772665863159474602604647289052079991768/21485753507365901947528588896402264670781310878547726104482740647554738151100954835784115119035980523529677083504495839730499664052882400915208251594384038810917282207449860876251558307288700200910747338758723324686939379138206117634546981163355060740270734146780942696291669461182599512320099625',
|
||||
'2158989152301022938148680102142188531448821359505188055264665167313418619665693092337665573150374231484840948447637297247277576415460889296724813940128955070240137590073233263168835678714131062764247434144994737610229909964847568491446606012581370840699582055341626266533733744293929658949697805855362114229666626620766245630122333733703618176/531794915405164005613733454597931482878479882704956110685223892325074211694837836221759995948610212818642789132749082430059593652854659130217225506942675608692701447738732031302987802196501895840510235161825501235133794449421919927396142470196961877376701957829921152848178076410141813926924749057304222282687697297216661687583257901415465125',
|
||||
'139432548574396829074586704387656697097760057897628994548358619815052936481650396157428747411173567801047221928593253479330480454469358220685854351236980383914223693722868233819483137401339800304943891968050399345430243790898955416907228948287367356990263740207046902209563417267686591994743547621/34201151688775214071963206765436083445901621442002061707492082843232231754829227303539041286301398668437202547003300396162741375435703188500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
||||
'11008517174872833286150985180322584448162884832099344969609291070844193524816852920942383850580217443209402836100467940651581092350600329145627967515818684442171571156446321228596914355704205623857871497315955269266498229823278800717909321269179839084452384509142712677235552103459737790674103994445173074670347080506698168482564009465276165824768/2689223396936080856855299215659204161946704205931885125148201643087176556822542895325191478283706585400237901215485150928036895428721912118467760766508162631903585126377676412573187912443878232521444786090510891599171741773242011017926658231638022943018461086517502584854390836347781674626615709751386455292026775663545470794167629144456268750125',
|
||||
'16108638074211260588800537540680707641986073914251424878121255234668558067988171568946079848860335948991834525552515669040163026131919804987340113244760738846884911038097907756220945883750502673899084880578229601870882631165510396775126850307838505063922101682333806284668762825609556049426829531780/3919363961344261777100658318137884299575193089462944554282218278496298610828757650104922583359642384253066896538203596057302203635134833545580869871333892935330950583664400555463557735723364497947986885146043017010159347046389604172186788902608216894094289769850517098027486468084407618748895626853',
|
||||
'34420755849180279597302103726180110022640946692592540634353734157479505420320000324260530767186132260970572450489530034440214259559325114511265075416512316229177952140217732655405289808326341696986755141965043719344169685611217958619102774617224847284122901023774956887687026904767714958090256282893003000752947427857703259704682455375442735857024/8342030311716679826889917494957593165464748884572298173556257652389845294530325764837124998293398445804458613956489096007564811101361266196542129764287084823604897187311540561857741285793447174119667215803837719660675298308873496219385226998078648428368061868944322478384684509466965129972030932418920415308276430355882329457342937549162000252625',
|
||||
'345888075261020004071220843714060353763382280664960929903544964118831237876694384053904571498830068831026644303797377762345709976595360421502594656308937649239978525713471393570536680412814805076323426256584504251728507416368609420882442293831684681071553766603478479006495757222912500012444787804577811/83505703731469734628961395063481893801938371516752417759131774530720075262459158384433785006689548434701904106312038822969658455364219435022841597243178757423598248565463985786213156556523685666430799283870548238467817226915680747412191245046634279766450629886904716776719219698922088211154187845632000',
|
||||
'1061717830619177527082296723099890392273896386613997004874669053445943252046748251883532634529759169500795452576392700472771365240996842610207274128102329096619028487369622001737128463631016494371635687841733644339636164570819431573829173533941056258744442930643735587780907310433371453992062647737259587563398111688659657406089003293576961475848704/255359631537215747979895955806995352799574790340218399351168178555478073997876110889483456972687438702262017800167048243754141722496276537685853311434069991222324039005160057724073156957530106623908696241268268096879569794431919729620178375212905203484165745866913773304319069321426245521467122472046370356725530914587807274074293673038482666015625',
|
||||
'274122944106300296738399632684955400761495830361663966466225652918683099779465438024846903286816813856490888796372134557295699980528187779624865098445756013563535339056233912394908544185885547842235097677765325396255649207317018754967666450708249125316192200151505568416495274671679500594656671785202496/65687592621976546250581560102201535533608158256953087745856906437400149205693427285162333502528793675585022025602144243543064185647792948495372442630333800126269123531636800213405254045262127593759539706750242430153456891792533267948231185296091297979933562727112487057234422009426868531651634706262125',
|
||||
'7842680480716516803148821198697967237136721860017131244266974996267074742248599085253569637183007740566941125452215834642683053334607896723447140851344501084122965014242091312411884985569341166545074688756440728922408743841592658677792796881188604773469108807869960161395759837407978596679911066586626885830991556090978327508459276025943279064965688960/1872528612245648675720382138045071131304652050696842872529163720558126655075937845539792108048310219395746259570506175902206215101518698490144716531697689534559827422735649881381597761684154409796315455445459537515308174919488497154409643876490472215352056502193150125644288086294418253309947229151074464928874881827227706992859640236086417889990541889',
|
||||
'5300824422251242070074569186825929119848111723012841627275830216301188228660779008353049603527567784119877706984722171178137272986345560485784907345500893648715341273841147320288851034078863843374665850852481747000237834238703248634174397792745914847774297223176674917912406659831206869442510948965571661/1261140476013707338477604677428573831791396352814802149994640617701773078174882455512668089072441176857892331468691160991310474734143842336092636848492066592397892638052212250229129355009939118431643425836944282456647571558383755315238500832868535816144280088644939696339160092963629012001958205063168000',
|
||||
'2220223718762215584659309059880106334425515875615107369399767892051551634000614327272260081056973863669004224981561870246078120862256383581012183852291444462730018546753183156982897386563561418424093883164027305254176874653780425452987066512563140531367766900610414277825262239199580925879453806414860409441845631158680721091621460775043562065815179617536/526383206607841251253861841374779803798480623722760367843070466043030228662340154304405180907941079883976168609082254331465595267209149963786388600028701073430773581228212441424400748220833542964971495005714483235359479470452593264280645360131482713147116366500300066771223383007216182988263355451923333319170174755334598973202740108032097242475554128875',
|
||||
'521891797109626296684891455959263713257353500867652268541535940159815152120871142196535233326890353914761242025931373491906127275561002910157909306979093246574207104081108188995072105948138299097848175016082947174156278439986705241571619793059501724269644447572323501261424770743329858038040685313621446524/123310256826873923765604825413207481739886340225713108649758575106598510022338480189649787216845041382860899099250547657534972156328080736149239332330143771138115695598493059325064119176038137294863053148618656356436332991079150723235214278848602671333076219529535123842212129829931654967367649078369140625',
|
||||
'66086044538329677372986118727999622900471937619891337714357792768200341519193500393739322894033303245376225584865369486696276607060432449792893028061817203932068085863800494054274423512956136695211796751845295921015953538329385253280866669403169919614982155350899648626481405781514434761541281229159396787287553493046927448595964103589100429722948913403008/15562137339474350565671240515273666798063901504051979980452491653975250630723677279081058884163396938548780856293034775459223871281049026140999055923743471466471830572672766633086347312178711643724485955576579988182546105048041649947277672869613992334541438784737993706482731696809943027528882927942967419447250586964258807454003775693567366165507144866375',
|
||||
'335377615394100148751647837967017467711612297170079949298328061159559939969228226474615711044891085626519877634842694983669611974807129333052471799687426665556738316626171408219730853872410792831871526174987402129691897433888027072807302411474690613948951673562473758814664346259109886876538510453475290967835/78715592752271462306588358880337347638000605031000575876214116610339827495261512281635361568951675037834544811575026718101166562072917855004822606752296233435017284127594847656529606648345533195437635894948829857913798336356647286032372695130461573940500785137424365840081503133157308796505622439791698116608',
|
||||
'20090879701618729602554170716780970848925039917987945471322994867171660307998603515745066411687983450400412739285577269751603921163835619296822801840348319742203974023505186187060251544248644338412667631232247108675504629538319425769464277309915502144443973397371136256151336255138506001292355330875114245901820438821732843540725116728866301271466614762497024/4700223519410528857298732096729483544820841497820611795617923063440946097326817340637303431283005509904481323205480729806879570430868897342398783028649633951362398196137429076844504529051072393709154483678349272930361110568616112723747726853614661953537957117231900032044221535502745676310313569997665352252492568100075191900969170979460298189170486601502625',
|
||||
'2904778979985524171206573028445379872240558084236464200857594814631031581387804621371822074061289363372523364167184697785570324832815972970658633551879143187709707164796663015180877412717910872234647704536817108676736661804878068078543241390828229923424191204586313620612539678930999769543756218765870513049986792/677408099044823641581658869221044375312077929976719183424865834811543737800956896926637625166844372424044003929341361734886232742770909683021563822987505236295727478159938135467975522336774471915167606673489722102077041330652185811196423400701795791669780695158730756241178262962515917389382302757366325768069625',
|
||||
'31270155809329751863885224732454397292230969002004953832354065319735530624996254695453061851449600345977646455072512400760539747054003851289540339425848681804190284451253462663731135337775088379954403740058084949675460445909826322297817535400604180338201322667139062500269285493417563095365899631360901732684124930296643108551710704785906431324876072470231424/7269578038000504017073007978844992319987411732848567116655821196644382777088703228960020894756722675887473977480537577509061256138261063926845643360849217556370868752909531088361229374467207196928745673402380473721018157327193509586295879051411183657185176812738231456253321187419224704301236205478184115996135940848503487199394612616742961108684539794921875',
|
||||
'432538822079707760382094121020421735679118830363764570640789368235407853152380328891350816400541189148550353337874309885334920995713154225799660601389784410911658967499100610376065640785585342035058364676314084595283850213942576431310823836792440218271879354669291052589804956435743500204185107215929849054782893113/100246574739326291035824954677502591279343311051719151327066341370995390423713403739043396503785261917771859220535505691760472395306543276314938287868734009582906895763073519374099272340577921671298878837400921045252035507925021904954445172372479744465666760762909731237634082051855588025732494461939980856983552000',
|
||||
'1914333673689206389116942789116917579088664511118582610293383428712902211612554212779880638065888518488492298586641997844141510832940409501694726851666478650414191249534733087933879981733561565249818572204932715347752949087800778646065986244657260832234479202201129845117955957242616947361016603702640821256099895469088229339240402478576285854783063612307200/442333801076281757298117784528962837115323835962460661601905131618341609371649347131724700192551574625400701487125984359494804828935577124602622582550626336986871791407398609915208114339012374456785705161994343348351473385887949809051796407340988735853810174589261300681030826392672282630613354461927208579993042968520650313621522933214063366558703422757071',
|
||||
'5704691626402072213006354545292364761246893919997531024861408248746241619817955824682639582830486790618668221530365426203424888737658778881721063941495350237419723855000515747725926735319471480027293210991869255971365522170749568996651406002311020883635577590045650037569906001924971041810547543163363976464780729932/1314240362076792592671773873754757443276256223533339004339330559325754574023619698171225777585408160438834449576526997055649849875516310105297894855264038450585295422343454458568152668980131977005808840141079502436391909349182185596381509091427752151860204235071122788499996991078935216686010888734471173124487937875',
|
||||
'451219364084386208718456142329444023337343409261545444643031014769484085278440612677813682892926852469540118625738238137458321112005189595703619178533263706811689687213128887082197330137502064260105387500552856851972124172206996205919556553246133218441769325133725631665593372188755090094541462474970525820334385058333756591222492801647110594428922046641945259392/103648250172203340865458115839764297558925693061798169434516001775068769911768281084188883278842546791612199025413573394167639925287068809631958006622842716869211374513136766899877504136929177267362862319748507372147243911522667591375015611312165464514308451222180272935398828092646393830572838772085366567154646425598388620105539619174837489536378605144891769625',
|
||||
'1591076564577634575701791393842535460875733974464805197283632670013516183281542903377750304419996681222758401497321278555686661981435637461350320471258386388843198706277657208526372100698700615835733712519332548607115875288787602084336341594576426630670911478276101702119972195558314357975365863803265163991961173/364433108410193393847203348728981296285742202617988970384277162225847256295865554341611171460436362780497179090329831885853324392923449579538286804729856000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
||||
'5307507148709435807261229345132535134848030343901300324623409721698217134932866488937772986386501538026693956121121771089740095760486362654754229904633476234090792827930433598453200536551779789049352131005222659995845427680686677324963223653949277037520711609343166926627713758990384011274777087968799793410506391884872456420041494598273703914689586637995139580794368/1212244290381524115082005575105703496583315188540177702780216570265159923654197746388568151706509243638302707511238539845531608313334248788980188054651601536067740709977678222790481348003085034888244447626347312094586389347535167903408519024105748523384932942441843118813819781347089702286481924493361058339731949844620763272778960860509570622673048786968660129650125',
|
||||
'3046577098843580578619955603029073328361298436129791931304665777036152915858575282362742008136721284817899542475666054101957899858138590963789072530710775790433466847100422875906866496318804986202089528198912098377828580031852152997907433335176267300286466072997014023120087988748396598176622765923059385876855303940720/693911859077752895978833241338902695755528613729508555938416419864772385336777924892434701804078893822446727762077537955240605927550548046309823051841326355655848406646248595628715185413852040295631448250459076043816328082561248420925930431777502622148019371383254316636979070731427737764160145097293260938978413488819',
|
||||
'155310926743873343426312607182060072939030765297630534544899230213054714482456829198485999782086944271490229412707980997432528653509945301574794242118433868747172279224118534460563400440670015323324602117517342201640016853927923976796453577048995243600967202462466681112804476672348206491490513419336308575442086511015101999847896197546900512764233674747415844045184/35278162986589659300679088538176070649273991865663041616088851923111175518405117391134863644540911005782372831496121355954470744169969774540892096320978686548284501139861783292226140413769665461494668479430833892857992401391262903582109993838728281915806394747833588629467613308837196269424421131934859079260185052081536487462257809987002198437182539441349474426375',
|
||||
'46729591025621874782758519074451728476386657576036360734358719976400940301493939192083339293779149127132651616972817165172116269307276487158069293114860391502484125554406945249728802484128756924044633825692779783425628292959170597009935305429239702926898931975023598456207165067568919757902764815108751735753431153581849/10585868084079030838651390738371141142245086465033459640458366146849314274285871375459898014414833295804139979016362796357043372316321872357817727821559232353993714062535883074661734509440994664726425399880995133711038483607773782532430879196405793694658185175583691180757783597895616920432527125993118171361116684288000',
|
||||
'3736372348124144720852190769710129461145889011598636925228657393934132828633132357232883470466940330848177425542748100211498184494252714616379450272611850068867816250209867530921278645286769418080018709947826876461419654782341309127709703626401211996255743831998918894661053669189312375159058718767082163156988766821194002596331826150321864927832618126580509732359424/844176851007504003627016945212023239308348428094023437269532743221937069345682900884618378849283002998220513307273333096775669556093005637615012750733216460458689282791632437851364674879152695438589692227159038555722539345972833161146108367329370564838671791241431125003401861887477969954740544092178721327724620450947646209914621007186497081420384347438812255859375',
|
||||
'1258774755828991281578968023382624723772927642002016270484090409043454336040857926581316994594109169123354553321469500848146015719851609220423736153365139804086413284787598253618361769125996755159571523632747129480387254164008968993734442164892486441152227433281625391753702577143985047832519062595123255569172968685060844/283659859661671181526547833415653453506477950678651675193210969173130116121017723360258249430884213011988678011357458727603413521688184521573094783291496368005697481333739504490647194454695504722542014845706216224432373442438242355188461951883454664693262684873988061018976711201351382163527093784753398257094429403691625',
|
||||
'84932063355292829988908961192574710493098897148701473172754949846455626381329456661808566365329266898990829247446356970454502007127269708487563279536825277374133681167235811080298134899629580318813382668399644553111080625918213250223197440426147821225593304993621451053135332451997633132772608233430131400186571793929377129211228689703376067763625568623535588709576320/19089978133324852910950469658566458037096027722326716800113107848115231563787455584278193954518442601810776347091253561956877155673550458955562102935555510392954425196165785410319126098393353878286400877305164869548380670204577544630353859009177051698096136470072137579698473017257397949994722015089768745013713383769765609613514021200888647472804720456494757423299627',
|
||||
'802638881530832431828249604040579750916118423833791608589560402449036920165704012070349537114920882938466635598602387718300074733476150548724726460209016834416094317724261857969955414000155807312852092720310159572547644569797512233899495300028159721348599816083166712365215075728968005941610056018023633235372936903015771583/179951197386119079732438617407921535065140503043429174394605652913879982486051627760652197484142547447000508189455126493868229565647284332735552462525598465192073558793335913005459266977086104359621022691931002488052727597513413492393525660272900161375677499228252863529934576881596384036401784035248649026076581302370304000',
|
||||
'25208742399375362881099811032135575360109715964024747212026245529087599633280142314962581193303683759605084995818253124445773115574470717199218828756449187055537877478033129862600982068782249943150019637186466260707552416433010545437321814115233841687700051830170191107127799355485920046505591193770164750886037885397478191534797655616745528343172318318678405576430544896/5637713398995569614196397857525646325234056219513202928587580534159596897880731043336790273040813044077153051260989730372846713618900145444802234629922717464041261370803598799826604841654608724727320798324006129524610666235998113655193642594744544226880944882342204407750193512869672849910003246504052298022468012594459974209940607450480609190841893267203392880360823875',
|
||||
'732332637178584560220688900268566130246820235956768724845747830959547501950765063982943061181526237061809052444110437930100210105274824607344902764184151030827266142225894655531497849161692760385938786736436977268616029862577293984376170905024712952813234949508186306774257035535805330366742322777611063402455261391021097128/163377988152179636922409938163005948596822656868040662831003991395905185823836089887990201522673196719628247106777881741843786365701135555917263285268753605514812568258179391272113405920369587922702002732667842511108732068683407168090725712401954314293193572654347237716691784386690948494003094992876867763698101043701171875',
|
||||
'22391374854299462107923583267570593886002658786775211597896252879708753450794332301142909715845151746786016535157797023153041007263258732991465037648536702217273897876864351559736449481285518249846264600935543582562018874574655740660800634883403597181876364712521253650744197321080655028374602772696770227741610874399454362583980465225235674816785988164157351243193265401728/4983275997188967758382167867656806681565521964135131710678447245984239733544941881418957694612909599261843693554043504855207170782744518943778096768859337980470219783210191765678908537645270392500777411134164918803949155037581108678968701961544979451081836872425574123683574475790997414488641534074873248802450108535183413970057319247033379016606809226547805269752123746375',
|
||||
'21252344995592269775107236774689012760750850598567799560343535731251766780016530978663079386453430272698006977668063208502408607227544322945446079708000304179073184745623740466334127867464883762843346619853325415963062181018736410570113171387891518398846617197097407612852037905687899800932343156979591740310928257766081697645/4718515378484509142377558412184183991357747235892194234377932213613746008373918923611236346330189287336493794499188640380975364991548794341177060325509698404571318037298112293926833877103554546466055612498927474225619680186119313129604319096374924037870803554153458104225741255753314955115645304948564151765516183663634546688',
|
||||
'76703085666560609319365659209445044957052359500745196718269665234646121134787807938680787341023203786904308047981099228357850016602203539979246579454229078497537148799349353250167621935384193502383187510928609818011142642954550330069991570983902841318203794113898871515702445720207144863877026526025447819537396146822201819435536435835285098485990260677737192230985069824/16989892821104122916312992616665764943723222199277412857053896319814438961475117951904867066845412639236790762432996309717924037467863024360211163971190006272168845197000304328480920483651558672879967350982199768255256753110375467976246339260326422891913564151453729285191351273342206198366624882195135056176502724912849575385576554208844814085228696838834665339811027625',
|
||||
'40153831166521391225489894857551838468150576827129133168972859441122728710165233865309084872159583787083130713473702296122465319276461991457173085312407612757280915853584698420083436946976844240063731333638150599017886937783470942007376523947840104246927580254612991191040951001539641947153847776050138532219595746056076776809812/8873576113581065493273519627544307418139908640325227196912114520903505426722086265723900326735989771660987609604507503750212298754414472822477243293638486047313990878348820573874809636084574108909615044524663712495422565151340037564156235745628465172219286664653343193162657374410926171153516871765680561505768223660055617934625',
|
||||
'200736374277835272485186523480177159453030082779872429648449412313247640312479214261681899286513818912991945046297334418102711629933437654377760028476767482162031641781499114859553677502002901248124750539270235098492377826240319766677856465093871367817683734079621359186477306173620331748496369122143019303740252461824523590096923561928354020960176605008889579578495073283712/44259440065125442964014453739391594153290923937737136823355312892557975399035370631521014554980129291582486968083228888083079118116286312583989433037097397252506140224404130371160608365777625538148303917306340620019882928744151294738308047800711028158277500317134537540772759486365658024194428796846364486187105343266398338060080611643110071184992193593643605709075927734375',
|
||||
'488459049325494693259159444507437983381645757291858092983371672334043029615965882574409808932509285079401239403272414271652617474184321852388397021836909585659327974611273820676128650810907598106588433939541654215766888212287311943387232664417347883717203611092450971786083806323404432266197250919880225892099146722474124874256523/107455855466267410923480140898552598306699341366032095904938430084768624148829473848763761936703072253841751966906142283640379336131083461646777390874405323298406517250231389493084918065243079819898691146841695350589992171102939046740169198394671965069747042621265775948217054519855346617967866614303497258042439052681849864192000',
|
||||
'8430374068596413768975326329313648683222744787097373111477788794692418932056901235174019333392845529821722488982447683794746518712070635063397464904489452840169472596494433874412726723065560358405027764015273789053064830528919091414531400589850704395887250756457780868677553068164933299802944396576360356896758745030955907258232257358056931680617626606715393885378887657377280/1850480358582748412767893656294669486390769246349349706200869446679212812264400876685764762211659843658541567071977813110496329850010288179702589501255367345935389050373021591572539260951149696801665953123597521983257852207280970577287472932167362521740230973547070264273850381896786822127290397075758780174083415923482640313277341120938690594859118603017930500014640169211239',
|
||||
'58273615882491925540881784328370957720539797010816130007322211512070634295154626813477498607761260613353633996451797686143131893884559909668805628269959901044961666535533876340116728982072626875010391195372759135469446048394835148654407846242721443845351855234347422256700864370273092971804207514736924787577569355949601385705517152/12763172138328432984573837529764902730448666594097466544652830703737195804747682450548488162227364538052585484418491914323658215406327363805353540266048943197822838925184942600367647278764952826664684839953902567694832690683467635727409976388280092262839126779730168889686628630302519063225007497707028600865624133968363921915016625',
|
||||
'2685278694947152969468407055109959900160804835228466214479525891103128459065131221736713585038130737099067040196326815283171857977706979990467444622550497565521863441617001097354649972660183038850033950647594754644997436624623325003537308428828750008305464174443210079059253920383411910911594636045517662796811001285984447671371943267419151220518726940455756833040934506414133888/586864816044968996825907488721678304211296267371936463272955488077666796280028127333229453342355554450708284934221461946991556216095213627059047984199091625921432110913096253352065238765240063555347292393427726758010888546427415276046238297382474708612526770055488323003698641493939145624184943388159614111350839555141202024991876221874110644879676602924730506882384603409121625',
|
||||
'27700345710264347957758638741952394530538598225904772664391173119594616680996031886665218221392453628824570256438960349220263575741495285780845399187013582169907518462437966962923592601721119258663490655013419675469864809004562272799012227293974254329929404036071055528766397079743200179924243479370594973626764330855493789112414191/6040997839051213541001279276287478343874107660287651711609067205111574718442229224212245187655300348484144001298428958025052162253854676210451657425437588244676589965820312500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
||||
'80160269787574270953020489212619791839643207793889009503234879683494928396231769167082355143564723274576166638869430572977442020236299319718643894871736539109822173538266278165276591962215927816541165548699380129014840956895931285215480350321259054359835472320394089646764361802445068798451796525897304221529850133401768027469126408090577121172238802281079916258951770135485696/17444957084936455555074876721808241311651668802091828589911310253709517226715880722948267977560381436307152342821304525073651379763144281678911047276389231713860392794975453518107055568765009486973017341479560154792340888780337029481646046233967923289468141293699236815196553819373828280398488966665994092877680860643266914861837639322607761804740256920429518434353839878280875',
|
||||
'2352816897072623416220002134476921108405735955266703519252095150412419264317091785317803024028565851487861978477208755211589867803009700996828082850796103789009194767813611798769297617674631277847910238088040257037678126316866517601388289837575778165301828137259948173292658462765645645868094197736382683775415645307647043205988394700/510973411316690313485681833991645423802776865720580280058344111363363091616558230182669376841383375321912553771027107891991318313938505988903735114191313214327683449514676737796942389784957658395806951715454372568523597420866050825822028817422805046287798054682484158011143949412956697442991898914560191411624040490122951328364833797',
|
||||
'6403295584873165688372907494046202150046769667837790834896334486679541887567517050446119511695248926941383207478170182650927368177009669717288184903306689332746127845953193587519575304974203099873732502605739219028995266139383163062837007982999189114810534856227848568800302527760100163350814120545587074865568436789021082619398126713943637898657861949091545516403987546145915409024/1387794272010111535893205703999712437783041553900341112488641528986385462810448493444968969845352401058333929711237978223214186693177251566069419805757440174840170213159651962686467523533938145629444468366235554597245713128812532716616087753947246800626006504878203666972651384731498770435755225220796872155249202960801768048854869001310722927230237083418017482134588865244642658875',
|
||||
'82993247683514419570466529457059660634483860665557779709153549045427987672829778520201315148149878525274005978368939092115193636113741972236218502664881450367443614971109677363668874484696543982239492409231870942414193419634675024621942196087473557914167832058111113476295926250739099284241826553737074679953551191767148712684157318697/17950946423927357725787689855263532224005643859095168852729513034456208872420513601894508438640531171097082516559962755244698695622824386001219435651555513795509616906355363573638916895074349491513539093024980575852693293474288638209680085037286354050958859425647536735341886663074581909148323105020337857959651624760873736590065664000',
|
||||
'584891611376763781852144397260140844977346305541197362434227194779766612939978629636198589818106137319267243431810481928639442343946346034433828599323416877248326356345631611148749005937144684862502198147087702668524450709118588741606955966569427636630159793409544653944608958808602551061186799401212712216156799273254257486955348236914237644151956226336750212957092204830385441792/126256710861549838395499078249922986417488985866522660521294610280820007499099176190820291214370699289977888813109514854178180265382471972583921477022411657285850911270389720508719377071949050253246021921916685716353393018411683757067093259101151481189635423399611625009617884983318141463140199839724797412514785751549277231259345923662499623640886881048572831787168979644775390625',
|
||||
'4743054867460856425399742072925732465660626340183690464743217147109403130730445842673866624947360862438925194786600531200056131309608642363389833474026007798643235346104937733349791667694862514383520689594596660275306247615314272223660862212527346572811422915223417783887717426641317921972456913534338708745549252254865615725859881457906376/1021840037832289788284691535543138164288462770384961802287250236519983887262771944174557087207566030730743075334169628971336358708580124427857321953981475719503574867471090607795242192417162791762511940848493176847925838738242153177311649130718886794249673254183907621449520415060660496225939963471753122766421338241291756892571824984344625',
|
||||
'3784664074155769467702999785016514468281913375341134899878893061325465790589101335015569840325786070795267055386681356241209412947116340524588831510768864231937929260236754881829005065056310226407358204278658699999612596866156294195316867934035877283950841910726224355461522065773816109849107487214275801829843762482082803559183694631856772777313673086715631547326400170962722842240/813783281473223559981291694175087508812520505931454895884442580280342455516154674683217348039336712901850738745276445107477919518905155156380620466135946654952837573797479076027688866326359448543065305071605591497778941561986579230698384305536224430794233462949056326864499827444363206162251104552740175503996670997705423888382328014210171324376593090187604268086334796245552762333',
|
||||
'1946671258536842642381655747294621776070051525209940130115769153666368932042152311477746728678182920842238801763565726086459485050237860693394471644023999467670017470376746609892484192072297938254898880354014176373253875722410186683852253828299669530022166361304397631667133689128358365296701757782382475692465977794960109690362462330857/417774039698408581013003883929127512062321623871486379101498968145670269174833505080260389860863417408848209525427705249526516766731427603641806256289098209429110794311660844125377702016056984573671024035213045475132134896835814746425864304907974005950155657789157496389234919107772201305672421982198984613601511388413381215220924416000',
|
||||
'328578487723377153600821813410631465225159589727320893988991729306688663770697528156625398176929288628930496338036815910669579019719633124832162035588583242320537435554101841406566009219059042888412893352417446437227440076869427577701706713564212185163005644118869496869980925214424591264870388237079776503547314587137721633918824664946974571838634860386893404364499977966899068777016064/70382695927096628347637455030970644630942872888311870124896575877585692281624151103204551947776906510366521972834357031777287506848887607457751667816514216907338309372900327919413372044625602555866294337672022362865161523684052940126509854895320132828009551012639156341878073682501185688461720664801829933689280752623773789373199210473995823367199793407048741191786377993390965297664875',
|
||||
'2617201476921368517857942326432090876874414269689140439151907982631768946799200744678055980827789859579196832718849393046147656447672531861353456343821196812881882023188898815179947651274131166835133965629115749368441605680383605331300030886676081418867305170012032824011912531673468215561506423833911621270680104083533917562622600478548/559567121085534865189976875600841717161617153776904752351231210970240323460800785728739412474960421770806162360125774000625035522428160837855944215417821324758873127567654244465281354654299068546570789547691342609793050301258532586131013585959164719533123826201937801986942606844854123769532447570107525081084531848318874835968017578125',
|
||||
'636225736038986537559880265988431731529837451289737542395494683393492040808565905376235074534207026537145408562785279823547657299565440309510931336394031904920056464959974743525662459433889398003683078967642651812081450227654478095420306880762753401111260630654049389197602389949892636943971690212484981672010398108426002137114819838399222096538921225458913266993881960732663394566024064/135776510176793971074115131648637508758953050390591773574951317807919051619690313331192027871176160424663811116849856489187562728496099757910540362703888937768555824513740118941387831822900198029266206334350448626733139136083404404120210893986654422850183837974770675600952078956326317698998103770833069712616832650406225828969036781514645731022616236082175582937900731419575337473384125',
|
||||
'266695771933124633677367149389643417608461366874310588884377151539325854547826373711099517873721616543570605935954334944030816383858485296542260152894035979141266909050267414072982042090341712035518685997484257326212454742816979806460287972757626105526907510197321350895873473656215941034605746494172316089636216915825022339855304925515685/56811706665210352283362623728191218698295056176625217939528332247537278605113496147630185544004654583441448319260578659922931798845493756189402805173037491645434052737405379674607517658118427614090338938517963215812444779184193933749520313676564187507594274551791388039139409235056119788261689087831209441779870873305232021728002651979776',
|
||||
'3511806683161697708497547617957719390189982761002154386881580160856792742952365159764830433511949678304281539875366378131195670004345568047690216126001067194904446295336734931691743477531830892015690816210752795806120303198745685394015161323982229908105397857791180342330098240702332072396030780386362735967021055250450666535422528637737695053315137238368787607412459874094164027214953984/746741569878639983491390741637813989978804202898438708743258000150996080386381281307609038830698579358879333079215327202911977568726258527646560497079622703052765164031089590965199628534477381843079892123440214378949632707668935001371616475282883095939750704292616758568964786737752325652839226013335092148777788733453702438432279149298482004122593243860829060557386699231448957232420125',
|
||||
'499543951252504651717279461487337168721376180441322735807713499521878076780205427598086756686061009718016175215146305489885835839673419698751530207404115002383180054704927695269921072232395828258826213554806570423602966743337801741633869267620843626804016742412046844770601322513184123515405692795346790813502805238635003099976693786012816/106032986203682550514602969462803214831559442358811656484036951559991322084935692953211120289352600484987931812632505499612220494311324679562152078883141464406938524087376964935494245010636163705698220308243980789514815579319533341929989455211613890905485981353837321342730307819060825125980500975023479813757024395422041501208805339176375',
|
||||
'8540849722242122835873311629952985285477986765819584558233324868363310302206006773828897816547299245727087876984857726652932481899766510637403577175623031467933486823994040576431755172220921921877192006685053572215922347418846423419683723609799729359551828522978186208983138345476801247634773975311405913274552616037005854836040162212761099347518242200807692224460514057530656658616850816/1809694575992816440924165741094996511361288430727981159314194000585536832192004274726651828760263522962868944612215633976203596150606503849595633388319338251977160257979846235995654357082321611719654136058257458753105122024859078420173515189641515736029725847315662215778251370398007468665986604529263071981040014321717598469319370651877296085250322599247141397427185438573360443115234375',
|
||||
'51377057693118720457387330519321684810411289582445982078756989158447711080414032055843805733348558621949380554029375294451303430269197115810494758213980833379214402452992657502777067850631862453239349835215260705131864911194740832694498014048717871429797341104227759557199716944554347879767916801106279081864127192611546757627038037780244683/10867246748205139797826516105458406878398263495890048637741584969144406206902037615496936724683237700974333817527350121106320991054058406504571347680049945745432432384570515723033245430148177097144850158758462968940525568041926860856763884474605057056550146491001458649244094211354153171337463406192651150028767064600270112838159624568832000',
|
||||
'2517199821548153657910904242290029026229621935918771922146425373057248090467388430999870036608278542449661971830076845113363443421757278612394167195431759807908413353743377586893872930828548256216510734912027450642648789925142482023713336937258932978503310551945630359646777080073329099268529064437120002567758664419443374110941824106727218341795100740316967386497091058915066147604481280/531524260324016969370728057738851340792702640911631807178654723224511108065633433027773388768889035083734041289308124943130099580765995149865288328550751289346866467755881013217287175392101334538392596956248952430438673292186779006015549928829953979591250274457658042926539541422697852390294886719770034058916886894408015624288115732154492554105512936468520265305162358665491880968652687',
|
||||
'937159485027553069020805864547349554547661693803137696125140510201297417077233061462068930061915381468160677500445535971818609631674361074587732183297775728389124966713749450996643183965795829780345678634356741756033039293590264355732129789110416943789326482663579413229893878422643110852069225671783017610387570935863228248464396615988222968188/197552642195166614912991396771845374656891854357948006221100585931656016640982345159207601253599235265353060942313995345483862152489766993442419668492877390774016299878943454249209917002169480134131612530977452579520564400124908392339263545911443803619972765231094502745980083350567509014991735227390978850048088378721210219321877150756855372125',
|
||||
'5119346675109082499980756672052066247676641510661024682574296075659671743397880591881419516307292610449161239233139088348510165442597278965718490070997173182184417648987611422446347235659085092530927312760229499083348217392125906851921500360553758600756698605047197529446594606336369425040236883171542367713951500007600355406492128741649090501989105696576697387613035366083536629358444229248/1077349190930018641197987339365056771667455373500846354778079878558146320193384166982231171317419356136237346389570422332278447217033773023556383420372685049620361481309067017697564760747047839930422553324681218253359586320952896352245366885171583221864112040038531116947187482413031483309534553461613717398523132176948321964066833901215878133594739833910960843185464241840039390204295097875',
|
||||
'58469980853327028628854378052548435225264353747414624620847476036979290604604988637469977624105451959796142112200285366199247075503424204044884512269071061176877225315884188402954249683107639777479663275428465242905314418930053869461904934138752031124685541853599407727446896558064252696813370692668496452783003306386669962110734257851955001481/12284458784412533668960387046583440199646081362484321078872891438551639470826582933879934711861551014258493898870867541349962531447724393381011633785884743889445407749238183080941846528000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
||||
'632694338036291086868292541736272151517212971647680989553388199374739841494700315711180961760384484042159533979873604395621016272772109619401112452105475906812313548587880899058905419220497456502543971131226430775692744506047039958551641270070996110661036371362526401525207493270590999387476275629412687473362310414634469788979236895300129227082421021999210841384596617452097273320509348159488/132710874087075221626157136110978536488168313127228703025408943153782802265472978945703742152954881438058373686972477195846162708986982742647038014147903817539418442869011995988024638278746664230046452545499722614320284478925059527086663455478374200352375121910000967044366831275681254530914306064535110528723482282501358749001657608122307686482720926189537227492041135055090784059994118192625',
|
||||
'2022422468242151190347511883185841333968390991430363660248687284021524206079162212007263606819387821055183192871951076644707111802972354128011904720586729919826758651167984507804867954970064938022106484656560866447290934136610214175516487579122328055482392137619829739560923807880779865442016530712986750711199998190148885726144596663143651240/423528009127070458603837580844559347493775236970253513203186912091140251858605354218715508091394564000304400858761758838182654201653901408046974668599216804223721114331085507752600832390227068985693006017591304916519540245902645712532993889000048992026931475754508248265356181099817467608863068068953756055334213584649136541490530849577363647',
|
||||
'150542201776735231618709653388506394887777837646651406023174643241783006776019388336318216053045300417086929061565868663031237502137150880313922488106751241006715449458736723129545400133450376339156062387859536289694727635468338235084533552501618659272047966040838509286942825414463511175531001410923014751124732748099355476332649458471135027773371541572374016038525336728276024676601778299264/31475618522527975728853391146702172347290819252268551219771201846701658848754300838383637247014705706066050673096278897933831272607774110183488615338021656923433335661890414740664480671162459271850877855521869741833030384682549648626038314686632195158507693321505500888988183270276134667071014075017317692121986144742473139078857816578133693776368882596780120407949076129685719314703426278875',
|
||||
'162664351499283182546788129866259011542529017155637405328005084357078048074065548349602626724265307733005745244338982661090506394708170105916177679714664432754153795772811389612700051002336719842784098507498286260450939289109123759859710248621544296008356071415713931721102824434964336667980062728653562369697289761913925215639430496443820411811651/33956433298509232632327667206936379248172049619640091186698677954874297837764235300806836350019565970550533206216590639008637925266405524715041372407701947953283803854153876844320283735281209725651036513491246238622967031979233265136280687995181375582844780830517825595245506321879177965868747546127685256867456319112247572348416655367667187712000',
|
||||
'27228583713734183629451808889147559240254570554044667607519238865891084165382075658707675405673276615039702916443152954157535195508033284355580742761569101934821088036084480297442018005972653124248838420021440899368869744141698556359295489470499243402799483082406238128875386939389123336303631993557844565927960009744780823166462314116039469622216263221278402673028225214534859076857413557466368/5675134787340359276387085910732530678163604187009567576895331134521132071092293139167824957253050637085885426100206370974479279292804959049999647539684766632278449357893560561608633329561385765035738071726214510832066885156767060324427330401967034889966727638276371320401053302413136551142103678412692111973028587922399670456670380189570979581674699102934733563330382821732200682163238525390625',
|
||||
'1340937726818688387636018677960518622106989311577445614347061615417831160296369500704524022869612973331444242000788110133157689786976834913297544087249593601420972111789552905846168933014570399562460789075871112224533723259660967481288525220689577290386781169316008987012566216235876842757183233207943071523447855623001920854571597886491163165150556/279054368814522483241446594911968519760869180429079780837994812436395792594449929617293503975169489945136244602350941695478861584261665582123038340824700224583170270427163469762789603657025958695551448477050958557681299495041994050951692916032501884523150972841782658389836059236040091605838583126400660344344601879969354053137568588338096589095875',
|
||||
'8910951660339249479517731530773509037034977353457185453617569046885132434552771722727442649095007828468878374139001808825741829728375370140505088133244952288239619141544661695065318568074863317054989982620944602245967155400712702259180443238473885690297281525960618905625084639870407113029712510345262279507055197024677292523214467306898573956604928730377318210588837126450942086527893437954864000/1851573255256476362977604759655284263358595254392797078730401113550522214182030016660458780180636930591149034499142464357511158868625309442843792189394292787657774627425816694628839987456477799348874853555586931850788645677998004186437300173080088839930384424797706381282430208961565555953254145002318893188197561315855286173276564036402546798318351229978103276843822345004757661005876638005274037',
|
||||
'84997663352987536417038496128111159210386455419431137931308301989414462311135708114322046234708053448098538629166672456703651524480738219822858469100454933583774404032968304328859365484249897913676382909050450855222567147661842184538302991526635974158862033287154321908483007506326807469239745851379559259262425675035790574846229470041596801343087041/17634687575122715507915388096857069366146981156897230620371714518707518105157483283253374797127075689236298114335734874553183291806268149747826050838089931351158483980735336772879045027213982661634865556129548356487722537193914986994283729746578044621115589195066212043187229606921062007047940293972381121200723292297716621231320254309060321476608000',
|
||||
'1648849257486312935416274009474520589799750757877252390543329851029437452988499485179026845292068377957697884739052491455807688429630524210823560524122596610276957537756142278585091315141096146341773982302888007751632401090642472506538817998043326415693840862261701364114923327286462650797963969289743571514890723145251583881752876340628073395708716735192690808352188867334842743895738955762067968/341580411902962140529547622267381834425264893226447658723654120920549166416114849731588552578626041597847002946105459812230979900480135423787717825697955152193526724029890829615845314765605770060975538542976523177976182401820926854406945392167140347444433885786340415437006775075196031433365827874254477351374236932624514469241634422312990555534789607377023339236417434574755566856489013828206625',
|
||||
'4899626659231633406569362199187192352933567344960498139854460545644210119722856870634652562532083546344965166340063297647520613455037240144734014575196764021102614748218834054157165781057405100578668000290493431869797883155764963238760612333048336953434663867580862692267574574465371615023999531030446250351305046572267686566999209229202587258144318592/1013522377571209303732207048597964555261512342860326827024786500686692475697570284120359840724596444209277948648863014479460188496587721578075151793081823795161404110811480269106846359964436993190293615381593756076515012514100289406862487971220020752628653845345780766244842386136740768474703009091552043945843632199999007070800871588289737701416015625',
|
||||
'362903847358088423032098939589019598036593525722040887883164115981767777873860799955715587436566233010543937498922435963385091400670303956612881974450548904906949032005878241638546734597308786514086678561900713741746905742866635779432216761799371793260025549698865319760786921444505323758235164807966277024031605673481480590193724980659448692487108892154809490199414370023696404259691299614147712/74959603173756091922435708504751461448318819548943586676159545028063979695880930926805574951275602951047845904770828900645717477098988124553474616764040938121171776556999069351772286262529729056288451037717198273521810585868307402693261479298923209587143468206907278220909447483738730657438961435587752873879244136801168836767273934997417156543866722171766794171623224855358640156858606843576125',
|
||||
'8521348154958613550574095745596657249924328336262127244827955886474742319277424381360677655239017583606851483318951440691757875270001283229585075137091617073509041644097526047127618954293881264113694362598157456878784814696577083997902588926421675843291157049153701100218784481406086810264243143337016042301806376864424632290226377881206765051896745/1757584673254145109614914862134217345456065873908262753169404848658913774363650863904627183104777337461445063704655164325930891269843669860409763569362863600932267319335889540453302785099532688264425988677082044905948179153286276609910158854349711054144968131934713419772852663860893241364433872853338630216232313607969790083448433666334742108176384',
|
||||
'1802461420562646993856730082999823508145602238125054717836501201545920604020389361370931345491160549787411668288359013059160331370751496329806488246135100776263777863399096485894306306621852596694700845918608199329091852956315870664531614358379176680326508877329862713333336188556181470928613423972314983964578645688876556351433429494008513812136152576866650152623510296911708111085518974142728903424/371238439252064016214448115231139360835481920731625321084601566992497057371416121407293286027832357816257507287295719261141426130159269433199862002841433235955835172774430690352481806933860390015899973299268361513643524088242973968537595085600335021120523852420135081643310663869327371821064862449426404044951571585406092523478861879148545678639697679215289523234342337347529225543852787599016265125',
|
||||
'88795280670112240977945082069219541902481768504536167808816453021962616596410396813316064685579412429940019071114917828928080181638058444302439626425155946562362550070187433083541414569447612195370911498321149367969974762244140788494955146280201020345849385865084095189982291190135293114489407801749533448443222584092575861096946605418438181411489276/18262596223069549313969288693970246663726147886364354584825624311486557881239003216971630599389642752648815377795018130449384513824927138566203714891090017787258920529990523519479383032564209629983810866231367438270859421572641494420831435563580050954867101292568399107965982704216434467243223964964535464624988344342084084237484262612100980519602625',
|
||||
'752911409358158070688133336918078236438086521781731735123294741731669530734909563155732092406099003582749182967246236657021875488130304108082404134004381196925042617909108654109138702993903561036987511410396567636331465803949049946947888490287210013788802287247422993384874670368649616782837096233384279817294778916419067404863458318451722971117669642134894906235066332205740872938406767767019265664/154635332883086377656813458754577288834216641577439356891585125701020579768315520324293435008561626926046988056302745604248220408154454267643222328696606367409715369481567213640103076112146615938180089303294709009813772509602954514391675152597458428326644988225555987883250642389550531163153338541870029415077982539614784206119074702022117790679351778993541426110436276530890609137713909149169921875',
|
||||
'15286089077439918584953144558775765002061832952090847117286473868694909713791678395221438112006475047633028543501632631679316850512797978594476229228325228403849089079675681042224057234415951253954044235099139983386056610384489202589484570852048157272551098909455253037561994944665563291526597323199530997923272894119350512727802414680772513760081360291/3135161418037836259442831302977219750614726139657718770206960776514822107155928800694375522572523712277389679035632531727155957120566617736817225541673148354829212969778664144907503411589126111888917929495106029890532891919001138770210977012708107496187227751496980000137223047079127096028939624830654227758737339725345681745847598157538483308068864000',
|
||||
'17723482381737693269787076798246423310802126092500438681864673375704464394105734049099094818738115345706100191580982712146882034618103161268453808819471603805345990162762722549964406042295355916458624836894291267553138041035528315839108252422478610879301656207772491548742981990469454529537049123948604931661837539943536878260665617524824604291547373872716194154198824107266275716083848262625068958720/3630091565725887087605600771358216927099748013831912080380983545872077197366172619225751217619097657405660172801849926220948413919823038425519006413005998509677472226729295278652284861834178791332630676772284971762683579047508016687969040658592436230062049051143001484384767836353233324570686249824980508520186643477313941054900197268584495128610811273834192099099592687485176400539748616360799518357',
|
||||
'39160514032490258389003214587901781721548011632821053230881239001436341012396166857515197251499460842954863342641015306304854226194551189224721257664420778416733889030680295206179109679627522113592373938602737416822981698362363553492610842201005420727750505681358595340127099855823746410663750133019743170700888375554918213182862789028531284364877435896/8009919337434786244380818390213546866794958389603666737562053423775386173149395943310276429895991903753441584557917565343673605019989336264748588735390182130334278887642569515202100870042209261561001627386525182096090194076839370589780431326097965428311786635366819094633651297179987340498215947144870164066409654464551017761658775207392803825996394875',
|
||||
'18188388167811476762477659006849121912679763597132233588170406666718758511478154418948855160838212151370453213943784067796172102916618102024199200086478021533171984254799008541948973652219533819511807681148179806051835318645591102296256347927018942348574774821478499092054497390790734798143983945096982240517058861467440217558586822663985584775920517755909444876443211404222400335500608733284461685888/3715278241795087610941547133490827711133909145414878217965273061493740719983191775105550559905283733134189195190321643965858771461560130864714477448011225816210219450040558581571206711791272284197614810026220745160693898421068168630870850392739817340370146186703867097982858560347971281142766235850971837330222970900661653800041795020382847588362774677568059355238174402415384788280852059532958121625',
|
||||
'46353430636874284402376008361176880938798775506236714098123916668545331718677407145199311191108199195405139575147933283523305343027297808443653460575141799729554209181454676876263582758919969948546918471128299522715691694855904535448703118221685887195383025133924852224568922999317583667985459047407473419196367812949579858081181660610155317911318937349/9455890179897829052705408931064120820559254220394152447502395298827357144081378513080398823188243663943177539817982481713456798757563952162598464929784370305696420245648035222083207596797327498500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
||||
'13607414211126412689435920170832779523235279308039712294681570127241403406455725064605210473923965685858012114332021572088551127762628892611459785625407107300355287623761046438724706918300976102581928643740596336865698485838431972050267114940079353996100620311097561405192365496385584164725035842307146351211183443267583872067352881387294766804298681841402861626441205037637282007810811602289501477350144/2772205947240095684743358245950494400934136973845543049938212403703005150954425087866758842625041409060217562878157900197951382737615822655863548356735553650755342108183641353142157728364469706919993519071329069805875834057743793434164720370026073143626055293058601124362130539040404491770346043977106988100969901297669523170508321703496903672333781306743488573086901583022550356345156457496396749797875',
|
||||
'23264350093315690507131500726914401771633259602904894387312742275462601574788485574913195967854009886824436237740789910851843045306673524324611130536719802717912615609109285261163220007241492964833933975789257681755002054890453747803418465119567389315754801791950553304034666399677630030683759466627797123971742061866928213780452325847848901682792886840980/4733430508316851445525799359992627142007028326865116523033957869609125212251136452396410236930025876982265915963089597825393317909648054808274488356887197933393799241941120691826916741647173843634076299630884655997401370400734954098249519111198509015387869462198674657171808841005212058822254513429836457539629035675822719720295352508916898476137111981397',
|
||||
'538960934807826161927564251064896730663918579817797131801326848839524373936888402762331254883505157846659830555799909822861853145306547699817986010353774500545305596630924815657016569444824389893996399550343022444937690798126189162954188680257730893424775303801562327757655350002001080811866451425665301674688928515041429654624970707803336800125648742168380676815769457467038849213225206824671025803392/109517448833211208909213522579666376007937648875042608355810687327424621469323788799169718267715808580775296460944943658428295481347915131091297305260674731651980547668762317138753250458639043477453950774629862540017034566407286963562574188172753764978963685535201028286359818524519980885827473904574840285411260037264057197155527809916623487144493019982419606308144576939532055057697567269576800542875',
|
||||
'24451481554350251656598350124163983489544445991992661049829014268366250968046589127810466483349378560701491445658725080497078590986743512397927747885285095846398852574219367580068049729894667374214861939897557930527646930793431512683750733092448862551580494245486308114129295467976874843852138144539250235632985676953662288536669286915983261820949386845806781/4962240683851186225340479316959605207278948397833849899236383273145853045488131674290332609876751222112917095096150531825255611911377071887145019702433883343849119017643117746273523770961304609989779231048095665388209125939879595509516490230961558353074751312876252070889983946768175488341370453053495319823381943793679003096504200440997908071027962806272000',
|
||||
'166591964685609920525429719961758335887941461052357297159523350448241484106515158310298216150030768812873781356326923214637847449893575074813991141092681107583183275003570804134461187213143832123628701876041315736048451374824101256980832991214945762266748755723551899001458245164317581617731332466039757786839492869211248064662857987111284518155462148044812626055842061283907063784492325693994911811072/33765943466859756347231737285768516240749714264564410814839966011490233598006227246396923594069488496368994857718734549787000213944990695920130591750377483911116845697231074543526982580529937081378303555262933800375576494067959129682770065443667091668104132884375840812635624862495548888467575848428463895756394050252457592540570490061561616914443009224917652440212112452400106121785938739776611328125',
|
||||
'142464729519206642943985715465196065680101634264787375740459792619754842396453535646509170066091478069799080172299369994438836484938378991283020978342879057271045033903518255546705211647900945143546293360496661466657600494787228476009216508124736254967761901224547269496682078470378676865981855568197705507111970901389105723589666709580851611656838285777566736/28839643075889294346635430272525349651555039807615358795334663734940126053968249608601725991946342449660829847322517564370606746964714098508298705743689729306507554363954348780144675937510900983622836770223945538844314468550644543502501484521561567788992156632892028591678742014134130878101553633195551605796158103033794695984958987367799607407626319024034625',
|
||||
'380457154857422076784416625436182734840012277442496600178664323821064977485271288502806124740204295811759917468598315932331219906040253463960961995849761235911145436552991677050565022181516596464968316292789048809559129516930574340710998611088996508613850407294743457197519091160170604426002924022923388338999324182261954685794522778833029950214443271526469607532589775916381786492089432407550224137344640/76922142291839859196033215395526448408989799722090330866256930898223012427332209054699494033511478050536463051459450844520020426942553107946282832072826069342120835351421637970462054707566481086202287686453371483126873188020538661871147778312986131920021929455276187425940666580158173676284545586944833678819622895550345083175685846944633982680908505900099388037050135210936852210263717185637166725605577',
|
||||
'79999800708369666670693340813609265988600461251844239982904928629083975986385235816587497657678297663151298709076884516168798694292862559083730259839524968813932151813233366175091317046758260934357096757322369138747731224414176608247605314668045838769433811781490030635374856042786947109101585097035794969502111354126773165391577217071956928171336060247633607/16154850265413610119312596299635391433380319311682208755390341659152160578366244774029302281068833963848811596545791104919556346810193385001196395489740571188884873246131813864782581356887110499639544662322008824704830818136660544673293865921754569356583698951612797219776290835161780326945625621758204931298948625885081325268123344406261740843197232316416000',
|
||||
'26517014004241498798848003208133111307300592620947908933072661812816683085261359696887064436611747292468717739609776825664344064024535028242737616786238200200498291226984541942836459188328346609549239724653950302145388877472274074665166631509490297630344351573245265440164082334092997307729007429218385933622623935704482371360985836659823269511115937443161532987660855490906546130131716829909388620539153152/5348257529530073524280989257577050878033239486885664777340947102417077875628238501791711336803418503616042922880218860266611595594400020206192599559925169589023339295256084579966533260861249529670141854632965231468533448709363477120437277477668173149958267956654411822360053335695956270719680086524322530514249891157439878407417971839744392044064720476417844056346174795601710447583861127956455406902139625',
|
||||
'71197348290771978405602732839537012310626009318133570890482364371056186523989330157966045289127980687764218363064941391483781008265266963537623770685390148810455244349859354857421474277963009857011653865476081389968122418691934354122230780300833008753686234433642072919923583176333668289998661499146617277981691372057042930131717336250309680764289256080895092/14342680993892886303954853579883363225921109635079605392320944523109187641283117181461662846411912306480691901515655220490850586434947504504317080144378185989596578890875561336496479576651516902048606328604696610314441229321219627021610788398730173161473456954861916116391687748151443335513249810808309201830214153565966339609616397865465842187404632568359375',
|
||||
'342401018754023891059352629509715357053454506037284788725127073168445189422181540408896452651232759821766616850264122591225331120588918865882061344322996688880669665450231982002597398732201789017495427566260651180470635467002453249479483900268320570004293679361696197905823690145326531180324429318916092471863347575533714871355651558610515931373080997536792242278529575700808878786732127630024064418468916608/68894647682461956089382461406499759613691148650738957016015844830333168307020284281532026442272618372290646283794122772122665531108484704223311663270836645399554940741928358317884620420886609705267248656765357348477857852684140561714900614303731786621656041881975328169543884661780531057279582784036689401206706069028229798913082362331334839997133771880514515972024899408556517159759907883793675672065360125',
|
||||
'190286571838805495686895463752492174798162827967490343625540320679760700551286395956241059969689020885714190883323266375067607282890063444652926151862650426323339187596278760962390404656342309737428214493422028545912504503974763216230927196187743439226243880933947417760147785405444976177822143722569696182520826218120595298059020305548681823408268556482981325/38242608291912408815838081993045123864617058390583394828737640036167893859507494091864963383596867701436244007905770284939991912884465101575490548813894148831966132970730263562113961685364216989999844961688441914455435934703797359360780356482629495214859917249465567846934284121032247250332634283689409883499295469118485912489058982596725212975311140618764288',
|
||||
'15345379701153714436938289567639102307887273898834051456693981514956329731572600026866162286097662958712922836383881840211072389867463987895894067288699955858337488812343674818565432780224654423872618380928462939482247900055572079914753652915184313685270722583926261499687728814560680092231526501970431280783177357735398580700300402036873780385926225005561766822479878280891130232036389707452005183370152749056/3080426623087819678602049171756961824196425548529619576972793653585089380165862889092832375934535464160425580449193072125955685099151500755278763306599307218302635233595329082702148677300945556130106197481236122437978375963261172524503625593066102601042060541617791008722569799735202779005407381267021641545644264334109052258081225159748043792288699674153722046659880085850980843460715080965607434114275256125',
|
||||
'36443601662144279337033484452272105547257065463167427418525882851507989449375452076227652634440476502702584889057376829320548464677623038648843251719168810701744338735202853412651160681259533015293997501525430222870696235141468274274064390273214484922080219643095978640300874086585386991338440415203863901714217818222666715642467237183267303214471854472286478344/7307237990148312451155090506264642657872224684879860008436735430879314349554023096366846856470375025555139219961666754780864835035902856314981482268319886395881809659368275720375576152008289949085453326722156553603358879420820012964006978808113307340321824660484441618487292138631579616351777191144073820686595597986548374588839763719082323114663844043046087375',
|
||||
'1575294930860663922843149113097927168024893739962464327016763741314068438447271326302129681931564068796631337571133444163024224071270063174829664848259482066723104251718808936762938249028582992557822551950212098797718627418782376027133576209718376784834456568694813577764300716139817031545408608110880973380162736536313173329973171844528083756524804574267826691156368696436273087171467254161930265910972517504/315498546654512047637461608750018349421492986465373840347519942494904200733840189483934245529561231604050629653992944140351106887747105945984325466452136415067807666316202346895657914393512001212656936450425268507235863153258769360296536050028695266363553141327896064944506216017933135251875087069031098099192325817395958019524956320840925176589616439477134480262100237057953933117460110224783420562744140625',
|
||||
'7692057599553133417225997786980128299372439542315125030077404519679450372805989755158000864378924877036225189784045171045139667691524763609840822628706422918926410377185494048355128046783294954373197082851501927410350313585640237550107070936498164597231431242858771457898152684298047446530220873884022213114819556822050178579707210264243087257050468000092842761/1538814487241112760739561704846381585063002784795057066438330325566843108114787083741788078680861495340026117682606689156065008672344707615420501964123121620401073675430690541617930520947170306763541251152854304975131871537939618684186766168376514664494488691311397307418298556185724993104760692216619178358268934740746932420562664234738576394742559349407744000',
|
||||
'15190836470550557926140012263310230378455340797095100474720776113765300081463960235506950799869073894127884189857505951314428810423734978025409205413789025720642609035117121385453856877706229778165533034683130064865256448444765902482146676668197546098809055341050873494446231793312407516463576380102073171090898973046611572020564659042146465345334273595086941562425006763960197801018796906114925005810069684480/3035556598829526968124942916297025416522606357010637652635799096498208643970273828496655227572100159414352020909846298817521082538898702393916708804775798552206401718323929157863173504795127180177622667376867235806208021329981136637175570409791442328468473111768140746895005561471574356889047981458002257872092408771959478275534098789982899056118498417649167444480913552548249005268380838457505944462494874581',
|
||||
'2923754549090941424546974281011770689534961442264350984393235983189168024079617848190236639328676436485460311093391982825027512234467853098757486086492333410753919927528867817406389678461354257841007722472106464785557179844602277350411593959136520732862935740433163383885526249897183805981219416420390137516812715245569037561966929572448247077578419534651990827044/583599691615378545100950777601104007812792435102945248411439102112424251478512952087517463400054567832353967731887062349496616445419782499817548928078665212077449420216892583997505908858360011413290038645573504472422610780073975450391707084016931661068694024023083417126286468909117254531307085545768412513447197205482285022417611018126886491416133665625891075125',
|
||||
'441922815213568908489589193556560586318864326425397702047965372289295962228254658331201274291697626694859414786292266008476137667843874780506886929181835469302142365772763129838217316953835831059616268288399119642314758261677639945004547833810080700127847214666763863338091523753898362699222880772298183760217731228628442782431573977490761665091005756534420789258276075174436091798475838370152192077494553263232/88113527373573049332749294663402406454524640221866407165839162576979477138694653404281889227416977618913249275712917582743426231744980461918772793673202077716234026090308791893409200158969108910565467273725064769890443408324386897017825489615600787634985397885230455168591240328585041836771921929567914891379235573982442311269392541694564675053636078990836491104116280492254795616055818375774141667319774630875',
|
||||
'91197133767962483852278456285591810579974783077874307589555559715577599604626819245000672929518645004771085106770784762994476973842713035791487204122653245797126906189270204029735872508462106527240320932458036231707311992868347221176460407003487373754014365881209649632547685657666691589606571865740595829983849941049450471212387808617247938941417000965198123059467/18163679611214677813643455199979098261849405189805364986036911945012016248297645596270602508015864371788974684317239728239847888960155365342460843559658233069613218442910226128113196984801126248862273503232000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
||||
'733396724434554481470656964167233690851279923085197144879644750437402627936723531635898542414136943003236478432848965516925891956615111678444586384493883113385335463639674699443808084827715994137410743992130712903997815471369201406683862985677774155704267039224546352414417632475628136460919900347977754644214725932476856018335195927604771408949980634142255497812566866795799230501026066145434755838336990365184/145912495644159810313026420025053989609942265686388630266226856650796088768164358961644113076809511566125145395699271530802891523741636707532676065833327082092521989958971154798465784847092870457944857590623097781956884297103961713362268264159015928554707158239398721416218953003215109202771651993622244788505649098445109438104466256408060944298437033058380804126574878377874459371251056800895394904459469832875',
|
||||
'167606423853658713615749101123336326908381619586838606922565378505211386185130407423587264776519589295420758009764678759213587906216543160801059029133457062067529468464636376639307690657945597847080706144898920305379653754920571797771178341550808041652360635584401789901525709166172283390177673009483341826911179861584213314395893323035527116283758445038413646757280/33310364848744488727470761889861069993071965180756187311178124887286133554324042422503263994717993129539448310743598138364172889305532401505890384806078790034557049074169245078416704866867380089103064821732673967120808611636618526737138435792818828990403374766228037003927569643972203993263329403469747342204808360947840103843317533210523853631711601450617095736473',
|
||||
'22466735478643771803624909352181817912387732117378538284107485621001246177152644141853604633676939694208131243081227668237716194316732125445041260568403752325416203858329469477774720998849334961751541191761521224644520742691981372782009394826739249604257098248751626957528543558542443122470657230835176462612530464044652140581367666271771882198747182114685116336512604038833327707163107207664705120728068987996560512/4460342260599110071819142535903990817893424192972341872394014748315090430999224107274777695306032783105944721337053156074851240201179902835353097683269298594190456286888326067932777720561181760369783486872666974623910515795208353605697065447942804414120030678603538761538385793829787734830129764152263691296719726909633580318897787729004972323599773548689272008712427926734258913271101899780538635213826239542884125',
|
||||
'1972862002066252798837977155272535342727415329632252213508267781390870646909686724955861608046325765650407340102472833428905279473185818898693854344138403744757857928883601637909604082588338869612221777263582365745419719805587946558326048087138197694016224613118304623864709183681172362693106460535117459192992832969130895829916210933710820035479169562970982784580861/391262854245936198649509275719624152305206807261382621390946060636032376057385723427601113233050260359684153963520782285097664628279596618521963444574543409076428660874564026651742234728997339844161514273669735686516761239076529088145208958938384732800586298943281728435207452253170935715239392534815683367114290676180839217919539381651841151674859715992309727232000',
|
||||
'25408691426286653276004556426513401767718328640048945917745783225459709487487356095164164652218100925888847689099153320777389381851598236046402124684511968385325107497047025485302333503075085562533472482645023963717347867696861885363417595298751490212495932766872748125111115039311507374363000062513977436921772096443948159289707996749061007112458126301618769295162649600673247799961118662602756889448152517364992/5033870770817104909025512331238419776407331281944565644457345507262881100618908405924951559853352841154730802006750688581603270849880756026262217651685813584900300357685874044482329601894134443542753690048962260914642678445058891387041919581376195543193199307524572671344323361282633828144708683536721118910549933336800461248338059209853766305058943888965279863852107507075916981165164543199352920055389404296875',
|
||||
'616565940481577765011174617619124841631603944267256178093555235591208387588894068005162766513720546626399308168923904625210748051788652328072241494243040185611373035059619136712559505023703153194815611268503291295059648269772663939137446253437248541496631852986499688052678961495116210133458596866684413931188348798244969797973846568160999564540652111599082181871436/122025682149627484528651507368051367620578469392233875493556828623437869650284356703295836998178256875766319508797903923638967073687991229708051285605187358616531926112736402511567721889845693991963610192161413692928378708223712791930113386840109729368772998347036090902364801014045328348830748307179590755723160142468563977011700764517880467116766150322736003147125',
|
||||
'979882056834583241477188686550561625289337025747787132773853690143359657519481397575561812727394470125172272246774578404543257210124412718725081536309818383310896102958167337333430555168671327992395916735022711297435263157670501117734412587691271084981140167017421900852550822579410636184333142944411060788723653596377015359036101170054828623669169047118551010102258802465554033173968468183492058271365801755986560/193732066620504775981759812703028757608600125742477178751189607602983198559778039141192492589734277739017909889924488699372940787977794555519474807920198944109549023547259912324279741006975129518191081373554817584214941629021088206977532599055014150433581544599721903881284581257812850697423350805715586436114842183295730680490110793225514602133304729484547181740290534056445482186430729398701311563285494964645419',
|
||||
'1138059441890428903336718712159907364975959482163004276901676063186939296217569849990951930955270849692775077813359573394435924934540269310629731801376134549169322946310756738187834128956671195358413024062230774111935109203207768898525882417078119314003162592087920755572050993355843681931139257303484526223461840939880759908449181135029327442039666513325916729112853/224777393704985303260433439401503507705148576727364388720387174977154204629336985298082893443205223017966145098064861566754244624732671470935725326043491911142467178027013112527668183783450932992534517827863293825061296257420183669383719043873666284378647952385401303733274771042385666436214517205678674213118374889121542369996042313879258622144464565403138392064000',
|
||||
'614262617634904509410555695398092190166323289787703954329113412647342230855826568873946233136303566178604275296873234926652080462259552775254755109931312516616838091852915397846984381183732773411263828600108443070778861986849272794442105107285234729077818614332155890946804257724112143631642663084660012362691100872465148864413343598067567142977134846266425075195546265101720326272502009234689388355365794889444257557504/121200854655310345878069155190734381476914416893031941761756379148622591310152793328910334357724762079139509813381997287954319983165971628429295803162563856219211627796200599196263868781937234015821367575512548522790158101892507996780675863234181700253545912643548466256938936835469646114659598727592390628209362096738343417775009361678599075671154405137663077798419897932404104579113056038271753988669813988076515476125',
|
||||
'106893445531995430131837569895703697604978445859412739331042151546083234373443601923983714252801103551874986033841637079479794895452077803182698291822629713500021995241940133037200268381793282318691568234728329378829301282121562652452936297422136961147432559766896143081059518238234777055931999714973999553456547746825617168005650221542417216291412324174772893635832/21070287546583154895102927029203125577385528350171457634203261197137238923923245382493868134869009844094435068284335909266152042903157700510534904847257436666980671067329084409346149729385625108233234542133838924281113454526471855573271066865534480974484196644173270141056470857666632902827625475719738076524047736123182255306429677688129231682978570461273193359375',
|
||||
'1112742798776748551928744983308584493817112400536084744850969025063360679785378789764875076529575583792571198510306186417773101346425704969671256564494135750783642910394213727189639661184379341961737135764570942961692159177189753139209638929302928699705042832922774012622017270172331722295881412111812933593370743210214549038212395941675257558442878012136484052827439007483777859391215222647499741572327725689840850048/219121905514652575155579185322367175400669386131891843382447432851122462422992147756333729893028362013318264155489537138133372703295804002840920402482110260590055749843170064921843460498824128274420925357547023876488939786863662801268798987685503391058250902634999352652063668663245829496836853509616873246554292796823779397317819134891873583857621623143253788854530146303641400986028503534408870194989478421253057625',
|
||||
'983995170259637497940121707857673268620603982844439610076334749792119502557380361624096960070148627663305171311646851566811683351232268240169848810658089698606409195744471343416197434926513684882439596141368794175162315633192028977211909698595778839497844847281262559899914504080092844816319298286511436690429175757804604673752838786852277255064434365401871903307643705/193579499709739777577893639665643417521924510242119561765932078835121983190401762561436208758210519231103767232899529457440602166918701550386152070813526760823513884391055054076485295945972185402977095815609448651316824455250005505410192101148149284756928250258632031981439345164037318129987999546683113483700660879431801728391911161900686707231543216283304182063038464',
|
||||
'30691013076836380319652884848585431917439358375118437035944950498156836885409110087616602043242747413728866747975631727566613593257623099549583001362246649928075859944695060947434885707102684568632559339383409378997568182445019183928058902445738702610436187907199482604867148445903921474846103586253320950728989228898324829144701782792767763794895137313050381112872972806712570559942583906619373159053654481250809168128/6031936190594376585238407905469035598668211679521553144958053099463931217856780171872253258738635531385711702176347575597387389685526098889278278029314982721284691198967204664771178761416891317672705719303088746953644978471289001577698554025410820041497108311270431130911021653860874238212040640737171103823631718483572464361276703378587106311153455557896527384426327053165214814954154328483257657424873929502112904625',
|
||||
'33633002641218571027682690377239300567277297301791434890431645909447040830457253186262970747405514897340315490970846553666201190386457221846694126637998462873786334837901001833771510353365825087187582807843940479192544772559118293851131234910638705258664901225494129023244916125545283621607920983287799648992632814461828998517949449887522866259724675318233522060729654796/6603800575190864815125307817934327053530311915572323544225173935830559756077637251538096164499112411672913910767032482188570876526606118068929913515497400397562722669603861813453418621246538114872330536044519242663538662076583511078523573320035084744399535581216815034837553165853576616422592790103728141443697362408727056813977234078214386771115586436985105038873998875',
|
||||
'9090550743395453237608373761422705922989356346696677314277463900103283296889488490217663400349361962695279888433677856471792856853109469433355422802663947305503479561493033851081993247556828544308258821557994512133708656174864544457143793106703578476914924328960109390350040954158797845288786321215010324565010420734044059968931558099077932776525974187717655105951506223649947806568984848883258144616897526257038550656/1783219994432430133950472014582130883873515063182143523712063859964133099770763874744121895885645346884371273930727450069430704145915261127061538288702855011220545530326588799412076879985026006593893648911637986337776174555198865379578424838210466703683642106934154603710587408512357351455891557594977191641529451749437270208258072920514602243985184744955476557012910446023911358037761232253615162335336208343505859375',
|
||||
'396226087129511787616470716244996965529391571019620342657409150252952330537366537352182763477820160355931003174946109480036915624974500187179367618457008802667089004678925595775249725629495848146669773574395699032785024522882892752987825063933573399691764866816284097232869198405981830106568826923407996367321353003569052419999880089180418759265274331299787468134974811/77651132902382550540201641628906752945078964002822585000619340261128555048628863996784740469908853602590877726845475670110127527874088625214912598995012971630852593597027649154487264595100300180591016581254965295280089902497540043916128232871685159131211213497239568483141031137909208031392464815187505714066235868748950184065923236670418822911614780061752905170944000',
|
||||
'1288523130900767412477856952035690867153496584747510253984686336382980221603917538996138294311274357713893956725672062832264871415025750136690143411884111580335458539824573239771267043414225271729141508235839982033182605445173050590151912297720800168687369852280323356691345317649445843078291369394324862039143247087442309135074453265658730028145500871559458519282433012494054724678302957276371418936282902434203225600/252284450515080370603949213084529993395050575951188044032908270470514999880554862449223347761507020354321422785760279154913310488296485989979706208254206974075438570521939826124860483842758137231373182496438568202110337751291106165654945352183167578675508602832478752400011567482434301614269579368882858973850914513870297071866544875650189106576672289734326492492059816669288907018095832842249738081022227281715919357',
|
||||
'559506782175368748187145067236900803925823792088293851453005882621173124590368101213652371965740365119375069053747415415684193092607523211636912074219215053809881814859129515591479780669511533619258835199715146655774919418434080807837587396115955105126806762961292429904380132116986984435528307613054828929294602185927322238461491059740342387266084431988727321711184308176/109446188216864405154936682374609448082436347600301765944233278303643744736544109262794383069197271409398615846380701459882784547857821831816863216080069944818453908760476475874319791512782445408136169878207358663250499628002971644430164185578966399908622640732411885568638054229180673755690889471388585521113862492432946110902070139679164884208988489955874131206343597375',
|
||||
'365842109776085256207829452385781129402656684569922320812625438296958174120084800064669048017227033185969596458778213306152397766619692644694641677223213185977626940193107608138827771259334379895020377700759789774994800008933137569543592829220611543060189849601828342989023692876182148818363689293009174358168826340010491728715549904988196347316703985894355217235339548078187164176255405596310602921647203428200269666176/71497266937783826306774367225149833789788300609530680399994804704196212252148324552193149332755057982662844988588678159610516634667597707204818296457616315484973288944637139939912166034691155272078609876888919170894915224022982785138928008133003473320018151370486895637936102520353951511525996197267572355782608352075229547764732602557133494648237258900273354364050662921889937355682486287799239363976556848320046933875',
|
||||
'59378684894605773397070783936113244261297966863668546282887298999811950599772732377979382620766180662159460666220894896896564357533604962977968843474062092083279165837424210591042655271577974272394203253875965589611493384384257117088518521598379301169916060951350650621757237206805037261878212424267780312614942173870651684587166691539909068452932891649554575072205080763/11593916877920131260484148322810317629326850763057953025674154928920977965095141485536566217873796019006709247938019404840440511433467040918770165456925609124166970862430104329357419669175323763143377917913760028500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
||||
'440706629842402075137926797732405268015915199334341565218443748798479563505125605478061664327658137598204192554352688830679475840386882350034150077506804799859586772904929402683276898098601814091430598259347980573084686932949502809068565420937301632494387691284902026088950093888426872222813857928092871267130505744095697172494119689779474840037266295586359244899477781345249657455186847452249040895151288114085352974933708544/85971876318334010542313048056773419567285701852816309711738644074302467763075671531248793080425349570584102078764464196653843973936119037322000695949209283327606280423406302517302874806092835694196353778161663338667091195199032831587897410329509487165187753418615356836636352319310103639079123698118997500068644500893282438137614420288839230827096109835206605385673197123994357393257304049093683934224826006704198171592304125',
|
||||
'13220371178312284248107863849287558696210741535280184003301555188086761767176560823174196915070378763091889333203635784037294769747641959860810039816533609140662043493962707445564057197150603226797446933532133956054671103942520142568674339411250001585991743608723150284270101326419464088290151483823061613403673956918458456165552482913079283537536214967294211244361353060/2576683013797055224377707713704899315377379548630511095136568220145808780162671473907260623503552385384482723173913562423316997528913651044277418227616447815609873991028073011738113927961824632868956802271260292075825561646156680740323686126811928372672670917332613490241615850953660246271541592056143611581223295045062157404996958100136358649109693659623847024011265147',
|
||||
'1538372578072246705266078247950089219881197139399081643650271348770110178217959856819170325400476655944115553238940541695152110949081675985299189913515975242840473616169775880272489805123124548256666658584129873597192213080038937409046114908719298170078476325423466734429870048697383304265342125425087105698152643145531760853127801123272389134514333752173173161562273487752064850461823726948588221913540799816489566883798912/299566102446745552813544808169323270315327122249327394279467999308669285416179486075255023523726449428151807672528368606071047812907935426406886253284580296349349558797128593446316786243666290649442956134448480598303527795996222703837663605600624271807461605295956674925277658331760000117761013381199880905748188151556184620779667670278558839116983636972680749836497346863752662521197700717702790359162307617024566689271625',
|
||||
'281696937222363685333688097915539146272117417024070728031063821781398985795813333650547982965341298894634762289669256129805545549280355643122501181852136332529515756074502902204877664395456278205770206019663082511098956468724829475220996427130295010430751942126263620206886404244432409164651524408198286831329682605959881993845189585640874220032515465014097345759490750307207/54806274927935495596891312029173862473687610099325998583092091915101265393157631244775387832621032230434007486602816972765210205596482942091518459849775458089575743699797096697542306049628939919466378189656065461708686421130956996965004776108499273871055615413566043083634090083096967567965373203683231171594922347812966905149873265654962896456451681332216735396249206784000',
|
||||
'228506238533410805238253122961471637501498406104430046727793420497895852412513560203475440156216063574654372726194215013407325207763369075250819386897086702757125418327084312933061738392406321003231853349865003322697360659712771167066453430309997639548320673279964210730701373707765051246595806673865646607841876463608039111252607452843372970797479959213447237540830563469297144446069689149549515457318899981763489792/44418759813188470246596822974281932219088697005123285598909329328419879212164564561558656547678211265529803086702843380128457744445765859393426675396393053094588240457569380155526179040891009717323059641035189017918495485630657360525410506453554255734005459614858972904374739673834145083512160883402151636664208253116362089211658645312778404886460203600762546810897889629330022309705583438699250109493732452392578125',
|
||||
'2396110297187570506257836426251930521245117960980667897194680897478996322590925380310917400763581787981992131769075961259393685331523600287377841990678117113566991100720996059187144934569703045966210306282348527729533806882408323961336166190162075154579344185244957673138372347997134388801085788353482276257401005734929059700346687783400772331280644647089520646562669857652648/465370154262863030800729180763091967573522779260407615567563274096037297593764955820626213827962697601334935773802201919483657310282236100250543992334865461800976838791035335861420892967699843608855185710753433852279737724446294443337363478679601049432404435672469098850676302728116369979460222148206944323730586138087862719386327396334468302608499235112520318322763857676375',
|
||||
'1188272888075247520453568790651819187486389546755745961952033150576243575862120569399808467067800830890874391487822723879717525264875722720416173276963170672622214121308038530968291758445510982186014289745026439477197148445087614813262170975056866881909056450023324743409114115141145340480846088869807540798073417035168139476153897066325127540609756310068558610112731607522096096821938163134080426356894899245575410494080/230586762395914863625709048789913840511134004337945700368117795195769576856434588144361459740833007125516905017415184787078710001969815416281381427387949263792200577791644278234661952852149216540491852757174842146376788280677956350901082838474705227642420261641783722891948710833898248566893322144435457967452449965772543077421407816225704952270697016152452032506898311260979594550143344971416786595888053149375287120733',
|
||||
'1072184299258468249004114536023934869518152618369895613271618698908405514630154819453979477077738526673716264830106745031385951736108376866253955319711026042389282009348594845497252059906901748860111986811345742998968284077119165082435598381810306691078037018432766116865792677955520465319184311529667642995217214507658693259694586107847099911341219913576038605872218680562047/207882129535607118520818565811162643811811563516006836924341123976848022970024793004700710992938987476728640943834012473687892481059618823520721737479364352887178017866425027953255856440099446112508663603319745066750522936408781591578321031173620445865113295407603090322283539974563690157418931060565698315406021569491751612761888641141446406531645379264720684248398299136000',
|
||||
'158094023904374464057702010126993231151433474361820266584020400101533651587072432728050776506672527718975042616651177034635188178967462982926261516748842073587981953038445977514446838733557926696939591069712397947055261777416704028506441173540338327591514650398349069348381309688677335132100441905603664282735310299686268721634369502296867409514777359935022301425165750780988231639828675424239931533849896744198012747670272/30626384366513923671063955469537776291182594080083494737341604579575446319190916924682809546024369330706012653799783122328775875399363759308193432327358837772825609356553955350401725689682838701996446404559005896479618311357573203081153855961484398360670837599853294627153740224543175497655414585918231215639906924313779933211774291565425360415684556655021031131349317390382320286092667852173815259422737956767922154798375',
|
||||
'12216278924286401897470540169822542417611356797676160128176706721343511598951866107858729405615631246003458062949723290192242594609994613705183460814797527214549142043573863947658832694800184691222426521703728394203722420336773310908687071670107794543022567943478843065246011544888390757810534211554666736239506503613750756246364621509359804902243487113673014588325806249988332/2364584127734680580481708368927921125934330000162935118371174269642831409084634375654239735054363928748162229294829526339927907778510530444646754603322373712788147193691037688552857183116332292048731726305451516954679277320777955342295564000586923227775208580772868590742136150431152495718132142742153643081187691849828217592954519485869135930755646768375299870967864990234375',
|
||||
'578939195017447864385837341975558679292903716580114421030538728778491606282153588930983966100514147976321305616196463174070345884292672842497715966933722088536619333912687108737657068701352918582656105418491672377271304970407730786932913490869797471081081431072836814581099271832696990064828717287258860166842791984158634684078346453202928137987930328826212011774670396356151265781053547412633310327737900786639842674288716416/111966291649651600701875186827360807558960817589719096407887999108024557254221008849510152469119211567152616745769256123968832250104006560061907741567034087109512483357851919402017950812795426393415409198268246337542639470834168040295134130657073201020340432236723824670803498949071870597039309004562024033656488150406105175401372709011082725359382691270659245312133919518760328422827067578306005644825927050994860723405473375',
|
||||
'8041514821545833865350873212853873042112343327573364523948780030849543753072688526717886590013028227714585965095216381829914996539132359247488639692828639170075726301411777307168737667077730792892326979446426349813642680537699905651125068091362135055902152994216233622733424128982011248923051410141033146604688083721413402395290842738021365861670415833356403364696759583009635/1553937833499559104910551811592138833189892464446761247731330672284191313590708439680460540329836344767557599746234213006493842842879398684745864919973051635127080738612248150108793397855113122368507483429209127035263504194847798809612278864438287815470711036548695032421148125849669175729331459149833945183228413961985381338162155596008680906676498435954178226973272371625984',
|
||||
'12059659399309169167301353842123191409306995788633965680414620302285606896146667751472818541537183787935352700725982053536032930866708413010971856856768384974849695774437178790890015242323873769697407411558706626105140596160049735605389301414460825674631122462760014451298494875244754354054447697502521388381253293175367558014453865905104467382184997528879171136492277222127399012368677604910497789360212571238146627048080568400384/2328494054002703067219004484782931471919751585969487160602269605916676223315055450845942763690581877771159615428854802721943114463834992106300185328876910271612375509166975628540839858771078495267566008071532876816826751069408153323840515037497664871689085370699991860738368371510578076683395101062439493831773194498579620513073693022191702398601471237166387045790480861932005012807234071429628725981935147069738988682858417182625',
|
||||
'683009426705008850682549700382901603742691705123356866338951397347368059105140826655486518416578697931244347554322654428162554903861783227680935341690740579867651545205562690172425016836559678937794312287639193745517811234257062356826622207475918293015140472942579713388671782202262396642735640315948612572908444501108579457520714632371056288874351015666995549139003946314085696/131769214246522869780429817236150406548549044658474416909806179734873399109656411633488099249198882165279745627038984487365589625562901914030850178330133080409420340227179447548044572594184601425691821617928130423410997853384425373999967759499571890326740433081063424903098252502791477854302204200941376983295622430177445507316706609802292121282957519571162158620971952084486375',
|
||||
'1418116259749091420309271913909337143756589314597557128553117870573928055109322864794169592355058370891251425125187770724828108988587888087023982451205475980371440423992838481547236638456717520835668859093413956339261802305597694403381021863258753441889536361061407579332570493634254542013337999844407181346815549594836374497512447969751514466481466717590255369810506813543577735080228718660747940956248427356785784199073569408/273368882980516556922532339653303822104510203916024760371844491705599093435133738774975538364241630079468712981056336655766117378160822393876348755370711942273876284944422610974149712212944211513462921649872232656449940987552227525504529710373144228818606127541937831103642415534318303187393593236209298242574677293352798419428712873685798636474667204083938875672934235816396371477146089168375198141802684403955936431884765625',
|
||||
'546411077581845999248238069684194831621691193366466972898628141511422317954477211865167402284211971296286346492698354214043840080181702652132835681239302798339667835439189275457116051279366194551632712819028177402221815140045210690908631767786711675029194359998826252332434573121740830827417293557234891406021102083555472155479148332697839972702631641910213821827820374586459319/105246945889799140338072754365901705544530661781295362563214285953881890233987516230568663143342952799036341701952267011236663118111226623815975847494372076420199418712941018288738590264475520229651370015377608638075866459684692488079223686296541020897904588347083045723167762682959422858996673894180805702299907478685374065593161226849447131100462022089783265346597553176576000',
|
||||
'2398330640958841474772606439916070050977544535580605737383995160447105736276950196885906408317628083110923322157113892928963237845914017845444295040924101784423382681801754191301860383927129006953354739240926643562987838836997453985855576402628166875869041032631651591871962852884189548538272285387092843044669499688035134181859376665409767886188304314888753894905317929877238322615838524354191263502347881033855441181420399360/461588070868590122892265681879734295007029130965626060552783760068897000195207878227714842617470320231527222074701444349530952699708435668339712860464533455345665068841333232359698449088497137068713309811942968433868609329301082001752617420002377892756821532220676085014874112083615054550278903960627185675459015343606391094523511117705747842645927349130302549554534056269331809016770715819934970200483161548527932617036185253',
|
||||
'6041015879424725383006424536130409209607854044642113747266098198777011981328765528361630516108680392500990580908509403483891763219659726090675140672989657743882183951954294745396417829943469201306594018454995862321821016087416840247422350906412007336103086620396467456181771583200365740253389107968122850063607085957109965406634738740996318415514360956028575560979203447735121436/1161752799109428422288020947061281540989708937450568100764830251908850596717606701047413407636907934320789870175907792017513896999208892282137299070761467096211814586909598705615312819596495636017728313513520193786266452836805291464826226833593878504804389728477191170027729963773716267868284479768397603444919008915279522376004326398403851684761808785381609370767169521034383625',
|
||||
'13240077436443988749179508462267267187169441948722358165090554769250505713747934643200804819418670147225695324432684266924694524337920816452346599774452681831320005286326986675907899608537972384924882996757503264622991355949039882526389342174307168805166215838138277557052303430492669193939212362638263582899713198716541723383138016564027766560215944409353427176135895982596327685665844815618402881202645610620284792793420780517248/2544223084468158291883698813309541801455311468982232546872485444308211415529998472787377800559884210837213042932180479090277285630234238711851480232520137856848809986631784843528381778520727465146661792797924458540957133423665746229799675650290296217658444899605236550972043549278128087645211909479009099766619355677984218929672461506691980442071860591767266913041147587815452007726513853820116629482732060593116624596368806566625',
|
||||
'1953999166296955830935495158735359200362904181792947794529339487489730042568305997099959302322956898299616194932283060554261566410988618045107398092345476532371402134206635235570281738377188438407703089325315446371127042537576093536896282955524842632708645655481028161471313608974238110718242273935956977555610147714316158486553633871312187084618154014921190595222799283957140353/375191165084882521037046014569185165885459082629136124177286500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
||||
];
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = {
|
||||
PRIMES,
|
||||
PRIMES_SET,
|
||||
LONG_PI,
|
||||
LONG_E,
|
||||
BIG_LOG_CACHE,
|
||||
};
|
||||
}
|
||||
+17838
File diff suppressed because it is too large
Load Diff
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "commonjs"
|
||||
}
|
||||
@@ -99,8 +99,9 @@
|
||||
function checkApiKey() {
|
||||
const apiKey = config().apiKey;
|
||||
|
||||
// No API key configured — server doesn't require auth, no need to validate.
|
||||
// This mirrors the early return in validateApiKey() to avoid redundant /props requests.
|
||||
// Without a stored key there is nothing to re-validate here; the keyless
|
||||
// 401 case is handled by validateApiKey() at navigation time, and the
|
||||
// reload below must never fire in a keyless loop.
|
||||
if (!apiKey || apiKey.trim() === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
declare module 'virtual:nerdamer' {
|
||||
const code: string;
|
||||
export default code;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
import { validateApiKey } from '$lib/utils/api-key-validation';
|
||||
import { settingsStore } from '$lib/stores/settings.svelte';
|
||||
import { CONFIG_LOCALSTORAGE_KEY } from '$lib/constants/storage';
|
||||
|
||||
function fakeFetch(status: number, capture: { auth?: string | null } = {}) {
|
||||
return (async (_url: RequestInfo | URL, init?: RequestInit) => {
|
||||
capture.auth = (init?.headers as Record<string, string>)?.['Authorization'] ?? null;
|
||||
return new Response(status === 200 ? '{}' : 'Unauthorized', { status });
|
||||
}) as typeof globalThis.fetch;
|
||||
}
|
||||
|
||||
const is401 = (err: unknown) =>
|
||||
typeof err === 'object' && err !== null && 'status' in err && err.status === 401;
|
||||
|
||||
describe('api key validation surfaces the splash', () => {
|
||||
beforeEach(() => {
|
||||
localStorage.removeItem(CONFIG_LOCALSTORAGE_KEY);
|
||||
settingsStore.initialize();
|
||||
});
|
||||
|
||||
it('keyed server, no stored key: throws 401 so the splash shows (onboarding)', async () => {
|
||||
await expect(validateApiKey(fakeFetch(401))).rejects.toSatisfy(is401);
|
||||
});
|
||||
|
||||
it('keyed server, wrong stored key: throws 401 so the splash shows', async () => {
|
||||
settingsStore.updateConfig('apiKey', 'wrong-key');
|
||||
await expect(validateApiKey(fakeFetch(401))).rejects.toSatisfy(is401);
|
||||
});
|
||||
|
||||
it('open server, no stored key: passes silently', async () => {
|
||||
await expect(validateApiKey(fakeFetch(200))).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('valid stored key: passes and sends the bearer header', async () => {
|
||||
settingsStore.updateConfig('apiKey', 'sk-good');
|
||||
const capture: { auth?: string | null } = {};
|
||||
await expect(validateApiKey(fakeFetch(200, capture))).resolves.toBeUndefined();
|
||||
expect(capture.auth).toBe('Bearer sk-good');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,59 @@
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
import { SandboxService } from '$lib/services/sandbox.service';
|
||||
import { SANDBOX_TOOL_NAME } from '$lib/constants';
|
||||
|
||||
const run = (code: string, timeoutMs?: number) =>
|
||||
SandboxService.executeTool(SANDBOX_TOOL_NAME, {
|
||||
code,
|
||||
...(timeoutMs !== undefined ? { timeout_ms: timeoutMs } : {})
|
||||
});
|
||||
|
||||
describe('sandbox service', () => {
|
||||
beforeEach(async () => {
|
||||
const { settingsStore } = await import('$lib/stores/settings.svelte');
|
||||
settingsStore.config = {
|
||||
...settingsStore.config,
|
||||
symbolicMathEnabled: true
|
||||
};
|
||||
});
|
||||
|
||||
it('executes plain JavaScript', async () => {
|
||||
const reply = await run('return 1 + 1;');
|
||||
expect(reply.isError).toBe(false);
|
||||
expect(reply.content).toContain('=> 2');
|
||||
});
|
||||
|
||||
it('exposes nerdamer for symbolic computation', async () => {
|
||||
const reply = await run("return nerdamer.diff('sin(x)/x', 'x').toString();");
|
||||
expect(reply.isError).toBe(false);
|
||||
expect(reply.content).toContain('cos(x)');
|
||||
});
|
||||
|
||||
it('computes exact rational arithmetic', async () => {
|
||||
const reply = await run("return nerdamer('1/3 + 1/6').toString();");
|
||||
expect(reply.isError).toBe(false);
|
||||
expect(reply.content).toContain('=> 1/2');
|
||||
});
|
||||
|
||||
it('proves a polynomial identity symbolically', async () => {
|
||||
const reply = await run(
|
||||
"return nerdamer('expand((1+x*y)^3 - (1 + 3*x*y + 3*x^2*y^2 + x^3*y^3))').toString();"
|
||||
);
|
||||
expect(reply.isError).toBe(false);
|
||||
expect(reply.content).toContain('=> 0');
|
||||
});
|
||||
|
||||
it('blocks network egress in the worker via CSP', async () => {
|
||||
const reply = await run(
|
||||
"try { await fetch('https://example.com/'); return 'leaked'; } catch { return 'blocked'; }"
|
||||
);
|
||||
expect(reply.isError).toBe(false);
|
||||
expect(reply.content).toContain('=> blocked');
|
||||
});
|
||||
|
||||
it('enforces the timeout on runaway code', async () => {
|
||||
const reply = await run('while (true) {}', 500);
|
||||
expect(reply.isError).toBe(true);
|
||||
expect(reply.content).toContain('timed out');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,107 @@
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
import { settingsStore, config } from '$lib/stores/settings.svelte';
|
||||
import { serverStore } from '$lib/stores/server.svelte';
|
||||
import { ParameterSyncService } from '$lib/services/parameter-sync.service';
|
||||
import { SETTING_CONFIG_DEFAULT } from '$lib/constants/settings-registry';
|
||||
import { CONFIG_LOCALSTORAGE_KEY } from '$lib/constants/storage';
|
||||
import type { SettingsConfigType } from '$lib/types';
|
||||
|
||||
type Primitive = string | number | boolean;
|
||||
|
||||
const KEYS = Object.keys(SETTING_CONFIG_DEFAULT).filter(
|
||||
(k) => ['string', 'number', 'boolean'].includes(typeof SETTING_CONFIG_DEFAULT[k]) && k !== 'theme'
|
||||
);
|
||||
|
||||
function divergent(key: string, base: Primitive): Primitive {
|
||||
if (typeof base === 'boolean') return !base;
|
||||
if (typeof base === 'number') return base + 7;
|
||||
return `user-${key}`;
|
||||
}
|
||||
|
||||
function baselineFor(key: string, base: Primitive): Primitive {
|
||||
if (typeof base === 'boolean') return !base;
|
||||
if (typeof base === 'number') return base + 42;
|
||||
return `admin-${key}`;
|
||||
}
|
||||
|
||||
function mockProps(uiSettings: Record<string, Primitive>) {
|
||||
Object.defineProperty(serverStore, 'props', {
|
||||
configurable: true,
|
||||
get: () =>
|
||||
({
|
||||
default_generation_settings: { params: { temperature: 0.8 } },
|
||||
ui_settings: uiSettings
|
||||
}) as unknown as typeof serverStore.props
|
||||
});
|
||||
}
|
||||
|
||||
const setUser = (key: string, value: Primitive) =>
|
||||
settingsStore.updateConfig(key as keyof SettingsConfigType, value as never);
|
||||
|
||||
const current = (key: string) => (config() as Record<string, unknown>)[key];
|
||||
|
||||
describe('registry-wide invariants', () => {
|
||||
beforeEach(() => {
|
||||
localStorage.removeItem(CONFIG_LOCALSTORAGE_KEY);
|
||||
});
|
||||
|
||||
it('I1: no load ever modifies a stored user value, for any key of any type', () => {
|
||||
settingsStore.initialize();
|
||||
const userValues: Record<string, Primitive> = {};
|
||||
for (const key of KEYS) {
|
||||
userValues[key] = divergent(key, SETTING_CONFIG_DEFAULT[key] as Primitive);
|
||||
setUser(key, userValues[key]);
|
||||
}
|
||||
|
||||
// simulated F5 + adverse admin baseline on every key, synced twice
|
||||
settingsStore.initialize();
|
||||
const adverse: Record<string, Primitive> = {};
|
||||
for (const key of KEYS)
|
||||
adverse[key] = baselineFor(key, SETTING_CONFIG_DEFAULT[key] as Primitive);
|
||||
mockProps(adverse);
|
||||
settingsStore.syncWithServerDefaults();
|
||||
settingsStore.syncWithServerDefaults();
|
||||
|
||||
for (const key of KEYS) {
|
||||
expect(current(key), key).toBe(userValues[key]);
|
||||
}
|
||||
});
|
||||
|
||||
it('first visit: the baseline applies for every key, false and 0 included', () => {
|
||||
settingsStore.initialize();
|
||||
const baseline: Record<string, Primitive> = {};
|
||||
for (const key of KEYS)
|
||||
baseline[key] = baselineFor(key, SETTING_CONFIG_DEFAULT[key] as Primitive);
|
||||
mockProps(baseline);
|
||||
|
||||
settingsStore.syncWithServerDefaults();
|
||||
|
||||
for (const key of KEYS) {
|
||||
if (ParameterSyncService.canSyncParameter(key)) continue;
|
||||
expect(current(key), key).toBe(baseline[key]);
|
||||
}
|
||||
});
|
||||
|
||||
it('I3: Reset returns every key to baseline when defined, factory default otherwise', () => {
|
||||
settingsStore.initialize();
|
||||
for (const key of KEYS) setUser(key, divergent(key, SETTING_CONFIG_DEFAULT[key] as Primitive));
|
||||
|
||||
const baseline: Record<string, Primitive> = {};
|
||||
KEYS.filter((_, i) => i % 2 === 0).forEach((key) => {
|
||||
baseline[key] = baselineFor(key, SETTING_CONFIG_DEFAULT[key] as Primitive);
|
||||
});
|
||||
mockProps(baseline);
|
||||
|
||||
settingsStore.forceSyncWithServerDefaults();
|
||||
|
||||
for (const key of KEYS) {
|
||||
if (key in baseline) {
|
||||
expect(current(key), key).toBe(baseline[key]);
|
||||
} else if (ParameterSyncService.canSyncParameter(key)) {
|
||||
expect(current(key), key).toBe('');
|
||||
} else {
|
||||
expect(current(key), key).toBe(SETTING_CONFIG_DEFAULT[key]);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,73 @@
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
import { settingsStore, config } from '$lib/stores/settings.svelte';
|
||||
import { serverStore } from '$lib/stores/server.svelte';
|
||||
import { CONFIG_LOCALSTORAGE_KEY } from '$lib/constants/storage';
|
||||
|
||||
function mockProps(uiSettings: Record<string, string | number | boolean>) {
|
||||
Object.defineProperty(serverStore, 'props', {
|
||||
configurable: true,
|
||||
get: () =>
|
||||
({
|
||||
default_generation_settings: { params: { temperature: 0.8 } },
|
||||
ui_settings: uiSettings
|
||||
}) as unknown as typeof serverStore.props
|
||||
});
|
||||
}
|
||||
|
||||
describe('server ui_settings application semantics', () => {
|
||||
beforeEach(() => {
|
||||
localStorage.removeItem(CONFIG_LOCALSTORAGE_KEY);
|
||||
});
|
||||
|
||||
it('applies the admin defaults once for a new user', () => {
|
||||
settingsStore.initialize();
|
||||
mockProps({ theme: 'dark', apiKey: '' });
|
||||
|
||||
settingsStore.syncWithServerDefaults();
|
||||
|
||||
expect(config().theme).toBe('dark');
|
||||
});
|
||||
|
||||
it('never reapplies on later loads: the user config diverges freely', () => {
|
||||
settingsStore.initialize();
|
||||
settingsStore.updateConfig('theme', 'light');
|
||||
settingsStore.updateConfig('apiKey', 'sk-user-key');
|
||||
|
||||
// simulated F5: config now exists in localStorage
|
||||
settingsStore.initialize();
|
||||
mockProps({ theme: 'dark', apiKey: '' });
|
||||
|
||||
settingsStore.syncWithServerDefaults();
|
||||
settingsStore.syncWithServerDefaults();
|
||||
|
||||
expect(config().theme).toBe('light');
|
||||
expect(config().apiKey).toBe('sk-user-key');
|
||||
const stored = JSON.parse(localStorage.getItem(CONFIG_LOCALSTORAGE_KEY) ?? '{}');
|
||||
expect(stored.apiKey).toBe('sk-user-key');
|
||||
});
|
||||
|
||||
it('Reset to Default reapplies the full baseline, api key included', () => {
|
||||
settingsStore.initialize();
|
||||
settingsStore.updateConfig('theme', 'light');
|
||||
settingsStore.updateConfig('apiKey', 'sk-user-key');
|
||||
mockProps({ theme: 'dark', apiKey: '' });
|
||||
|
||||
settingsStore.forceSyncWithServerDefaults();
|
||||
|
||||
expect(config().theme).toBe('dark');
|
||||
expect(config().apiKey).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('syncable scope (spec section 4)', () => {
|
||||
it('sampling params keep their live server twin, ui settings carry none', async () => {
|
||||
const { ParameterSyncService } = await import('$lib/services/parameter-sync.service');
|
||||
|
||||
expect(ParameterSyncService.canSyncParameter('temperature')).toBe(true);
|
||||
expect(ParameterSyncService.canSyncParameter('samplers')).toBe(true);
|
||||
expect(ParameterSyncService.canSyncParameter('theme')).toBe(false);
|
||||
expect(ParameterSyncService.canSyncParameter('systemMessage')).toBe(false);
|
||||
expect(ParameterSyncService.canSyncParameter('apiKey')).toBe(false);
|
||||
expect(ParameterSyncService.canSyncParameter('customCss')).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -25,7 +25,7 @@
|
||||
".storybook/**/*.ts",
|
||||
".storybook/**/*.svelte"
|
||||
],
|
||||
"exclude": ["src/lib/services/sandbox-worker.js"]
|
||||
"exclude": ["src/lib/services/sandbox-worker.js", "src/lib/vendors/**"]
|
||||
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
|
||||
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
|
||||
//
|
||||
|
||||
@@ -9,6 +9,7 @@ import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';
|
||||
import { splashScreenPlugin } from './scripts/vite-plugin-splash-screen';
|
||||
import { buildInfoPlugin } from './scripts/vite-plugin-build-info';
|
||||
import { relativizeBasePlugin } from './scripts/vite-plugin-relativize-base';
|
||||
import { nerdamerPlugin } from './scripts/vite-plugin-nerdamer';
|
||||
import { playwright } from '@vitest/browser-playwright';
|
||||
import { SVELTEKIT_PWA_OPTIONS } from './src/lib/constants/pwa';
|
||||
|
||||
@@ -46,6 +47,7 @@ export default defineConfig({
|
||||
SvelteKitPWA(SVELTEKIT_PWA_OPTIONS),
|
||||
splashScreenPlugin(),
|
||||
buildInfoPlugin(),
|
||||
nerdamerPlugin(),
|
||||
relativizeBasePlugin()
|
||||
],
|
||||
|
||||
|
||||
Vendored
+704
-125
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user