-
Notifications
You must be signed in to change notification settings - Fork 328
feat(ctl): add Terminal-Bench chat runner #1175
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| //! Chat subcommands. | ||
|
|
||
| use { | ||
| crate::client::CtlClient, | ||
| serde_json::{Value, json}, | ||
| }; | ||
|
|
||
| pub async fn send(client: &mut CtlClient, text: &str, session_key: &str) -> anyhow::Result<Value> { | ||
| client | ||
| .call( | ||
| "chat.send", | ||
| json!({ | ||
| "text": text, | ||
| "_session_key": session_key, | ||
| }), | ||
| ) | ||
| .await | ||
| .map_err(Into::into) | ||
| } | ||
|
Comment on lines
+8
to
+19
|
||
|
|
||
| pub async fn history(client: &mut CtlClient, session_key: &str) -> anyhow::Result<Value> { | ||
| client | ||
| .call( | ||
| "chat.history", | ||
| json!({ | ||
| "_session_key": session_key, | ||
| }), | ||
| ) | ||
| .await | ||
| .map_err(Into::into) | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
|
|
||
| #[test] | ||
| fn send_params_preserve_terminal_bench_session() { | ||
| let params = json!({ | ||
| "text": "fix the task", | ||
| "_session_key": "terminal-bench:task-1", | ||
| }); | ||
|
|
||
| assert_eq!(params["text"], "fix the task"); | ||
| assert_eq!(params["_session_key"], "terminal-bench:task-1"); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| //! CLI subcommand modules. | ||
|
|
||
| pub mod chat; | ||
| pub mod config; | ||
| pub mod health; | ||
| pub mod mcp; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| # Running Moltis in Terminal-Bench / Harbor | ||
|
|
||
| `moltis-ctl chat` drives a task through the same authenticated WebSocket RPC and | ||
| agent loop as the web UI. The wrapper in this directory gives Harbor a simple | ||
| installed-agent entry point. | ||
|
|
||
| ## Build | ||
|
|
||
| ```sh | ||
| cargo build --release -p moltis-ctl | ||
| ``` | ||
|
|
||
| Make `target/release/moltis-ctl` available in the task container (or set | ||
| `MOLTIS_CTL_BIN` to its mounted path), then configure the installed-agent | ||
| command as: | ||
|
|
||
| ```sh | ||
| tools/terminal_bench/run_moltis_agent.sh | ||
| ``` | ||
|
|
||
| The wrapper accepts the task instruction as arguments, from stdin, or via | ||
| `HARBOR_TASK_INSTRUCTION`. | ||
|
|
||
| ## Required environment | ||
|
|
||
| - `MOLTIS_API_KEY`: gateway API key (required; do not commit it) | ||
| - `MOLTIS_GATEWAY_URL`: gateway URL, default | ||
| `http://host.docker.internal:13131` | ||
| - `MOLTIS_CTL_BIN`: `moltis-ctl` path, default `moltis-ctl` | ||
| - `MOLTIS_SESSION_KEY`: optional stable per-task session key; defaults to | ||
| `terminal-bench:${HARBOR_TASK_ID:-task}` | ||
|
|
||
| Each task must use a distinct session key so model/tool history cannot leak | ||
| between benchmark tasks. | ||
|
|
||
| ## Smoke test | ||
|
|
||
| A representative end-to-end task can be run without Harbor orchestration: | ||
|
|
||
| ```sh | ||
| MOLTIS_API_KEY='...' \ | ||
| MOLTIS_GATEWAY_URL='http://127.0.0.1:13131' \ | ||
| tools/terminal_bench/run_moltis_agent.sh \ | ||
| 'Create /tmp/terminal-bench-smoke.txt containing exactly moltis-ok' | ||
|
|
||
| test "$(cat /tmp/terminal-bench-smoke.txt)" = moltis-ok | ||
| ``` | ||
|
|
||
| The `chat` command does not return until the Moltis agent loop finishes. Its | ||
| JSON output includes the assistant text and token/round usage. Use | ||
| `moltis-ctl chat-history --session-key <key>` when debugging a failed run. |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,28 @@ | ||||||||||||||||||||||||||||
| #!/usr/bin/env bash | ||||||||||||||||||||||||||||
| set -euo pipefail | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| : "${MOLTIS_API_KEY:?MOLTIS_API_KEY must be set}" | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| MOLTIS_CTL_BIN="${MOLTIS_CTL_BIN:-moltis-ctl}" | ||||||||||||||||||||||||||||
| MOLTIS_GATEWAY_URL="${MOLTIS_GATEWAY_URL:-http://host.docker.internal:13131}" | ||||||||||||||||||||||||||||
| MOLTIS_SESSION_KEY="${MOLTIS_SESSION_KEY:-terminal-bench:${HARBOR_TASK_ID:-task}}" | ||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When two benchmark invocations omit both Knowledge Base Used: CLI and Ctl: |
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
Comment on lines
+6
to
+9
|
||||||||||||||||||||||||||||
| if (($#)); then | ||||||||||||||||||||||||||||
| instruction="$*" | ||||||||||||||||||||||||||||
| elif [[ -n "${HARBOR_TASK_INSTRUCTION:-}" ]]; then | ||||||||||||||||||||||||||||
| instruction="$HARBOR_TASK_INSTRUCTION" | ||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||
| instruction="$(cat)" | ||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if [[ -z "${instruction//[[:space:]]/}" ]]; then | ||||||||||||||||||||||||||||
| echo "terminal-bench: missing task instruction" >&2 | ||||||||||||||||||||||||||||
| exit 2 | ||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| exec "$MOLTIS_CTL_BIN" \ | ||||||||||||||||||||||||||||
| --gateway-url "$MOLTIS_GATEWAY_URL" \ | ||||||||||||||||||||||||||||
| --api-key "$MOLTIS_API_KEY" \ | ||||||||||||||||||||||||||||
| chat \ | ||||||||||||||||||||||||||||
|
Comment on lines
+23
to
+26
|
||||||||||||||||||||||||||||
| --session-key "$MOLTIS_SESSION_KEY" \ | ||||||||||||||||||||||||||||
| "$instruction" | ||||||||||||||||||||||||||||
|
Comment on lines
+23
to
+28
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
A non-empty task instruction beginning with a hyphen is passed without an option terminator, so clap interprets it as an option rather than the
Suggested change
Knowledge Base Used: CLI and Ctl: |
||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| #!/usr/bin/env bash | ||
| set -euo pipefail | ||
|
|
||
| script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||
| tmp="$(mktemp -d)" | ||
| trap 'rm -rf "$tmp"' EXIT | ||
|
|
||
| cat >"$tmp/moltis-ctl" <<'MOCK' | ||
| #!/usr/bin/env bash | ||
| printf '%s\n' "$@" >"${CAPTURE_FILE:?}" | ||
| MOCK | ||
| chmod +x "$tmp/moltis-ctl" | ||
|
|
||
| export CAPTURE_FILE="$tmp/args" | ||
| export MOLTIS_API_KEY="test-key" | ||
| export MOLTIS_CTL_BIN="$tmp/moltis-ctl" | ||
| export MOLTIS_GATEWAY_URL="http://gateway:13131" | ||
| export HARBOR_TASK_ID="representative-task" | ||
|
|
||
| "$script_dir/run_moltis_agent.sh" "write" "the file" | ||
|
|
||
| mapfile -t args <"$CAPTURE_FILE" | ||
| expected=( | ||
| --gateway-url http://gateway:13131 | ||
| --api-key test-key | ||
| chat | ||
| --session-key terminal-bench:representative-task | ||
| "write the file" | ||
| ) | ||
| [[ "${args[*]}" == "${expected[*]}" ]] | ||
|
|
||
| unset HARBOR_TASK_ID | ||
| export MOLTIS_SESSION_KEY="terminal-bench:stdin-task" | ||
| printf 'instruction from stdin\n' | "$script_dir/run_moltis_agent.sh" | ||
| mapfile -t args <"$CAPTURE_FILE" | ||
| [[ "${args[6]}" == "terminal-bench:stdin-task" ]] | ||
| [[ "${args[7]}" == "instruction from stdin" ]] | ||
|
|
||
| if MOLTIS_API_KEY='' "$script_dir/run_moltis_agent.sh" test 2>/dev/null; then | ||
| echo "expected missing API key to fail" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "terminal-bench wrapper tests passed" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a benchmark's model and tool loop takes longer than 120 seconds,
chat.sendremains subject toCtlClient::call's fixed deadline, causingmoltis-ctlto exit with a timeout before the documented wait-for-completion behavior finishes.Knowledge Base Used: CLI and Ctl:
moltisbinary andmoltis-ctlgateway control