From 02e60f7e6e25043fb25b912835ca870311bed5b4 Mon Sep 17 00:00:00 2001 From: Mark Williamson Date: Fri, 8 Aug 2025 17:31:39 +0100 Subject: [PATCH 1/2] Explain: Improve error reporting in Amp backend. --- explain/amp_agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/explain/amp_agent.py b/explain/amp_agent.py index d60cffc..75fc97e 100644 --- a/explain/amp_agent.py +++ b/explain/amp_agent.py @@ -126,7 +126,7 @@ async def ask(self, question: str, port: int, tools: list[str]) -> str: assert amp_start_thread.stdout and amp_start_thread.stderr stdout_bytes, stderr_bytes = await amp_start_thread.communicate() - assert not stderr_bytes + assert not stderr_bytes, stderr_bytes self._thread_id = stdout_bytes.decode("utf-8").rstrip() From 09e8ec3d498a8f44bd5e9aa610788719d14b670f Mon Sep 17 00:00:00 2001 From: Mark Williamson Date: Fri, 8 Aug 2025 18:11:40 +0100 Subject: [PATCH 2/2] Explain: Add some Genuine People Personalities. --- explain/assets.py | 3 +++ explain/explain.py | 18 +++++++++++++++--- explain/output_utils.py | 4 +++- explain/styles.json | 18 ++++++++++++++++++ explain/styles.py | 27 +++++++++++++++++++++++++++ 5 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 explain/styles.json create mode 100644 explain/styles.py diff --git a/explain/assets.py b/explain/assets.py index d8d99ea..aa785cd 100644 --- a/explain/assets.py +++ b/explain/assets.py @@ -21,3 +21,6 @@ CODEX_PROMPT = (_EXTENSION_PATH / "codex_prompt.md").read_text(encoding="UTF-8") """Codex-specific prompt to encourage thorough investigation.""" + +STYLE_DEFINITIONS = (_EXTENSION_PATH / "styles.json").read_text(encoding="UTF-8") +"""Definitions of explain output styles.""" diff --git a/explain/explain.py b/explain/explain.py index 4f4c2b2..ad8b969 100644 --- a/explain/explain.py +++ b/explain/explain.py @@ -29,6 +29,7 @@ from src.udbpy.gdb_extensions import command, command_args, gdbio, gdbutils, udb_base, udb_last # Agent modules are imported to trigger registration. +from . import styles from .agents import AgentRegistry, BaseAgent from .amp_agent import AmpAgent # pylint: disable=unused-import from .assets import MCP_INSTRUCTIONS, SYSTEM_PROMPT, THINKING_MSGS @@ -585,9 +586,6 @@ async def explain_query(agent: BaseAgent, gateway: UdbMcpGateway, why: str) -> s server = uvicorn.Server(config) mcp_task = asyncio.create_task(server.serve(sockets=[sock])) - console_whizz(f" * {random.choice(THINKING_MSGS)}...") - print_agent(agent.display_name, agent.agent_bin) - explanation = await agent.ask(why, port, tools=gateway.tools) finally: @@ -611,6 +609,11 @@ async def explain_query(agent: BaseAgent, gateway: UdbMcpGateway, why: str) -> s short="a", value=command_args.Choice(AgentRegistry.available_agents(), optional=True), ), + command_args.Option( + long="style", + short="s", + value=command_args.Choice(styles.names(), optional=True), + ), allow_remainders=True, ), ) @@ -637,6 +640,12 @@ def explain(udb: udb_base.Udb, args: Any) -> None: why += ui.get_user_input(prompt="> ") + "\n" print() + explain_style_description = "" + if args.style: + explain_style = styles.get(args.style) + why += f"\nExplain in the style of {explain_style.prompt}" + explain_style_description = explain_style.description + gateway = UdbMcpGateway(udb) global event_loop @@ -650,6 +659,9 @@ def explain(udb: udb_base.Udb, args: Any) -> None: gdbutils.breakpoints_suspended(), unittest.mock.patch.object(udb, "_volatile_mode_explained", True), ): + console_whizz(f" * {random.choice(THINKING_MSGS)}...") + print_agent(agent.display_name, agent.agent_bin, explain_style_description) + explanation = event_loop.run_until_complete(explain_query(agent, gateway, why)) print_explanation(explanation) diff --git a/explain/output_utils.py b/explain/output_utils.py index 05e16b4..020eddb 100644 --- a/explain/output_utils.py +++ b/explain/output_utils.py @@ -125,7 +125,7 @@ def console_whizz(msg: str, end: str = "\n") -> None: print(end=end) -def print_agent(display_name: str, agent_bin: Path) -> None: +def print_agent(display_name: str, agent_bin: Path, style: str | None) -> None: """ Print agent details at startup. """ @@ -134,6 +134,8 @@ def print_agent(display_name: str, agent_bin: Path) -> None: table.add_column("Value") table.add_row("AI Agent:", display_name) table.add_row("Agent Path:", str(agent_bin)) + if style: + table.add_row("Style:", style) console.print(ExplainPanel(table)) diff --git a/explain/styles.json b/explain/styles.json new file mode 100644 index 0000000..1888975 --- /dev/null +++ b/explain/styles.json @@ -0,0 +1,18 @@ +{ + "human": { + "description": "Definitely a human.", + "prompt": "an alien or robot that is posing as a human and wants me to understand that they are definitely human." + }, + "vizier": { + "description": "Your loyal Grand Vizier.", + "prompt": "a traitorous Grand Vizier who is secretly plotting to replace me." + }, + "pirate": { + "description": "Arrrrrr the C!", + "prompt": "a jolly pirate" + }, + "detective": { + "description": "Private Detective", + "prompt": "a hard drinking, no-nonsense private detective from 1930s LA" + } +} diff --git a/explain/styles.py b/explain/styles.py new file mode 100644 index 0000000..cccb892 --- /dev/null +++ b/explain/styles.py @@ -0,0 +1,27 @@ +""" +Genuine People Personalities for the "explain" command. +""" + +import pydantic + +from . import assets + + +class Style(pydantic.BaseModel): + description: str + prompt: str + + +class _StyleDatabase(pydantic.RootModel[dict[str, Style]]): + ... + + +STYLES = _StyleDatabase.model_validate_json(assets.STYLE_DEFINITIONS) + + +def names() -> list[str]: + return list(STYLES.root.keys()) + + +def get(name: str) -> Style | None: + return STYLES.root.get(name)