feat(06-01): create /consensus command handler
- Add consensus_command handler with typing indicator
- Add _format_consensus helper for Markdown output
- Register CommandHandler("consensus") in __init__.py
- Displays existing consensus or generates new one
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
8242de5289
commit
ee9f8ca3a4
2 changed files with 126 additions and 1 deletions
|
|
@ -9,6 +9,7 @@ from telegram.ext import Application, CommandHandler, MessageHandler, filters
|
||||||
from moai.bot.handlers.commands import help_command, start_command
|
from moai.bot.handlers.commands import help_command, start_command
|
||||||
from moai.bot.handlers.discussion import (
|
from moai.bot.handlers.discussion import (
|
||||||
ask_command,
|
ask_command,
|
||||||
|
consensus_command,
|
||||||
discuss_command,
|
discuss_command,
|
||||||
mention_handler,
|
mention_handler,
|
||||||
next_command,
|
next_command,
|
||||||
|
|
@ -42,6 +43,7 @@ def register_handlers(app: Application) -> None:
|
||||||
app.add_handler(CommandHandler("discuss", discuss_command))
|
app.add_handler(CommandHandler("discuss", discuss_command))
|
||||||
app.add_handler(CommandHandler("next", next_command))
|
app.add_handler(CommandHandler("next", next_command))
|
||||||
app.add_handler(CommandHandler("stop", stop_command))
|
app.add_handler(CommandHandler("stop", stop_command))
|
||||||
|
app.add_handler(CommandHandler("consensus", consensus_command))
|
||||||
|
|
||||||
# @mention handler - MessageHandler registered AFTER CommandHandlers
|
# @mention handler - MessageHandler registered AFTER CommandHandlers
|
||||||
# Matches messages starting with @claude, @gpt, or @gemini followed by content
|
# Matches messages starting with @claude, @gpt, or @gemini followed by content
|
||||||
|
|
|
||||||
|
|
@ -6,15 +6,22 @@ from telegram.ext import ContextTypes
|
||||||
from moai.bot.handlers.projects import get_selected_project
|
from moai.bot.handlers.projects import get_selected_project
|
||||||
from moai.core.ai_client import MODEL_MAP, get_ai_client
|
from moai.core.ai_client import MODEL_MAP, get_ai_client
|
||||||
from moai.core.models import DiscussionType, RoundType
|
from moai.core.models import DiscussionType, RoundType
|
||||||
from moai.core.orchestrator import query_model_direct, query_models_parallel, run_discussion_round
|
from moai.core.orchestrator import (
|
||||||
|
generate_consensus,
|
||||||
|
query_model_direct,
|
||||||
|
query_models_parallel,
|
||||||
|
run_discussion_round,
|
||||||
|
)
|
||||||
from moai.core.services.discussion import (
|
from moai.core.services.discussion import (
|
||||||
complete_discussion,
|
complete_discussion,
|
||||||
create_discussion,
|
create_discussion,
|
||||||
create_message,
|
create_message,
|
||||||
create_round,
|
create_round,
|
||||||
get_active_discussion,
|
get_active_discussion,
|
||||||
|
get_consensus,
|
||||||
get_current_round,
|
get_current_round,
|
||||||
get_discussion,
|
get_discussion,
|
||||||
|
save_consensus,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -425,3 +432,119 @@ async def mention_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await update.message.reply_text(f"Error: {e}")
|
await update.message.reply_text(f"Error: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
async def consensus_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
|
"""Handle /consensus command - generate or display consensus summary.
|
||||||
|
|
||||||
|
Requires a selected project with an active discussion. If a consensus
|
||||||
|
already exists, displays it. Otherwise generates a new one using AI.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
/consensus
|
||||||
|
"""
|
||||||
|
# Require a selected project
|
||||||
|
project = await get_selected_project(context)
|
||||||
|
if project is None:
|
||||||
|
await update.message.reply_text("No project selected. Use /project select <name> first.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check for active discussion
|
||||||
|
discussion = await get_active_discussion(project.id)
|
||||||
|
if discussion is None:
|
||||||
|
await update.message.reply_text(
|
||||||
|
"No active discussion. Start one with /open <question> first."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check if consensus already exists
|
||||||
|
existing_consensus = await get_consensus(discussion.id)
|
||||||
|
if existing_consensus is not None:
|
||||||
|
# Display existing consensus
|
||||||
|
response_text = _format_consensus(
|
||||||
|
agreements=existing_consensus.agreements,
|
||||||
|
disagreements=existing_consensus.disagreements,
|
||||||
|
generated_by=existing_consensus.generated_by,
|
||||||
|
)
|
||||||
|
await update.message.reply_text(response_text, parse_mode="Markdown")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Show typing indicator while generating
|
||||||
|
await update.message.chat.send_action("typing")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Reload discussion with full context
|
||||||
|
discussion = await get_discussion(discussion.id)
|
||||||
|
|
||||||
|
# Generate consensus
|
||||||
|
consensus_model = "claude"
|
||||||
|
result = await generate_consensus(discussion, model=consensus_model)
|
||||||
|
|
||||||
|
# Check for errors
|
||||||
|
if "error" in result:
|
||||||
|
await update.message.reply_text(f"Failed to generate consensus: {result['error']}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Save consensus
|
||||||
|
await save_consensus(
|
||||||
|
discussion_id=discussion.id,
|
||||||
|
agreements=result["agreements"],
|
||||||
|
disagreements=result["disagreements"],
|
||||||
|
generated_by=consensus_model,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Format and display
|
||||||
|
response_text = _format_consensus(
|
||||||
|
agreements=result["agreements"],
|
||||||
|
disagreements=result["disagreements"],
|
||||||
|
generated_by=consensus_model,
|
||||||
|
)
|
||||||
|
await update.message.reply_text(response_text, parse_mode="Markdown")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
await update.message.reply_text(f"Error: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def _format_consensus(
|
||||||
|
agreements: list,
|
||||||
|
disagreements: list,
|
||||||
|
generated_by: str,
|
||||||
|
) -> str:
|
||||||
|
"""Format consensus data into a Markdown string.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
agreements: List of agreement strings.
|
||||||
|
disagreements: List of disagreement dicts with topic and positions.
|
||||||
|
generated_by: The model that generated the consensus.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Formatted Markdown string.
|
||||||
|
"""
|
||||||
|
lines = ["*Consensus Summary*\n"]
|
||||||
|
|
||||||
|
# Agreements section
|
||||||
|
lines.append("*Agreements:*")
|
||||||
|
if agreements:
|
||||||
|
for point in agreements:
|
||||||
|
lines.append(f"- {point}")
|
||||||
|
else:
|
||||||
|
lines.append("- None identified")
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
# Disagreements section
|
||||||
|
lines.append("*Disagreements:*")
|
||||||
|
if disagreements:
|
||||||
|
for disagreement in disagreements:
|
||||||
|
topic = disagreement.get("topic", "Unknown topic")
|
||||||
|
positions = disagreement.get("positions", {})
|
||||||
|
lines.append(f"- *{topic}*")
|
||||||
|
for model, position in positions.items():
|
||||||
|
lines.append(f" - {model.title()}: {position}")
|
||||||
|
else:
|
||||||
|
lines.append("- None identified")
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
# Footer
|
||||||
|
lines.append(f"_Generated by {generated_by.title()}_")
|
||||||
|
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue