Skip to content

Commit 50dc2b2

Browse files
committed
ci: unblock task branch checks
Fix the local base-branch CI blockers shared by PR #953/#954: - apply rustfmt to pre-existing terraphim_rlm formatting drift - replace Rust 1.91-only str::floor_char_boundary with MSRV-safe UTF-8 boundary logic for Rust 1.80 - harden Firecracker VM create diagnostics and error handling - install cargo-nextest via the existing upstream installer pattern - run cargo nextest directly so filter expression quoting survives rch Verification: - cargo fmt -- --check - cargo test -p terraphim_rlm query_loop::tests::test_truncate -- --nocapture - cargo clippy -p terraphim_rlm -- -D warnings - Firecracker workflow YAML parsed and shell path simulated - independent reviews passed
1 parent 018d186 commit 50dc2b2

6 files changed

Lines changed: 95 additions & 40 deletions

File tree

.github/workflows/ci-firecracker.yml

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,30 @@ jobs:
4242
- name: Create VM
4343
id: vm
4444
run: |
45-
RESPONSE=$(curl -sf -X POST $FCCTL_URL/api/vms \
45+
RESPONSE_FILE=$(mktemp)
46+
trap 'rm -f "${RESPONSE_FILE}"' EXIT
47+
if HTTP_STATUS=$(curl -sS -o "$RESPONSE_FILE" -w "%{http_code}" -X POST "$FCCTL_URL/api/vms" \
4648
-H 'Content-Type: application/json' \
47-
-d "{\"vm_type\": \"$VM_TYPE\"}")
49+
-d "{\"vm_type\": \"$VM_TYPE\"}"); then
50+
:
51+
else
52+
CURL_EXIT=$?
53+
RESPONSE=$(cat "$RESPONSE_FILE")
54+
echo "fcctl create transport failed: curl exit $CURL_EXIT"
55+
if [ -n "$RESPONSE" ]; then
56+
echo "$RESPONSE"
57+
fi
58+
exit 1
59+
fi
60+
RESPONSE=$(cat "$RESPONSE_FILE")
61+
rm -f "$RESPONSE_FILE"
62+
trap - EXIT
63+
echo "fcctl create status: $HTTP_STATUS"
4864
echo "$RESPONSE"
65+
if [ "$HTTP_STATUS" -lt 200 ] || [ "$HTTP_STATUS" -ge 300 ]; then
66+
echo "ERROR: fcctl-web VM create failed with HTTP $HTTP_STATUS"
67+
exit 1
68+
fi
4969
VM_ID=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
5070
if [ -z "$VM_ID" ] || [ "$VM_ID" = "null" ]; then
5171
echo "ERROR: failed to parse vm id from response"
@@ -144,8 +164,8 @@ jobs:
144164
# bigbox; see .cargo/config.toml for the Darwin `dynamic_lookup`
145165
# linker workaround on local Mac dev.
146166
#
147-
# All cargo invocations are dispatched via `rch exec --` so they
148-
# share rchd's queue + slot accounting with ADF agents (see
167+
# Build-oriented cargo invocations are dispatched via `rch exec --` so
168+
# they share rchd's queue + slot accounting with ADF agents (see
149169
# .docs/adr-rch-build-queue-not-firecracker-ci.md). Fail-open: if
150170
# rchd is down or no slot is available, rch falls through to local
151171
# cargo with no behaviour change.
@@ -161,15 +181,19 @@ jobs:
161181
- name: Install cargo-nextest
162182
run: |
163183
if ! command -v cargo-nextest >/dev/null 2>&1; then
164-
cargo install cargo-nextest --locked
184+
curl -LsSf https://get.nexte.st/latest/linux | tar zxf - -C "${CARGO_HOME:-$HOME/.cargo}/bin"
165185
fi
166186
cargo nextest --version
167187
168188
- name: cargo nextest run --workspace
169189
# Only test_chat_command is skipped: it requires LLM API credentials
170190
# not present in CI. All other failures must be fixed at the source,
171191
# not skipped. nextest uses filter expressions instead of --skip.
172-
run: /home/alex/.local/bin/rch exec -- cargo nextest run --workspace --profile ci -E 'not test(test_chat_command)'
192+
# Do not wrap this invocation in `rch exec`: rch's non-compilation
193+
# command path loses shell quoting around the filter expression and
194+
# makes `/bin/sh` parse the parentheses in `test(...)`.
195+
# `RUSTC_WRAPPER=sccache` above still applies to rustc invocations.
196+
run: cargo nextest run --workspace --profile ci -E 'not test(test_chat_command)'
173197

174198
- name: sccache stats
175199
if: always()

crates/terraphim_rlm/src/main.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ async fn run(cli: Cli) -> Result<CliResponse, Box<dyn std::error::Error>> {
4848
#[cfg(feature = "llm")]
4949
{
5050
if let Err(e) = rlm.auto_configure_llm().await {
51-
log::warn!("LLM auto-configuration failed: {}. rlm_query will be unavailable.", e);
51+
log::warn!(
52+
"LLM auto-configuration failed: {}. rlm_query will be unavailable.",
53+
e
54+
);
5255
}
5356
}
5457

crates/terraphim_rlm/src/query_loop.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,12 @@ fn truncate(s: &str, max_len: usize) -> String {
690690
if s.len() <= max_len {
691691
s.to_string()
692692
} else {
693-
let boundary = s.floor_char_boundary(max_len);
693+
let boundary = s
694+
.char_indices()
695+
.map(|(idx, _)| idx)
696+
.take_while(|idx| *idx <= max_len)
697+
.last()
698+
.unwrap_or(0);
694699
format!("{}...", &s[..boundary])
695700
}
696701
}

crates/terraphim_rlm/src/rlm.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -910,8 +910,7 @@ impl TerraphimRlm {
910910
"llm_provider".to_string(),
911911
serde_json::Value::String("ollama".to_string()),
912912
);
913-
let ollama_model = std::env::var("RLM_MODEL")
914-
.unwrap_or_else(|_| "gemma3:270m".to_string());
913+
let ollama_model = std::env::var("RLM_MODEL").unwrap_or_else(|_| "gemma3:270m".to_string());
915914
role.extra.insert(
916915
"llm_model".to_string(),
917916
serde_json::Value::String(ollama_model.clone()),
@@ -929,7 +928,9 @@ impl TerraphimRlm {
929928
])
930929
.output()
931930
.ok()?;
932-
String::from_utf8(output.stdout).ok().map(|s| s.trim().to_string())
931+
String::from_utf8(output.stdout)
932+
.ok()
933+
.map(|s| s.trim().to_string())
933934
});
934935

935936
if let Some(ref key) = or_api_key {
@@ -938,7 +939,9 @@ impl TerraphimRlm {
938939
role.llm_api_key = Some(key.clone());
939940
role.llm_model = Some(or_model.clone());
940941
// Cache for child processes and build_llm_from_role
941-
unsafe { std::env::set_var("OPENROUTER_API_KEY", key); }
942+
unsafe {
943+
std::env::set_var("OPENROUTER_API_KEY", key);
944+
}
942945
log::info!("RLM auto-configure: openrouter model={}", or_model);
943946
}
944947

@@ -958,11 +961,10 @@ impl TerraphimRlm {
958961
..Default::default()
959962
});
960963

961-
let client = terraphim_service::llm::build_llm_from_role(&role)
962-
.ok_or_else(|| {
963-
log::warn!("RLM auto-configure: no LLM provider available");
964-
RlmError::LlmNotConfigured
965-
})?;
964+
let client = terraphim_service::llm::build_llm_from_role(&role).ok_or_else(|| {
965+
log::warn!("RLM auto-configure: no LLM provider available");
966+
RlmError::LlmNotConfigured
967+
})?;
966968

967969
log::info!(
968970
"RLM LLM bridge configured: providers={} strategy={:?}",

crates/terraphim_rlm/tests/backend_demo.rs

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
//! Demonstration: RLM running locally (LocalExecutor) and via Docker (DockerExecutor).
22
//! Run: cargo test -p terraphim_rlm --test backend_demo -- --nocapture
33
4-
use terraphim_rlm::config::{BackendType, RlmConfig};
54
use terraphim_rlm::TerraphimRlm;
5+
use terraphim_rlm::config::{BackendType, RlmConfig};
66

77
#[tokio::test]
88
async fn demo_local_executor() {
@@ -19,11 +19,22 @@ async fn demo_local_executor() {
1919

2020
// Python
2121
let r = rlm.execute_code(&session.id, "print(2+2)").await.unwrap();
22-
println!(" [Python] 2+2 = {} (exit {})", r.stdout.trim(), r.exit_code);
22+
println!(
23+
" [Python] 2+2 = {} (exit {})",
24+
r.stdout.trim(),
25+
r.exit_code
26+
);
2327

2428
// Bash
25-
let r = rlm.execute_command(&session.id, "echo hello-from-local").await.unwrap();
26-
println!(" [Bash] echo = {} (exit {})", r.stdout.trim(), r.exit_code);
29+
let r = rlm
30+
.execute_command(&session.id, "echo hello-from-local")
31+
.await
32+
.unwrap();
33+
println!(
34+
" [Bash] echo = {} (exit {})",
35+
r.stdout.trim(),
36+
r.exit_code
37+
);
2738

2839
// Show backend type
2940
let status = rlm.get_session_status(&session.id, false).await.unwrap();
@@ -55,11 +66,22 @@ async fn demo_docker_executor() {
5566

5667
// Python
5768
let r = rlm.execute_code(&session.id, "print(2+2)").await.unwrap();
58-
println!(" [Python] 2+2 = {} (exit {})", r.stdout.trim(), r.exit_code);
69+
println!(
70+
" [Python] 2+2 = {} (exit {})",
71+
r.stdout.trim(),
72+
r.exit_code
73+
);
5974

6075
// Bash
61-
let r = rlm.execute_command(&session.id, "echo hello-from-docker").await.unwrap();
62-
println!(" [Bash] echo = {} (exit {})", r.stdout.trim(), r.exit_code);
76+
let r = rlm
77+
.execute_command(&session.id, "echo hello-from-docker")
78+
.await
79+
.unwrap();
80+
println!(
81+
" [Bash] echo = {} (exit {})",
82+
r.stdout.trim(),
83+
r.exit_code
84+
);
6385

6486
// Show backend type
6587
let status = rlm.get_session_status(&session.id, false).await.unwrap();
@@ -73,11 +95,17 @@ async fn demo_docker_executor() {
7395
println!(" [Container hostname] {}", r.stdout.trim());
7496

7597
// Show Python version inside container
76-
let r = rlm.execute_code(&session.id, "import sys; print(sys.version)").await.unwrap();
98+
let r = rlm
99+
.execute_code(&session.id, "import sys; print(sys.version)")
100+
.await
101+
.unwrap();
77102
println!(" [Python version] {}", r.stdout.trim());
78103

79104
// Show container filesystem
80-
let r = rlm.execute_command(&session.id, "ls / | head -5").await.unwrap();
105+
let r = rlm
106+
.execute_command(&session.id, "ls / | head -5")
107+
.await
108+
.unwrap();
81109
println!(" [Container root]\n{}", r.stdout);
82110

83111
rlm.destroy_session(&session.id).await.unwrap();

crates/terraphim_rlm/tests/skills_demo.rs

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,17 @@ use terraphim_rlm::{RlmConfig, TerraphimRlm};
66
#[tokio::test]
77
async fn demo_all_skills() {
88
let config = RlmConfig::minimal();
9-
let rlm = TerraphimRlm::with_executor(
10-
config,
11-
terraphim_rlm::LocalExecutor::new(),
12-
)
13-
.unwrap();
9+
let rlm = TerraphimRlm::with_executor(config, terraphim_rlm::LocalExecutor::new()).unwrap();
1410

1511
// 1. Session create
1612
let session = rlm.create_session().await.unwrap();
17-
println!("[session create] id={} state={:?}", session.id, session.state);
13+
println!(
14+
"[session create] id={} state={:?}",
15+
session.id, session.state
16+
);
1817

1918
// 2. Code execution
20-
let result = rlm
21-
.execute_code(&session.id, "print(2+2)")
22-
.await
23-
.unwrap();
19+
let result = rlm.execute_code(&session.id, "print(2+2)").await.unwrap();
2420
println!(
2521
"[code: 2+2] exit={} stdout={:?}",
2622
result.exit_code, result.stdout
@@ -64,10 +60,7 @@ async fn demo_all_skills() {
6460
assert_eq!(val, None);
6561

6662
// 8. Status
67-
let status = rlm
68-
.get_session_status(&session.id, false)
69-
.await
70-
.unwrap();
63+
let status = rlm.get_session_status(&session.id, false).await.unwrap();
7164
println!(
7265
"[status] backend={:?} snapshots={}",
7366
status.backend_type, status.snapshot_count

0 commit comments

Comments
 (0)