mirror of
https://github.com/alphaXiv/OpenResearch.git
synced 2026-11-12 10:05:24 +08:00
changes
This commit is contained in:
@@ -129,8 +129,12 @@ run command:
|
||||
```
|
||||
6. **Keep the budget saturated.** With a cap of N concurrent runs, never leave a
|
||||
slot idle while there's useful parallel work. Block on the next finish with
|
||||
`orx exp wait --project <projectId>` (returns on the first change to any run),
|
||||
then immediately launch the next queued child to refill the freed slot.
|
||||
`orx exp wait --project <projectId>` — it returns the moment any run
|
||||
*completes* (reaches a terminal state), which is exactly the "a slot freed"
|
||||
signal. Then immediately launch the next queued child to refill the freed
|
||||
slot. **Don't hand-roll a poll loop over `orx runs`** — `exp wait` already
|
||||
does the polling, knows the real terminal states, and wakes only on
|
||||
completions.
|
||||
7. **Analyze, then iterate.** When a run finishes, **actually read its results**
|
||||
before deciding — `orx artifact <runId> EVAL.md`, `orx chart wandb …`,
|
||||
`orx query …`. Don't infer from status alone. Promising children become parents
|
||||
@@ -209,15 +213,24 @@ to act as soon as a run finishes. Two modes, picked by argument:
|
||||
```sh
|
||||
orx exp wait <expId> # level trigger: poll this experiment's latest run
|
||||
# until it reaches a terminal state (done/failed/cancelled)
|
||||
orx exp wait --project <projectId> # edge trigger: return on the FIRST change to ANY run
|
||||
# in the project (new run, or a status transition)
|
||||
orx exp wait --project <projectId> # edge trigger: return when the FIRST run in the
|
||||
# project COMPLETES (transitions into done/failed/cancelled)
|
||||
orx exp wait <expId> --interval 10 --timeout 3600 # tune polling
|
||||
```
|
||||
|
||||
- Pass **exactly one** of `<expId>` or `--project` (not both, not neither).
|
||||
- `--project` is the **budget-loop** primitive: it wakes only on a **completion**
|
||||
(a run reaching `done`/`failed`/`cancelled`) — i.e. a freed slot. Run *starts*,
|
||||
new queued runs, and `queued→running` transitions are intentionally ignored, so
|
||||
it won't wake you on non-events. **Prefer this over a custom poll loop over
|
||||
`orx runs`.**
|
||||
- Because it only fires on a *new* completion, call `--project` **while runs are
|
||||
in flight** (right after launching). If every run is already terminal when you
|
||||
call it, there's nothing left to complete and it blocks until `--timeout`.
|
||||
- `--interval` is seconds between polls (default `5`); `--timeout` gives up after
|
||||
N seconds (default `1800`) and exits **non-zero** so callers can branch on it.
|
||||
- Progress lines go to **stderr**; the final state line(s) go to **stdout**.
|
||||
- Progress lines go to **stderr**; the final completion line(s) go to **stdout**,
|
||||
each as `<runId> <prev> -> <status>` (or `<runId> <status> (new)`).
|
||||
|
||||
## Experiment description / notes — `orx exp desc`
|
||||
|
||||
|
||||
+27
-13
@@ -46,7 +46,7 @@ fn is_terminal(status: &str) -> bool {
|
||||
///
|
||||
/// Two modes, picked by argument:
|
||||
/// - `<expId>` — level trigger: poll the experiment's latest run until it reaches a terminal state (done/failed/cancelled).
|
||||
/// - `--project` — edge trigger: snapshot every run in the project and return on the first change of any kind (new run or status change).
|
||||
/// - `--project` — edge trigger: snapshot every run in the project and return when the first run *completes* — i.e. transitions into a terminal state (done/failed/cancelled). This is the "a slot just freed" signal a budget-saturation loop wants; run starts and queued→running transitions are intentionally ignored.
|
||||
///
|
||||
/// Polls every `--interval` seconds (default 5), gives up after `--timeout`
|
||||
/// seconds (default 1800) with a non-zero exit so callers can branch on it.
|
||||
@@ -103,8 +103,16 @@ async fn wait_experiment(
|
||||
}
|
||||
}
|
||||
|
||||
/// Edge trigger: return as soon as any run in the project changes vs. the
|
||||
/// snapshot taken on entry (new run id, or a status transition).
|
||||
/// Edge trigger: return as soon as any run in the project *completes* — i.e.
|
||||
/// transitions into a terminal state (done/failed/cancelled) vs. the snapshot
|
||||
/// taken on entry. Run starts, new queued runs, and queued→running transitions
|
||||
/// are ignored: the useful project-wide signal is "a slot just freed", so a
|
||||
/// budget-saturation loop can analyze the finished run and launch the next one.
|
||||
///
|
||||
/// Note: runs already terminal at entry don't count (they're in the snapshot as
|
||||
/// terminal). If *every* run is already terminal when this is called, there's
|
||||
/// nothing left to complete and it blocks until `--timeout`. Call it right after
|
||||
/// launching, while runs are in flight.
|
||||
async fn wait_project(
|
||||
creds: &crate::config::Credentials,
|
||||
project_id: &str,
|
||||
@@ -117,27 +125,33 @@ async fn wait_project(
|
||||
.into_iter()
|
||||
.map(|r| (r.id, r.status))
|
||||
.collect();
|
||||
let in_flight = snapshot.values().filter(|s| !is_terminal(s)).count();
|
||||
eprintln!(
|
||||
"Watching {} run(s) in project — returning on the first change…",
|
||||
snapshot.len()
|
||||
"Watching {} run(s) in project ({} in flight) — returning on the first completion…",
|
||||
snapshot.len(),
|
||||
in_flight
|
||||
);
|
||||
|
||||
loop {
|
||||
sleep_until_or_timeout(interval, deadline).await?;
|
||||
|
||||
let current = list_runs(creds, project_id).await?.runs;
|
||||
let mut changes: Vec<String> = Vec::new();
|
||||
let mut completed: Vec<String> = Vec::new();
|
||||
for r in ¤t {
|
||||
if !is_terminal(&r.status) {
|
||||
continue;
|
||||
}
|
||||
// Fire only on a *new* terminal: a run that was non-terminal before,
|
||||
// or a brand-new run that's already terminal. Skip runs that were
|
||||
// already terminal in the entry snapshot.
|
||||
match snapshot.get(&r.id) {
|
||||
None => changes.push(format!("{} {} (new)", r.id, r.status)),
|
||||
Some(prev) if prev != &r.status => {
|
||||
changes.push(format!("{} {} -> {}", r.id, prev, r.status))
|
||||
}
|
||||
Some(_) => {}
|
||||
Some(prev) if is_terminal(prev) => {}
|
||||
Some(prev) => completed.push(format!("{} {} -> {}", r.id, prev, r.status)),
|
||||
None => completed.push(format!("{} {} (new)", r.id, r.status)),
|
||||
}
|
||||
}
|
||||
if !changes.is_empty() {
|
||||
for c in &changes {
|
||||
if !completed.is_empty() {
|
||||
for c in &completed {
|
||||
println!("{}", c);
|
||||
}
|
||||
return Ok(());
|
||||
|
||||
Reference in New Issue
Block a user