use super::executor::{ExecConfig, GraphExecutor};
use crate::backend::{Backend, BackendError};
use crate::core::tensor::Tensor;
use crate::format::LoadedModel;
use crate::generate::ModelRunner;
use crate::ir::family_graph;
use crate::arch::decoder::LlamaConfig;
use std::collections::HashMap;
pub struct GraphRunner {
executor: GraphExecutor,
}
impl GraphRunner {
pub fn from_loaded(
lm: &LoadedModel,
llama_cfg: &LlamaConfig,
backend: Box<dyn Backend>,
) -> Option<Result<Self, BackendError>> {
let graph = if let Some(g) = lm.graph() {
g
} else {
family_graph(llama_cfg)?
};
let weights = build_weight_map(lm);
let exec = GraphExecutor::prepare(graph, weights, backend, ExecConfig::default());
Some(exec.map(|e| Self { executor: e }))
}
}
impl ModelRunner for GraphRunner {
fn step(&mut self, token: u32, _backend: &dyn Backend) -> Result<Vec<f32>, BackendError> {
let mut inputs = HashMap::new();
inputs.insert(
"input_ids".into(),
Tensor::from_f32(vec![1], vec![token as f32]),
);
let out = self.executor.run(inputs)?;
out.get("logits")
.map(|t| t.as_f32().to_vec())
.ok_or_else(|| BackendError::Internal("graph executor: no logits output".into()))
}
fn reset(&mut self) {
self.executor.reset();
}
}
fn build_weight_map(lm: &LoadedModel) -> HashMap<String, Tensor> {
use crate::backend::cpu::quant::try_dequantize_to_f32;
let mut weights = HashMap::new();
for meta in &lm.tensors {
let Some(bytes) = lm.tensor_bytes(&meta.name) else {
log::warn!("weight bytes missing for {}", meta.name);
continue;
};
match try_dequantize_to_f32(bytes, meta.dtype) {
Ok(f32s) => {
weights.insert(meta.name.clone(), Tensor::from_f32(meta.shape.clone(), f32s));
}
Err(e) => {
log::warn!("dequant failed for {}: {e}", meta.name);
}
}
}
if !weights.contains_key("lm_head.weight") {
if let Some(embed) = weights.get("model.embed_tokens.weight").cloned() {
weights.insert("lm_head.weight".to_string(), embed);
}
}
weights
}