Agent-built designs: stop redoing whole-design work on every command (#1148)
A design built over MCP is many small commands, and each one did work over the whole design, so building or showing a 1,300-body design took minutes: - moved components were transformed and meshed again after every change; world_state now reuses each placed body whose definition and placement are unchanged - next_d_name searched the design once per candidate number - Document::eval evaluated every parameter for every expression; now only when the expression names one - param_rows' used_by was quadratic; the desktop no longer gathers completions on every change while no text box has focus solidcraft-cli bench-agent builds a generated ~1,300-body bicycle command by command and times it (241 s -> 16 s here). Eval output is unchanged apart from timings. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
37547f8eba
commit
6de8f6db95
11 files changed
+439
-21
No files matched your search
@@ -467,10 +467,14 @@ impl Document {
|
||||
|
||||
/// The next free `dN` name.
|
||||
pub fn next_d_name(&self) -> String {
|
||||
// Every taken name at once: asking `name_taken` for d1, d2, … searched the whole
|
||||
// design once per number, quadratic in the number of named inputs.
|
||||
let taken: std::collections::HashSet<&str> =
|
||||
self.params.iter().map(|p| p.name.as_str()).chain(self.features.iter().flat_map(|f| f.param_names.iter().map(String::as_str))).collect();
|
||||
let mut n = 1;
|
||||
loop {
|
||||
let c = format!("d{n}");
|
||||
if !self.name_taken(&c) {
|
||||
if !taken.contains(c.as_str()) {
|
||||
return c;
|
||||
}
|
||||
n += 1;
|
||||
@@ -657,7 +661,15 @@ impl Document {
|
||||
let (vals, errs) = self.param_values();
|
||||
let all = self.all_param_exprs();
|
||||
let refs: BTreeMap<String, Vec<String>> = all.iter().map(|(n, e, _)| (n.clone(), expr::references(e))).collect();
|
||||
let used_by = |n: &str| -> Vec<String> { refs.iter().filter(|(_, r)| r.iter().any(|x| x == n)).map(|(k, _)| k.clone()).collect() };
|
||||
// Who uses each name, in name order: one pass over the references (a design an agent
|
||||
// built names every feature input, so a search per row was quadratic).
|
||||
let mut users: BTreeMap<&str, Vec<String>> = BTreeMap::new();
|
||||
for (k, r) in &refs {
|
||||
for x in r {
|
||||
users.entry(x.as_str()).or_default().push(k.clone());
|
||||
}
|
||||
}
|
||||
let used_by = |n: &str| -> Vec<String> { users.get(n).cloned().unwrap_or_default() };
|
||||
let value = |n: &str, k: Kind| vals.get(n).and_then(|v| v.to_kind(k).ok()).map(|x| if k == Kind::Angle { x.to_degrees() } else { x });
|
||||
let mut rows = Vec::new();
|
||||
for p in &self.params {
|
||||
|
||||
Reference in new issue
Block a user