mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
* feat(tokenjuice): implement core functionality for terminal-output compaction engine - Introduced the `tokenjuice` module, which includes the classification and reduction of tool outputs based on JSON-configured rules. - Added new dependencies for Unicode handling: `unicode-segmentation` and `unicode-width`. - Implemented the `classify` module to match tool execution inputs against predefined rules, enhancing the ability to process and summarize terminal outputs. - Created a comprehensive set of types and utilities for managing tool execution inputs and classification results. - Established a built-in rule set for common tools, improving the initial setup and usability of the `tokenjuice` engine. - Enhanced testing framework with integration tests to ensure the accuracy of output compaction and classification. These changes lay the groundwork for a robust terminal-output management system, facilitating better interaction with various tools and improving overall user experience. * feat(tokenjuice): implement tokenjuice module for terminal output compaction - Introduced the `tokenjuice` module, which includes functionality for classifying and reducing terminal output based on JSON-configured rules. - Added new dependencies: `unicode-segmentation` and `unicode-width` to support text processing. - Created a new `classify.rs` file for rule classification logic, including matching helpers and scoring functions. - Implemented a `reduce.rs` file to handle the main reduction pipeline and text normalization. - Established a structured approach for loading and compiling rules from multiple sources, including built-in and user-defined rules. - Added integration tests to ensure the correctness of the output reduction process. These changes enhance the application's ability to manage and compact verbose tool outputs, improving overall efficiency and user experience. * test(tokenjuice): enhance test coverage for classification and reduction logic - Added a series of unit tests to `classify.rs` to validate the behavior of tool name filters and argument matching, ensuring correct classification of tool executions. - Introduced tests for edge cases in `reduce.rs`, including command tokenization and normalization of execution inputs, to improve robustness against various input formats. - Expanded tests in `builtin.rs` to cover duplicate ID reporting and compile issues, enhancing error handling and reporting mechanisms. - Implemented additional tests in `compiler.rs` to verify regex handling in rule definitions, ensuring invalid patterns are correctly ignored. These enhancements improve the overall test coverage and reliability of the tokenjuice module, facilitating better maintenance and future development. * test(tokenjuice): add edge-case tests for gh table and reduction pipeline * style(tokenjuice): apply cargo fmt * feat(tokenjuice): wire into agent tool loop output compaction Add `tokenjuice::compact_tool_output` helper and call it in the agent tool loop after credential scrubbing (and on error paths with exit=1), before any optional payload_summarizer. Derives argv/command heuristically from JSON tool arguments (command / args / argv / cmd shapes) so shell-wrapping tools still match upstream family rules (git/*, package/*, tests/*, etc.). Pass-through safe: outputs under 512 bytes or where compaction saves <5% are returned untouched. * fix(tokenjuice): address coderabbit review comments - classify: derive command from argv join when input.command is unset, so commandIncludes* rules still match argv-only callers - rules/loader: log read_dir / file_type / read_to_string failures at debug level so permission or filesystem issues are observable rather than silently skipped - text/ansi: add trace log at strip_ansi entry/exit with lengths (no text content) per the project debug-logging rules - tests: remove orphan src/openhuman/tokenjuice/tests/integration.rs which was never wired into any module declaration; the real fixture- parity runner lives at tests/tokenjuice_integration.rs and asserts hard when the fixtures directory is missing Vendored-rule issues (docker-ps / kubectl-describe / git/branch / grep casing / counter-pattern overbreadth / etc.) come from upstream and are left as-is; this module is a straight port of the upstream rule set and should not fork from it in v1.
90 lines
2.9 KiB
Rust
90 lines
2.9 KiB
Rust
//! Integration tests for the TokenJuice module.
|
|
//!
|
|
//! Iterates vendored `*.fixture.json` files under
|
|
//! `src/openhuman/tokenjuice/tests/fixtures/` and asserts that
|
|
//! `reduce_execution_with_rules` produces the expected output.
|
|
|
|
use openhuman_core::openhuman::tokenjuice::{
|
|
reduce::reduce_execution_with_rules, rules::load_builtin_rules, types::RuleFixture,
|
|
};
|
|
|
|
/// Fixture names that are known to produce different output from the upstream
|
|
/// TypeScript — typically due to `Intl.Segmenter` vs `unicode-segmentation`
|
|
/// grapheme-boundary differences. See `KNOWN_DRIFT.md` for rationale.
|
|
const KNOWN_DRIFT_FIXTURES: &[&str] = &[
|
|
// None currently.
|
|
];
|
|
|
|
fn fixtures_dir() -> std::path::PathBuf {
|
|
let manifest = std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set");
|
|
std::path::PathBuf::from(manifest).join("src/openhuman/tokenjuice/tests/fixtures")
|
|
}
|
|
|
|
#[test]
|
|
fn vendored_fixtures_match_expected_output() {
|
|
let dir = fixtures_dir();
|
|
assert!(
|
|
dir.is_dir(),
|
|
"fixtures directory not found: {}",
|
|
dir.display()
|
|
);
|
|
|
|
let rules = load_builtin_rules();
|
|
let mut entries: Vec<_> = std::fs::read_dir(&dir)
|
|
.expect("read fixtures dir")
|
|
.filter_map(|e| e.ok())
|
|
.filter(|e| e.file_name().to_string_lossy().ends_with(".fixture.json"))
|
|
.collect();
|
|
entries.sort_by_key(|e| e.file_name());
|
|
|
|
let mut passed = 0usize;
|
|
let mut skipped = 0usize;
|
|
let mut failures: Vec<String> = Vec::new();
|
|
|
|
for entry in &entries {
|
|
let path = entry.path();
|
|
let name = path.file_name().unwrap().to_string_lossy().to_string();
|
|
|
|
if KNOWN_DRIFT_FIXTURES.iter().any(|&s| s == name) {
|
|
eprintln!("[SKIP] {} (known drift)", name);
|
|
skipped += 1;
|
|
continue;
|
|
}
|
|
|
|
let json = std::fs::read_to_string(&path).expect("read fixture file");
|
|
let fixture: RuleFixture = serde_json::from_str(&json)
|
|
.unwrap_or_else(|e| panic!("JSON parse error in {}: {}", name, e));
|
|
|
|
let opts = fixture.options.clone().unwrap_or_default();
|
|
let result = reduce_execution_with_rules(fixture.input.clone(), &rules, &opts);
|
|
|
|
if result.inline_text.trim() == fixture.expected_output.trim() {
|
|
passed += 1;
|
|
} else {
|
|
let msg = format!(
|
|
"[FAIL] {}\n desc: {}\n expected: {:?}\n actual: {:?}",
|
|
name,
|
|
fixture.description.as_deref().unwrap_or("(none)"),
|
|
fixture.expected_output.trim(),
|
|
result.inline_text.trim()
|
|
);
|
|
eprintln!("{}", msg);
|
|
failures.push(name);
|
|
}
|
|
}
|
|
|
|
eprintln!(
|
|
"\ntokenjuice integration: {} passed, {} skipped, {} failed",
|
|
passed,
|
|
skipped,
|
|
failures.len()
|
|
);
|
|
|
|
assert!(
|
|
failures.is_empty(),
|
|
"{} fixture(s) failed: {}",
|
|
failures.len(),
|
|
failures.join(", ")
|
|
);
|
|
}
|