Test speedwagon tool#48
Merged
ljhh-0611 merged 11 commits intorefactoring-applied-backed-v2from May 6, 2026
Merged
Conversation
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
Integrate base branch features (SQLite repository, per-session sandbox, SSE streaming, message persistence, session CRUD) with speedwagon RAG subagent, DashMap concurrency, and ApiError/ApiResult error patterns. Key integration points: - build_agent() combines builtin tools (bash/python/web_search), per-session sandbox, and speedwagon subagent - DashMap-based AppState with injected SharedStore/ToolSet - resolve_agent() lazy-creates agents with full history restoration - All tests updated for new AppState::new(repo, store, toolset) signature Co-Authored-By: Claude Opus 4.6 <[email protected]>
Co-authored-by: Copilot <[email protected]>
khj809
reviewed
Apr 29, 2026
khj809
reviewed
Apr 29, 2026
Contributor
|
@jhlee525 Please review my latest commit, which applies the changes in brekkylab/ailoy#391. I'm not sure if this follows your expected practices exactly. |
|
I think it align with current design! Thank you for changes. |
- Remove unused `into_runtime` and `into_runtime_with_provider` from SpeedwagonSpec (never called anywhere) - Migrate e2e_test.rs to use shared `common` module helpers instead of local `json_request` and `extract_assistant_text` duplicates - Fix `.sandbox()` → `.runenv()` in commented-out alternative build_agent Co-Authored-By: Claude Opus 4.6 <[email protected]>
khj809
approved these changes
May 6, 2026
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
POST /sessions/{id}/messagesendpoint that streams agent responses and returns collected messages as JSON (with 120s timeout)SharedStorefromMutextoRwLockfor concurrent read access in tool operations (search/find/read usestore.read().await)Arc<Mutex<AppState>>withArc<AppState>usingDashMapfor interior mutability — eliminates unnecessary outer locklib.rsto expose modules for integration test accessApiError/ApiResulttype aliases andAppError::internal()/not_found()helpersChanged Files
router.rsOnceLock), subagent session creationstate.rsHashMap→DashMap<Uuid, Arc<Mutex<Agent>>>, sync APImain.rsMutexonAppStateerror.rsApiError,ApiResulttypes + helper constructorsmodel/message.rsSendMessageRequest,SendMessageResponseDTOslib.rsCargo.tomldashmap,tokio/time, dev-deps for E2Estore/mod.rsSharedStore = Arc<RwLock<Store>>type aliastool/*.rs.lock().await→.read().awaitmain.rsSharedStorewithRwLock.env.example,.gitignore,Cargo.toml.speedwagon/, ailoy rev bumpDesign History
1. SharedStore:
Arc<Store>→Arc<RwLock<Store>>Why not just
Arc<Store>?build_toolset(store)creates tool closures that capture a clone of theArc. The sameArcis also held by router/test code that callsingest()/purge(). Here's where it breaks:Arc<T>can only provide&T(shared reference). There's no way to get&mut Tfrom it — that's by design, since multiple clones of the sameArccould exist. So any code path that needs mutation requires interior mutability.Why not
Arc<Mutex<Store>>?Mutex works, but every access — including concurrent tool reads — takes an exclusive lock. If agent A is searching while agent B tries to search, B blocks until A finishes. Tools are read-only (
search,find,readall take&self), so serializing them is unnecessary.Decision:
Arc<RwLock<Store>>— the standard reader-writer solution.Type alias
pub type SharedStore = Arc<RwLock<Store>>is exported from the speedwagon crate so backend-v2, CLI, and tests all share the same type.2. AppState: Remove Outer Mutex → DashMap Interior Mutability
Problem:
Arc<Mutex<AppState>>locks the entire state for every handler call. Two concurrent requests to different sessions serialize on the same Mutex.Decision: Replace
HashMapwithDashMap(sharded concurrent map).insert_agentandget_agentbecome&selfmethods — no.await, no outer lock. Main becomesArc::new(AppState::new()).3. Why
Arc<Mutex<Agent>>Inside DashMapThree layers, each handling a different concurrency level:
DashMapArcMutex<Agent>Agent::run(&mut self)requires exclusive access while the stream is aliveTwo messages to different sessions run fully in parallel. Two messages to the same session serialize at the Agent Mutex — correct behavior since agent maintains conversation state.
4. Why
lib.rsIs NeededRust integration tests (
tests/*.rs) are external to the crate. Withoutlib.rs, modules defined inmain.rsare invisible to tests. Addinglib.rswithpub mod router; pub mod state; ...creates a library crate that both the binary and tests can import asagent_k_backend::*.5. STORE / TOOLSET Singletons (OnceLock)
Problem: Creating a new Store per session means each agent gets a separate store — documents ingested in session A are invisible to session B.
Decision:
OnceLock<SharedStore>andOnceLock<ToolSet>— process-wide singletons, initialized lazily on first call. The ToolSet captures the Store via closures, so they must reference the same instance.CARGO_MANIFEST_DIR6. Agent Creation: Direct vs Subagent Pattern
Two patterns exist in
create_session— subagent is active, direct is commented out for easy switching.Direct:
Agent::try_with_tools(SpeedwagonSpec.into_spec(), provider, toolset)— agent owns search/find/read tools directly. SpeedwagonSpec's built-in SYSTEM_PROMPT instructs tool usage.Subagent:
AgentBuilder::new(model).subagent(card, sw_agent).build()— main agent has a single "speedwagon" tool that delegates to a separate agent. Allows main/sub to use different models.E2E Test Behavior Comparison
Subagent Failure → Fix Timeline
"Speedwagon agent") → main agent didn't recognize the tool's purpose"You MUST use the speedwagon tool..."→ still failed"Search the knowledge base for answers. This tool has access to uploaded documents..."→ improvedBoth patterns are intentionally kept in the code (active vs commented) because they have different trade-offs.
7. Why
send_messageAPI Was Addedcreate_sessiononly creates a session with an agent. Withoutsend_message, there's no way to talk to it. Current design is synchronous JSON (not SSE):final_contentfieldmessagesarrayE2E Test
test_ingest_message_purge_cycle(#[ignore], requiresOPENAI_API_KEY):cargo test -p agent-k-backend test_ingest_message_purge_cycle -- --ignored🤖 Generated with Claude Code