#include "models.h"
#include "llama-ext.h"
#include <cmath>
void llama_model_vllm_decision2::load_arch_hparams(llama_model_loader & ml) {
llama_model_qwen3::load_arch_hparams(ml);
ml.get_key(LLM_KV_ATTENTION_LAYERNORM_EPS, hparams.f_norm_eps);
hparams.n_embd_out_impl = 1;
}
void llama_model_vllm_decision2::load_arch_tensors(llama_model_loader & ml) {
llama_model_qwen3::load_arch_tensors(ml);
LLAMA_LOAD_LOCALS;
const auto * w_key = ml.get_weight(tn(LLM_TENSOR_DECISION_KEY, "weight").str().c_str());
if (w_key == nullptr) {
throw std::runtime_error("the decision head is missing");
}
const int64_t n_embd_h = w_key->tensor->ne[1];
option_norm = create_tensor(tn(LLM_TENSOR_DECISION_OPTION_NORM, "weight"), {n_embd}, 0);
option_norm_b = create_tensor(tn(LLM_TENSOR_DECISION_OPTION_NORM, "bias"), {n_embd}, 0);
query_norm = create_tensor(tn(LLM_TENSOR_DECISION_QUERY_NORM, "weight"), {n_embd}, 0);
query_norm_b = create_tensor(tn(LLM_TENSOR_DECISION_QUERY_NORM, "bias"), {n_embd}, 0);
key = create_tensor(tn(LLM_TENSOR_DECISION_KEY, "weight"), {n_embd, n_embd_h}, 0);
query = create_tensor(tn(LLM_TENSOR_DECISION_QUERY, "weight"), {n_embd, n_embd_h}, 0);
option_mlp = create_tensor(tn(LLM_TENSOR_DECISION_OPTION_MLP, "weight"), {n_embd, n_embd_h}, 0);
option_mlp_b = create_tensor(tn(LLM_TENSOR_DECISION_OPTION_MLP, "bias"), {n_embd_h}, 0);
query_mlp = create_tensor(tn(LLM_TENSOR_DECISION_QUERY_MLP, "weight"), {n_embd, n_embd_h}, 0);
scorer_out = create_tensor(tn(LLM_TENSOR_DECISION_SCORER_OUT, "weight"), {n_embd_h, 1}, 0);
}
std::unique_ptr<llm_graph_context> llama_model_vllm_decision2::build_arch_graph(const llm_graph_params & params) const {
return std::make_unique<graph>(*this, params);
}
struct decision2_rows {
std::vector<int32_t> options;
int32_t query = 0;
bool valid = false;
};
static decision2_rows decision2_get_rows(const llama_ubatch & ubatch) {
decision2_rows res;
const int32_t n_tokens = ubatch.n_tokens;
bool ok = ubatch.decision_order != nullptr && ubatch.n_seqs_unq == 1;
std::vector<int32_t> rows(n_tokens, -1);
for (int32_t i = 0, n = 0; i < n_tokens; i++) {
if (ubatch.output[i]) {
rows[i] = n++;
}
}
int32_t n_questions = 0;
for (int32_t i = 0; ok && i < n_tokens;) {
const int32_t order = ubatch.decision_order[i];
int32_t end = i + 1;
while (end < n_tokens && ubatch.decision_order[end] == order) {
end++;
}
switch (order) {
case LLAMA_DECISION_ORDER_NONE:
break;
case LLAMA_DECISION_ORDER_QUESTION_NOUL:
case LLAMA_DECISION_ORDER_QUESTION_CHOICE:
case LLAMA_DECISION_ORDER_QUESTION_SCORE:
res.query = rows[end - 1];
n_questions++;
break;
case LLAMA_DECISION_ORDER_OPTION:
res.options.push_back(rows[end - 1]);
break;
default:
ok = false;
}
ok = ok && (order == LLAMA_DECISION_ORDER_NONE || rows[end - 1] >= 0);
i = end;
}
res.valid = ok && n_questions == 1 && !res.options.empty();
if (!res.valid) {
res.options.assign(1, 0);
res.query = 0;
}
return res;
}
class llama_model_vllm_decision2::input_decision : public llm_graph_input_i {
public:
input_decision(const llama_ubatch & ubatch) : n_options(decision2_get_rows(ubatch).options.size()) {}
void set_input(const llama_ubatch * ubatch) override {
const auto rows = decision2_get_rows(*ubatch);
GGML_ASSERT(rows.options.size() == n_options);
const float status_data = rows.valid || ubatch->decision_order == nullptr ? 0.0f : NAN;
ggml_backend_tensor_set(options, rows.options.data(), 0, ggml_nbytes(options));
ggml_backend_tensor_set(query, &rows.query, 0, ggml_nbytes(query));
ggml_backend_tensor_set(status, &status_data, 0, ggml_nbytes(status));
}
bool can_reuse(const llm_graph_params & params) override {
return decision2_get_rows(params.ubatch).options.size() == n_options;
}
ggml_tensor * options = nullptr; ggml_tensor * query = nullptr; ggml_tensor * status = nullptr;
const size_t n_options;
};
llama_model_vllm_decision2::graph::graph(const llama_model & model_base, const llm_graph_params & params) :
llama_model_qwen3::graph(params, no_build()), model(static_cast<const llama_model_vllm_decision2 &>(model_base)) {
auto inp_decision_ptr = std::make_unique<input_decision>(ubatch);
auto * inp_decision = inp_decision_ptr.get();
res->add_input(std::move(inp_decision_ptr));
ggml_tensor * cur = build_backbone(model);
cur = build_head(cur, inp_decision);
cur = ggml_pad(ctx0, cur, 0, n_outputs - cur->ne[1], 0, 0);
cur = ggml_add(ctx0, cur, inp_decision->status);
cb(cur, "result_decision", -1);
res->t_embd = cur;
ggml_build_forward_expand(gf, cur);
}
ggml_tensor * llama_model_vllm_decision2::graph::build_head(ggml_tensor * hidden, input_decision * inp) {
inp->options = ggml_new_tensor_1d(ctx0, GGML_TYPE_I32, inp->n_options);
inp->query = ggml_new_tensor_1d(ctx0, GGML_TYPE_I32, 1);
inp->status = ggml_new_tensor_1d(ctx0, GGML_TYPE_F32, 1);
for (ggml_tensor * t : { inp->options, inp->query, inp->status }) {
ggml_set_input(t);
}
ggml_tensor * options = ggml_get_rows(ctx0, hidden, inp->options); ggml_tensor * query = ggml_get_rows(ctx0, hidden, inp->query);
options = build_norm(options, model.option_norm, model.option_norm_b, LLM_NORM, -1);
query = build_norm(query, model.query_norm, model.query_norm_b, LLM_NORM, -1);
ggml_tensor * k = ggml_mul_mat(ctx0, model.key, options); ggml_tensor * q = ggml_mul_mat(ctx0, model.query, query); ggml_tensor * bilinear = ggml_sum_rows(ctx0, ggml_mul(ctx0, k, q));
bilinear = ggml_scale(ctx0, bilinear, 1.0f / sqrtf((float) k->ne[0]));
ggml_tensor * mlp = ggml_add(ctx0, ggml_mul_mat(ctx0, model.option_mlp, options), model.option_mlp_b);
mlp = ggml_add(ctx0, mlp, ggml_mul_mat(ctx0, model.query_mlp, query));
mlp = ggml_gelu_erf(ctx0, mlp);
ggml_tensor * nonlinear = ggml_mul_mat(ctx0, model.scorer_out, mlp);
ggml_tensor * scores = ggml_add(ctx0, bilinear, nonlinear);
cb(scores, "decision_scores", -1);
return scores;
}