stream: soul pass s2 fixes (thinking window, capped per repo, no proxy env, identical lines, forge read at gap marks)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-11-07 02:01:02 +00:00
commit 5defa18afa
3 changed files with 1614 additions and 1499 deletions

View file

@ -1,110 +1,109 @@
//! `mind-stream --instance <slug> [--once] [--dry-run]`: her stream of thought. //! `mind-stream --instance <slug> [--once] [--dry-run]`: her stream of thought.
//! //!
//! Without `--once` it runs forever: one `mind_body::stream::tick` per `POLL_S`, or per //! Without `--once` it runs forever: one `mind_body::stream::tick` per `STREAM_POLL_S`, or per
//! `STREAM_MIN_GAP_S` after a blank or a fault. `--dry-run` runs one tick that admits nothing and //! `STREAM_MIN_GAP_S` after a blank or a fault. `--dry-run` runs one tick that admits nothing and
//! prints the thought it would admit as JSON. A model or mind fault ends the tick, never the //! prints the thought it would admit as JSON. A model or mind fault ends the tick, never the
//! process: it is one line on stderr. This reads the process's arguments and environment, which //! process: it is one line on stderr. This reads the process's arguments and environment, which
//! the library never does. //! the library never does.
//! //!
//! Environment: `HUGINN_ENDPOINT` (`rudp://<ip>:<port>`, her own daemon), `EUREKA_INSTANCE` //! Environment: `HUGINN_ENDPOINT` (`rudp://<ip>:<port>`, her own daemon), `EUREKA_INSTANCE`
//! (when `--instance` is not given), `MIND_STREAM_ENDPOINT` (`http://<loopback>:<port>`, the //! (when `--instance` is not given), `MIND_STREAM_ENDPOINT` (`http://<loopback>:<port>`, the
//! model host), `MIND_STREAM_PROJECTOR_MODEL` and `MIND_STREAM_BASE_MODEL`. //! model host), `MIND_STREAM_PROJECTOR_MODEL` and `MIND_STREAM_BASE_MODEL`.
use std::net::SocketAddr; use std::net::SocketAddr;
use std::process::ExitCode; use std::process::ExitCode;
use std::time::Duration; use std::time::Duration;
use anyhow::{Result, anyhow, bail}; use anyhow::{Result, anyhow, bail};
use chrono::Utc; use chrono::Utc;
use eureka_pipeline::Slug; use eureka_pipeline::Slug;
use eureka_state::HuginnClient; use eureka_state::HuginnClient;
use mind_body::control::read_effective; use mind_body::control::read_effective;
use mind_body::queue::HuginnMind; use mind_body::queue::HuginnMind;
use mind_body::stream::{HuginnPersonaSource, Mode, OllamaStream, Organs, Outcome, STREAM_MIN_GAP_S, tick}; use mind_body::stream::{HuginnPersonaSource, Mode, OllamaStream, Organs, Outcome, STREAM_MIN_GAP_S, STREAM_POLL_S, tick};
const TIMEOUT: Duration = Duration::from_secs(30); const TIMEOUT: Duration = Duration::from_secs(30);
const POLL_S: u64 = 60;
#[allow(clippy::disallowed_methods, reason = "the binary reads its own environment, and hands it to the library")]
#[allow(clippy::disallowed_methods, reason = "the binary reads its own environment, and hands it to the library")] fn setting(name: &str) -> Option<String> {
fn setting(name: &str) -> Option<String> { std::env::var(name).ok().filter(|value| !value.is_empty())
std::env::var(name).ok().filter(|value| !value.is_empty()) }
}
fn required(name: &str) -> Result<String> {
fn required(name: &str) -> Result<String> { setting(name).ok_or_else(|| anyhow!("{name} is not set"))
setting(name).ok_or_else(|| anyhow!("{name} is not set")) }
}
struct Args {
struct Args { instance: Option<String>,
instance: Option<String>, once: bool,
once: bool, dry_run: bool,
dry_run: bool, }
}
fn arguments() -> Result<Args> {
fn arguments() -> Result<Args> { let mut args = Args { instance: None, once: false, dry_run: false };
let mut args = Args { instance: None, once: false, dry_run: false }; let mut given = std::env::args().skip(1);
let mut given = std::env::args().skip(1); while let Some(argument) = given.next() {
while let Some(argument) = given.next() { match argument.as_str() {
match argument.as_str() { "--instance" => args.instance = Some(given.next().ok_or_else(|| anyhow!("--instance needs a slug"))?),
"--instance" => args.instance = Some(given.next().ok_or_else(|| anyhow!("--instance needs a slug"))?), "--once" => args.once = true,
"--once" => args.once = true, "--dry-run" => args.dry_run = true,
"--dry-run" => args.dry_run = true, other => bail!("unknown argument {other}; usage: mind-stream --instance <slug> [--once] [--dry-run]"),
other => bail!("unknown argument {other}; usage: mind-stream --instance <slug> [--once] [--dry-run]"), }
} }
} Ok(args)
Ok(args) }
}
fn run() -> Result<()> {
fn run() -> Result<()> { let args = arguments()?;
let args = arguments()?; let instance = Slug(args.instance.map_or_else(|| required("EUREKA_INSTANCE"), Ok)?);
let instance = Slug(args.instance.map_or_else(|| required("EUREKA_INSTANCE"), Ok)?); let address: SocketAddr = required("HUGINN_ENDPOINT")?
let address: SocketAddr = required("HUGINN_ENDPOINT")? .strip_prefix("rudp://")
.strip_prefix("rudp://") .and_then(|address| address.parse().ok())
.and_then(|address| address.parse().ok()) .ok_or_else(|| anyhow!("HUGINN_ENDPOINT is not rudp://<ip>:<port>"))?;
.ok_or_else(|| anyhow!("HUGINN_ENDPOINT is not rudp://<ip>:<port>"))?; let projector_model = required("MIND_STREAM_PROJECTOR_MODEL")?;
let projector_model = required("MIND_STREAM_PROJECTOR_MODEL")?; let base_model = required("MIND_STREAM_BASE_MODEL")?;
let base_model = required("MIND_STREAM_BASE_MODEL")?; let model = OllamaStream::open(&required("MIND_STREAM_ENDPOINT")?, &projector_model, &base_model)?;
let model = OllamaStream::open(&required("MIND_STREAM_ENDPOINT")?, &projector_model, &base_model)?; let mind = HuginnMind::new(HuginnClient::new(address, instance.clone(), TIMEOUT));
let mind = HuginnMind::new(HuginnClient::new(address, instance.clone(), TIMEOUT)); let persona = HuginnPersonaSource::new(HuginnClient::new(address, instance.clone(), TIMEOUT));
let persona = HuginnPersonaSource::new(HuginnClient::new(address, instance.clone(), TIMEOUT)); let organs = Organs { mind: &mind, persona: &persona, model: &model, projector_model: &projector_model, base_model: &base_model };
let organs = Organs { mind: &mind, persona: &persona, model: &model, projector_model: &projector_model, base_model: &base_model }; let mode = if args.dry_run { Mode::DryRun } else { Mode::Live };
let mode = if args.dry_run { Mode::DryRun } else { Mode::Live }; let once = args.once || args.dry_run;
let once = args.once || args.dry_run; loop {
loop { let mut faulted = false;
let mut faulted = false; let waits = match tick(&organs, &|| read_effective(&instance), mode, Utc::now()) {
let waits = match tick(&organs, &|| read_effective(&instance), mode, Utc::now()) { Ok(Outcome::Thought { thought, admitted }) => {
Ok(Outcome::Thought { thought, admitted }) => { if admitted {
if admitted { println!("admitted {}:thought:{}", instance.0, thought.label.0);
println!("admitted {}:thought:{}", instance.0, thought.label.0); } else {
} else { println!("{}", serde_json::to_string_pretty(&*thought)?);
println!("{}", serde_json::to_string_pretty(&*thought)?); }
} false
false }
} Ok(Outcome::Blank) => true,
Ok(Outcome::Blank) => true, Ok(Outcome::Held(reason)) => {
Ok(Outcome::Held(reason)) => { eprintln!("mind-stream: held ({reason:?}); nothing ticked");
eprintln!("mind-stream: held ({reason:?}); nothing ticked"); false
false }
} Ok(Outcome::NotDue) => false,
Ok(Outcome::NotDue) => false, Err(fault) => {
Err(fault) => { eprintln!("mind-stream: {}", format!("{fault:#}").replace('\n', " "));
eprintln!("mind-stream: {}", format!("{fault:#}").replace('\n', " ")); faulted = true;
faulted = true; true
true }
} };
}; if once {
if once { return if faulted { Err(anyhow!("the tick faulted")) } else { Ok(()) };
return if faulted { Err(anyhow!("the tick faulted")) } else { Ok(()) }; }
} std::thread::sleep(Duration::from_secs(if waits { STREAM_MIN_GAP_S as u64 } else { STREAM_POLL_S as u64 }));
std::thread::sleep(Duration::from_secs(if waits { STREAM_MIN_GAP_S as u64 } else { POLL_S })); }
} }
}
fn main() -> ExitCode {
fn main() -> ExitCode { match run() {
match run() { Ok(()) => ExitCode::SUCCESS,
Ok(()) => ExitCode::SUCCESS, Err(fault) => {
Err(fault) => { eprintln!("mind-stream: {}", format!("{fault:#}").replace('\n', " "));
eprintln!("mind-stream: {}", format!("{fault:#}").replace('\n', " ")); ExitCode::FAILURE
ExitCode::FAILURE }
} }
} }
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff