Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion explain/amp_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
3 changes: 3 additions & 0 deletions explain/assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
18 changes: 15 additions & 3 deletions explain/explain.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand All @@ -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,
),
)
Expand All @@ -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
Expand All @@ -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)
4 changes: 3 additions & 1 deletion explain/output_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
"""
Expand All @@ -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))


Expand Down
18 changes: 18 additions & 0 deletions explain/styles.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
27 changes: 27 additions & 0 deletions explain/styles.py
Original file line number Diff line number Diff line change
@@ -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)