652769c46c
CI / push-validation (pull_request) Successful in 31s
CI / helm (pull_request) Successful in 42s
CI / build (pull_request) Successful in 42s
CI / lint (pull_request) Successful in 1m6s
CI / quality (pull_request) Successful in 1m6s
CI / typecheck (pull_request) Successful in 1m12s
CI / security (pull_request) Successful in 1m25s
CI / integration_tests (pull_request) Successful in 3m17s
CI / unit_tests (pull_request) Successful in 4m37s
CI / docker (pull_request) Successful in 1m26s
CI / coverage (pull_request) Successful in 11m50s
CI / status-check (pull_request) Successful in 5s
Wires the TUI normal text input path to the LLM via A2aLocalFacade, closing the gap where typing a message produced no LLM call. All infrastructure (facade, SessionWorkflow, ProviderRegistry) already existed in the CLI; this PR adds the TUI wiring on top. Key changes: - _run_llm_dispatch(): module-level, Textual-free dispatch function covering all domain exception paths with user-friendly messages - _format_worker_outcome(): testable worker result/error translator - _create_tui_session(): DB-backed session with persona actor binding - _build_tui_facade(): wired A2aLocalFacade + SessionWorkflow singleton - run_worker(thread=True, exclusive=True): non-blocking, serialised - _dispatch_gen counter: prevents cancelled worker callbacks from corrupting the multi-turn transcript - SessionView.transcript: accumulated conversation history, pre-escaped - Markup escaping: _escape() applied before transcript storage - on_mount focus: prompt.focus() so keyboard input is received Tests: 18 BDD scenarios + 6 Robot Framework integration tests with FakeListLLM (no real API keys required). ISSUES CLOSED: #11230
92 lines
4.2 KiB
Gherkin
92 lines
4.2 KiB
Gherkin
Feature: TUI normal text input dispatches to LLM via A2A facade
|
|
As a user of the CleverAgents TUI
|
|
I want my normal text messages to be sent to an LLM actor
|
|
So that I can have a real conversation through the TUI interface
|
|
|
|
Background:
|
|
Given the TUI app is initialised with a mock A2A facade and session
|
|
|
|
Scenario: Normal text input dispatches to LLM and renders response
|
|
When I submit the text "hello there"
|
|
Then the facade should have received a message/send request
|
|
And the conversation should show the assistant response
|
|
|
|
Scenario: Dispatch passes active persona actor as override
|
|
When I dispatch "hello there" overriding actor with "openai/gpt-4o"
|
|
Then the facade should have received a message/send request with actor "openai/gpt-4o"
|
|
|
|
Scenario: Normal text with no facade falls back to preview mode
|
|
Given the TUI app is initialised without a facade
|
|
When I submit the text "hello there"
|
|
Then the conversation should show the preview text "hello there"
|
|
|
|
Scenario: LLM dispatch handles SessionActorNotConfiguredError gracefully
|
|
Given the facade raises SessionActorNotConfiguredError
|
|
When I submit the text "hello there"
|
|
Then the conversation should show the no-actor error message
|
|
|
|
Scenario: LLM dispatch handles generic facade error gracefully
|
|
Given the facade returns an A2A error response
|
|
When I submit the text "hello there"
|
|
Then the conversation should show an error message
|
|
|
|
Scenario: _create_tui_session returns real session_id from service
|
|
Given a mock session service that returns session_id "abc-123"
|
|
When I call _create_tui_session with that service
|
|
Then the returned session_id should be "abc-123"
|
|
|
|
Scenario: _create_tui_session falls back to default when service unavailable
|
|
Given the session service raises an exception
|
|
When I call _create_tui_session with that service
|
|
Then the returned session_id should be "default"
|
|
|
|
Scenario: LLM dispatch handles SessionNotFoundError gracefully
|
|
Given the facade raises SessionNotFoundError
|
|
When I submit the text "hello"
|
|
Then the conversation should show a session-not-found error message
|
|
|
|
Scenario: LLM dispatch handles DatabaseError gracefully
|
|
Given the facade raises DatabaseError
|
|
When I submit the text "hello"
|
|
Then the conversation should show a database error message
|
|
|
|
Scenario: Worker error is surfaced in the conversation widget
|
|
When the worker completes with error "network timeout"
|
|
Then the formatted outcome should show an error message
|
|
|
|
Scenario: Worker success result is passed through unchanged
|
|
When the worker completes with result "You: hi\n\nAssistant: hello"
|
|
Then the formatted outcome should be "You: hi\n\nAssistant: hello"
|
|
|
|
Scenario: Empty assistant response shows fallback text
|
|
Given the TUI app is initialised with a facade that returns empty response
|
|
When I submit the text "hello"
|
|
Then the conversation should show "(no response)"
|
|
|
|
Scenario: Rich markup in user input is escaped before storage in transcript
|
|
Given the TUI app is initialised with a mock A2A facade and session
|
|
When I submit the text "[bold]attack[/bold]"
|
|
Then the transcript entry should have markup escaped
|
|
|
|
Scenario: _format_worker_outcome re-raises KeyboardInterrupt
|
|
When the worker completes with a KeyboardInterrupt error
|
|
Then _format_worker_outcome should re-raise it
|
|
|
|
Scenario: _format_worker_outcome re-raises SystemExit
|
|
When the worker completes with a SystemExit error
|
|
Then _format_worker_outcome should re-raise it
|
|
|
|
Scenario: _format_worker_outcome returns None when both result and error are None
|
|
When the worker completes with neither result nor error
|
|
Then the formatted outcome should be None
|
|
|
|
Scenario: _build_tui_facade returns None when facade construction fails
|
|
When _build_tui_facade is called with a broken container
|
|
Then the returned facade should be None
|
|
|
|
Scenario: _create_tui_session binds active persona actor at session creation
|
|
Given a mock session service that returns session_id "abc-123"
|
|
And a persona state with active actor "anthropic/claude-4-sonnet"
|
|
When I call _create_tui_session with that service and persona state
|
|
Then the session should have been created with actor "anthropic/claude-4-sonnet"
|